aboutsummaryrefslogtreecommitdiff
path: root/libphobos/src/std
diff options
context:
space:
mode:
authorIain Buclaw <ibuclaw@gcc.gnu.org>2018-10-28 19:51:47 +0000
committerIain Buclaw <ibuclaw@gcc.gnu.org>2018-10-28 19:51:47 +0000
commitb4c522fabd0df7be08882d2207df8b2765026110 (patch)
treeb5ffc312b0a441c1ba24323152aec463fdbe5e9f /libphobos/src/std
parent01ce9e31a02c8039d88e90f983735104417bf034 (diff)
downloadgcc-b4c522fabd0df7be08882d2207df8b2765026110.zip
gcc-b4c522fabd0df7be08882d2207df8b2765026110.tar.gz
gcc-b4c522fabd0df7be08882d2207df8b2765026110.tar.bz2
Add D front-end, libphobos library, and D2 testsuite.
ChangeLog: * Makefile.def (target_modules): Add libphobos. (flags_to_pass): Add GDC, GDCFLAGS, GDC_FOR_TARGET and GDCFLAGS_FOR_TARGET. (dependencies): Make libphobos depend on libatomic, libbacktrace configure, and zlib configure. (language): Add language d. * Makefile.in: Rebuild. * Makefile.tpl (BUILD_EXPORTS): Add GDC and GDCFLAGS. (HOST_EXPORTS): Add GDC. (POSTSTAGE1_HOST_EXPORTS): Add GDC and GDC_FOR_BUILD. (BASE_TARGET_EXPORTS): Add GDC. (GDC_FOR_BUILD, GDC, GDCFLAGS): New variables. (GDC_FOR_TARGET, GDC_FLAGS_FOR_TARGET): New variables. (EXTRA_HOST_FLAGS): Add GDC. (STAGE1_FLAGS_TO_PASS): Add GDC. (EXTRA_TARGET_FLAGS): Add GDC and GDCFLAGS. * config-ml.in: Treat GDC and GDCFLAGS like other compiler/flag environment variables. * configure: Rebuild. * configure.ac: Add target-libphobos to target_libraries. Set and substitute GDC_FOR_BUILD and GDC_FOR_TARGET. config/ChangeLog: * multi.m4: Set GDC. gcc/ChangeLog: * Makefile.in (tm_d_file_list, tm_d_include_list): New variables. (TM_D_H, D_TARGET_DEF, D_TARGET_H, D_TARGET_OBJS): New variables. (tm_d.h, cs-tm_d.h, default-d.o): New rules. (d/d-target-hooks-def.h, s-d-target-hooks-def-h): New rules. (s-tm-texi): Also check timestamp on d-target.def. (generated_files): Add TM_D_H and d-target-hooks-def.h. (build/genhooks.o): Also depend on D_TARGET_DEF. * config.gcc (tm_d_file, d_target_objs, target_has_targetdm): New variables. * config/aarch64/aarch64-d.c: New file. * config/aarch64/aarch64-linux.h (GNU_USER_TARGET_D_CRITSEC_SIZE): Define. * config/aarch64/aarch64-protos.h (aarch64_d_target_versions): New prototype. * config/aarch64/aarch64.h (TARGET_D_CPU_VERSIONS): Define. * config/aarch64/t-aarch64 (aarch64-d.o): New rule. * config/arm/arm-d.c: New file. * config/arm/arm-protos.h (arm_d_target_versions): New prototype. * config/arm/arm.h (TARGET_D_CPU_VERSIONS): Define. * config/arm/linux-eabi.h (EXTRA_TARGET_D_OS_VERSIONS): Define. * config/arm/t-arm (arm-d.o): New rule. * config/default-d.c: New file. * config/glibc-d.c: New file. * config/gnu.h (GNU_USER_TARGET_D_OS_VERSIONS): Define. * config/i386/i386-d.c: New file. * config/i386/i386-protos.h (ix86_d_target_versions): New prototype. * config/i386/i386.h (TARGET_D_CPU_VERSIONS): Define. * config/i386/linux-common.h (EXTRA_TARGET_D_OS_VERSIONS): Define. (GNU_USER_TARGET_D_CRITSEC_SIZE): Define. * config/i386/t-i386 (i386-d.o): New rule. * config/kfreebsd-gnu.h (GNU_USER_TARGET_D_OS_VERSIONS): Define. * config/kopensolaris-gnu.h (GNU_USER_TARGET_D_OS_VERSIONS): Define. * config/linux-android.h (ANDROID_TARGET_D_OS_VERSIONS): Define. * config/linux.h (GNU_USER_TARGET_D_OS_VERSIONS): Define. * config/mips/linux-common.h (EXTRA_TARGET_D_OS_VERSIONS): Define. * config/mips/mips-d.c: New file. * config/mips/mips-protos.h (mips_d_target_versions): New prototype. * config/mips/mips.h (TARGET_D_CPU_VERSIONS): Define. * config/mips/t-mips (mips-d.o): New rule. * config/powerpcspe/linux.h (GNU_USER_TARGET_D_OS_VERSIONS): Define. * config/powerpcspe/linux64.h (GNU_USER_TARGET_D_OS_VERSIONS): Define. * config/powerpcspe/powerpcspe-d.c: New file. * config/powerpcspe/powerpcspe-protos.h (rs6000_d_target_versions): New prototype. * config/powerpcspe/powerpcspe.c (rs6000_output_function_epilogue): Support GNU D by using 0 as the language type. * config/powerpcspe/powerpcspe.h (TARGET_D_CPU_VERSIONS): Define. * config/powerpcspe/t-powerpcspe (powerpcspe-d.o): New rule. * config/riscv/riscv-d.c: New file. * config/riscv/riscv-protos.h (riscv_d_target_versions): New prototype. * config/riscv/riscv.h (TARGET_D_CPU_VERSIONS): Define. * config/riscv/t-riscv (riscv-d.o): New rule. * config/rs6000/linux.h (GNU_USER_TARGET_D_OS_VERSIONS): Define. * config/rs6000/linux64.h (GNU_USER_TARGET_D_OS_VERSIONS): Define. * config/rs6000/rs6000-d.c: New file. * config/rs6000/rs6000-protos.h (rs6000_d_target_versions): New prototype. * config/rs6000/rs6000.c (rs6000_output_function_epilogue): Support GNU D by using 0 as the language type. * config/rs6000/rs6000.h (TARGET_D_CPU_VERSIONS): Define. * config/rs6000/t-rs6000 (rs6000-d.o): New rule. * config/s390/s390-d.c: New file. * config/s390/s390-protos.h (s390_d_target_versions): New prototype. * config/s390/s390.h (TARGET_D_CPU_VERSIONS): Define. * config/s390/t-s390 (s390-d.o): New rule. * config/sparc/sparc-d.c: New file. * config/sparc/sparc-protos.h (sparc_d_target_versions): New prototype. * config/sparc/sparc.h (TARGET_D_CPU_VERSIONS): Define. * config/sparc/t-sparc (sparc-d.o): New rule. * config/t-glibc (glibc-d.o): New rule. * configure: Regenerated. * configure.ac (tm_d_file): New variable. (tm_d_file_list, tm_d_include_list, d_target_objs): Add substitutes. * doc/contrib.texi (Contributors): Add self for the D frontend. * doc/frontends.texi (G++ and GCC): Mention D as a supported language. * doc/install.texi (Configuration): Mention libphobos as an option for --enable-shared. Mention d as an option for --enable-languages. (Testing): Mention check-d as a target. * doc/invoke.texi (Overall Options): Mention .d, .dd, and .di as file name suffixes. Mention d as a -x option. * doc/sourcebuild.texi (Top Level): Mention libphobos. * doc/standards.texi (Standards): Add section on D language. * doc/tm.texi: Regenerated. * doc/tm.texi.in: Add @node for D language and ABI, and @hook for TARGET_CPU_VERSIONS, TARGET_D_OS_VERSIONS, and TARGET_D_CRITSEC_SIZE. * dwarf2out.c (is_dlang): New function. (gen_compile_unit_die): Use DW_LANG_D for D. (declare_in_namespace): Return module die for D, instead of adding extra declarations into the namespace. (gen_namespace_die): Generate DW_TAG_module for D. (gen_decl_die): Handle CONST_DECLSs for D. (dwarf2out_decl): Likewise. (prune_unused_types_walk_local_classes): Handle DW_tag_interface_type. (prune_unused_types_walk): Handle DW_tag_interface_type same as other kinds of aggregates. * gcc.c (default_compilers): Add entries for .d, .dd and .di. * genhooks.c: Include d/d-target.def. gcc/po/ChangeLog: * EXCLUDES: Add sources from d/dmd. gcc/testsuite/ChangeLog: * gcc.misc-tests/help.exp: Add D to option descriptions check. * gdc.dg/asan/asan.exp: New file. * gdc.dg/asan/gdc272.d: New test. * gdc.dg/compilable.d: New test. * gdc.dg/dg.exp: New file. * gdc.dg/gdc254.d: New test. * gdc.dg/gdc260.d: New test. * gdc.dg/gdc270a.d: New test. * gdc.dg/gdc270b.d: New test. * gdc.dg/gdc282.d: New test. * gdc.dg/gdc283.d: New test. * gdc.dg/imports/gdc170.d: New test. * gdc.dg/imports/gdc231.d: New test. * gdc.dg/imports/gdc239.d: New test. * gdc.dg/imports/gdc241a.d: New test. * gdc.dg/imports/gdc241b.d: New test. * gdc.dg/imports/gdc251a.d: New test. * gdc.dg/imports/gdc251b.d: New test. * gdc.dg/imports/gdc253.d: New test. * gdc.dg/imports/gdc254a.d: New test. * gdc.dg/imports/gdc256.d: New test. * gdc.dg/imports/gdc27.d: New test. * gdc.dg/imports/gdcpkg256/package.d: New test. * gdc.dg/imports/runnable.d: New test. * gdc.dg/link.d: New test. * gdc.dg/lto/lto.exp: New file. * gdc.dg/lto/ltotests_0.d: New test. * gdc.dg/lto/ltotests_1.d: New test. * gdc.dg/runnable.d: New test. * gdc.dg/simd.d: New test. * gdc.test/gdc-test.exp: New file. * lib/gdc-dg.exp: New file. * lib/gdc.exp: New file. libphobos/ChangeLog: * Makefile.am: New file. * Makefile.in: New file. * acinclude.m4: New file. * aclocal.m4: New file. * config.h.in: New file. * configure: New file. * configure.ac: New file. * d_rules.am: New file. * libdruntime/Makefile.am: New file. * libdruntime/Makefile.in: New file. * libdruntime/__entrypoint.di: New file. * libdruntime/__main.di: New file. * libdruntime/gcc/attribute.d: New file. * libdruntime/gcc/backtrace.d: New file. * libdruntime/gcc/builtins.d: New file. * libdruntime/gcc/config.d.in: New file. * libdruntime/gcc/deh.d: New file. * libdruntime/gcc/libbacktrace.d.in: New file. * libdruntime/gcc/unwind/arm.d: New file. * libdruntime/gcc/unwind/arm_common.d: New file. * libdruntime/gcc/unwind/c6x.d: New file. * libdruntime/gcc/unwind/generic.d: New file. * libdruntime/gcc/unwind/package.d: New file. * libdruntime/gcc/unwind/pe.d: New file. * m4/autoconf.m4: New file. * m4/druntime.m4: New file. * m4/druntime/cpu.m4: New file. * m4/druntime/libraries.m4: New file. * m4/druntime/os.m4: New file. * m4/gcc_support.m4: New file. * m4/gdc.m4: New file. * m4/libtool.m4: New file. * src/Makefile.am: New file. * src/Makefile.in: New file. * src/libgphobos.spec.in: New file. * testsuite/Makefile.am: New file. * testsuite/Makefile.in: New file. * testsuite/config/default.exp: New file. * testsuite/lib/libphobos-dg.exp: New file. * testsuite/lib/libphobos.exp: New file. * testsuite/testsuite_flags.in: New file. From-SVN: r265573
Diffstat (limited to 'libphobos/src/std')
-rw-r--r--libphobos/src/std/algorithm/comparison.d2159
-rw-r--r--libphobos/src/std/algorithm/internal.d77
-rw-r--r--libphobos/src/std/algorithm/iteration.d5187
-rw-r--r--libphobos/src/std/algorithm/mutation.d2909
-rw-r--r--libphobos/src/std/algorithm/package.d198
-rw-r--r--libphobos/src/std/algorithm/searching.d4600
-rw-r--r--libphobos/src/std/algorithm/setops.d1521
-rw-r--r--libphobos/src/std/algorithm/sorting.d4468
-rw-r--r--libphobos/src/std/array.d3775
-rw-r--r--libphobos/src/std/ascii.d729
-rw-r--r--libphobos/src/std/base64.d2099
-rw-r--r--libphobos/src/std/bigint.d1705
-rw-r--r--libphobos/src/std/bitmanip.d4009
-rw-r--r--libphobos/src/std/compiler.d58
-rw-r--r--libphobos/src/std/complex.d994
-rw-r--r--libphobos/src/std/concurrency.d2531
-rw-r--r--libphobos/src/std/container/array.d2419
-rw-r--r--libphobos/src/std/container/binaryheap.d595
-rw-r--r--libphobos/src/std/container/dlist.d1039
-rw-r--r--libphobos/src/std/container/package.d1156
-rw-r--r--libphobos/src/std/container/rbtree.d2065
-rw-r--r--libphobos/src/std/container/slist.d846
-rw-r--r--libphobos/src/std/container/util.d189
-rw-r--r--libphobos/src/std/conv.d6290
-rw-r--r--libphobos/src/std/csv.d1701
-rw-r--r--libphobos/src/std/datetime/date.d10580
-rw-r--r--libphobos/src/std/datetime/interval.d9131
-rw-r--r--libphobos/src/std/datetime/package.d733
-rw-r--r--libphobos/src/std/datetime/stopwatch.d428
-rw-r--r--libphobos/src/std/datetime/systime.d11151
-rw-r--r--libphobos/src/std/datetime/timezone.d4235
-rw-r--r--libphobos/src/std/demangle.d89
-rw-r--r--libphobos/src/std/digest/crc.d705
-rw-r--r--libphobos/src/std/digest/digest.d21
-rw-r--r--libphobos/src/std/digest/hmac.d336
-rw-r--r--libphobos/src/std/digest/md.d590
-rw-r--r--libphobos/src/std/digest/murmurhash.d755
-rw-r--r--libphobos/src/std/digest/package.d1171
-rw-r--r--libphobos/src/std/digest/ripemd.d762
-rw-r--r--libphobos/src/std/digest/sha.d1291
-rw-r--r--libphobos/src/std/encoding.d3662
-rw-r--r--libphobos/src/std/exception.d2316
-rw-r--r--libphobos/src/std/experimental/allocator/building_blocks/affix_allocator.d441
-rw-r--r--libphobos/src/std/experimental/allocator/building_blocks/allocator_list.d640
-rw-r--r--libphobos/src/std/experimental/allocator/building_blocks/bitmapped_block.d1423
-rw-r--r--libphobos/src/std/experimental/allocator/building_blocks/bucketizer.d241
-rw-r--r--libphobos/src/std/experimental/allocator/building_blocks/fallback_allocator.d355
-rw-r--r--libphobos/src/std/experimental/allocator/building_blocks/free_list.d1205
-rw-r--r--libphobos/src/std/experimental/allocator/building_blocks/free_tree.d487
-rw-r--r--libphobos/src/std/experimental/allocator/building_blocks/kernighan_ritchie.d882
-rw-r--r--libphobos/src/std/experimental/allocator/building_blocks/null_allocator.d85
-rw-r--r--libphobos/src/std/experimental/allocator/building_blocks/package.d313
-rw-r--r--libphobos/src/std/experimental/allocator/building_blocks/quantizer.d234
-rw-r--r--libphobos/src/std/experimental/allocator/building_blocks/region.d784
-rw-r--r--libphobos/src/std/experimental/allocator/building_blocks/scoped_allocator.d221
-rw-r--r--libphobos/src/std/experimental/allocator/building_blocks/segregator.d361
-rw-r--r--libphobos/src/std/experimental/allocator/building_blocks/stats_collector.d735
-rw-r--r--libphobos/src/std/experimental/allocator/common.d683
-rw-r--r--libphobos/src/std/experimental/allocator/gc_allocator.d167
-rw-r--r--libphobos/src/std/experimental/allocator/mallocator.d387
-rw-r--r--libphobos/src/std/experimental/allocator/mmap_allocator.d79
-rw-r--r--libphobos/src/std/experimental/allocator/package.d3028
-rw-r--r--libphobos/src/std/experimental/allocator/showcase.d92
-rw-r--r--libphobos/src/std/experimental/allocator/typed.d423
-rw-r--r--libphobos/src/std/experimental/checkedint.d3063
-rw-r--r--libphobos/src/std/experimental/logger/core.d3187
-rw-r--r--libphobos/src/std/experimental/logger/filelogger.d265
-rw-r--r--libphobos/src/std/experimental/logger/multilogger.d197
-rw-r--r--libphobos/src/std/experimental/logger/nulllogger.d39
-rw-r--r--libphobos/src/std/experimental/logger/package.d185
-rw-r--r--libphobos/src/std/experimental/note.md1
-rw-r--r--libphobos/src/std/experimental/typecons.d1078
-rw-r--r--libphobos/src/std/file.d4325
-rw-r--r--libphobos/src/std/format.d6028
-rw-r--r--libphobos/src/std/functional.d1564
-rw-r--r--libphobos/src/std/getopt.d1857
-rw-r--r--libphobos/src/std/internal/cstring.d267
-rw-r--r--libphobos/src/std/internal/digest/sha_SSSE3.d729
-rw-r--r--libphobos/src/std/internal/math/biguintcore.d2571
-rw-r--r--libphobos/src/std/internal/math/biguintnoasm.d370
-rw-r--r--libphobos/src/std/internal/math/biguintx86.d1353
-rw-r--r--libphobos/src/std/internal/math/errorfunction.d1145
-rw-r--r--libphobos/src/std/internal/math/gammafunction.d1834
-rw-r--r--libphobos/src/std/internal/scopebuffer.d398
-rw-r--r--libphobos/src/std/internal/test/dummyrange.d565
-rw-r--r--libphobos/src/std/internal/test/range.d25
-rw-r--r--libphobos/src/std/internal/test/uda.d16
-rw-r--r--libphobos/src/std/internal/unicode_comp.d2984
-rw-r--r--libphobos/src/std/internal/unicode_decomp.d5301
-rw-r--r--libphobos/src/std/internal/unicode_grapheme.d293
-rw-r--r--libphobos/src/std/internal/unicode_norm.d548
-rw-r--r--libphobos/src/std/internal/unicode_tables.d11081
-rw-r--r--libphobos/src/std/internal/windows/advapi32.d69
-rw-r--r--libphobos/src/std/json.d1859
-rw-r--r--libphobos/src/std/math.d8413
-rw-r--r--libphobos/src/std/mathspecial.d361
-rw-r--r--libphobos/src/std/meta.d1679
-rw-r--r--libphobos/src/std/mmfile.d721
-rw-r--r--libphobos/src/std/net/curl.d5109
-rw-r--r--libphobos/src/std/net/isemail.d1864
-rw-r--r--libphobos/src/std/numeric.d3467
-rw-r--r--libphobos/src/std/outbuffer.d418
-rw-r--r--libphobos/src/std/parallelism.d4636
-rw-r--r--libphobos/src/std/path.d4115
-rw-r--r--libphobos/src/std/process.d4047
-rw-r--r--libphobos/src/std/random.d3344
-rw-r--r--libphobos/src/std/range/interfaces.d567
-rw-r--r--libphobos/src/std/range/package.d12019
-rw-r--r--libphobos/src/std/range/primitives.d2320
-rw-r--r--libphobos/src/std/regex/internal/backtracking.d1495
-rw-r--r--libphobos/src/std/regex/internal/generator.d187
-rw-r--r--libphobos/src/std/regex/internal/ir.d788
-rw-r--r--libphobos/src/std/regex/internal/kickstart.d579
-rw-r--r--libphobos/src/std/regex/internal/parser.d1751
-rw-r--r--libphobos/src/std/regex/internal/tests.d1120
-rw-r--r--libphobos/src/std/regex/internal/thompson.d1188
-rw-r--r--libphobos/src/std/regex/package.d1735
-rw-r--r--libphobos/src/std/signals.d708
-rw-r--r--libphobos/src/std/socket.d3670
-rw-r--r--libphobos/src/std/stdint.d131
-rw-r--r--libphobos/src/std/stdio.d5159
-rw-r--r--libphobos/src/std/string.d6952
-rw-r--r--libphobos/src/std/system.d74
-rw-r--r--libphobos/src/std/traits.d8058
-rw-r--r--libphobos/src/std/typecons.d8029
-rw-r--r--libphobos/src/std/typetuple.d40
-rw-r--r--libphobos/src/std/uni.d9756
-rw-r--r--libphobos/src/std/uri.d592
-rw-r--r--libphobos/src/std/utf.d4058
-rw-r--r--libphobos/src/std/uuid.d1731
-rw-r--r--libphobos/src/std/variant.d2771
-rw-r--r--libphobos/src/std/windows/charset.d122
-rw-r--r--libphobos/src/std/windows/registry.d1868
-rw-r--r--libphobos/src/std/windows/syserror.d201
-rw-r--r--libphobos/src/std/xml.d3103
-rw-r--r--libphobos/src/std/zip.d990
-rw-r--r--libphobos/src/std/zlib.d760
137 files changed, 288434 insertions, 0 deletions
diff --git a/libphobos/src/std/algorithm/comparison.d b/libphobos/src/std/algorithm/comparison.d
new file mode 100644
index 0000000..faa4d44
--- /dev/null
+++ b/libphobos/src/std/algorithm/comparison.d
@@ -0,0 +1,2159 @@
+// Written in the D programming language.
+/**
+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))
+$(T2 castSwitch,
+ $(D (new A()).castSwitch((A a)=>1,(B b)=>2)) returns $(D 1).)
+$(T2 clamp,
+ $(D clamp(1, 3, 6)) returns $(D 3). $(D clamp(4, 3, 6)) returns $(D 4).)
+$(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 $(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
+ $(LINK2 https://en.wikipedia.org/wiki/Levenshtein_distance,
+ Levenshtein distance _algorithm).)
+$(T2 max,
+ $(D max(3, 4, 2)) returns $(D 4).)
+$(T2 min,
+ $(D min(3, 4, 2)) returns $(D 2).)
+$(T2 mismatch,
+ $(D mismatch("oh hi", "ohayo")) returns $(D tuple(" hi", "ayo")).)
+$(T2 predSwitch,
+ $(D 2.predSwitch(1, "one", 2, "two", 3, "three")) returns $(D "two").)
+)
+
+Copyright: Andrei Alexandrescu 2008-.
+
+License: $(HTTP boost.org/LICENSE_1_0.txt, Boost License 1.0).
+
+Authors: $(HTTP erdani.com, Andrei Alexandrescu)
+
+Source: $(PHOBOSSRC std/algorithm/_comparison.d)
+
+Macros:
+T2=$(TR $(TDNW $(LREF $1)) $(TD $+))
+ */
+module std.algorithm.comparison;
+
+// FIXME
+import std.functional; // : unaryFun, binaryFun;
+import std.range.primitives;
+import std.traits;
+// FIXME
+import std.meta : allSatisfy;
+import std.typecons; // : tuple, Tuple, Flag, Yes;
+
+/**
+Find $(D value) _among $(D values), returning the 1-based index
+of the first matching value in $(D values), or $(D 0) if $(D value)
+is not _among $(D values). The predicate $(D pred) is used to
+compare values, and uses equality by default.
+
+Params:
+ pred = The predicate used to compare the values.
+ value = The value to search for.
+ values = The values to compare the value to.
+
+Returns:
+ 0 if value was not found among the values, otherwise the index of the
+ found value plus one is returned.
+
+See_Also:
+$(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)
+{
+ foreach (uint i, ref v; values)
+ {
+ import std.functional : binaryFun;
+ if (binaryFun!pred(value, v)) return i + 1;
+ }
+ return 0;
+}
+
+/// Ditto
+template among(values...)
+if (isExpressionTuple!values)
+{
+ uint among(Value)(Value value)
+ if (!is(CommonType!(Value, values) == void))
+ {
+ switch (value)
+ {
+ foreach (uint i, v; values)
+ case v:
+ return i + 1;
+ default:
+ return 0;
+ }
+ }
+}
+
+///
+@safe unittest
+{
+ assert(3.among(1, 42, 24, 3, 2));
+
+ if (auto pos = "bar".among("foo", "bar", "baz"))
+ assert(pos == 2);
+ else
+ assert(false);
+
+ // 42 is larger than 24
+ assert(42.among!((lhs, rhs) => lhs > rhs)(43, 24, 100) == 2);
+}
+
+/**
+Alternatively, $(D values) can be passed at compile-time, allowing for a more
+efficient search, but one that only supports matching on equality:
+*/
+@safe unittest
+{
+ assert(3.among!(2, 3, 4));
+ assert("bar".among!("foo", "bar", "baz") == 2);
+}
+
+@safe unittest
+{
+ import std.meta : AliasSeq;
+
+ if (auto pos = 3.among(1, 2, 3))
+ assert(pos == 3);
+ else
+ assert(false);
+ assert(!4.among(1, 2, 3));
+
+ auto position = "hello".among("hello", "world");
+ assert(position);
+ assert(position == 1);
+
+ alias values = AliasSeq!("foo", "bar", "baz");
+ auto arr = [values];
+ assert(arr[0 .. "foo".among(values)] == ["foo"]);
+ assert(arr[0 .. "bar".among(values)] == ["foo", "bar"]);
+ assert(arr[0 .. "baz".among(values)] == arr);
+ assert("foobar".among(values) == 0);
+
+ if (auto pos = 3.among!(1, 2, 3))
+ assert(pos == 3);
+ else
+ assert(false);
+ assert(!4.among!(1, 2, 3));
+
+ position = "hello".among!("hello", "world");
+ assert(position);
+ assert(position == 1);
+
+ static assert(!__traits(compiles, "a".among!("a", 42)));
+ static assert(!__traits(compiles, (Object.init).among!(42, "a")));
+}
+
+// Used in castSwitch to find the first choice that overshadows the last choice
+// in a tuple.
+private template indexOfFirstOvershadowingChoiceOnLast(choices...)
+{
+ alias firstParameterTypes = Parameters!(choices[0]);
+ alias lastParameterTypes = Parameters!(choices[$ - 1]);
+
+ static if (lastParameterTypes.length == 0)
+ {
+ // If the last is null-typed choice, check if the first is null-typed.
+ enum isOvershadowing = firstParameterTypes.length == 0;
+ }
+ else static if (firstParameterTypes.length == 1)
+ {
+ // If the both first and last are not null-typed, check for overshadowing.
+ enum isOvershadowing =
+ is(firstParameterTypes[0] == Object) // Object overshadows all other classes!(this is needed for interfaces)
+ || is(lastParameterTypes[0] : firstParameterTypes[0]);
+ }
+ else
+ {
+ // If the first is null typed and the last is not - the is no overshadowing.
+ enum isOvershadowing = false;
+ }
+
+ static if (isOvershadowing)
+ {
+ enum indexOfFirstOvershadowingChoiceOnLast = 0;
+ }
+ else
+ {
+ enum indexOfFirstOvershadowingChoiceOnLast =
+ 1 + indexOfFirstOvershadowingChoiceOnLast!(choices[1..$]);
+ }
+}
+
+/**
+Executes and returns one of a collection of handlers based on the type of the
+switch object.
+
+The first choice that $(D switchObject) can be casted to the type
+of argument it accepts will be called with $(D switchObject) casted to that
+type, and the value it'll return will be returned by $(D castSwitch).
+
+If a choice's return type is void, the choice must throw an exception, unless
+all the choices are void. In that case, castSwitch itself will return void.
+
+Throws: If none of the choice matches, a $(D SwitchError) will be thrown. $(D
+SwitchError) will also be thrown if not all the choices are void and a void
+choice was executed without throwing anything.
+
+Params:
+ choices = The $(D choices) needs to be composed of function or delegate
+ 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.
+
+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 = {
+ bool result = true;
+ foreach (index, choice; choices)
+ {
+ result &= is(ReturnType!choice == void);
+ }
+ return result;
+ }();
+
+ if (switchObject !is null)
+ {
+
+ // Checking for exact matches:
+ const classInfo = typeid(switchObject);
+ foreach (index, choice; choices)
+ {
+ static assert(isCallable!choice,
+ "A choice handler must be callable");
+
+ alias choiceParameterTypes = Parameters!choice;
+ static assert(choiceParameterTypes.length <= 1,
+ "A choice handler can not have more than one argument.");
+
+ static if (choiceParameterTypes.length == 1)
+ {
+ alias CastClass = choiceParameterTypes[0];
+ static assert(is(CastClass == class) || is(CastClass == interface),
+ "A choice handler can have only class or interface typed argument.");
+
+ // Check for overshadowing:
+ immutable indexOfOvershadowingChoice =
+ 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,
+ Parameters!(choices[indexOfOvershadowingChoice])[0].stringof));
+
+ if (classInfo == typeid(CastClass))
+ {
+ static if (is(ReturnType!(choice) == void))
+ {
+ choice(cast(CastClass) switchObject);
+ static if (areAllHandlersVoidResult)
+ {
+ return;
+ }
+ else
+ {
+ throw new SwitchError("Handlers that return void should throw");
+ }
+ }
+ else
+ {
+ return choice(cast(CastClass) switchObject);
+ }
+ }
+ }
+ }
+
+ // Checking for derived matches:
+ foreach (choice; choices)
+ {
+ alias choiceParameterTypes = Parameters!choice;
+ static if (choiceParameterTypes.length == 1)
+ {
+ if (auto castedObject = cast(choiceParameterTypes[0]) switchObject)
+ {
+ static if (is(ReturnType!(choice) == void))
+ {
+ choice(castedObject);
+ static if (areAllHandlersVoidResult)
+ {
+ return;
+ }
+ else
+ {
+ throw new SwitchError("Handlers that return void should throw");
+ }
+ }
+ else
+ {
+ return choice(castedObject);
+ }
+ }
+ }
+ }
+ }
+ else // If switchObject is null:
+ {
+ // Checking for null matches:
+ foreach (index, choice; choices)
+ {
+ static if (Parameters!(choice).length == 0)
+ {
+ immutable indexOfOvershadowingChoice =
+ indexOfFirstOvershadowingChoiceOnLast!(choices[0 .. index + 1]);
+
+ // Check for overshadowing:
+ static assert(indexOfOvershadowingChoice == index,
+ "choice number %d(null reference) is overshadowed by choice number %d(null reference)".format(
+ index + 1, indexOfOvershadowingChoice + 1));
+
+ if (switchObject is null)
+ {
+ static if (is(ReturnType!(choice) == void))
+ {
+ choice();
+ static if (areAllHandlersVoidResult)
+ {
+ return;
+ }
+ else
+ {
+ throw new SwitchError("Handlers that return void should throw");
+ }
+ }
+ else
+ {
+ return choice();
+ }
+ }
+ }
+ }
+ }
+
+ // In case nothing matched:
+ throw new SwitchError("Input not matched by any choice");
+}
+
+///
+@system unittest
+{
+ import std.algorithm.iteration : map;
+ import std.format : format;
+
+ class A
+ {
+ int a;
+ this(int a) {this.a = a;}
+ @property int i() { return a; }
+ }
+ interface I { }
+ class B : I { }
+
+ Object[] arr = [new A(1), new B(), null];
+
+ auto results = arr.map!(castSwitch!(
+ (A a) => "A with a value of %d".format(a.a),
+ (I i) => "derived from I",
+ () => "null reference",
+ ))();
+
+ // A is handled directly:
+ assert(results[0] == "A with a value of 1");
+ // B has no handler - it is handled by the handler of I:
+ assert(results[1] == "derived from I");
+ // null is handled by the null handler:
+ assert(results[2] == "null reference");
+}
+
+/// Using with void handlers:
+@system unittest
+{
+ import std.exception : assertThrown;
+
+ class A { }
+ class B { }
+ // Void handlers are allowed if they throw:
+ assertThrown!Exception(
+ new B().castSwitch!(
+ (A a) => 1,
+ (B d) { throw new Exception("B is not allowed!"); }
+ )()
+ );
+
+ // Void handlers are also allowed if all the handlers are void:
+ new A().castSwitch!(
+ (A a) { },
+ (B b) { assert(false); },
+ )();
+}
+
+@system unittest
+{
+ import core.exception : SwitchError;
+ import std.exception : assertThrown;
+
+ interface I { }
+ class A : I { }
+ class B { }
+
+ // Nothing matches:
+ assertThrown!SwitchError((new A()).castSwitch!(
+ (B b) => 1,
+ () => 2,
+ )());
+
+ // Choices with multiple arguments are not allowed:
+ static assert(!__traits(compiles,
+ (new A()).castSwitch!(
+ (A a, B b) => 0,
+ )()));
+
+ // Only callable handlers allowed:
+ static assert(!__traits(compiles,
+ (new A()).castSwitch!(
+ 1234,
+ )()));
+
+ // Only object arguments allowed:
+ static assert(!__traits(compiles,
+ (new A()).castSwitch!(
+ (int x) => 0,
+ )()));
+
+ // Object overshadows regular classes:
+ static assert(!__traits(compiles,
+ (new A()).castSwitch!(
+ (Object o) => 0,
+ (A a) => 1,
+ )()));
+
+ // Object overshadows interfaces:
+ static assert(!__traits(compiles,
+ (new A()).castSwitch!(
+ (Object o) => 0,
+ (I i) => 1,
+ )()));
+
+ // No multiple null handlers allowed:
+ static assert(!__traits(compiles,
+ (new A()).castSwitch!(
+ () => 0,
+ () => 1,
+ )()));
+
+ // No non-throwing void handlers allowed(when there are non-void handlers):
+ assertThrown!SwitchError((new A()).castSwitch!(
+ (A a) {},
+ (B b) => 2,
+ )());
+
+ // All-void handlers work for the null case:
+ null.castSwitch!(
+ (Object o) { assert(false); },
+ () { },
+ )();
+
+ // Throwing void handlers work for the null case:
+ assertThrown!Exception(null.castSwitch!(
+ (Object o) => 1,
+ () { throw new Exception("null"); },
+ )());
+}
+
+@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))).
+
+Params:
+ val = The value to _clamp.
+ lower = The _lower bound of the _clamp.
+ upper = The _upper bound of the _clamp.
+
+Returns:
+ Returns $(D val), if it is between $(D lower) and $(D upper).
+ Otherwise returns the nearest of the two.
+
+*/
+auto clamp(T1, T2, T3)(T1 val, T2 lower, T3 upper)
+in
+{
+ import std.functional : greaterThan;
+ assert(!lower.greaterThan(upper));
+}
+body
+{
+ return max(lower, min(upper, val));
+}
+
+///
+@safe unittest
+{
+ assert(clamp(2, 1, 3) == 2);
+ assert(clamp(0, 1, 3) == 1);
+ assert(clamp(4, 1, 3) == 3);
+
+ assert(clamp(1, 1, 1) == 1);
+
+ assert(clamp(5, -1, 2u) == 2);
+}
+
+@safe unittest
+{
+ int a = 1;
+ short b = 6;
+ double c = 2;
+ static assert(is(typeof(clamp(c,a,b)) == double));
+ assert(clamp(c, a, b) == c);
+ assert(clamp(a-c, a, b) == a);
+ assert(clamp(b+c, a, b) == b);
+ // mixed sign
+ a = -5;
+ uint f = 5;
+ static assert(is(typeof(clamp(f, a, b)) == int));
+ assert(clamp(f, a, b) == f);
+ // similar type deduction for (u)long
+ static assert(is(typeof(clamp(-1L, -2L, 2UL)) == long));
+
+ // user-defined types
+ import std.datetime : Date;
+ assert(clamp(Date(1982, 1, 4), Date(1012, 12, 21), Date(2012, 12, 21)) == Date(1982, 1, 4));
+ assert(clamp(Date(1982, 1, 4), Date.min, Date.max) == Date(1982, 1, 4));
+ // UFCS style
+ assert(Date(1982, 1, 4).clamp(Date.min, Date.max) == Date(1982, 1, 4));
+
+}
+
+// cmp
+/**********************************
+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
+finished, $(D cmp) returns a negative value if $(D r1) has fewer
+elements than $(D r2), a positive value if $(D r1) has more elements
+than $(D r2), and $(D 0) if the ranges have the same number of
+elements.
+
+If the ranges are strings, $(D cmp) performs UTF decoding
+appropriately and compares the ranges one code point at a time.
+
+Params:
+ pred = The predicate used for comparison.
+ r1 = The first range.
+ r2 = The second range.
+
+Returns:
+ 0 if both ranges compare equal. -1 if the first differing element of $(D
+ r1) is less than the corresponding element of $(D r2) according to $(D
+ pred). 1 if the first differing element of $(D r2) is less than the
+ corresponding element of $(D r1) according to $(D pred).
+
+*/
+int cmp(alias pred = "a < b", R1, R2)(R1 r1, R2 r2)
+if (isInputRange!R1 && isInputRange!R2 && !(isSomeString!R1 && isSomeString!R2))
+{
+ for (;; r1.popFront(), r2.popFront())
+ {
+ if (r1.empty) return -cast(int)!r2.empty;
+ if (r2.empty) return !r1.empty;
+ auto a = r1.front, b = r2.front;
+ if (binaryFun!pred(a, b)) return -1;
+ if (binaryFun!pred(b, a)) return 1;
+ }
+}
+
+/// ditto
+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;
+
+ static if (is(typeof(pred) : string))
+ enum isLessThan = pred == "a < b";
+ else
+ enum isLessThan = false;
+
+ // For speed only
+ static int threeWay(size_t a, size_t b)
+ {
+ static if (size_t.sizeof == int.sizeof && isLessThan)
+ return a - b;
+ else
+ return binaryFun!pred(b, a) ? 1 : binaryFun!pred(a, b) ? -1 : 0;
+ }
+ // For speed only
+ // @@@BUG@@@ overloading should be allowed for nested functions
+ static int threeWayInt(int a, int b)
+ {
+ static if (isLessThan)
+ return a - b;
+ else
+ return binaryFun!pred(b, a) ? 1 : binaryFun!pred(a, b) ? -1 : 0;
+ }
+
+ static if (typeof(r1[0]).sizeof == typeof(r2[0]).sizeof && isLessThan)
+ {
+ static if (typeof(r1[0]).sizeof == 1)
+ {
+ immutable len = min(r1.length, r2.length);
+ immutable result = __ctfe ?
+ {
+ foreach (i; 0 .. len)
+ {
+ if (r1[i] != r2[i])
+ return threeWayInt(r1[i], r2[i]);
+ }
+ return 0;
+ }()
+ : () @trusted { return memcmp(r1.ptr, r2.ptr, len); }();
+ if (result) return result;
+ }
+ else
+ {
+ auto p1 = r1.ptr, p2 = r2.ptr,
+ pEnd = p1 + min(r1.length, r2.length);
+ for (; p1 != pEnd; ++p1, ++p2)
+ {
+ if (*p1 != *p2) return threeWayInt(cast(int) *p1, cast(int) *p2);
+ }
+ }
+ return threeWay(r1.length, r2.length);
+ }
+ else
+ {
+ for (size_t i1, i2;;)
+ {
+ if (i1 == r1.length) return threeWay(i2, r2.length);
+ if (i2 == r2.length) return threeWay(r1.length, i1);
+ immutable c1 = decode(r1, i1),
+ c2 = decode(r2, i2);
+ if (c1 != c2) return threeWayInt(cast(int) c1, cast(int) c2);
+ }
+ }
+}
+
+///
+@safe unittest
+{
+ int result;
+
+ result = cmp("abc", "abc");
+ assert(result == 0);
+ result = cmp("", "");
+ assert(result == 0);
+ result = cmp("abc", "abcd");
+ assert(result < 0);
+ result = cmp("abcd", "abc");
+ assert(result > 0);
+ result = cmp("abc"d, "abd");
+ assert(result < 0);
+ result = cmp("bbc", "abc"w);
+ assert(result > 0);
+ result = cmp("aaa", "aaaa"d);
+ assert(result < 0);
+ result = cmp("aaaa", "aaa"d);
+ assert(result > 0);
+ result = cmp("aaa", "aaa"d);
+ assert(result == 0);
+ result = cmp(cast(int[])[], cast(int[])[]);
+ assert(result == 0);
+ result = cmp([1, 2, 3], [1, 2, 3]);
+ assert(result == 0);
+ result = cmp([1, 3, 2], [1, 2, 3]);
+ assert(result > 0);
+ result = cmp([1, 2, 3], [1L, 2, 3, 4]);
+ assert(result < 0);
+ result = cmp([1L, 2, 3], [1, 2]);
+ assert(result > 0);
+}
+
+// equal
+/**
+Compares two ranges for equality, as defined by predicate $(D pred)
+(which is $(D ==) by default).
+*/
+template equal(alias pred = "a == b")
+{
+ enum isEmptyRange(R) =
+ isInputRange!R && __traits(compiles, {static assert(R.empty);});
+
+ enum hasFixedLength(T) = hasLength!T || isNarrowString!T;
+
+ /++
+ 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.
+ r2 = The second range to be compared.
+
+ Returns:
+ $(D true) if and only if the two ranges compare _equal element
+ for element, according to binary predicate $(D pred).
+
+ See_Also:
+ $(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))))
+ {
+ 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)))
+ {
+ 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 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;
+ }
+ return true;
+ }
+ else
+ {
+ //Generic case, we have to walk both ranges making sure neither is empty
+ for (; !r1.empty; r1.popFront(), r2.popFront())
+ {
+ if (r2.empty) return false;
+ if (!binaryFun!(pred)(r1.front, r2.front)) return false;
+ }
+ static if (!isInfinite!Range1)
+ return r2.empty;
+ }
+ }
+}
+
+///
+@safe unittest
+{
+ import std.algorithm.comparison : equal;
+ import std.math : approxEqual;
+
+ 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];
+ assert(!equal(a, b[1..$]));
+ assert(equal(a, b));
+
+ // predicated: ensure that two vectors are approximately equal
+ double[] c = [ 1.005, 2, 4, 3];
+ assert(equal!approxEqual(b, c));
+}
+
+/++
+Tip: $(D equal) can itself be used as a predicate to other functions.
+This can be very useful when the element type of a range is itself a
+range. In particular, $(D equal) can be its own predicate, allowing
+range of range (of range...) comparisons.
+ +/
+@safe unittest
+{
+ import std.algorithm.comparison : equal;
+ import std.range : iota, chunks;
+ assert(equal!(equal!equal)(
+ [[[0, 1], [2, 3]], [[4, 5], [6, 7]]],
+ iota(0, 8).chunks(2).chunks(2)
+ ));
+}
+
+@safe unittest
+{
+ import std.algorithm.iteration : map;
+ import std.internal.test.dummyrange : ReferenceForwardRange,
+ ReferenceInputRange, ReferenceInfiniteForwardRange;
+ import std.math : approxEqual;
+
+ // various strings
+ assert(equal("æøå", "æøå")); //UTF8 vs UTF8
+ assert(!equal("???", "æøå")); //UTF8 vs UTF8
+ assert(equal("æøå"w, "æøå"d)); //UTF16 vs UTF32
+ assert(!equal("???"w, "æøå"d));//UTF16 vs UTF32
+ assert(equal("æøå"d, "æøå"d)); //UTF32 vs UTF32
+ assert(!equal("???"d, "æøå"d));//UTF32 vs UTF32
+ assert(!equal("hello", "world"));
+
+ // same strings, but "explicit non default" comparison (to test the non optimized array comparison)
+ assert( equal!("a == b")("æøå", "æøå")); //UTF8 vs UTF8
+ assert(!equal!("a == b")("???", "æøå")); //UTF8 vs UTF8
+ assert( equal!("a == b")("æøå"w, "æøå"d)); //UTF16 vs UTF32
+ assert(!equal!("a == b")("???"w, "æøå"d));//UTF16 vs UTF32
+ assert( equal!("a == b")("æøå"d, "æøå"d)); //UTF32 vs UTF32
+ assert(!equal!("a == b")("???"d, "æøå"d));//UTF32 vs UTF32
+ assert(!equal!("a == b")("hello", "world"));
+
+ //Array of string
+ assert(equal(["hello", "world"], ["hello", "world"]));
+ assert(!equal(["hello", "world"], ["hello"]));
+ assert(!equal(["hello", "world"], ["hello", "Bob!"]));
+
+ //Should not compile, because "string == dstring" is illegal
+ static assert(!is(typeof(equal(["hello", "world"], ["hello"d, "world"d]))));
+ //However, arrays of non-matching string can be compared using equal!equal. Neat-o!
+ equal!equal(["hello", "world"], ["hello"d, "world"d]);
+
+ //Tests, with more fancy map ranges
+ int[] a = [ 1, 2, 4, 3 ];
+ assert(equal([2, 4, 8, 6], map!"a*2"(a)));
+ double[] b = [ 1.0, 2, 4, 3];
+ double[] c = [ 1.005, 2, 4, 3];
+ assert(equal!approxEqual(map!"a*2"(b), map!"a*2"(c)));
+ assert(!equal([2, 4, 1, 3], map!"a*2"(a)));
+ assert(!equal([2, 4, 1], map!"a*2"(a)));
+ assert(!equal!approxEqual(map!"a*3"(b), map!"a*2"(c)));
+
+ //Tests with some fancy reference ranges.
+ ReferenceInputRange!int cir = new ReferenceInputRange!int([1, 2, 4, 3]);
+ ReferenceForwardRange!int cfr = new ReferenceForwardRange!int([1, 2, 4, 3]);
+ assert(equal(cir, a));
+ cir = new ReferenceInputRange!int([1, 2, 4, 3]);
+ assert(equal(cir, cfr.save));
+ assert(equal(cfr.save, cfr.save));
+ cir = new ReferenceInputRange!int([1, 2, 8, 1]);
+ assert(!equal(cir, cfr));
+
+ //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)
+{
+ static if (T.length == 1)
+ {
+ alias MaxType = T[0];
+ }
+ else static if (T.length == 2)
+ {
+ static if (!is(typeof(T[0].min)))
+ alias MaxType = CommonType!T;
+ else static if (T[1].max > T[0].max)
+ alias MaxType = T[1];
+ else
+ alias MaxType = T[0];
+ }
+ else
+ {
+ alias MaxType = MaxType!(MaxType!(T[0 .. ($+1)/2]), MaxType!(T[($+1)/2 .. $]));
+ }
+}
+
+// levenshteinDistance
+/**
+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
+convert $(D s) into $(D t). For example, if $(D s = "cat") and $(D
+"cars"), the minimal sequence that transforms $(D s) into $(D t) is:
+skip two characters, replace 't' with 'r', and insert an 's'. Working
+with edit operations is useful in applications such as spell-checkers
+(to find the closest word to a given misspelled word), approximate
+searches, diff-style programs that compute the difference between
+files, efficient encoding of patches, DNA sequence analysis, and
+plagiarism detection.
+*/
+
+enum EditOp : char
+{
+ /** Current items are equal; no editing is necessary. */
+ none = 'n',
+ /** Substitute current item in target with current item in source. */
+ substitute = 's',
+ /** Insert current item from the source into the target. */
+ insert = 'i',
+ /** Remove current item from the target. */
+ remove = 'r'
+}
+
+///
+@safe unittest
+{
+ with(EditOp)
+ {
+ assert(levenshteinDistanceAndPath("foo", "foobar")[1] == [none, none, none, insert, insert, insert]);
+ assert(levenshteinDistanceAndPath("banana", "fazan")[1] == [substitute, none, substitute, none, none, remove]);
+ }
+}
+
+private struct Levenshtein(Range, alias equals, CostType = size_t)
+{
+ EditOp[] path()
+ {
+ import std.algorithm.mutation : reverse;
+
+ EditOp[] result;
+ size_t i = rows - 1, j = cols - 1;
+ // restore the path
+ 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))
+ {
+ case 0:
+ result ~= matrix(i - 1,j - 1) == matrix(i,j)
+ ? EditOp.none
+ : EditOp.substitute;
+ --i;
+ --j;
+ break;
+ case 1:
+ result ~= EditOp.insert;
+ --j;
+ break;
+ default:
+ result ~= EditOp.remove;
+ --i;
+ break;
+ }
+ }
+ reverse(result);
+ return result;
+ }
+
+ ~this() {
+ FreeMatrix();
+ }
+
+private:
+ CostType _deletionIncrement = 1,
+ _insertionIncrement = 1,
+ _substitutionIncrement = 1;
+ CostType[] _matrix;
+ size_t rows, cols;
+
+ // 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) @trusted {
+ import core.checkedint : mulu;
+ bool overflow;
+ const rc = mulu(r, c, overflow);
+ if (overflow) assert(0);
+ rows = r;
+ cols = c;
+ if (_matrix.length < rc)
+ {
+ import core.exception : onOutOfMemoryError;
+ import core.stdc.stdlib : realloc;
+ 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;
+ foreach (c; 0 .. cols)
+ matrix(0,c) = c * _insertionIncrement;
+ }
+
+ static uint min_index(CostType i0, CostType i1, CostType i2)
+ {
+ if (i0 <= i1)
+ {
+ return i0 <= i2 ? 0 : 2;
+ }
+ else
+ {
+ return i1 <= i2 ? 1 : 2;
+ }
+ }
+
+ CostType distanceWithPath(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;
+ auto tt = t.save;
+ 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;
+ }
+ }
+ s.popFront();
+ }
+ return matrix(slen,tlen);
+ }
+
+ CostType distanceLowMem(Range s, Range t, CostType slen, CostType tlen)
+ {
+ CostType lastdiag, olddiag;
+ AllocMatrix(slen + 1, 1);
+ foreach (y; 1 .. slen + 1)
+ {
+ _matrix[y] = y;
+ }
+ foreach (x; 1 .. tlen + 1)
+ {
+ auto tfront = t.front;
+ auto ss = s.save;
+ _matrix[0] = x;
+ lastdiag = x - 1;
+ foreach (y; 1 .. rows)
+ {
+ olddiag = _matrix[y];
+ auto cSub = lastdiag + (equals(ss.front, tfront) ? 0 : _substitutionIncrement);
+ ss.popFront();
+ auto cIns = _matrix[y - 1] + _insertionIncrement;
+ auto cDel = _matrix[y] + _deletionIncrement;
+ switch (min_index(cSub, cIns, cDel))
+ {
+ case 0:
+ _matrix[y] = cSub;
+ break;
+ case 1:
+ _matrix[y] = cIns;
+ break;
+ default:
+ _matrix[y] = cDel;
+ break;
+ }
+ lastdiag = olddiag;
+ }
+ t.popFront();
+ }
+ return _matrix[slen];
+ }
+}
+
+/**
+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
+equals) and occupies $(BIGOH s.length * t.length) storage.
+
+Params:
+ equals = The binary predicate to compare the elements of the two ranges.
+ s = The original range.
+ t = The transformation target
+
+Returns:
+ The minimal number of edits to transform s into t.
+
+Does not allocate GC memory.
+*/
+size_t levenshteinDistance(alias equals = (a,b) => a == b, Range1, Range2)
+ (Range1 s, Range2 t)
+if (isForwardRange!(Range1) && isForwardRange!(Range2))
+{
+ alias eq = binaryFun!(equals);
+
+ for (;;)
+ {
+ if (s.empty) return t.walkLength;
+ if (t.empty) return s.walkLength;
+ if (eq(s.front, t.front))
+ {
+ s.popFront();
+ t.popFront();
+ continue;
+ }
+ static if (isBidirectionalRange!(Range1) && isBidirectionalRange!(Range2))
+ {
+ if (eq(s.back, t.back))
+ {
+ s.popBack();
+ t.popBack();
+ continue;
+ }
+ }
+ break;
+ }
+
+ auto slen = walkLength(s.save);
+ auto tlen = walkLength(t.save);
+
+ if (slen == 1 && tlen == 1)
+ {
+ return eq(s.front, t.front) ? 0 : 1;
+ }
+
+ if (slen > tlen)
+ {
+ Levenshtein!(Range1, eq, size_t) lev;
+ return lev.distanceLowMem(s, t, slen, tlen);
+ }
+ else
+ {
+ Levenshtein!(Range2, eq, size_t) lev;
+ return lev.distanceLowMem(t, s, tlen, slen);
+ }
+}
+
+///
+@safe unittest
+{
+ import std.algorithm.iteration : filter;
+ import std.uni : toUpper;
+
+ assert(levenshteinDistance("cat", "rat") == 1);
+ assert(levenshteinDistance("parks", "spark") == 2);
+ assert(levenshteinDistance("abcde", "abcde") == 0);
+ assert(levenshteinDistance("abcde", "abCde") == 1);
+ assert(levenshteinDistance("kitten", "sitting") == 3);
+ 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).
+
+Params:
+ equals = The binary predicate to compare the elements of the two ranges.
+ s = The original range.
+ t = The transformation target
+
+Returns:
+ 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 for the returned EditOp[] array.
+*/
+Tuple!(size_t, EditOp[])
+levenshteinDistanceAndPath(alias equals = (a,b) => a == b, Range1, Range2)
+ (Range1 s, Range2 t)
+if (isForwardRange!(Range1) && isForwardRange!(Range2))
+{
+ Levenshtein!(Range1, binaryFun!(equals)) lev;
+ auto d = lev.distanceWithPath(s, t);
+ return tuple(d, lev.path());
+}
+
+///
+@safe unittest
+{
+ string a = "Saturday", b = "Sundays";
+ auto p = levenshteinDistanceAndPath(a, b);
+ assert(p[0] == 4);
+ assert(equal(p[1], "nrrnsnnni"));
+}
+
+@safe unittest
+{
+ assert(levenshteinDistance("a", "a") == 0);
+ assert(levenshteinDistance("a", "b") == 1);
+ assert(levenshteinDistance("aa", "ab") == 1);
+ assert(levenshteinDistance("aa", "abc") == 2);
+ assert(levenshteinDistance("Saturday", "Sunday") == 3);
+ 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.
+
+Params:
+ args = The values to select the maximum from. At least two arguments must
+ be passed.
+
+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)
+{
+ //Get "a"
+ static if (T.length <= 2)
+ alias a = args[0];
+ else
+ auto a = max(args[0 .. ($+1)/2]);
+ alias T0 = typeof(a);
+
+ //Get "b"
+ static if (T.length <= 3)
+ alias b = args[$-1];
+ else
+ auto b = max(args[($+1)/2 .. $]);
+ alias T1 = typeof(b);
+
+ import std.algorithm.internal : algoFormat;
+ 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
+ import std.functional : lessThan;
+ immutable chooseB = lessThan!(T0, T1)(a, b);
+ return cast(typeof(return)) (chooseB ? b : a);
+}
+
+///
+@safe unittest
+{
+ int a = 5;
+ short b = 6;
+ double c = 2;
+ auto d = max(a, b);
+ assert(is(typeof(d) == int));
+ assert(d == 6);
+ auto e = min(a, b, c);
+ assert(is(typeof(e) == double));
+ assert(e == 2);
+}
+
+@safe unittest
+{
+ int a = 5;
+ short b = 6;
+ double c = 2;
+ auto d = max(a, b);
+ static assert(is(typeof(d) == int));
+ assert(d == 6);
+ auto e = max(a, b, c);
+ static assert(is(typeof(e) == double));
+ assert(e == 6);
+ // mixed sign
+ a = -5;
+ uint f = 5;
+ static assert(is(typeof(max(a, f)) == uint));
+ assert(max(a, f) == 5);
+
+ //Test user-defined types
+ import std.datetime : Date;
+ assert(max(Date(2012, 12, 21), Date(1982, 1, 4)) == Date(2012, 12, 21));
+ assert(max(Date(1982, 1, 4), Date(2012, 12, 21)) == Date(2012, 12, 21));
+ assert(max(Date(1982, 1, 4), Date.min) == Date(1982, 1, 4));
+ assert(max(Date.min, Date(1982, 1, 4)) == Date(1982, 1, 4));
+ assert(max(Date(1982, 1, 4), Date.max) == Date.max);
+ assert(max(Date.max, Date(1982, 1, 4)) == Date.max);
+ assert(max(Date.min, Date.max) == Date.max);
+ assert(max(Date.max, Date.min) == Date.max);
+}
+
+// MinType
+private template MinType(T...)
+if (T.length >= 1)
+{
+ static if (T.length == 1)
+ {
+ alias MinType = T[0];
+ }
+ else static if (T.length == 2)
+ {
+ static if (!is(typeof(T[0].min)))
+ alias MinType = CommonType!T;
+ else
+ {
+ enum hasMostNegative = is(typeof(mostNegative!(T[0]))) &&
+ is(typeof(mostNegative!(T[1])));
+ static if (hasMostNegative && mostNegative!(T[1]) < mostNegative!(T[0]))
+ alias MinType = T[1];
+ else static if (hasMostNegative && mostNegative!(T[1]) > mostNegative!(T[0]))
+ alias MinType = T[0];
+ else static if (T[1].max < T[0].max)
+ alias MinType = T[1];
+ else
+ alias MinType = T[0];
+ }
+ }
+ else
+ {
+ alias MinType = MinType!(MinType!(T[0 .. ($+1)/2]), MinType!(T[($+1)/2 .. $]));
+ }
+}
+
+// min
+/**
+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)
+{
+ //Get "a"
+ static if (T.length <= 2)
+ alias a = args[0];
+ else
+ auto a = min(args[0 .. ($+1)/2]);
+ alias T0 = typeof(a);
+
+ //Get "b"
+ static if (T.length <= 3)
+ alias b = args[$-1];
+ else
+ auto b = min(args[($+1)/2 .. $]);
+ alias T1 = typeof(b);
+
+ import std.algorithm.internal : algoFormat;
+ 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
+ import std.functional : lessThan;
+ immutable chooseA = lessThan!(T0, T1)(a, b);
+ return cast(typeof(return)) (chooseA ? a : b);
+}
+
+///
+@safe unittest
+{
+ int a = 5;
+ short b = 6;
+ double c = 2;
+ auto d = min(a, b);
+ static assert(is(typeof(d) == int));
+ assert(d == 5);
+ auto e = min(a, b, c);
+ static assert(is(typeof(e) == double));
+ assert(e == 2);
+
+ // 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);
+
+ // 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));
+ assert(min(Date(1982, 1, 4), Date.min) == Date.min);
+ assert(min(Date.min, Date(1982, 1, 4)) == Date.min);
+ assert(min(Date(1982, 1, 4), Date.max) == Date(1982, 1, 4));
+ assert(min(Date.max, Date(1982, 1, 4)) == Date(1982, 1, 4));
+ assert(min(Date.min, Date.max) == Date.min);
+ assert(min(Date.max, Date.min) == Date.min);
+}
+
+// mismatch
+/**
+Sequentially compares elements in $(D r1) and $(D r2) in lockstep, and
+stops at the first mismatch (according to $(D pred), by default
+equality). Returns a tuple with the reduced ranges that start with the
+two mismatched values. Performs $(BIGOH min(r1.length, r2.length))
+evaluations of $(D pred).
+
+See_Also:
+ $(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))
+{
+ for (; !r1.empty && !r2.empty; r1.popFront(), r2.popFront())
+ {
+ if (!binaryFun!(pred)(r1.front, r2.front)) break;
+ }
+ return tuple(r1, r2);
+}
+
+///
+@safe unittest
+{
+ int[] x = [ 1, 5, 2, 7, 4, 3 ];
+ double[] y = [ 1.0, 5, 2, 7.3, 4, 8 ];
+ auto m = mismatch(x, y);
+ assert(m[0] == x[3 .. $]);
+ assert(m[1] == y[3 .. $]);
+}
+
+@safe unittest
+{
+ int[] a = [ 1, 2, 3 ];
+ int[] b = [ 1, 2, 4, 5 ];
+ auto mm = mismatch(a, b);
+ assert(mm[0] == [3]);
+ assert(mm[1] == [4, 5]);
+}
+
+/**
+Returns one of a collection of expressions based on the value of the switch
+expression.
+
+$(D choices) needs to be composed of pairs of test expressions and return
+expressions. Each test-expression is compared with $(D switchExpression) using
+$(D pred)($(D switchExpression) is the first argument) and if that yields true
+- the return expression is returned.
+
+Both the test and the return expressions are lazily evaluated.
+
+Params:
+
+switchExpression = The first argument for the predicate.
+
+choices = Pairs of test expressions and return expressions. The test
+expressions will be the second argument for the predicate, and the return
+expression will be returned if the predicate yields true with $(D
+switchExpression) and the test expression as arguments. May also have a
+default return expression, that needs to be the last expression without a test
+expression before it. A return expression may be of void type only if it
+always throws.
+
+Returns: The return expression associated with the first test expression that
+made the predicate yield true, or the default return expression if no test
+expression matched.
+
+Throws: If there is no default return expression and the predicate does not
+yield true with any test expression - $(D SwitchError) is thrown. $(D
+SwitchError) is also thrown if a void return expression was executed without
+throwing anything.
+*/
+auto predSwitch(alias pred = "a == b", T, R ...)(T switchExpression, lazy R choices)
+{
+ import core.exception : SwitchError;
+ alias predicate = binaryFun!(pred);
+
+ foreach (index, ChoiceType; R)
+ {
+ //The even places in `choices` are for the predicate.
+ static if (index % 2 == 1)
+ {
+ if (predicate(switchExpression, choices[index - 1]()))
+ {
+ static if (is(typeof(choices[index]()) == void))
+ {
+ choices[index]();
+ throw new SwitchError("Choices that return void should throw");
+ }
+ else
+ {
+ return choices[index]();
+ }
+ }
+ }
+ }
+
+ //In case nothing matched:
+ static if (R.length % 2 == 1) //If there is a default return expression:
+ {
+ static if (is(typeof(choices[$ - 1]()) == void))
+ {
+ choices[$ - 1]();
+ throw new SwitchError("Choices that return void should throw");
+ }
+ else
+ {
+ return choices[$ - 1]();
+ }
+ }
+ else //If there is no default return expression:
+ {
+ throw new SwitchError("Input not matched by any pattern");
+ }
+}
+
+///
+@safe unittest
+{
+ string res = 2.predSwitch!"a < b"(
+ 1, "less than 1",
+ 5, "less than 5",
+ 10, "less than 10",
+ "greater or equal to 10");
+
+ assert(res == "less than 5");
+
+ //The arguments are lazy, which allows us to use predSwitch to create
+ //recursive functions:
+ int factorial(int n)
+ {
+ return n.predSwitch!"a <= b"(
+ -1, {throw new Exception("Can not calculate n! for n < 0");}(),
+ 0, 1, // 0! = 1
+ n * factorial(n - 1) // n! = n * (n - 1)! for n >= 0
+ );
+ }
+ assert(factorial(3) == 6);
+
+ //Void return expressions are allowed if they always throw:
+ import std.exception : assertThrown;
+ assertThrown!Exception(factorial(-9));
+}
+
+@system unittest
+{
+ import core.exception : SwitchError;
+ import std.exception : assertThrown;
+
+ //Nothing matches - with default return expression:
+ assert(20.predSwitch!"a < b"(
+ 1, "less than 1",
+ 5, "less than 5",
+ 10, "less than 10",
+ "greater or equal to 10") == "greater or equal to 10");
+
+ //Nothing matches - without default return expression:
+ assertThrown!SwitchError(20.predSwitch!"a < b"(
+ 1, "less than 1",
+ 5, "less than 5",
+ 10, "less than 10",
+ ));
+
+ //Using the default predicate:
+ assert(2.predSwitch(
+ 1, "one",
+ 2, "two",
+ 3, "three",
+ ) == "two");
+
+ //Void return expressions must always throw:
+ assertThrown!SwitchError(1.predSwitch(
+ 0, "zero",
+ 1, {}(), //A void return expression that doesn't throw
+ 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/libphobos/src/std/algorithm/internal.d b/libphobos/src/std/algorithm/internal.d
new file mode 100644
index 0000000..ca03fd7
--- /dev/null
+++ b/libphobos/src/std/algorithm/internal.d
@@ -0,0 +1,77 @@
+// Written in the D programming language.
+
+/// Helper functions for std.algorithm package.
+module std.algorithm.internal;
+
+
+// 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 algoFormat()
+{
+ import std.format : format;
+ alias algoFormat = format;
+}
+
+// Internal random array generators
+version (unittest)
+{
+ package enum size_t maxArraySize = 50;
+ package enum size_t minArraySize = maxArraySize - 1;
+
+ package string[] rndstuff(T : string)()
+ {
+ import std.random : Random, unpredictableSeed, uniform;
+
+ static Random rnd;
+ static bool first = true;
+ if (first)
+ {
+ rnd = Random(unpredictableSeed);
+ first = false;
+ }
+ string[] result =
+ new string[uniform(minArraySize, maxArraySize, rnd)];
+ string alpha = "abcdefghijABCDEFGHIJ";
+ foreach (ref s; result)
+ {
+ foreach (i; 0 .. uniform(0u, 20u, rnd))
+ {
+ auto j = uniform(0, alpha.length - 1, rnd);
+ s ~= alpha[j];
+ }
+ }
+ return result;
+ }
+
+ package int[] rndstuff(T : int)()
+ {
+ import std.random : Random, unpredictableSeed, uniform;
+
+ static Random rnd;
+ static bool first = true;
+ if (first)
+ {
+ rnd = Random(unpredictableSeed);
+ first = false;
+ }
+ int[] result = new int[uniform(minArraySize, maxArraySize, rnd)];
+ foreach (ref i; result)
+ {
+ i = uniform(-100, 100, rnd);
+ }
+ return result;
+ }
+
+ package double[] rndstuff(T : double)()
+ {
+ double[] result;
+ foreach (i; rndstuff!(int)())
+ {
+ result ~= i / 50.0;
+ }
+ return result;
+ }
+}
+
+package(std) T* addressOf(T)(ref T val) { return &val; }
diff --git a/libphobos/src/std/algorithm/iteration.d b/libphobos/src/std/algorithm/iteration.d
new file mode 100644
index 0000000..7e57824
--- /dev/null
+++ b/libphobos/src/std/algorithm/iteration.d
@@ -0,0 +1,5187 @@
+// Written in the D programming language.
+/**
+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 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 => 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)).)
+$(T2 joiner,
+ $(D joiner(["hello", "world!"], "; ")) returns a range that iterates
+ over the characters $(D "hello; world!"). No new string is created -
+ the existing inputs are iterated.)
+$(T2 map,
+ $(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) => 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 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: $(HTTP boost.org/LICENSE_1_0.txt, Boost License 1.0).
+
+Authors: $(HTTP erdani.com, Andrei Alexandrescu)
+
+Source: $(PHOBOSSRC std/algorithm/_iteration.d)
+
+Macros:
+T2=$(TR $(TDNW $(LREF $1)) $(TD $+))
+ */
+module std.algorithm.iteration;
+
+// FIXME
+import std.functional; // : unaryFun, binaryFun;
+import std.range.primitives;
+import std.traits;
+
+private template aggregate(fun...)
+if (fun.length >= 1)
+{
+ /* --Intentionally not ddoc--
+ * Aggregates elements in each subrange of the given range of ranges using
+ * the given aggregating function(s).
+ * Params:
+ * fun = One or more aggregating functions (binary functions that return a
+ * single _aggregate value of their arguments).
+ * ror = A range of ranges to be aggregated.
+ *
+ * Returns:
+ * A range representing the aggregated value(s) of each subrange
+ * of the original range. If only one aggregating function is specified,
+ * each element will be the aggregated value itself; if multiple functions
+ * are specified, each element will be a tuple of the aggregated values of
+ * each respective function.
+ */
+ auto aggregate(RoR)(RoR ror)
+ if (isInputRange!RoR && isIterable!(ElementType!RoR))
+ {
+ return ror.map!(reduce!fun);
+ }
+
+ @safe unittest
+ {
+ import std.algorithm.comparison : equal, max, min;
+
+ auto data = [[4, 2, 1, 3], [4, 9, -1, 3, 2], [3]];
+
+ // Single aggregating function
+ auto agg1 = data.aggregate!max;
+ assert(agg1.equal([4, 9, 3]));
+
+ // Multiple aggregating functions
+ import std.typecons : tuple;
+ auto agg2 = data.aggregate!(max, min);
+ assert(agg2.equal([
+ tuple(4, 1),
+ tuple(9, -1),
+ tuple(3, 3)
+ ]));
+ }
+}
+
+/++
+$(D cache) eagerly evaluates $(D front) of $(D range)
+on each construction or call to $(D popFront),
+to store the result in a cache.
+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 $(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
+$(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.
+
+$(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 $(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)
+{
+ return _Cache!(Range, false)(range);
+}
+
+/// ditto
+auto cacheBidirectional(Range)(Range range)
+if (isBidirectionalRange!Range)
+{
+ return _Cache!(Range, true)(range);
+}
+
+///
+@safe unittest
+{
+ import std.algorithm.comparison : equal;
+ import std.range, std.stdio;
+ import std.typecons : tuple;
+
+ ulong counter = 0;
+ double fun(int x)
+ {
+ ++counter;
+ // http://en.wikipedia.org/wiki/Quartic_function
+ return ( (x + 4.0) * (x + 1.0) * (x - 1.0) * (x - 3.0) ) / 14.0 + 0.5;
+ }
+ // Without cache, with array (greedy)
+ auto result1 = iota(-4, 5).map!(a =>tuple(a, fun(a)))()
+ .filter!(a => a[1] < 0)()
+ .map!(a => a[0])()
+ .array();
+
+ // the values of x that have a negative y are:
+ assert(equal(result1, [-3, -2, 2]));
+
+ // Check how many times fun was evaluated.
+ // As many times as the number of items in both source and result.
+ assert(counter == iota(-4, 5).length + result1.length);
+
+ counter = 0;
+ // Without array, with cache (lazy)
+ auto result2 = iota(-4, 5).map!(a =>tuple(a, fun(a)))()
+ .cache()
+ .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]));
+
+ // Check how many times fun was evaluated.
+ // Only as many times as the number of items in source.
+ assert(counter == iota(-4, 5).length);
+}
+
+/++
+Tip: $(D cache) is eager when evaluating elements. If calling front on the
+underlying _range has a side effect, it will be observable before calling
+front on the actual cached _range.
+
+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.
+If calling front has no side effect though, placing $(D take) after $(D cache)
+may yield a faster _range.
+
+Either way, the resulting ranges will be equivalent, but maybe not at the
+same cost or side effects.
++/
+@safe unittest
+{
+ import std.algorithm.comparison : equal;
+ import std.range;
+ int i = 0;
+
+ auto r = iota(0, 4).tee!((a){i = a;}, No.pipeOnPop);
+ auto r1 = r.take(3).cache();
+ auto r2 = r.cache().take(3);
+
+ assert(equal(r1, [0, 1, 2]));
+ assert(i == 2); //The last "seen" element was 2. The data in cache has been cleared.
+
+ assert(equal(r2, [0, 1, 2]));
+ assert(i == 3); //cache has accessed 3. It is still stored internally by cache.
+}
+
+@safe unittest
+{
+ import std.algorithm.comparison : equal;
+ import std.range;
+ auto a = [1, 2, 3, 4];
+ 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]));
+ assert(equal(r2, [2, 3, 4]));
+}
+
+@safe unittest
+{
+ import std.algorithm.comparison : equal;
+
+ //immutable test
+ static struct S
+ {
+ int i;
+ this(int i)
+ {
+ //this.i = i;
+ }
+ }
+ immutable(S)[] s = [S(1), S(2), S(3)];
+ assert(equal(s.cache(), s));
+ assert(equal(s.cacheBidirectional(), s));
+}
+
+@safe pure nothrow unittest
+{
+ import std.algorithm.comparison : equal;
+
+ //safety etc
+ auto a = [1, 2, 3, 4];
+ assert(equal(a.cache(), a));
+ assert(equal(a.cacheBidirectional(), a));
+}
+
+@safe unittest
+{
+ char[][] stringbufs = ["hello".dup, "world".dup];
+ auto strings = stringbufs.map!((a)=>a.idup)().cache();
+ assert(strings.front is strings.front);
+}
+
+@safe unittest
+{
+ import std.range : cycle;
+ import std.algorithm.comparison : equal;
+
+ auto c = [1, 2, 3].cycle().cache();
+ c = c[1 .. $];
+ auto d = c[0 .. 1];
+ assert(d.equal([2]));
+}
+
+@safe unittest
+{
+ static struct Range
+ {
+ bool initialized = false;
+ bool front() @property {return initialized = true;}
+ void popFront() {initialized = false;}
+ enum empty = false;
+ }
+ auto r = Range().cache();
+ assert(r.source.initialized == true);
+}
+
+private struct _Cache(R, bool bidir)
+{
+ import core.exception : RangeError;
+
+ private
+ {
+ import std.algorithm.internal : algoFormat;
+ import std.meta : AliasSeq;
+
+ alias E = ElementType!R;
+ alias UE = Unqual!E;
+
+ R source;
+
+ 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
+ )
+ );
+ }
+
+ this(R range)
+ {
+ source = range;
+ if (!range.empty)
+ {
+ caches[0] = source.front;
+ static if (bidir)
+ caches[1] = source.back;
+ }
+ }
+
+ static if (isInfinite!R)
+ enum empty = false;
+ else
+ bool empty() @property
+ {
+ return source.empty;
+ }
+
+ static if (hasLength!R) auto length() @property
+ {
+ return source.length;
+ }
+
+ E front() @property
+ {
+ version (assert) if (empty) throw new RangeError();
+ return caches[0];
+ }
+ static if (bidir) E back() @property
+ {
+ version (assert) if (empty) throw new RangeError();
+ return caches[1];
+ }
+
+ void popFront()
+ {
+ version (assert) if (empty) throw new RangeError();
+ source.popFront();
+ if (!source.empty)
+ caches[0] = source.front;
+ else
+ caches = CacheTypes.init;
+ }
+ static if (bidir) void popBack()
+ {
+ version (assert) if (empty) throw new RangeError();
+ source.popBack();
+ if (!source.empty)
+ caches[1] = source.back;
+ else
+ caches = CacheTypes.init;
+ }
+
+ static if (isForwardRange!R)
+ {
+ private this(R source, ref CacheTypes caches)
+ {
+ this.source = source;
+ this.caches = caches;
+ }
+ typeof(this) save() @property
+ {
+ return typeof(this)(source.save, caches);
+ }
+ }
+
+ static if (hasSlicing!R)
+ {
+ enum hasEndSlicing = is(typeof(source[size_t.max .. $]));
+
+ static if (hasEndSlicing)
+ {
+ private static struct DollarToken{}
+ enum opDollar = DollarToken.init;
+
+ auto opSlice(size_t low, DollarToken)
+ {
+ return typeof(this)(source[low .. $]);
+ }
+ }
+
+ static if (!isInfinite!R)
+ {
+ typeof(this) opSlice(size_t low, size_t high)
+ {
+ return typeof(this)(source[low .. high]);
+ }
+ }
+ else static if (hasEndSlicing)
+ {
+ auto opSlice(size_t low, size_t high)
+ in
+ {
+ assert(low <= high, "Bounds error when slicing cache.");
+ }
+ body
+ {
+ import std.range : takeExactly;
+ return this[low .. $].takeExactly(high - low);
+ }
+ }
+ }
+}
+
+/**
+$(D auto map(Range)(Range r) if (isInputRange!(Unqual!Range));)
+
+Implements the homonym function (also known as $(D transform)) present
+in many languages of functional flavor. The call $(D map!(fun)(range))
+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:
+ $(HTTP en.wikipedia.org/wiki/Map_(higher-order_function), Map (higher-order function))
+*/
+template map(fun...)
+if (fun.length >= 1)
+{
+ auto map(Range)(Range r) if (isInputRange!(Unqual!Range))
+ {
+ import std.meta : AliasSeq, staticMap;
+
+ alias RE = ElementType!(Range);
+ static if (fun.length > 1)
+ {
+ import std.functional : adjoin;
+ import std.meta : staticIndexOf;
+
+ alias _funs = staticMap!(unaryFun, fun);
+ alias _fun = adjoin!_funs;
+
+ // 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);
+
+ // 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);
+ }
+}
+
+///
+@safe unittest
+{
+ import std.algorithm.comparison : equal;
+ import std.range : chain;
+ int[] arr1 = [ 1, 2, 3, 4 ];
+ int[] arr2 = [ 5, 6 ];
+ auto squares = map!(a => a * a)(chain(arr1, arr2));
+ assert(equal(squares, [ 1, 4, 9, 16, 25, 36 ]));
+}
+
+/**
+Multiple functions can be passed to $(D map). In that case, the
+element type of $(D map) is a tuple containing one element for each
+function.
+*/
+@safe unittest
+{
+ auto sums = [2, 4, 6, 8];
+ auto products = [1, 4, 9, 16];
+
+ size_t i = 0;
+ foreach (result; [ 1, 2, 3, 4 ].map!("a + a", "a * a"))
+ {
+ assert(result[0] == sums[i]);
+ assert(result[1] == products[i]);
+ ++i;
+ }
+}
+
+/**
+You may alias $(D map) with some function(s) to a symbol and use
+it separately:
+*/
+@safe unittest
+{
+ import std.algorithm.comparison : equal;
+ import std.conv : to;
+
+ alias stringize = map!(to!string);
+ 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;
+ R _input;
+
+ static if (isBidirectionalRange!R)
+ {
+ @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();
+ }
+ }
+
+ this(R input)
+ {
+ _input = input;
+ }
+
+ static if (isInfinite!R)
+ {
+ // Propagate infinite-ness.
+ enum bool empty = false;
+ }
+ else
+ {
+ @property bool empty()
+ {
+ return _input.empty;
+ }
+ }
+
+ 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);
+ }
+
+ static if (isRandomAccessRange!R)
+ {
+ static if (is(typeof(_input[ulong.max])))
+ private alias opIndex_t = ulong;
+ else
+ private alias opIndex_t = uint;
+
+ auto ref opIndex(opIndex_t index)
+ {
+ return fun(_input[index]);
+ }
+ }
+
+ static if (hasLength!R)
+ {
+ @property auto length()
+ {
+ return _input.length;
+ }
+
+ alias opDollar = length;
+ }
+
+ static if (hasSlicing!R)
+ {
+ static if (is(typeof(_input[ulong.max .. ulong.max])))
+ private alias opSlice_t = ulong;
+ else
+ private alias opSlice_t = uint;
+
+ static if (hasLength!R)
+ {
+ auto opSlice(opSlice_t low, opSlice_t high)
+ {
+ return typeof(this)(_input[low .. high]);
+ }
+ }
+ else static if (is(typeof(_input[opSlice_t.max .. $])))
+ {
+ struct DollarToken{}
+ enum opDollar = DollarToken.init;
+ auto opSlice(opSlice_t low, DollarToken)
+ {
+ return typeof(this)(_input[low .. $]);
+ }
+
+ auto opSlice(opSlice_t low, opSlice_t high)
+ {
+ import std.range : takeExactly;
+ return this[low .. $].takeExactly(high - low);
+ }
+ }
+ }
+
+ static if (isForwardRange!R)
+ {
+ @property auto save()
+ {
+ return typeof(this)(_input.save);
+ }
+ }
+}
+
+@safe unittest
+{
+ import std.algorithm.comparison : equal;
+ import std.conv : to;
+ import std.functional : adjoin;
+
+ alias stringize = map!(to!string);
+ assert(equal(stringize([ 1, 2, 3, 4 ]), [ "1", "2", "3", "4" ]));
+
+ uint counter;
+ alias count = map!((a) { return counter++; });
+ assert(equal(count([ 10, 2, 30, 4 ]), [ 0, 1, 2, 3 ]));
+
+ counter = 0;
+ adjoin!((a) { return counter++; }, (a) { return counter++; })(1);
+ alias countAndSquare = map!((a) { return counter++; }, (a) { return counter++; });
+ //assert(equal(countAndSquare([ 10, 2 ]), [ tuple(0u, 100), tuple(1u, 4) ]));
+}
+
+@safe unittest
+{
+ import std.algorithm.comparison : equal;
+ import std.ascii : toUpper;
+ import std.internal.test.dummyrange;
+ import std.range;
+ import std.typecons : tuple;
+ import std.random : unpredictableSeed, uniform, Random;
+
+ int[] arr1 = [ 1, 2, 3, 4 ];
+ const int[] arr1Const = arr1;
+ int[] arr2 = [ 5, 6 ];
+ auto squares = map!("a * a")(arr1Const);
+ assert(squares[$ - 1] == 16);
+ assert(equal(squares, [ 1, 4, 9, 16 ][]));
+ assert(equal(map!("a * a")(chain(arr1, arr2)), [ 1, 4, 9, 16, 25, 36 ][]));
+
+ // Test the caching stuff.
+ assert(squares.back == 16);
+ auto squares2 = squares.save;
+ assert(squares2.back == 16);
+
+ assert(squares2.front == 1);
+ squares2.popFront();
+ assert(squares2.front == 4);
+ squares2.popBack();
+ assert(squares2.front == 4);
+ assert(squares2.back == 9);
+
+ assert(equal(map!("a * a")(chain(arr1, arr2)), [ 1, 4, 9, 16, 25, 36 ][]));
+
+ uint i;
+ foreach (e; map!("a", "a * a")(arr1))
+ {
+ assert(e[0] == ++i);
+ assert(e[1] == i * i);
+ }
+
+ // Test length.
+ assert(squares.length == 4);
+ assert(map!"a * a"(chain(arr1, arr2)).length == 6);
+
+ // Test indexing.
+ assert(squares[0] == 1);
+ assert(squares[1] == 4);
+ assert(squares[2] == 9);
+ assert(squares[3] == 16);
+
+ // Test slicing.
+ auto squareSlice = squares[1 .. squares.length - 1];
+ assert(equal(squareSlice, [4, 9][]));
+ assert(squareSlice.back == 9);
+ assert(squareSlice[1] == 9);
+
+ // Test on a forward range to make sure it compiles when all the fancy
+ // stuff is disabled.
+ auto fibsSquares = map!"a * a"(recurrence!("a[n-1] + a[n-2]")(1, 1));
+ assert(fibsSquares.front == 1);
+ fibsSquares.popFront();
+ fibsSquares.popFront();
+ assert(fibsSquares.front == 4);
+ fibsSquares.popFront();
+ assert(fibsSquares.front == 9);
+
+ auto repeatMap = map!"a"(repeat(1));
+ auto gen = Random(unpredictableSeed);
+ auto index = uniform(0, 1024, gen);
+ static assert(isInfinite!(typeof(repeatMap)));
+ assert(repeatMap[index] == 1);
+
+ auto intRange = map!"a"([1,2,3]);
+ static assert(isRandomAccessRange!(typeof(intRange)));
+ assert(equal(intRange, [1, 2, 3]));
+
+ foreach (DummyType; AllDummyRanges)
+ {
+ DummyType d;
+ auto m = map!"a * a"(d);
+
+ static assert(propagatesRangeType!(typeof(m), DummyType));
+ assert(equal(m, [1,4,9,16,25,36,49,64,81,100]));
+ }
+
+ //Test string access
+ string s1 = "hello world!";
+ dstring s2 = "日本語";
+ dstring s3 = "hello world!"d;
+ auto ms1 = map!(std.ascii.toUpper)(s1);
+ auto ms2 = map!(std.ascii.toUpper)(s2);
+ auto ms3 = map!(std.ascii.toUpper)(s3);
+ 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"));
+
+ // Issue 5753
+ static void voidFun(int) {}
+ static int nonvoidFun(int) { return 0; }
+ static assert(!__traits(compiles, map!voidFun([1])));
+ 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
+{
+ import std.algorithm.comparison : equal;
+ import std.range;
+ auto LL = iota(1L, 4L);
+ auto m = map!"a*a"(LL);
+ assert(equal(m, [1L, 4L, 9L]));
+}
+
+@safe unittest
+{
+ import std.range : iota;
+
+ // Issue #10130 - map of iota with const step.
+ const step = 2;
+ 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;
+ assert(map!(i => i)(iota(floatBegin, floatEnd, floatStep)).walkLength == 50);
+}
+
+@safe unittest
+{
+ import std.algorithm.comparison : equal;
+ import std.range;
+ //slicing infinites
+ auto rr = iota(0, 5).cycle().map!"a * a"();
+ alias RR = typeof(rr);
+ static assert(hasSlicing!RR);
+ rr = rr[6 .. $]; //Advances 1 cycle and 1 unit
+ assert(equal(rr[0 .. 5], [1, 4, 9, 16, 0]));
+}
+
+@safe unittest
+{
+ import std.range;
+ struct S {int* p;}
+ auto m = immutable(S).init.repeat().map!"a".save;
+ assert(m.front == immutable(S)(null));
+}
+
+// each
+/**
+Eagerly iterates over $(D r) and calls $(D pred) over _each element.
+
+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. $(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: $(REF tee, std,range)
+
+ */
+template each(alias pred = "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)));
+
+ enum isRangeBinaryIterable(R) =
+ is(typeof(binaryFun!BinaryArgs(0, R.init.front)));
+
+ enum isRangeIterable(R) =
+ isInputRange!R &&
+ (isRangeUnaryIterable!R || isRangeBinaryIterable!R);
+
+ enum isForeachUnaryIterable(R) =
+ is(typeof((R r) {
+ foreach (ref a; r)
+ cast(void) unaryFun!pred(a);
+ }));
+
+ enum isForeachBinaryIterable(R) =
+ is(typeof((R r) {
+ 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 (!isForeachIterable!Range && (
+ isRangeIterable!Range ||
+ __traits(compiles, typeof(r.front).length)))
+ {
+ static if (isRangeIterable!Range)
+ {
+ debug(each) pragma(msg, "Using while for ", Range.stringof);
+ static if (isRangeUnaryIterable!Range)
+ {
+ while (!r.empty)
+ {
+ cast(void) unaryFun!pred(r.front);
+ r.popFront();
+ }
+ }
+ else // if (isRangeBinaryIterable!Range)
+ {
+ 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)(auto ref Iterable r)
+ if (isForeachIterable!Iterable ||
+ __traits(compiles, Parameters!(Parameters!(r.opApply))))
+ {
+ static if (isForeachIterable!Iterable)
+ {
+ 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
+ {
+ // 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);
+ }
+ }
+}
+
+///
+@system unittest
+{
+ import std.range : iota;
+
+ long[] arr;
+ iota(5).each!(n => arr ~= n);
+ assert(arr == [0, 1, 2, 3, 4]);
+
+ // If the range supports it, the value can be mutated in place
+ arr.each!((ref n) => n++);
+ assert(arr == [1, 2, 3, 4, 5]);
+
+ 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++))));
+
+ // The default predicate consumes the range
+ auto m = arr.map!(n => n);
+ (&m).each();
+ assert(m.empty);
+
+ // Indexes are also available for in-place mutations
+ arr[] = 0;
+ arr.each!"a=i"();
+ assert(arr == [0, 1, 2, 3, 4]);
+
+ // 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. 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
+ range = Input range of elements
+
+Returns:
+ $(D filter!(predicate)(range)) returns a new range containing only elements $(D x) in $(D range) for
+ which $(D predicate(x)) returns $(D true).
+
+See_Also:
+ $(HTTP en.wikipedia.org/wiki/Filter_(higher-order_function), Filter (higher-order function))
+ */
+template filter(alias predicate)
+if (is(typeof(unaryFun!predicate)))
+{
+ auto filter(Range)(Range range) if (isInputRange!(Unqual!Range))
+ {
+ return FilterResult!(unaryFun!predicate, Range)(range);
+ }
+}
+
+///
+@safe unittest
+{
+ import std.algorithm.comparison : equal;
+ import std.math : approxEqual;
+ import std.range;
+
+ int[] arr = [ 1, 2, 3, 4, 5 ];
+
+ // Sum all elements
+ auto small = filter!(a => a < 3)(arr);
+ assert(equal(small, [ 1, 2 ]));
+
+ // Sum again, but with Uniform Function Call Syntax (UFCS)
+ auto sum = arr.filter!(a => a < 3);
+ assert(equal(sum, [ 1, 2 ]));
+
+ // In combination with chain() to span multiple ranges
+ int[] a = [ 3, -2, 400 ];
+ int[] b = [ 100, -101, 102 ];
+ auto r = chain(a, b).filter!(a => a > 0);
+ assert(equal(r, [ 3, 400, 100, 102 ]));
+
+ // Mixing convertible types is fair game, too
+ double[] c = [ 2.5, 3.0 ];
+ auto r1 = chain(c, a, b).filter!(a => cast(int) a != a);
+ assert(approxEqual(r1, [ 2.5 ]));
+}
+
+private struct FilterResult(alias pred, Range)
+{
+ alias R = Unqual!Range;
+ R _input;
+ private bool _primed;
+
+ private void prime()
+ {
+ 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; }
+
+ static if (isInfinite!Range)
+ {
+ enum bool empty = false;
+ }
+ else
+ {
+ @property bool empty() { prime; return _input.empty; }
+ }
+
+ void popFront()
+ {
+ do
+ {
+ _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;
+ }
+
+ static if (isForwardRange!R)
+ {
+ @property auto save()
+ {
+ return typeof(this)(_input.save, _primed);
+ }
+ }
+}
+
+@safe unittest
+{
+ import std.algorithm.comparison : equal;
+ import std.internal.test.dummyrange;
+ import std.range;
+
+ auto shouldNotLoop4ever = repeat(1).filter!(x => x % 2 == 0);
+ static assert(isInfinite!(typeof(shouldNotLoop4ever)));
+ assert(!shouldNotLoop4ever.empty);
+
+ int[] a = [ 3, 4, 2 ];
+ auto r = filter!("a > 3")(a);
+ static assert(isForwardRange!(typeof(r)));
+ assert(equal(r, [ 4 ][]));
+
+ a = [ 1, 22, 3, 42, 5 ];
+ auto under10 = filter!("a < 10")(a);
+ assert(equal(under10, [1, 3, 5][]));
+ static assert(isForwardRange!(typeof(under10)));
+ under10.front = 4;
+ assert(equal(under10, [4, 3, 5][]));
+ under10.front = 40;
+ assert(equal(under10, [40, 3, 5][]));
+ under10.front = 1;
+
+ auto infinite = filter!"a > 2"(repeat(3));
+ static assert(isInfinite!(typeof(infinite)));
+ static assert(isForwardRange!(typeof(infinite)));
+ assert(infinite.front == 3);
+
+ foreach (DummyType; AllDummyRanges)
+ {
+ DummyType d;
+ auto f = filter!"a & 1"(d);
+ assert(equal(f, [1,3,5,7,9]));
+
+ static if (isForwardRange!DummyType)
+ {
+ static assert(isForwardRange!(typeof(f)));
+ }
+ }
+
+ // With delegates
+ int x = 10;
+ int overX(int a) { return a > x; }
+ typeof(filter!overX(a)) getFilter()
+ {
+ return filter!overX(a);
+ }
+ auto r1 = getFilter();
+ assert(equal(r1, [22, 42]));
+
+ // With chain
+ auto nums = [0,1,2,3,4];
+ assert(equal(filter!overX(chain(a, nums)), [22, 42]));
+
+ // With copying of inner struct Filter to Map
+ auto arr = [1,2,3,4,5];
+ auto m = map!"a + 1"(filter!"a < 4"(arr));
+ assert(equal(m, [2, 3, 4]));
+}
+
+@safe unittest
+{
+ import std.algorithm.comparison : equal;
+
+ int[] a = [ 3, 4 ];
+ const aConst = a;
+ auto r = filter!("a > 3")(aConst);
+ assert(equal(r, [ 4 ][]));
+
+ a = [ 1, 22, 3, 42, 5 ];
+ auto under10 = filter!("a < 10")(a);
+ assert(equal(under10, [1, 3, 5][]));
+ assert(equal(under10.save, [1, 3, 5][]));
+ assert(equal(under10.save, under10));
+}
+
+@safe unittest
+{
+ import std.algorithm.comparison : equal;
+ import std.functional : compose, pipe;
+
+ assert(equal(compose!(map!"2 * a", filter!"a & 1")([1,2,3,4,5]),
+ [2,6,10]));
+ assert(equal(pipe!(filter!"a & 1", map!"2 * a")([1,2,3,4,5]),
+ [2,6,10]));
+}
+
+@safe unittest
+{
+ import std.algorithm.comparison : equal;
+
+ int x = 10;
+ int underX(int a) { return a < x; }
+ const(int)[] list = [ 1, 2, 10, 11, 3, 4 ];
+ assert(equal(filter!underX(list), [ 1, 2, 3, 4 ]));
+}
+
+/**
+ * $(D auto filterBidirectional(Range)(Range r) if (isBidirectionalRange!(Unqual!Range));)
+ *
+ * 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,
+ * $(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)
+{
+ auto filterBidirectional(Range)(Range r) if (isBidirectionalRange!(Unqual!Range))
+ {
+ return FilterBidiResult!(unaryFun!pred, Range)(r);
+ }
+}
+
+///
+@safe unittest
+{
+ import std.algorithm.comparison : equal;
+ import std.range;
+
+ int[] arr = [ 1, 2, 3, 4, 5 ];
+ auto small = filterBidirectional!("a < 3")(arr);
+ static assert(isBidirectionalRange!(typeof(small)));
+ assert(small.back == 2);
+ assert(equal(small, [ 1, 2 ]));
+ assert(equal(retro(small), [ 2, 1 ]));
+ // In combination with chain() to span multiple ranges
+ int[] a = [ 3, -2, 400 ];
+ int[] b = [ 100, -101, 102 ];
+ auto r = filterBidirectional!("a > 0")(chain(a, b));
+ assert(r.back == 102);
+}
+
+private struct FilterBidiResult(alias pred, Range)
+{
+ alias R = Unqual!Range;
+ R _input;
+
+ this(R r)
+ {
+ _input = r;
+ while (!_input.empty && !pred(_input.front)) _input.popFront();
+ while (!_input.empty && !pred(_input.back)) _input.popBack();
+ }
+
+ @property bool empty() { return _input.empty; }
+
+ void popFront()
+ {
+ do
+ {
+ _input.popFront();
+ } while (!_input.empty && !pred(_input.front));
+ }
+
+ @property auto ref front()
+ {
+ assert(!empty, "Attempting to fetch the front of an empty filterBidirectional.");
+ return _input.front;
+ }
+
+ void popBack()
+ {
+ do
+ {
+ _input.popBack();
+ } while (!_input.empty && !pred(_input.back));
+ }
+
+ @property auto ref back()
+ {
+ assert(!empty, "Attempting to fetch the back of an empty filterBidirectional.");
+ return _input.back;
+ }
+
+ @property auto save()
+ {
+ return typeof(this)(_input.save);
+ }
+}
+
+/**
+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;
+
+ private alias comp = binaryFun!pred;
+
+ private alias E = ElementType!R;
+ static if ((is(E == class) || is(E == interface)) &&
+ (is(E == const) || is(E == immutable)))
+ {
+ private alias MutableE = Rebindable!E;
+ }
+ else static if (is(E : Unqual!E))
+ {
+ private alias MutableE = Unqual!E;
+ }
+ else
+ {
+ private alias MutableE = E;
+ }
+
+ private R _input;
+ private Tuple!(MutableE, uint) _current;
+
+ ///
+ this(R input)
+ {
+ _input = input;
+ if (!_input.empty) popFront();
+ }
+
+ ///
+ void popFront()
+ {
+ if (_input.empty)
+ {
+ _current[1] = 0;
+ }
+ else
+ {
+ _current = tuple(_input.front, 1u);
+ _input.popFront();
+ while (!_input.empty && comp(_current[0], _input.front))
+ {
+ ++_current[1];
+ _input.popFront();
+ }
+ }
+ }
+
+ static if (isInfinite!R)
+ {
+ ///
+ enum bool empty = false; // Propagate infiniteness.
+ }
+ else
+ {
+ ///
+ @property bool empty()
+ {
+ return _current[1] == 0;
+ }
+ }
+
+ ///
+ @property auto ref front()
+ {
+ assert(!empty, "Attempting to fetch the front of an empty Group.");
+ return _current;
+ }
+
+ static if (isForwardRange!R)
+ {
+ ///
+ @property typeof(this) save() {
+ typeof(this) ret = this;
+ ret._input = this._input.save;
+ ret._current = this._current;
+ return ret;
+ }
+ }
+}
+
+///
+@safe unittest
+{
+ import std.algorithm.comparison : equal;
+ import std.typecons : tuple, Tuple;
+
+ 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) ][]));
+}
+
+/**
+ * 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;
+
+ 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)
+ {
+ DummyType d;
+ auto g = group(d);
+
+ static assert(d.rt == RangeType.Input || isForwardRange!(typeof(g)));
+
+ assert(equal(g, [tuple(1, 1u), tuple(2, 1u), tuple(3, 1u), tuple(4, 1u),
+ tuple(5, 1u), tuple(6, 1u), tuple(7, 1u), tuple(8, 1u),
+ tuple(9, 1u), tuple(10, 1u)]));
+ }
+}
+
+@safe unittest
+{
+ import std.algorithm.comparison : equal;
+ import std.typecons : tuple;
+
+ // Issue 13857
+ immutable(int)[] a1 = [1,1,2,2,2,3,4,4,5,6,6,7,8,9,9,9];
+ auto g1 = group(a1);
+ assert(equal(g1, [ tuple(1, 2u), tuple(2, 3u), tuple(3, 1u),
+ tuple(4, 2u), tuple(5, 1u), tuple(6, 2u),
+ tuple(7, 1u), tuple(8, 1u), tuple(9, 3u)
+ ]));
+
+ // Issue 13162
+ immutable(ubyte)[] a2 = [1, 1, 1, 0, 0, 0];
+ auto g2 = a2.group;
+ assert(equal(g2, [ tuple(1, 3u), tuple(0, 3u) ]));
+
+ // Issue 10104
+ const a3 = [1, 1, 2, 2];
+ auto g3 = a3.group;
+ assert(equal(g3, [ tuple(1, 2u), tuple(2, 2u) ]));
+
+ interface I {}
+ class C : I {}
+ const C[] a4 = [new const C()];
+ auto g4 = a4.group!"a is b";
+ assert(g4.front[1] == 1);
+
+ immutable I[] a5 = [new immutable C()];
+ auto g5 = a5.group!"a is b";
+ assert(g5.front[1] == 1);
+
+ const(int[][]) a6 = [[1], [1]];
+ auto g6 = a6.group;
+ assert(equal(g6.front[0], [1]));
+}
+
+// Used by implementation of chunkBy for non-forward input ranges.
+private struct ChunkByChunkImpl(alias pred, Range)
+if (isInputRange!Range && !isForwardRange!Range)
+{
+ alias fun = binaryFun!pred;
+
+ private Range r;
+ private ElementType!Range prev;
+
+ this(Range range, ElementType!Range _prev)
+ {
+ r = range;
+ prev = _prev;
+ }
+
+ @property bool empty()
+ {
+ return r.empty || !fun(prev, r.front);
+ }
+
+ @property ElementType!Range front() { return r.front; }
+ void popFront() { r.popFront(); }
+}
+
+private template ChunkByImplIsUnary(alias pred, Range)
+{
+ static if (is(typeof(binaryFun!pred(ElementType!Range.init,
+ ElementType!Range.init)) : bool))
+ enum ChunkByImplIsUnary = false;
+ else static if (is(typeof(
+ unaryFun!pred(ElementType!Range.init) ==
+ unaryFun!pred(ElementType!Range.init))))
+ enum ChunkByImplIsUnary = true;
+ else
+ static assert(0, "chunkBy expects either a binary predicate or "~
+ "a unary predicate on range elements of type: "~
+ ElementType!Range.stringof);
+}
+
+// Implementation of chunkBy for non-forward input ranges.
+private struct ChunkByImpl(alias pred, Range)
+if (isInputRange!Range && !isForwardRange!Range)
+{
+ enum bool isUnary = ChunkByImplIsUnary!(pred, Range);
+
+ static if (isUnary)
+ alias eq = binaryFun!((a, b) => unaryFun!pred(a) == unaryFun!pred(b));
+ else
+ alias eq = binaryFun!pred;
+
+ private Range r;
+ private ElementType!Range _prev;
+
+ this(Range _r)
+ {
+ r = _r;
+ if (!empty)
+ {
+ // Check reflexivity if predicate is claimed to be an equivalence
+ // relation.
+ assert(eq(r.front, r.front),
+ "predicate is not reflexive");
+
+ // _prev's type may be a nested struct, so must be initialized
+ // directly in the constructor (cannot call savePred()).
+ _prev = r.front;
+ }
+ else
+ {
+ // We won't use _prev, but must be initialized.
+ _prev = typeof(_prev).init;
+ }
+ }
+ @property bool empty() { return r.empty; }
+
+ @property auto front()
+ {
+ static if (isUnary)
+ {
+ import std.typecons : tuple;
+ return tuple(unaryFun!pred(_prev),
+ ChunkByChunkImpl!(eq, Range)(r, _prev));
+ }
+ else
+ {
+ return ChunkByChunkImpl!(eq, Range)(r, _prev);
+ }
+ }
+
+ void popFront()
+ {
+ while (!r.empty)
+ {
+ if (!eq(_prev, r.front))
+ {
+ _prev = r.front;
+ break;
+ }
+ r.popFront();
+ }
+ }
+}
+
+// Single-pass implementation of chunkBy for forward ranges.
+private struct ChunkByImpl(alias pred, Range)
+if (isForwardRange!Range)
+{
+ import std.typecons : RefCounted;
+
+ enum bool isUnary = ChunkByImplIsUnary!(pred, Range);
+
+ static if (isUnary)
+ alias eq = binaryFun!((a, b) => unaryFun!pred(a) == unaryFun!pred(b));
+ else
+ alias eq = binaryFun!pred;
+
+ // Outer range
+ static struct Impl
+ {
+ size_t groupNum;
+ Range current;
+ Range next;
+ }
+
+ // Inner range
+ static struct Group
+ {
+ private size_t groupNum;
+ private Range start;
+ private Range current;
+
+ private RefCounted!Impl mothership;
+
+ this(RefCounted!Impl origin)
+ {
+ groupNum = origin.groupNum;
+
+ start = origin.current.save;
+ current = origin.current.save;
+ assert(!start.empty);
+
+ mothership = origin;
+
+ // Note: this requires reflexivity.
+ assert(eq(start.front, current.front),
+ "predicate is not reflexive");
+ }
+
+ @property bool empty() { return groupNum == size_t.max; }
+ @property auto ref front() { return current.front; }
+
+ void popFront()
+ {
+ current.popFront();
+
+ // Note: this requires transitivity.
+ if (current.empty || !eq(start.front, current.front))
+ {
+ if (groupNum == mothership.groupNum)
+ {
+ // If parent range hasn't moved on yet, help it along by
+ // saving location of start of next Group.
+ mothership.next = current.save;
+ }
+
+ groupNum = size_t.max;
+ }
+ }
+
+ @property auto save()
+ {
+ auto copy = this;
+ copy.current = current.save;
+ return copy;
+ }
+ }
+ static assert(isForwardRange!Group);
+
+ private RefCounted!Impl impl;
+
+ this(Range r)
+ {
+ impl = RefCounted!Impl(0, r, r.save);
+ }
+
+ @property bool empty() { return impl.current.empty; }
+
+ @property auto front()
+ {
+ static if (isUnary)
+ {
+ import std.typecons : tuple;
+ return tuple(unaryFun!pred(impl.current.front), Group(impl));
+ }
+ else
+ {
+ return Group(impl);
+ }
+ }
+
+ void popFront()
+ {
+ // Scan for next group. If we're lucky, one of our Groups would have
+ // already set .next to the start of the next group, in which case the
+ // loop is skipped.
+ while (!impl.next.empty && eq(impl.current.front, impl.next.front))
+ {
+ impl.next.popFront();
+ }
+
+ impl.current = impl.next.save;
+
+ // Indicate to any remaining Groups that we have moved on.
+ impl.groupNum++;
+ }
+
+ @property auto save()
+ {
+ // Note: the new copy of the range will be detached from any existing
+ // satellite Groups, and will not benefit from the .next acceleration.
+ return typeof(this)(impl.current.save);
+ }
+
+ static assert(isForwardRange!(typeof(this)));
+}
+
+@system unittest
+{
+ import std.algorithm.comparison : equal;
+
+ size_t popCount = 0;
+ class RefFwdRange
+ {
+ int[] impl;
+
+ @safe nothrow:
+
+ this(int[] data) { impl = data; }
+ @property bool empty() { return impl.empty; }
+ @property auto ref front() { return impl.front; }
+ void popFront()
+ {
+ impl.popFront();
+ popCount++;
+ }
+ @property auto save() { return new RefFwdRange(impl); }
+ }
+ static assert(isForwardRange!RefFwdRange);
+
+ auto testdata = new RefFwdRange([1, 3, 5, 2, 4, 7, 6, 8, 9]);
+ auto groups = testdata.chunkBy!((a,b) => (a % 2) == (b % 2));
+ auto outerSave1 = groups.save;
+
+ // Sanity test
+ assert(groups.equal!equal([[1, 3, 5], [2, 4], [7], [6, 8], [9]]));
+ assert(groups.empty);
+
+ // Performance test for single-traversal use case: popFront should not have
+ // been called more times than there are elements if we traversed the
+ // segmented range exactly once.
+ assert(popCount == 9);
+
+ // Outer range .save test
+ groups = outerSave1.save;
+ assert(!groups.empty);
+
+ // Inner range .save test
+ auto grp1 = groups.front.save;
+ auto grp1b = grp1.save;
+ assert(grp1b.equal([1, 3, 5]));
+ assert(grp1.save.equal([1, 3, 5]));
+
+ // Inner range should remain consistent after outer range has moved on.
+ groups.popFront();
+ assert(grp1.save.equal([1, 3, 5]));
+
+ // Inner range should not be affected by subsequent inner ranges.
+ assert(groups.front.equal([2, 4]));
+ assert(grp1.save.equal([1, 3, 5]));
+}
+
+/**
+ * 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, 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
+ * ($(D pred(x,y) == pred(y,x))), and transitive ($(D pred(x,y) && pred(y,z))
+ * implies $(D pred(x,z))). If this is not the case, the range returned by
+ * chunkBy may assert at runtime or behave erratically.
+ *
+ * Params:
+ * pred = Predicate for determining equivalence.
+ * 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.
+ * With a unary predicate, a range of tuples is returned, with the tuple
+ * consisting of the result of the unary predicate for each subrange, and the
+ * subrange itself.
+ *
+ * Notes:
+ *
+ * Equivalent elements separated by an intervening non-equivalent element will
+ * appear in separate subranges; this function only considers adjacent
+ * equivalence. Elements in the subranges will always appear in the same order
+ * they appear in the original range.
+ *
+ * See_also:
+ * $(LREF group), which collapses adjacent equivalent elements into a single
+ * element.
+ */
+auto chunkBy(alias pred, Range)(Range r)
+if (isInputRange!Range)
+{
+ return ChunkByImpl!(pred, Range)(r);
+}
+
+/// Showing usage with binary predicate:
+/*FIXME: @safe*/ @system unittest
+{
+ import std.algorithm.comparison : equal;
+
+ // Grouping by particular attribute of each element:
+ auto data = [
+ [1, 1],
+ [1, 2],
+ [2, 2],
+ [2, 3]
+ ];
+
+ auto r1 = data.chunkBy!((a,b) => a[0] == b[0]);
+ assert(r1.equal!equal([
+ [[1, 1], [1, 2]],
+ [[2, 2], [2, 3]]
+ ]));
+
+ auto r2 = data.chunkBy!((a,b) => a[1] == b[1]);
+ assert(r2.equal!equal([
+ [[1, 1]],
+ [[1, 2], [2, 2]],
+ [[2, 3]]
+ ]));
+}
+
+version (none) // this example requires support for non-equivalence relations
+@safe unittest
+{
+ // Grouping by maximum adjacent difference:
+ import std.math : abs;
+ auto r3 = [1, 3, 2, 5, 4, 9, 10].chunkBy!((a, b) => abs(a-b) < 3);
+ assert(r3.equal!equal([
+ [1, 3, 2],
+ [5, 4],
+ [9, 10]
+ ]));
+
+}
+
+/// Showing usage with unary predicate:
+/* FIXME: pure @safe nothrow*/ @system unittest
+{
+ import std.algorithm.comparison : equal;
+ import std.range.primitives;
+ import std.typecons : tuple;
+
+ // Grouping by particular attribute of each element:
+ auto range =
+ [
+ [1, 1],
+ [1, 1],
+ [1, 2],
+ [2, 2],
+ [2, 3],
+ [2, 3],
+ [3, 3]
+ ];
+
+ auto byX = chunkBy!(a => a[0])(range);
+ auto expected1 =
+ [
+ tuple(1, [[1, 1], [1, 1], [1, 2]]),
+ tuple(2, [[2, 2], [2, 3], [2, 3]]),
+ tuple(3, [[3, 3]])
+ ];
+ foreach (e; byX)
+ {
+ assert(!expected1.empty);
+ assert(e[0] == expected1.front[0]);
+ assert(e[1].equal(expected1.front[1]));
+ expected1.popFront();
+ }
+
+ auto byY = chunkBy!(a => a[1])(range);
+ auto expected2 =
+ [
+ tuple(1, [[1, 1], [1, 1]]),
+ tuple(2, [[1, 2], [2, 2]]),
+ tuple(3, [[2, 3], [2, 3], [3, 3]])
+ ];
+ foreach (e; byY)
+ {
+ assert(!expected2.empty);
+ assert(e[0] == expected2.front[0]);
+ assert(e[1].equal(expected2.front[1]));
+ expected2.popFront();
+ }
+}
+
+/*FIXME: pure @safe nothrow*/ @system unittest
+{
+ import std.algorithm.comparison : equal;
+ import std.typecons : tuple;
+
+ struct Item { int x, y; }
+
+ // Force R to have only an input range API with reference semantics, so
+ // that we're not unknowingly making use of array semantics outside of the
+ // range API.
+ class RefInputRange(R)
+ {
+ R data;
+ this(R _data) pure @safe nothrow { data = _data; }
+ @property bool empty() pure @safe nothrow { return data.empty; }
+ @property auto front() pure @safe nothrow { return data.front; }
+ void popFront() pure @safe nothrow { data.popFront(); }
+ }
+ auto refInputRange(R)(R range) { return new RefInputRange!R(range); }
+
+ {
+ auto arr = [ Item(1,2), Item(1,3), Item(2,3) ];
+ static assert(isForwardRange!(typeof(arr)));
+
+ auto byX = chunkBy!(a => a.x)(arr);
+ static assert(isForwardRange!(typeof(byX)));
+
+ auto byX_subrange1 = byX.front[1].save;
+ auto byX_subrange2 = byX.front[1].save;
+ static assert(isForwardRange!(typeof(byX_subrange1)));
+ static assert(isForwardRange!(typeof(byX_subrange2)));
+
+ byX.popFront();
+ assert(byX_subrange1.equal([ Item(1,2), Item(1,3) ]));
+ byX_subrange1.popFront();
+ assert(byX_subrange1.equal([ Item(1,3) ]));
+ assert(byX_subrange2.equal([ Item(1,2), Item(1,3) ]));
+
+ auto byY = chunkBy!(a => a.y)(arr);
+ static assert(isForwardRange!(typeof(byY)));
+
+ auto byY2 = byY.save;
+ static assert(is(typeof(byY) == typeof(byY2)));
+ byY.popFront();
+ assert(byY.front[0] == 3);
+ assert(byY.front[1].equal([ Item(1,3), Item(2,3) ]));
+ assert(byY2.front[0] == 2);
+ assert(byY2.front[1].equal([ Item(1,2) ]));
+ }
+
+ // Test non-forward input ranges.
+ {
+ auto range = refInputRange([ Item(1,1), Item(1,2), Item(2,2) ]);
+ auto byX = chunkBy!(a => a.x)(range);
+ assert(byX.front[0] == 1);
+ assert(byX.front[1].equal([ Item(1,1), Item(1,2) ]));
+ byX.popFront();
+ assert(byX.front[0] == 2);
+ assert(byX.front[1].equal([ Item(2,2) ]));
+ byX.popFront();
+ assert(byX.empty);
+ assert(range.empty);
+
+ range = refInputRange([ Item(1,1), Item(1,2), Item(2,2) ]);
+ auto byY = chunkBy!(a => a.y)(range);
+ assert(byY.front[0] == 1);
+ assert(byY.front[1].equal([ Item(1,1) ]));
+ byY.popFront();
+ assert(byY.front[0] == 2);
+ assert(byY.front[1].equal([ Item(1,2), Item(2,2) ]));
+ byY.popFront();
+ assert(byY.empty);
+ assert(range.empty);
+ }
+}
+
+// Issue 13595
+version (none) // This requires support for non-equivalence relations
+@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);
+ assert(r.equal!equal([
+ [1],
+ [2, 3, 4],
+ [5, 6, 7],
+ [8, 9]
+ ]));
+}
+
+// Issue 13805
+@system unittest
+{
+ [""].map!((s) => s).chunkBy!((x, y) => true);
+}
+
+// joiner
+/**
+Lazily joins a range of ranges with a separator. The separator itself
+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 $(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:
+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:
+$(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)
+if (isInputRange!RoR && isInputRange!(ElementType!RoR)
+ && isForwardRange!Separator
+ && is(ElementType!Separator : ElementType!(ElementType!RoR)))
+{
+ static struct Result
+ {
+ private RoR _items;
+ private ElementType!RoR _current;
+ private Separator _sep, _currentSep;
+
+ // This is a mixin instead of a function for the following reason (as
+ // explained by Kenji Hara): "This is necessary from 2.061. If a
+ // struct has a nested struct member, it must be directly initialized
+ // in its constructor to avoid leaving undefined state. If you change
+ // setItem to a function, the initialization of _current field is
+ // wrapped into private member function, then compiler could not detect
+ // that is correctly initialized while constructing. To avoid the
+ // compiler error check, string mixin is used."
+ private enum setItem =
+ q{
+ if (!_items.empty)
+ {
+ // If we're exporting .save, we must not consume any of the
+ // subranges, since RoR.save does not guarantee that the states
+ // of the subranges are also saved.
+ static if (isForwardRange!RoR &&
+ isForwardRange!(ElementType!RoR))
+ _current = _items.front.save;
+ else
+ _current = _items.front;
+ }
+ };
+
+ private void useSeparator()
+ {
+ // Separator must always come after an item.
+ assert(_currentSep.empty && !_items.empty,
+ "joiner: internal error");
+ _items.popFront();
+
+ // If there are no more items, we're done, since separators are not
+ // terminators.
+ if (_items.empty) return;
+
+ if (_sep.empty)
+ {
+ // Advance to the next range in the
+ // input
+ while (_items.front.empty)
+ {
+ _items.popFront();
+ if (_items.empty) return;
+ }
+ mixin(setItem);
+ }
+ else
+ {
+ _currentSep = _sep.save;
+ assert(!_currentSep.empty);
+ }
+ }
+
+ private enum useItem =
+ q{
+ // FIXME: this will crash if either _currentSep or _current are
+ // class objects, because .init is null when the ctor invokes this
+ // mixin.
+ //assert(_currentSep.empty && _current.empty,
+ // "joiner: internal error");
+
+ // Use the input
+ if (_items.empty) return;
+ mixin(setItem);
+ if (_current.empty)
+ {
+ // No data in the current item - toggle to use the separator
+ useSeparator();
+ }
+ };
+
+ this(RoR items, Separator sep)
+ {
+ _items = items;
+ _sep = sep;
+
+ //mixin(useItem); // _current should be initialized in place
+ if (_items.empty)
+ _current = _current.init; // set invalid state
+ else
+ {
+ // If we're exporting .save, we must not consume any of the
+ // subranges, since RoR.save does not guarantee that the states
+ // of the subranges are also saved.
+ static if (isForwardRange!RoR &&
+ isForwardRange!(ElementType!RoR))
+ _current = _items.front.save;
+ else
+ _current = _items.front;
+
+ if (_current.empty)
+ {
+ // No data in the current item - toggle to use the separator
+ useSeparator();
+ }
+ }
+ }
+
+ @property auto empty()
+ {
+ return _items.empty;
+ }
+
+ @property ElementType!(ElementType!RoR) front()
+ {
+ if (!_currentSep.empty) return _currentSep.front;
+ assert(!_current.empty, "Attempting to fetch the front of an empty joiner.");
+ return _current.front;
+ }
+
+ void popFront()
+ {
+ assert(!_items.empty, "Attempting to popFront an empty joiner.");
+ // Using separator?
+ if (!_currentSep.empty)
+ {
+ _currentSep.popFront();
+ if (!_currentSep.empty) return;
+ mixin(useItem);
+ }
+ else
+ {
+ // we're using the range
+ _current.popFront();
+ if (!_current.empty) return;
+ useSeparator();
+ }
+ }
+
+ static if (isForwardRange!RoR && isForwardRange!(ElementType!RoR))
+ {
+ @property auto save()
+ {
+ Result copy = this;
+ copy._items = _items.save;
+ copy._current = _current.save;
+ copy._sep = _sep.save;
+ copy._currentSep = _currentSep.save;
+ return copy;
+ }
+ }
+ }
+ return Result(r, sep);
+}
+
+///
+@safe unittest
+{
+ import std.algorithm.comparison : equal;
+ import std.conv : text;
+
+ 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"));
+}
+
+@system unittest
+{
+ import std.algorithm.comparison : equal;
+ import std.range.interfaces;
+ import std.range.primitives;
+ // joiner() should work for non-forward ranges too.
+ auto r = inputRangeObject(["abc", "def"]);
+ assert(equal(joiner(r, "xyz"), "abcxyzdef"));
+}
+
+@system unittest
+{
+ import std.algorithm.comparison : equal;
+ import std.range;
+
+ // Related to issue 8061
+ auto r = joiner([
+ inputRangeObject("abc"),
+ inputRangeObject("def"),
+ ], "-*-");
+
+ assert(equal(r, "abc-*-def"));
+
+ // Test case where separator is specified but is empty.
+ auto s = joiner([
+ inputRangeObject("abc"),
+ inputRangeObject("def"),
+ ], "");
+
+ assert(equal(s, "abcdef"));
+
+ // Test empty separator with some empty elements
+ auto t = joiner([
+ inputRangeObject("abc"),
+ inputRangeObject(""),
+ inputRangeObject("def"),
+ inputRangeObject(""),
+ ], "");
+
+ assert(equal(t, "abcdef"));
+
+ // Test empty elements with non-empty separator
+ auto u = joiner([
+ inputRangeObject(""),
+ inputRangeObject("abc"),
+ inputRangeObject(""),
+ inputRangeObject("def"),
+ inputRangeObject(""),
+ ], "+-");
+
+ assert(equal(u, "+-abc+-+-def+-"));
+
+ // Issue 13441: only(x) as separator
+ string[][] lines = [null];
+ lines
+ .joiner(only("b"))
+ .array();
+}
+
+@safe unittest
+{
+ import std.algorithm.comparison : equal;
+
+ // Transience correctness test
+ struct TransientRange
+ {
+ @safe:
+ int[][] src;
+ int[] buf;
+
+ this(int[][] _src)
+ {
+ src = _src;
+ buf.length = 100;
+ }
+ @property bool empty() { return src.empty; }
+ @property int[] front()
+ {
+ assert(src.front.length <= buf.length);
+ buf[0 .. src.front.length] = src.front[0..$];
+ return buf[0 .. src.front.length];
+ }
+ void popFront() { src.popFront(); }
+ }
+
+ // Test embedded empty elements
+ auto tr1 = TransientRange([[], [1,2,3], [], [4]]);
+ assert(equal(joiner(tr1, [0]), [0,1,2,3,0,0,4]));
+
+ // Test trailing empty elements
+ auto tr2 = TransientRange([[], [1,2,3], []]);
+ assert(equal(joiner(tr2, [0]), [0,1,2,3,0]));
+
+ // Test no empty elements
+ auto tr3 = TransientRange([[1,2], [3,4]]);
+ assert(equal(joiner(tr3, [0,1]), [1,2,0,1,3,4]));
+
+ // Test consecutive empty elements
+ auto tr4 = TransientRange([[1,2], [], [], [], [3,4]]);
+ assert(equal(joiner(tr4, [0,1]), [1,2,0,1,0,1,0,1,0,1,3,4]));
+
+ // Test consecutive trailing empty elements
+ auto tr5 = TransientRange([[1,2], [3,4], [], []]);
+ 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))
+{
+ static struct Result
+ {
+ private:
+ RoR _items;
+ ElementType!RoR _current;
+ enum prepare =
+ q{
+ // Skip over empty subranges.
+ if (_items.empty) return;
+ while (_items.front.empty)
+ {
+ _items.popFront();
+ if (_items.empty) return;
+ }
+ // We cannot export .save method unless we ensure subranges are not
+ // consumed when a .save'd copy of ourselves is iterated over. So
+ // we need to .save each subrange we traverse.
+ static if (isForwardRange!RoR && isForwardRange!(ElementType!RoR))
+ _current = _items.front.save;
+ else
+ _current = _items.front;
+ };
+ this(RoR items, ElementType!RoR current)
+ {
+ _items = items;
+ _current = current;
+ }
+ public:
+ this(RoR r)
+ {
+ _items = r;
+ //mixin(prepare); // _current should be initialized in place
+
+ // Skip over empty subranges.
+ while (!_items.empty && _items.front.empty)
+ _items.popFront();
+
+ if (_items.empty)
+ _current = _current.init; // set invalid state
+ else
+ {
+ // We cannot export .save method unless we ensure subranges are not
+ // consumed when a .save'd copy of ourselves is iterated over. So
+ // we need to .save each subrange we traverse.
+ static if (isForwardRange!RoR && isForwardRange!(ElementType!RoR))
+ _current = _items.front.save;
+ else
+ _current = _items.front;
+ }
+ }
+ static if (isInfinite!RoR)
+ {
+ enum bool empty = false;
+ }
+ else
+ {
+ @property auto empty()
+ {
+ return _items.empty;
+ }
+ }
+ @property auto ref front()
+ {
+ assert(!empty, "Attempting to fetch the front of an empty joiner.");
+ return _current.front;
+ }
+ void popFront()
+ {
+ assert(!_current.empty, "Attempting to popFront an empty joiner.");
+ _current.popFront();
+ if (_current.empty)
+ {
+ assert(!_items.empty);
+ _items.popFront();
+ mixin(prepare);
+ }
+ }
+ static if (isForwardRange!RoR && isForwardRange!(ElementType!RoR))
+ {
+ @property auto save()
+ {
+ 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);
+}
+
+@safe unittest
+{
+ import std.algorithm.comparison : equal;
+ import std.range.interfaces : inputRangeObject;
+ import std.range : repeat;
+
+ static assert(isInputRange!(typeof(joiner([""]))));
+ static assert(isForwardRange!(typeof(joiner([""]))));
+ assert(equal(joiner([""]), ""));
+ assert(equal(joiner(["", ""]), ""));
+ assert(equal(joiner(["", "abc"]), "abc"));
+ assert(equal(joiner(["abc", ""]), "abc"));
+ assert(equal(joiner(["abc", "def"]), "abcdef"));
+ assert(equal(joiner(["Mary", "has", "a", "little", "lamb"]),
+ "Maryhasalittlelamb"));
+ 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] ]);
+ assert(equal(j, [44, 2, 3, 42, 43]));
+}
+
+
+@system unittest
+{
+ import std.algorithm.comparison : equal;
+ import std.range.interfaces : inputRangeObject;
+
+ // bugzilla 8240
+ assert(equal(joiner([inputRangeObject("")]), ""));
+
+ // issue 8792
+ auto b = [[1], [2], [3]];
+ auto jb = joiner(b);
+ auto js = jb.save;
+ assert(equal(jb, js));
+
+ auto js2 = jb.save;
+ jb.popFront();
+ assert(!equal(jb, js));
+ assert(equal(js2, js));
+ js.popFront();
+ assert(equal(jb, js));
+ assert(!equal(js2, js));
+}
+
+@safe unittest
+{
+ import std.algorithm.comparison : equal;
+
+ struct TransientRange
+ {
+ @safe:
+ int[] _buf;
+ int[][] _values;
+ this(int[][] values)
+ {
+ _values = values;
+ _buf = new int[128];
+ }
+ @property bool empty()
+ {
+ return _values.length == 0;
+ }
+ @property auto front()
+ {
+ foreach (i; 0 .. _values.front.length)
+ {
+ _buf[i] = _values[0][i];
+ }
+ return _buf[0 .. _values.front.length];
+ }
+ void popFront()
+ {
+ _values = _values[1 .. $];
+ }
+ }
+
+ auto rr = TransientRange([[1,2], [3,4,5], [], [6,7]]);
+
+ // Can't use array() or equal() directly because they fail with transient
+ // .front.
+ int[] result;
+ foreach (c; rr.joiner())
+ {
+ result ~= c;
+ }
+
+ assert(equal(result, [1,2,3,4,5,6,7]));
+}
+
+@safe unittest
+{
+ import std.algorithm.comparison : equal;
+ import std.algorithm.internal : algoFormat;
+
+ struct TransientRange
+ {
+ @safe:
+ dchar[] _buf;
+ dstring[] _values;
+ this(dstring[] values)
+ {
+ _buf.length = 128;
+ _values = values;
+ }
+ @property bool empty()
+ {
+ return _values.length == 0;
+ }
+ @property auto front()
+ {
+ foreach (i; 0 .. _values.front.length)
+ {
+ _buf[i] = _values[0][i];
+ }
+ return _buf[0 .. _values.front.length];
+ }
+ void popFront()
+ {
+ _values = _values[1 .. $];
+ }
+ }
+
+ auto rr = TransientRange(["abc"d, "12"d, "def"d, "34"d]);
+
+ // Can't use array() or equal() directly because they fail with transient
+ // .front.
+ dchar[] result;
+ foreach (c; rr.joiner())
+ {
+ result ~= c;
+ }
+
+ import std.conv : to;
+ assert(equal(result, "abc12def34"d),
+ //Convert to string for assert's message
+ to!string("Unexpected result: '%s'"d.algoFormat(result)));
+}
+
+// Issue 8061
+@system unittest
+{
+ import std.conv : to;
+ import std.range.interfaces;
+
+ auto r = joiner([inputRangeObject("ab"), inputRangeObject("cd")]);
+ assert(isForwardRange!(typeof(r)));
+
+ auto str = to!string(r);
+ 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. 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:
+ $(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
+ pairwise summing of floating point numbers.
++/
+template reduce(fun...)
+if (fun.length >= 1)
+{
+ import std.meta : staticMap;
+
+ alias binfuns = staticMap!(binaryFun, fun);
+ static if (fun.length > 1)
+ import std.typecons : tuple, isTuple;
+
+ /++
+ No-seed version. The first element of $(D r) is used as the seed's value.
+
+ For each function $(D f) in $(D fun), the corresponding
+ seed type $(D S) is $(D Unqual!(typeof(f(e, e)))), where $(D e) is an
+ element of $(D r): $(D ElementType!R) for ranges,
+ and $(D ForeachType!R) otherwise.
+
+ Once S has been determined, then $(D S s = e;) and $(D s = f(s, e);)
+ 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)
+ {
+ import std.exception : enforce;
+ alias E = Select!(isInputRange!R, ElementType!R, ForeachType!R);
+ alias Args = staticMap!(ReduceSeedType!E, binfuns);
+
+ static if (isInputRange!R)
+ {
+ 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);
+ }
+ else
+ {
+ auto result = Args.init;
+ return reduceImpl!true(r, result);
+ }
+ }
+
+ /++
+ 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 $(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)
+ {
+ static if (fun.length == 1)
+ return reducePreImpl(r, seed);
+ else
+ {
+ import std.algorithm.internal : algoFormat;
+ static assert(isTuple!S, algoFormat("Seed %s should be a Tuple", S.stringof));
+ return reducePreImpl(r, seed.expand);
+ }
+ }
+
+ private auto reducePreImpl(R, Args...)(R r, ref Args args)
+ {
+ alias Result = staticMap!(Unqual, Args);
+ static if (is(Result == Args))
+ alias result = args;
+ else
+ Result result = args;
+ return reduceImpl!false(r, result);
+ }
+
+ private auto reduceImpl(bool mustInitialize, R, Args...)(R r, ref Args args)
+ if (isIterable!R)
+ {
+ import std.algorithm.internal : algoFormat;
+ static assert(Args.length == fun.length,
+ algoFormat("Seed %s does not have the correct amount of fields (should be %s)", Args.stringof, fun.length));
+ alias E = Select!(isInputRange!R, ElementType!R, ForeachType!R);
+
+ static if (mustInitialize) bool initialized = false;
+ foreach (/+auto ref+/ E e; r) // @@@4707@@@
+ {
+ foreach (i, f; binfuns)
+ {
+ 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)
+ {
+ import std.conv : emplaceRef;
+ foreach (i, f; binfuns)
+ emplaceRef!(Args[i])(args[i], e);
+ initialized = true;
+ continue;
+ }
+
+ 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 (Args.length == 1)
+ return args[0];
+ else
+ return tuple(args);
+ }
+}
+
+/**
+Many aggregate range operations turn out to be solved with $(D reduce)
+quickly and easily. The example below illustrates $(D reduce)'s
+remarkable power and flexibility.
+*/
+@safe unittest
+{
+ import std.algorithm.comparison : max, min;
+ import std.math : approxEqual;
+ import std.range;
+
+ int[] arr = [ 1, 2, 3, 4, 5 ];
+ // Sum all elements
+ auto sum = reduce!((a,b) => a + b)(0, arr);
+ assert(sum == 15);
+
+ // Sum again, using a string predicate with "a" and "b"
+ sum = reduce!"a + b"(0, arr);
+ assert(sum == 15);
+
+ // Compute the maximum of all elements
+ auto largest = reduce!(max)(arr);
+ assert(largest == 5);
+
+ // Max again, but with Uniform Function Call Syntax (UFCS)
+ largest = arr.reduce!(max);
+ assert(largest == 5);
+
+ // Compute the number of odd elements
+ auto odds = reduce!((a,b) => a + (b & 1))(0, arr);
+ assert(odds == 3);
+
+ // Compute the sum of squares
+ auto ssquares = reduce!((a,b) => a + b * b)(0, arr);
+ assert(ssquares == 55);
+
+ // Chain multiple ranges into seed
+ int[] a = [ 3, 4 ];
+ int[] b = [ 100 ];
+ auto r = reduce!("a + b")(chain(a, b));
+ assert(r == 107);
+
+ // Mixing convertible types is fair game, too
+ double[] c = [ 2.5, 3.0 ];
+ auto r1 = reduce!("a + b")(chain(a, b, c));
+ assert(approxEqual(r1, 112.5));
+
+ // To minimize nesting of parentheses, Uniform Function Call Syntax can be used
+ auto r2 = chain(a, b, c).reduce!("a + b");
+ assert(approxEqual(r2, 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 $(D reduce) accepts multiple functions.
+If two or more functions are passed, $(D reduce) 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.math : approxEqual, sqrt;
+ import std.typecons : tuple, Tuple;
+
+ double[] a = [ 3.0, 4, 7, 11, 3, 2, 5 ];
+ // Compute minimum and maximum in one pass
+ auto r = reduce!(min, max)(a);
+ // The type of r is Tuple!(int, int)
+ assert(approxEqual(r[0], 2)); // minimum
+ assert(approxEqual(r[1], 11)); // maximum
+
+ // Compute sum and sum of squares in one pass
+ r = reduce!("a + b", "a + b * b")(tuple(0.0, 0.0), a);
+ assert(approxEqual(r[0], 35)); // sum
+ assert(approxEqual(r[1], 233)); // sum of squares
+ // Compute average and standard deviation from the above
+ auto avg = r[0] / a.length;
+ assert(avg == 5);
+ auto stdev = sqrt(r[1] / a.length - avg * avg);
+ assert(cast(int) stdev == 2);
+}
+
+@safe unittest
+{
+ import std.algorithm.comparison : max, min;
+ import std.range : chain;
+ import std.typecons : tuple, Tuple;
+
+ double[] a = [ 3, 4 ];
+ auto r = reduce!("a + b")(0.0, a);
+ assert(r == 7);
+ r = reduce!("a + b")(a);
+ assert(r == 7);
+ r = reduce!(min)(a);
+ assert(r == 3);
+ double[] b = [ 100 ];
+ auto r1 = reduce!("a + b")(chain(a, b));
+ assert(r1 == 107);
+
+ // two funs
+ auto r2 = reduce!("a + b", "a - b")(tuple(0.0, 0.0), a);
+ assert(r2[0] == 7 && r2[1] == -7);
+ auto r3 = reduce!("a + b", "a - b")(a);
+ assert(r3[0] == 7 && r3[1] == -1);
+
+ a = [ 1, 2, 3, 4, 5 ];
+ // 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(scope int delegate(ref int) dg)
+ {
+ int res;
+ if (actEmpty) return res;
+
+ foreach (i; 0 .. 100)
+ {
+ res = dg(i);
+ if (res) break;
+ }
+ return res;
+ }
+ }
+
+ OpApply oa;
+ auto hundredSum = reduce!"a + b"(iota(100));
+ assert(reduce!"a + b"(5, oa) == hundredSum + 5);
+ assert(reduce!"a + b"(oa) == hundredSum);
+ assert(reduce!("a + b", max)(oa) == tuple(hundredSum, 99));
+ 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]));
+
+ oa.actEmpty = true;
+ assertThrown(reduce!"a + b"(oa));
+}
+
+@safe unittest
+{
+ const float a = 0.0;
+ const float[] b = [ 1.2, 3, 3.3 ];
+ float[] c = [ 1.2, 3, 3.3 ];
+ auto r = reduce!"a + b"(a, b);
+ r = reduce!"a + b"(a, c);
+ assert(r == 7.5);
+}
+
+@safe unittest
+{
+ // Issue #10408 - Two-function reduce of a const array.
+ import std.algorithm.comparison : max, min;
+ import std.typecons : tuple, Tuple;
+
+ const numbers = [10, 30, 20];
+ immutable m = reduce!(min)(numbers);
+ assert(m == 10);
+ immutable minmax = reduce!(min, max)(numbers);
+ assert(minmax == tuple(10, 30));
+}
+
+@safe unittest
+{
+ //10709
+ import std.typecons : tuple, Tuple;
+
+ enum foo = "a + 0.5 * b";
+ auto r = [0, 1, 2, 3];
+ auto r1 = reduce!foo(r);
+ auto r2 = reduce!(foo, foo)(r);
+ assert(r1 == 3);
+ assert(r2 == tuple(3, 3));
+}
+
+@system unittest
+{
+ static struct OpApply
+ {
+ int opApply(int delegate(ref int) dg)
+ {
+ int[] a = [1, 2, 3];
+
+ int res = 0;
+ foreach (ref e; a)
+ {
+ res = dg(e);
+ if (res) break;
+ }
+ return res;
+ }
+ }
+ //test CTFE and functions with context
+ int fun(int a, int b) @safe {return a + b + 1;}
+ auto foo()
+ {
+ import std.algorithm.comparison : max;
+ import std.typecons : tuple, Tuple;
+
+ auto a = reduce!(fun)([1, 2, 3]);
+ auto b = reduce!(fun, fun)([1, 2, 3]);
+ auto c = reduce!(fun)(0, [1, 2, 3]);
+ auto d = reduce!(fun, fun)(tuple(0, 0), [1, 2, 3]);
+ auto e = reduce!(fun)(0, OpApply());
+ auto f = reduce!(fun, fun)(tuple(0, 0), OpApply());
+
+ return max(a, b.expand, c, d.expand, e, f.expand);
+ }
+ auto a = foo();
+ assert(a == 9);
+ enum b = foo();
+ assert(b == 9);
+}
+
+@safe unittest
+{
+ import std.algorithm.comparison : max, min;
+ import std.typecons : tuple, Tuple;
+
+ //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)
+ {
+ return reduce!(F, G)(tuple(ElementType!R.max,
+ ElementType!R.min), range);
+ }
+ assert(minmaxElement([1, 2, 3]) == tuple(1, 3));
+}
+
+@safe unittest //12569
+{
+ import std.algorithm.comparison : max, min;
+ 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"))));
+ static assert(!is(typeof(reduce!(min, max)(tuple(c, c, c), "hello"))));
+
+
+ //"Seed dchar should be a Tuple"
+ static assert(!is(typeof(reduce!(min, max)(c, "hello"))));
+ //"Seed (dchar) does not have the correct amount of fields (should be 2)"
+ static assert(!is(typeof(reduce!(min, max)(tuple(c), "hello"))));
+ //"Seed (dchar, dchar, dchar) does not have the correct amount of fields (should be 2)"
+ static assert(!is(typeof(reduce!(min, max)(tuple(c, c, c), "hello"))));
+ //"Incompatable function/seed/element: all(alias pred = "a")/int/dchar"
+ static assert(!is(typeof(reduce!all(1, "hello"))));
+ static assert(!is(typeof(reduce!(all, all)(tuple(1, 1), "hello"))));
+}
+
+@safe unittest //13304
+{
+ int[] data;
+ static assert(is(typeof(reduce!((a, b) => a + b)(data))));
+ assert(data.length == 0);
+}
+
+//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))));
+ assert(arr.length == 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
+any narrow string type or sliceable range type, but is most popular with string
+types.
+
+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.
+
+If splitting a string on whitespace and token compression is desired,
+consider using $(D splitter) without specifying a separator (see fourth overload
+below).
+
+Params:
+ pred = The predicate for comparing each element with the separator,
+ defaulting to $(D "a == b").
+ 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.
+
+Constraints:
+ The predicate $(D pred) needs to accept an element of $(D r) and the
+ separator $(D s).
+
+Returns:
+ An input range of the subranges of elements between separators. If $(D r)
+ 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:
+ $(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)
+if (is(typeof(binaryFun!pred(r.front, s)) : bool)
+ && ((hasSlicing!Range && hasLength!Range) || isNarrowString!Range))
+{
+ import std.algorithm.searching : find;
+ import std.conv : unsigned;
+
+ static struct Result
+ {
+ private:
+ Range _input;
+ Separator _separator;
+ // Do we need hasLength!Range? popFront uses _input.length...
+ enum size_t _unComputed = size_t.max - 1, _atEnd = size_t.max;
+ size_t _frontLength = _unComputed;
+ size_t _backLength = _unComputed;
+
+ static if (isNarrowString!Range)
+ {
+ size_t _separatorLength;
+ }
+ else
+ {
+ enum _separatorLength = 1;
+ }
+
+ static if (isBidirectionalRange!Range)
+ {
+ static size_t lastIndexOf(Range haystack, Separator needle)
+ {
+ import std.range : retro;
+ auto r = haystack.retro().find!pred(needle);
+ return r.retro().length - 1;
+ }
+ }
+
+ public:
+ this(Range input, Separator separator)
+ {
+ _input = input;
+ _separator = separator;
+
+ static if (isNarrowString!Range)
+ {
+ import std.utf : codeLength;
+
+ _separatorLength = codeLength!(ElementEncodingType!Range)(separator);
+ }
+ if (_input.empty)
+ _frontLength = _atEnd;
+ }
+
+ static if (isInfinite!Range)
+ {
+ enum bool empty = false;
+ }
+ else
+ {
+ @property bool empty()
+ {
+ return _frontLength == _atEnd;
+ }
+ }
+
+ @property Range front()
+ {
+ assert(!empty, "Attempting to fetch the front of an empty splitter.");
+ if (_frontLength == _unComputed)
+ {
+ auto r = _input.find!pred(_separator);
+ _frontLength = _input.length - r.length;
+ }
+ return _input[0 .. _frontLength];
+ }
+
+ void popFront()
+ {
+ assert(!empty, "Attempting to popFront an empty splitter.");
+ if (_frontLength == _unComputed)
+ {
+ front;
+ }
+ assert(_frontLength <= _input.length);
+ if (_frontLength == _input.length)
+ {
+ // no more input and need to fetch => done
+ _frontLength = _atEnd;
+
+ // Probably don't need this, but just for consistency:
+ _backLength = _atEnd;
+ }
+ else
+ {
+ _input = _input[_frontLength + _separatorLength .. _input.length];
+ _frontLength = _unComputed;
+ }
+ }
+
+ static if (isForwardRange!Range)
+ {
+ @property typeof(this) save()
+ {
+ auto ret = this;
+ ret._input = _input.save;
+ return ret;
+ }
+ }
+
+ static if (isBidirectionalRange!Range)
+ {
+ @property Range back()
+ {
+ assert(!empty, "Attempting to fetch the back of an empty splitter.");
+ if (_backLength == _unComputed)
+ {
+ immutable lastIndex = lastIndexOf(_input, _separator);
+ if (lastIndex == -1)
+ {
+ _backLength = _input.length;
+ }
+ else
+ {
+ _backLength = _input.length - lastIndex - 1;
+ }
+ }
+ return _input[_input.length - _backLength .. _input.length];
+ }
+
+ void popBack()
+ {
+ assert(!empty, "Attempting to popBack an empty splitter.");
+ if (_backLength == _unComputed)
+ {
+ // evaluate back to make sure it's computed
+ back;
+ }
+ assert(_backLength <= _input.length);
+ if (_backLength == _input.length)
+ {
+ // no more input and need to fetch => done
+ _frontLength = _atEnd;
+ _backLength = _atEnd;
+ }
+ else
+ {
+ _input = _input[0 .. _input.length - _backLength - _separatorLength];
+ _backLength = _unComputed;
+ }
+ }
+ }
+ }
+
+ return Result(r, s);
+}
+
+///
+@safe unittest
+{
+ import std.algorithm.comparison : equal;
+
+ assert(equal(splitter("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), w));
+ a = [ 0 ];
+ assert(equal(splitter(a, 0), [ (int[]).init, (int[]).init ]));
+ a = [ 0, 1 ];
+ assert(equal(splitter(a, 0), [ [], [1] ]));
+ w = [ [0], [1], [2] ];
+ assert(equal(splitter!"a.front == b"(w, 1), [ [[0]], [[2]] ]));
+}
+
+@safe unittest
+{
+ import std.algorithm;
+ import std.array : array;
+ import std.internal.test.dummyrange;
+ import std.range : retro;
+
+ 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))));
+
+ assert(equal(splitter(a, 0), w));
+ a = null;
+ assert(equal(splitter(a, 0), (int[][]).init));
+ a = [ 0 ];
+ assert(equal(splitter(a, 0), [ (int[]).init, (int[]).init ][]));
+ a = [ 0, 1 ];
+ assert(equal(splitter(a, 0), [ [], [1] ][]));
+
+ // Thoroughly exercise the bidirectional stuff.
+ auto str = "abc abcd abcde ab abcdefg abcdefghij ab ac ar an at ada";
+ assert(equal(
+ retro(splitter(str, 'a')),
+ retro(array(splitter(str, 'a')))
+ ));
+
+ // Test interleaving front and back.
+ auto split = splitter(str, 'a');
+ assert(split.front == "");
+ assert(split.back == "");
+ split.popBack();
+ assert(split.back == "d");
+ split.popFront();
+ assert(split.front == "bc ");
+ assert(split.back == "d");
+ split.popFront();
+ split.popBack();
+ assert(split.back == "t ");
+ split.popBack();
+ split.popBack();
+ split.popFront();
+ split.popFront();
+ assert(split.front == "b ");
+ assert(split.back == "r ");
+
+ foreach (DummyType; AllDummyRanges) { // Bug 4408
+ static if (isRandomAccessRange!DummyType)
+ {
+ static assert(isBidirectionalRange!DummyType);
+ DummyType d;
+ auto s = splitter(d, 5);
+ assert(equal(s.front, [1,2,3,4]));
+ assert(equal(s.back, [6,7,8,9,10]));
+
+ auto s2 = splitter(d, [4, 5]);
+ assert(equal(s2.front, [1,2,3]));
+ }
+ }
+}
+@safe unittest
+{
+ import std.algorithm;
+ import std.range;
+ auto L = retro(iota(1L, 10L));
+ auto s = splitter(L, 5L);
+ assert(equal(s.front, [9L, 8L, 7L, 6L]));
+ s.popFront();
+ assert(equal(s.front, [4L, 3L, 2L, 1L]));
+ s.popFront();
+ assert(s.empty);
+}
+
+/**
+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. 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
+empty elements.
+
+Params:
+ pred = The predicate for comparing each element with the separator,
+ defaulting to $(D "a == b").
+ 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
+ element of $(D s).
+
+Returns:
+ An input range of the subranges of elements between separators. If $(D r)
+ is a forward range or $(REF_ALTTEXT bidirectional range, isBidirectionalRange, std,range,primitives),
+ the returned range will be likewise.
+
+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)
+if (is(typeof(binaryFun!pred(r.front, s.front)) : bool)
+ && (hasSlicing!Range || isNarrowString!Range)
+ && isForwardRange!Separator
+ && (hasLength!Separator || isNarrowString!Separator))
+{
+ import std.algorithm.searching : find;
+ import std.conv : unsigned;
+
+ static struct Result
+ {
+ private:
+ Range _input;
+ Separator _separator;
+ // _frontLength == size_t.max means empty
+ size_t _frontLength = size_t.max;
+ static if (isBidirectionalRange!Range)
+ size_t _backLength = size_t.max;
+
+ @property auto separatorLength() { return _separator.length; }
+
+ void ensureFrontLength()
+ {
+ if (_frontLength != _frontLength.max) return;
+ assert(!_input.empty);
+ // compute front length
+ _frontLength = (_separator.empty) ? 1 :
+ _input.length - find!pred(_input, _separator).length;
+ static if (isBidirectionalRange!Range)
+ if (_frontLength == _input.length) _backLength = _frontLength;
+ }
+
+ void ensureBackLength()
+ {
+ static if (isBidirectionalRange!Range)
+ if (_backLength != _backLength.max) return;
+ assert(!_input.empty);
+ // compute back length
+ static if (isBidirectionalRange!Range && isBidirectionalRange!Separator)
+ {
+ import std.range : retro;
+ _backLength = _input.length -
+ find!pred(retro(_input), retro(_separator)).source.length;
+ }
+ }
+
+ public:
+ this(Range input, Separator separator)
+ {
+ _input = input;
+ _separator = separator;
+ }
+
+ @property Range front()
+ {
+ assert(!empty, "Attempting to fetch the front of an empty splitter.");
+ ensureFrontLength();
+ return _input[0 .. _frontLength];
+ }
+
+ static if (isInfinite!Range)
+ {
+ enum bool empty = false; // Propagate infiniteness
+ }
+ else
+ {
+ @property bool empty()
+ {
+ return _frontLength == size_t.max && _input.empty;
+ }
+ }
+
+ void popFront()
+ {
+ assert(!empty, "Attempting to popFront an empty splitter.");
+ ensureFrontLength();
+ if (_frontLength == _input.length)
+ {
+ // done, there's no separator in sight
+ _input = _input[_frontLength .. _frontLength];
+ _frontLength = _frontLength.max;
+ static if (isBidirectionalRange!Range)
+ _backLength = _backLength.max;
+ return;
+ }
+ if (_frontLength + separatorLength == _input.length)
+ {
+ // Special case: popping the first-to-last item; there is
+ // an empty item right after this.
+ _input = _input[_input.length .. _input.length];
+ _frontLength = 0;
+ static if (isBidirectionalRange!Range)
+ _backLength = 0;
+ return;
+ }
+ // Normal case, pop one item and the separator, get ready for
+ // reading the next item
+ _input = _input[_frontLength + separatorLength .. _input.length];
+ // mark _frontLength as uninitialized
+ _frontLength = _frontLength.max;
+ }
+
+ static if (isForwardRange!Range)
+ {
+ @property typeof(this) save()
+ {
+ auto ret = this;
+ ret._input = _input.save;
+ return ret;
+ }
+ }
+ }
+
+ return Result(r, s);
+}
+
+///
+@safe unittest
+{
+ import std.algorithm.comparison : equal;
+
+ assert(equal(splitter("hello world", " "), [ "hello", "world" ]));
+ int[] a = [ 1, 2, 0, 0, 3, 0, 4, 5, 0 ];
+ int[][] w = [ [1, 2], [3, 0, 4, 5, 0] ];
+ assert(equal(splitter(a, [0, 0]), w));
+ a = [ 0, 0 ];
+ assert(equal(splitter(a, [0, 0]), [ (int[]).init, (int[]).init ]));
+ a = [ 0, 0, 1 ];
+ assert(equal(splitter(a, [0, 0]), [ [], [1] ]));
+}
+
+@safe unittest
+{
+ import std.algorithm.comparison : equal;
+ import std.typecons : Tuple;
+
+ alias C = Tuple!(int, "x", int, "y");
+ auto a = [C(1,0), C(2,0), C(3,1), C(4,0)];
+ assert(equal(splitter!"a.x == b"(a, [2, 3]), [ [C(1,0)], [C(4,0)] ]));
+}
+
+@safe unittest
+{
+ import std.algorithm.comparison : equal;
+ import std.array : split;
+ import std.conv : text;
+
+ auto s = ",abc, de, fg,hi,";
+ auto sp0 = splitter(s, ',');
+ assert(equal(sp0, ["", "abc", " de", " fg", "hi", ""][]));
+
+ auto s1 = ", abc, de, fg, hi, ";
+ auto sp1 = splitter(s1, ", ");
+ assert(equal(sp1, ["", "abc", "de", " fg", "hi", ""][]));
+ static assert(isForwardRange!(typeof(sp1)));
+
+ int[] a = [ 1, 2, 0, 3, 0, 4, 5, 0 ];
+ int[][] w = [ [1, 2], [3], [4, 5], [] ];
+ uint i;
+ foreach (e; splitter(a, 0))
+ {
+ assert(i < w.length);
+ assert(e == w[i++]);
+ }
+ assert(i == w.length);
+ // // Now go back
+ // auto s2 = splitter(a, 0);
+
+ // foreach (e; retro(s2))
+ // {
+ // assert(i > 0);
+ // assert(equal(e, w[--i]), text(e));
+ // }
+ // assert(i == 0);
+
+ wstring names = ",peter,paul,jerry,";
+ auto words = split(names, ",");
+ assert(walkLength(words) == 5, text(walkLength(words)));
+}
+
+@safe unittest
+{
+ int[][] a = [ [1], [2], [0], [3], [0], [4], [5], [0] ];
+ int[][][] w = [ [[1], [2]], [[3]], [[4], [5]], [] ];
+ uint i;
+ foreach (e; splitter!"a.front == 0"(a, 0))
+ {
+ assert(i < w.length);
+ assert(e == w[i++]);
+ }
+ assert(i == w.length);
+}
+
+@safe unittest
+{
+ import std.algorithm.comparison : equal;
+ auto s6 = ",";
+ auto sp6 = splitter(s6, ',');
+ foreach (e; sp6) {}
+ assert(equal(sp6, ["", ""][]));
+}
+
+@safe unittest
+{
+ import std.algorithm.comparison : equal;
+
+ // Issue 10773
+ auto s = splitter("abc", "");
+ assert(s.equal(["a", "b", "c"]));
+}
+
+@safe unittest
+{
+ import std.algorithm.comparison : equal;
+
+ // Test by-reference separator
+ class RefSep {
+ @safe:
+ string _impl;
+ this(string s) { _impl = s; }
+ @property empty() { return _impl.empty; }
+ @property auto front() { return _impl.front; }
+ void popFront() { _impl = _impl[1..$]; }
+ @property RefSep save() { return new RefSep(_impl); }
+ @property auto length() { return _impl.length; }
+ }
+ auto sep = new RefSep("->");
+ auto data = "i->am->pointing";
+ auto words = splitter(data, sep);
+ assert(words.equal([ "i", "am", "pointing" ]));
+}
+
+/**
+
+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
+empty elements.
+
+Params:
+ isTerminator = The predicate for deciding where to split the range.
+ 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 $(REF_ALTTEXT bidirectional range, isBidirectionalRange, std,range,primitives),
+ the returned range will be likewise.
+
+See_Also: $(REF _splitter, std,regex) for a version that splits using a regular
+expression defined separator.
+ */
+auto splitter(alias isTerminator, Range)(Range input)
+if (isForwardRange!Range && is(typeof(unaryFun!isTerminator(input.front))))
+{
+ return SplitterResult!(unaryFun!isTerminator, Range)(input);
+}
+
+///
+@safe unittest
+{
+ import std.algorithm.comparison : equal;
+ import std.range.primitives : front;
+
+ 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 => a == 0)(a), w));
+ a = [ 0 ];
+ assert(equal(splitter!(a => a == 0)(a), [ (int[]).init, (int[]).init ]));
+ a = [ 0, 1 ];
+ assert(equal(splitter!(a => a == 0)(a), [ [], [1] ]));
+ w = [ [0], [1], [2] ];
+ assert(equal(splitter!(a => a.front == 1)(w), [ [[0]], [[2]] ]));
+}
+
+private struct SplitterResult(alias isTerminator, Range)
+{
+ import std.algorithm.searching : find;
+ enum fullSlicing = (hasLength!Range && hasSlicing!Range) || isSomeString!Range;
+
+ private Range _input;
+ private size_t _end = 0;
+ static if (!fullSlicing)
+ private Range _next;
+
+ private void findTerminator()
+ {
+ static if (fullSlicing)
+ {
+ auto r = find!isTerminator(_input.save);
+ _end = _input.length - r.length;
+ }
+ else
+ for ( _end = 0; !_next.empty ; _next.popFront)
+ {
+ if (isTerminator(_next.front))
+ break;
+ ++_end;
+ }
+ }
+
+ this(Range input)
+ {
+ _input = input;
+ static if (!fullSlicing)
+ _next = _input.save;
+
+ if (!_input.empty)
+ findTerminator();
+ else
+ _end = size_t.max;
+ }
+
+ static if (isInfinite!Range)
+ {
+ enum bool empty = false; // Propagate infiniteness.
+ }
+ else
+ {
+ @property bool empty()
+ {
+ return _end == size_t.max;
+ }
+ }
+
+ @property auto front()
+ {
+ version (assert)
+ {
+ import core.exception : RangeError;
+ if (empty)
+ throw new RangeError();
+ }
+ static if (fullSlicing)
+ return _input[0 .. _end];
+ else
+ {
+ import std.range : takeExactly;
+ return _input.takeExactly(_end);
+ }
+ }
+
+ void popFront()
+ {
+ version (assert)
+ {
+ import core.exception : RangeError;
+ if (empty)
+ throw new RangeError();
+ }
+
+ static if (fullSlicing)
+ {
+ _input = _input[_end .. _input.length];
+ if (_input.empty)
+ {
+ _end = size_t.max;
+ return;
+ }
+ _input.popFront();
+ }
+ else
+ {
+ if (_next.empty)
+ {
+ _input = _next;
+ _end = size_t.max;
+ return;
+ }
+ _next.popFront();
+ _input = _next.save;
+ }
+ findTerminator();
+ }
+
+ @property typeof(this) save()
+ {
+ auto ret = this;
+ ret._input = _input.save;
+ static if (!fullSlicing)
+ ret._next = _next.save;
+ return ret;
+ }
+}
+
+@safe unittest
+{
+ import std.algorithm.comparison : equal;
+ import std.range : iota;
+
+ auto L = iota(1L, 10L);
+ auto s = splitter(L, [5L, 6L]);
+ assert(equal(s.front, [1L, 2L, 3L, 4L]));
+ s.popFront();
+ assert(equal(s.front, [7L, 8L, 9L]));
+ s.popFront();
+ assert(s.empty);
+}
+
+@safe unittest
+{
+ import std.algorithm.comparison : equal;
+ import std.algorithm.internal : algoFormat;
+ import std.internal.test.dummyrange;
+
+ void compare(string sentence, string[] witness)
+ {
+ auto r = splitter!"a == ' '"(sentence);
+ assert(equal(r.save, witness), algoFormat("got: %(%s, %) expected: %(%s, %)", r, witness));
+ }
+
+ compare(" Mary has a little lamb. ",
+ ["", "Mary", "", "has", "a", "little", "lamb.", "", "", ""]);
+ compare("Mary has a little lamb. ",
+ ["Mary", "", "has", "a", "little", "lamb.", "", "", ""]);
+ compare("Mary has a little lamb.",
+ ["Mary", "", "has", "a", "little", "lamb."]);
+ compare("", (string[]).init);
+ compare(" ", ["", ""]);
+
+ static assert(isForwardRange!(typeof(splitter!"a == ' '"("ABC"))));
+
+ foreach (DummyType; AllDummyRanges)
+ {
+ static if (isRandomAccessRange!DummyType)
+ {
+ auto rangeSplit = splitter!"a == 5"(DummyType.init);
+ assert(equal(rangeSplit.front, [1,2,3,4]));
+ rangeSplit.popFront();
+ assert(equal(rangeSplit.front, [6,7,8,9,10]));
+ }
+ }
+}
+
+@safe unittest
+{
+ import std.algorithm.comparison : equal;
+ import std.algorithm.internal : algoFormat;
+ import std.range;
+
+ struct Entry
+ {
+ int low;
+ int high;
+ int[][] result;
+ }
+ Entry[] entries = [
+ Entry(0, 0, []),
+ Entry(0, 1, [[0]]),
+ Entry(1, 2, [[], []]),
+ Entry(2, 7, [[2], [4], [6]]),
+ Entry(1, 8, [[], [2], [4], [6], []]),
+ ];
+ foreach ( entry ; entries )
+ {
+ auto a = iota(entry.low, entry.high).filter!"true"();
+ auto b = splitter!"a%2"(a);
+ assert(equal!equal(b.save, entry.result), algoFormat("got: %(%s, %) expected: %(%s, %)", b, entry.result));
+ }
+}
+
+@safe unittest
+{
+ import std.algorithm.comparison : equal;
+ 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!"a=='本'"("日本語"), ["日", "語"]));
+}
+
+/++
+Lazily splits the string $(D s) into words, using whitespace as the delimiter.
+
+This function is string specific and, contrary to
+$(D splitter!(std.uni.isWhite)), runs of whitespace will be merged together
+(no empty tokens will be produced).
+
+Params:
+ s = The string to be split.
+
+Returns:
+ 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)
+{
+ import std.algorithm.searching : find;
+ static struct Result
+ {
+ private:
+ import core.exception : RangeError;
+ C[] _s;
+ size_t _frontLength;
+
+ void getFirst() pure @safe
+ {
+ import std.uni : isWhite;
+
+ auto r = find!(isWhite)(_s);
+ _frontLength = _s.length - r.length;
+ }
+
+ public:
+ this(C[] s) pure @safe
+ {
+ import std.string : strip;
+ _s = s.strip();
+ getFirst();
+ }
+
+ @property C[] front() pure @safe
+ {
+ version (assert) if (empty) throw new RangeError();
+ return _s[0 .. _frontLength];
+ }
+
+ void popFront() pure @safe
+ {
+ import std.string : stripLeft;
+ version (assert) if (empty) throw new RangeError();
+ _s = _s[_frontLength .. $].stripLeft();
+ getFirst();
+ }
+
+ @property bool empty() const @safe pure nothrow
+ {
+ return _s.empty;
+ }
+
+ @property inout(Result) save() inout @safe pure nothrow
+ {
+ return this;
+ }
+ }
+ return Result(s);
+}
+
+///
+@safe pure unittest
+{
+ import std.algorithm.comparison : equal;
+ auto a = " a bcd ef gh ";
+ assert(equal(splitter(a), ["a", "bcd", "ef", "gh"][]));
+}
+
+@safe pure unittest
+{
+ import std.algorithm.comparison : equal;
+ import std.meta : AliasSeq;
+ foreach (S; AliasSeq!(string, wstring, dstring))
+ {
+ import std.conv : to;
+ S a = " a bcd ef gh ";
+ assert(equal(splitter(a), [to!S("a"), to!S("bcd"), to!S("ef"), to!S("gh")]));
+ a = "";
+ assert(splitter(a).empty);
+ }
+
+ immutable string s = " a bcd ef gh ";
+ assert(equal(splitter(s), ["a", "bcd", "ef", "gh"][]));
+}
+
+@safe unittest
+{
+ import std.conv : to;
+ import std.string : strip;
+
+ // TDPL example, page 8
+ uint[string] dictionary;
+ char[][3] lines;
+ 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)))
+ {
+ if (word in dictionary) continue; // Nothing to do
+ auto newID = dictionary.length;
+ dictionary[to!string(word)] = cast(uint) newID;
+ }
+ }
+ assert(dictionary.length == 5);
+ assert(dictionary["line"]== 0);
+ assert(dictionary["one"]== 1);
+ assert(dictionary["two"]== 2);
+ assert(dictionary["yah"]== 3);
+ assert(dictionary["last"]== 4);
+}
+
+@safe unittest
+{
+ import std.algorithm.comparison : equal;
+ import std.algorithm.internal : algoFormat;
+ import std.array : split;
+ import std.conv : text;
+
+ // Check consistency:
+ // All flavors of split should produce the same results
+ foreach (input; [(int[]).init,
+ [0],
+ [0, 1, 0],
+ [1, 1, 0, 0, 1, 1],
+ ])
+ {
+ foreach (s; [0, 1])
+ {
+ auto result = split(input, s);
+
+ assert(equal(result, split(input, [s])), algoFormat(`"[%(%s,%)]"`, split(input, [s])));
+ //assert(equal(result, split(input, [s].filter!"true"()))); //Not yet implemented
+ assert(equal(result, split!((a) => a == s)(input)), text(split!((a) => a == s)(input)));
+
+ //assert(equal!equal(result, split(input.filter!"true"(), s))); //Not yet implemented
+ //assert(equal!equal(result, split(input.filter!"true"(), [s]))); //Not yet implemented
+ //assert(equal!equal(result, split(input.filter!"true"(), [s].filter!"true"()))); //Not yet implemented
+ assert(equal!equal(result, split!((a) => a == s)(input.filter!"true"())));
+
+ assert(equal(result, splitter(input, s)));
+ assert(equal(result, splitter(input, [s])));
+ //assert(equal(result, splitter(input, [s].filter!"true"()))); //Not yet implemented
+ assert(equal(result, splitter!((a) => a == s)(input)));
+
+ //assert(equal!equal(result, splitter(input.filter!"true"(), s))); //Not yet implemented
+ //assert(equal!equal(result, splitter(input.filter!"true"(), [s]))); //Not yet implemented
+ //assert(equal!equal(result, splitter(input.filter!"true"(), [s].filter!"true"()))); //Not yet implemented
+ assert(equal!equal(result, splitter!((a) => a == s)(input.filter!"true"())));
+ }
+ }
+ foreach (input; [string.init,
+ " ",
+ " hello ",
+ "hello hello",
+ " hello what heck this ? "
+ ])
+ {
+ foreach (s; [' ', 'h'])
+ {
+ auto result = split(input, s);
+
+ assert(equal(result, split(input, [s])));
+ //assert(equal(result, split(input, [s].filter!"true"()))); //Not yet implemented
+ assert(equal(result, split!((a) => a == s)(input)));
+
+ //assert(equal!equal(result, split(input.filter!"true"(), s))); //Not yet implemented
+ //assert(equal!equal(result, split(input.filter!"true"(), [s]))); //Not yet implemented
+ //assert(equal!equal(result, split(input.filter!"true"(), [s].filter!"true"()))); //Not yet implemented
+ assert(equal!equal(result, split!((a) => a == s)(input.filter!"true"())));
+
+ assert(equal(result, splitter(input, s)));
+ assert(equal(result, splitter(input, [s])));
+ //assert(equal(result, splitter(input, [s].filter!"true"()))); //Not yet implemented
+ assert(equal(result, splitter!((a) => a == s)(input)));
+
+ //assert(equal!equal(result, splitter(input.filter!"true"(), s))); //Not yet implemented
+ //assert(equal!equal(result, splitter(input.filter!"true"(), [s]))); //Not yet implemented
+ //assert(equal!equal(result, splitter(input.filter!"true"(), [s].filter!"true"()))); //Not yet implemented
+ assert(equal!equal(result, splitter!((a) => a == s)(input.filter!"true"())));
+ }
+ }
+}
+
+// sum
+/**
+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 $(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 $(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
+$(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 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
+$(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 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 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.
+ */
+auto sum(R)(R r)
+if (isInputRange!R && !isInfinite!R && is(typeof(r.front + r.front)))
+{
+ alias E = Unqual!(ElementType!R);
+ static if (isFloatingPoint!E)
+ alias Seed = typeof(E.init + 0.0); //biggest of double/real
+ else
+ alias Seed = typeof(r.front + r.front);
+ return sum(r, Unqual!Seed(0));
+}
+/// ditto
+auto sum(R, E)(R r, E seed)
+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);
+ }
+ else
+ {
+ return reduce!"a + b"(seed, r);
+ }
+}
+
+// Pairwise summation http://en.wikipedia.org/wiki/Pairwise_summation
+private auto sumPairwise(F, R)(R data)
+if (isInputRange!R && !isInfinite!R)
+{
+ 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)
+ {
+ 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);
+ Result c = 0;
+ for (; !r.empty; r.popFront())
+ {
+ immutable y = r.front - c;
+ immutable t = result + y;
+ c = (t - result) - y;
+ result = t;
+ }
+ return result;
+}
+
+/// Ditto
+@safe pure nothrow unittest
+{
+ import std.range;
+
+ //simple integral sumation
+ assert(sum([ 1, 2, 3, 4]) == 10);
+
+ //with integral promotion
+ assert(sum([false, true, true, false, true]) == 3);
+ assert(sum(ubyte.max.repeat(100)) == 25500);
+
+ //The result may overflow
+ assert(uint.max.repeat(3).sum() == 4294967293U );
+ //But a seed can be used to change the sumation primitive
+ assert(uint.max.repeat(3).sum(ulong.init) == 12884901885UL);
+
+ //Floating point sumation
+ assert(sum([1.0, 2.0, 3.0, 4.0]) == 10);
+
+ //Floating point operations have double precision minimum
+ static assert(is(typeof(sum([1F, 2F, 3F, 4F])) == double));
+ assert(sum([1F, 2, 3, 4]) == 10);
+
+ //Force pair-wise floating point sumation on large integers
+ import std.math : approxEqual;
+ assert(iota(ulong.max / 2, ulong.max / 2 + 4096).sum(0.0)
+ .approxEqual((ulong.max / 2) * 4096.0 + 4096^^2 / 2));
+}
+
+@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([ 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));
+ static assert(is(typeof(sum([1UL, 2UL, 3UL, 4UL])) == ulong));
+
+ int[] empty;
+ assert(sum(empty) == 0);
+ assert(sum([42]) == 42);
+ assert(sum([42, 43]) == 42 + 43);
+ assert(sum([42, 43, 44]) == 42 + 43 + 44);
+ assert(sum([42, 43, 44, 45]) == 42 + 43 + 44 + 45);
+}
+
+@safe pure nothrow unittest
+{
+ static assert(is(typeof(sum([1.0, 2.0, 3.0, 4.0])) == double));
+ static assert(is(typeof(sum([ 1F, 2F, 3F, 4F])) == double));
+ const(float[]) a = [1F, 2F, 3F, 4F];
+ assert(sum(a) == 10F);
+ static assert(is(typeof(sum(a)) == double));
+
+ double[] empty;
+ assert(sum(empty) == 0);
+ assert(sum([42.]) == 42);
+ assert(sum([42., 43.]) == 42 + 43);
+ assert(sum([42., 43., 44.]) == 42 + 43 + 44);
+ assert(sum([42., 43., 44., 45.5]) == 42 + 43 + 44 + 45.5);
+}
+
+@safe pure nothrow unittest
+{
+ import std.container;
+ static assert(is(typeof(sum(SList!float()[])) == double));
+ static assert(is(typeof(sum(SList!double()[])) == double));
+ static assert(is(typeof(sum(SList!real()[])) == real));
+
+ assert(sum(SList!double()[]) == 0);
+ assert(sum(SList!double(1)[]) == 1);
+ assert(sum(SList!double(1, 2)[]) == 1 + 2);
+ assert(sum(SList!double(1, 2, 3)[]) == 1 + 2 + 3);
+ assert(sum(SList!double(1, 2, 3, 4)[]) == 10);
+}
+
+@safe pure nothrow unittest // 12434
+{
+ immutable a = [10, 20];
+ auto s1 = sum(a);
+ assert(s1 == 30);
+ auto s2 = a.map!(x => x).sum;
+ assert(s2 == 30);
+}
+
+@system unittest
+{
+ import std.bigint;
+ import std.range;
+
+ immutable BigInt[] a = BigInt("1_000_000_000_000_000_000").repeat(10).array();
+ immutable ulong[] b = (ulong.max/2).repeat(10).array();
+ auto sa = a.sum();
+ auto sb = b.sum(BigInt(0)); //reduce ulongs into bigint
+ assert(sa == BigInt("10_000_000_000_000_000_000"));
+ 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 $(HTTP wikipedia.org/wiki/_Uniq, _uniq) system
+utility). Equivalence of elements is assessed by using the predicate
+$(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 $(REF_ALTTEXT input range, isInputRange, std,range,primitives) of
+ elements to filter.
+
+Returns:
+ 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))
+{
+ return UniqResult!(binaryFun!pred, Range)(r);
+}
+
+///
+@safe unittest
+{
+ import std.algorithm.comparison : equal;
+ import std.algorithm.mutation : copy;
+
+ int[] arr = [ 1, 2, 2, 2, 2, 3, 4, 4, 4, 5 ];
+ assert(equal(uniq(arr), [ 1, 2, 3, 4, 5 ][]));
+
+ // Filter duplicates in-place using copy
+ arr.length -= arr.uniq().copy(arr).length;
+ assert(arr == [ 1, 2, 3, 4, 5 ]);
+
+ // Note that uniqueness is only determined consecutively; duplicated
+ // elements separated by an intervening different element will not be
+ // eliminated:
+ assert(equal(uniq([ 1, 1, 2, 1, 1, 3, 1]), [1, 2, 1, 3, 1]));
+}
+
+private struct UniqResult(alias pred, Range)
+{
+ Range _input;
+
+ this(Range input)
+ {
+ _input = input;
+ }
+
+ auto opSlice()
+ {
+ return this;
+ }
+
+ void popFront()
+ {
+ assert(!empty, "Attempting to popFront an empty uniq.");
+ auto last = _input.front;
+ do
+ {
+ _input.popFront();
+ }
+ while (!_input.empty && pred(last, _input.front));
+ }
+
+ @property ElementType!Range front()
+ {
+ assert(!empty, "Attempting to fetch the front of an empty uniq.");
+ return _input.front;
+ }
+
+ static if (isBidirectionalRange!Range)
+ {
+ void popBack()
+ {
+ assert(!empty, "Attempting to popBack an empty uniq.");
+ auto last = _input.back;
+ do
+ {
+ _input.popBack();
+ }
+ while (!_input.empty && pred(last, _input.back));
+ }
+
+ @property ElementType!Range back()
+ {
+ assert(!empty, "Attempting to fetch the back of an empty uniq.");
+ return _input.back;
+ }
+ }
+
+ static if (isInfinite!Range)
+ {
+ enum bool empty = false; // Propagate infiniteness.
+ }
+ else
+ {
+ @property bool empty() { return _input.empty; }
+ }
+
+ static if (isForwardRange!Range)
+ {
+ @property typeof(this) save() {
+ return typeof(this)(_input.save);
+ }
+ }
+}
+
+@safe unittest
+{
+ import std.algorithm.comparison : equal;
+ import std.internal.test.dummyrange;
+ import std.range;
+
+ int[] arr = [ 1, 2, 2, 2, 2, 3, 4, 4, 4, 5 ];
+ auto r = uniq(arr);
+ static assert(isForwardRange!(typeof(r)));
+
+ assert(equal(r, [ 1, 2, 3, 4, 5 ][]));
+ assert(equal(retro(r), retro([ 1, 2, 3, 4, 5 ][])));
+
+ 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)
+ {
+ assert(equal(retro(u), [10,9,8,7,6,5,4,3,2,1]));
+ }
+ }
+}
+
+@safe unittest // https://issues.dlang.org/show_bug.cgi?id=17264
+{
+ import std.algorithm.comparison : equal;
+
+ const(int)[] var = [0, 1, 1, 2];
+ assert(var.uniq.equal([0, 1, 2]));
+}
+
+/**
+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;
+
+ ///
+ this(Range r)
+ {
+ import std.array : array;
+ import std.range : iota;
+
+ 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/libphobos/src/std/algorithm/mutation.d b/libphobos/src/std/algorithm/mutation.d
new file mode 100644
index 0000000..2b708ad
--- /dev/null
+++ b/libphobos/src/std/algorithm/mutation.d
@@ -0,0 +1,2909 @@
+// Written in the D programming language.
+/**
+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
+ $(D b = [7, 1, 2, 3]).)
+$(T2 copy,
+ Copies a range to another. If
+ $(D a = [1, 2, 3]) and $(D b = new int[5]), then $(D copy(a, b))
+ leaves $(D b = [1, 2, 3, 0, 0]) and returns $(D b[3 .. $]).)
+$(T2 fill,
+ Fills a range with a pattern,
+ e.g., if $(D a = new int[3]), then $(D fill(a, 4))
+ leaves $(D a = [4, 4, 4]) and $(D fill(a, [3, 4])) leaves
+ $(D a = [3, 4, 3]).)
+$(T2 initializeAll,
+ If $(D a = [1.2, 3.4]), then $(D initializeAll(a)) leaves
+ $(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 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.)
+$(T2 reverse,
+ If $(D a = [1, 2, 3]), $(D reverse(a)) changes it to $(D [3, 2, 1]).)
+$(T2 strip,
+ Strips all leading and trailing elements equal to a value, or that
+ satisfy a predicate.
+ If $(D a = [1, 1, 0, 1, 1]), then $(D strip(a, 1)) and
+ $(D strip!(e => e == 1)(a)) returns $(D [0]).)
+$(T2 stripLeft,
+ Strips all leading elements equal to a value, or that satisfy a
+ predicate. If $(D a = [1, 1, 0, 1, 1]), then $(D stripLeft(a, 1)) and
+ $(D stripLeft!(e => e == 1)(a)) returns $(D [0, 1, 1]).)
+$(T2 stripRight,
+ Strips all trailing elements equal to a value, or that satisfy a
+ predicate.
+ If $(D a = [1, 1, 0, 1, 1]), then $(D stripRight(a, 1)) and
+ $(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,
+ Fills a range (assumed uninitialized) with a value.)
+)
+
+Copyright: Andrei Alexandrescu 2008-.
+
+License: $(HTTP boost.org/LICENSE_1_0.txt, Boost License 1.0).
+
+Authors: $(HTTP erdani.com, Andrei Alexandrescu)
+
+Source: $(PHOBOSSRC std/algorithm/_mutation.d)
+
+Macros:
+T2=$(TR $(TDNW $(LREF $1)) $(TD $+))
+ */
+module std.algorithm.mutation;
+
+import std.range.primitives;
+import std.traits : isArray, isBlitAssignable, isNarrowString, Unqual, isSomeChar;
+// FIXME
+import std.typecons; // : tuple, Tuple;
+
+// bringToFront
+/**
+The $(D bringToFront) function has considerable flexibility and
+usefulness. It can rotate elements in one buffer left or right, swap
+buffers of equal length, and even move elements across disjoint
+buffers of different types and different lengths.
+
+$(D bringToFront) takes two ranges $(D front) and $(D back), which may
+be of different types. Considering the concatenation of $(D front) and
+$(D back) one unified range, $(D bringToFront) rotates that unified
+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).
+
+Preconditions:
+
+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).
+
+Params:
+ front = an $(REF_ALTTEXT input range, isInputRange, std,range,primitives)
+ back = a $(REF_ALTTEXT forward range, isForwardRange, std,range,primitives)
+
+Returns:
+ The number of elements brought to the front, i.e., the length of $(D back).
+
+See_Also:
+ $(HTTP sgi.com/tech/stl/_rotate.html, STL's rotate)
+*/
+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.array : sameHead;
+ import std.range : take, Take;
+ enum bool sameHeadExists = is(typeof(front.sameHead(back)));
+ size_t result;
+
+ for (bool semidone; !front.empty && !back.empty; )
+ {
+ static if (sameHeadExists)
+ {
+ if (front.sameHead(back)) break; // shortcut
+ }
+ // Swap elements until front and/or back ends.
+ auto back0 = back.save;
+ size_t nswaps;
+ do
+ {
+ static if (sameHeadExists)
+ {
+ // Detect the stepping-over condition.
+ if (front.sameHead(back0)) back0 = back.save;
+ }
+ swapFront(front, back);
+ ++nswaps;
+ front.popFront();
+ back.popFront();
+ }
+ while (!front.empty && !back.empty);
+
+ if (!semidone) result += nswaps;
+
+ // Now deal with the remaining elements.
+ if (back.empty)
+ {
+ if (front.empty) break;
+ // Right side was shorter, which means that we've brought
+ // all the back elements to the front.
+ semidone = true;
+ // Next pass: bringToFront(front, back0) to adjust the rest.
+ back = back0;
+ }
+ else
+ {
+ assert(front.empty);
+ // Left side was shorter. Let's step into the back.
+ static if (is(InputRange == Take!ForwardRange))
+ {
+ front = take(back0, nswaps);
+ }
+ else
+ {
+ immutable subresult = bringToFront(take(back0, nswaps),
+ back);
+ if (!semidone) result += subresult;
+ break; // done
+ }
+ }
+ }
+ return result;
+}
+
+/**
+The simplest use of $(D bringToFront) is for rotating elements in a
+buffer. For example:
+*/
+@safe unittest
+{
+ 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 ]);
+}
+
+/**
+The $(D front) range may actually "step over" the $(D back)
+range. This is very useful with forward ranges that cannot compute
+comfortably right-bounded subranges like $(D arr[0 .. 4]) above. In
+the example below, $(D r2) is a right subrange of $(D r1).
+*/
+@safe unittest
+{
+ 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[];
+ auto r2 = list[]; popFrontN(r2, 4);
+ assert(equal(r2, [ 1, 2, 3 ]));
+ bringToFront(r1, r2);
+ assert(equal(list[], [ 1, 2, 3, 4, 5, 6, 7 ]));
+}
+
+/**
+Elements can be swapped across ranges of different types:
+*/
+@safe unittest
+{
+ import std.algorithm.comparison : equal;
+ import std.container : SList;
+
+ auto list = SList!(int)(4, 5, 6, 7);
+ auto vec = [ 1, 2, 3 ];
+ bringToFront(list[], vec);
+ assert(equal(list[], [ 1, 2, 3, 4 ]));
+ 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;
+
+ // a more elaborate test
+ {
+ auto rnd = Random(unpredictableSeed);
+ int[] a = new int[uniform(100, 200, rnd)];
+ int[] b = new int[uniform(100, 200, rnd)];
+ foreach (ref e; a) e = uniform(-100, 100, rnd);
+ foreach (ref e; b) e = uniform(-100, 100, rnd);
+ int[] c = a ~ b;
+ // writeln("a= ", a);
+ // writeln("b= ", b);
+ auto n = bringToFront(c[0 .. a.length], c[a.length .. $]);
+ //writeln("c= ", c);
+ assert(n == b.length);
+ assert(c == b ~ a, text(c, "\n", a, "\n", b));
+ }
+ // different types, moveFront, no sameHead
+ {
+ static struct R(T)
+ {
+ T[] data;
+ size_t i;
+ @property
+ {
+ R save() { return this; }
+ bool empty() { return i >= data.length; }
+ T front() { return data[i]; }
+ T front(real e) { return data[i] = cast(T) e; }
+ }
+ void popFront() { ++i; }
+ }
+ auto a = R!int([1, 2, 3, 4, 5]);
+ auto b = R!real([6, 7, 8, 9]);
+ auto n = bringToFront(a, b);
+ assert(n == 4);
+ assert(a.data == [6, 7, 8, 9, 1]);
+ assert(b.data == [2, 3, 4, 5]);
+ }
+ // front steps over back
+ {
+ int[] arr, r1, r2;
+
+ // back is shorter
+ arr = [4, 5, 6, 7, 1, 2, 3];
+ r1 = arr;
+ r2 = arr[4 .. $];
+ bringToFront(r1, r2) == 3 || assert(0);
+ assert(equal(arr, [1, 2, 3, 4, 5, 6, 7]));
+
+ // front is shorter
+ arr = [5, 6, 7, 1, 2, 3, 4];
+ r1 = arr;
+ r2 = arr[3 .. $];
+ 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 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:
+ $(HTTP sgi.com/tech/stl/_copy.html, STL's _copy)
+ */
+TargetRange copy(SourceRange, TargetRange)(SourceRange source, TargetRange target)
+if (areCopyCompatibleArrays!(SourceRange, TargetRange))
+{
+ const tlen = target.length;
+ const slen = source.length;
+ assert(tlen >= slen,
+ "Cannot copy a source range into a smaller target range.");
+
+ 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[slen .. tlen];
+ }
+ else
+ {
+ // 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 .. $];
+ }
+}
+
+/// ditto
+TargetRange copy(SourceRange, TargetRange)(SourceRange source, TargetRange target)
+if (!areCopyCompatibleArrays!(SourceRange, TargetRange) &&
+ isInputRange!SourceRange &&
+ isOutputRange!(TargetRange, ElementType!SourceRange))
+{
+ // 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)
+ {
+ auto len = source.length;
+ foreach (idx; 0 .. len)
+ target[idx] = source[idx];
+ return target[len .. target.length];
+ }
+ else
+ {
+ put(target, source);
+ return target;
+ }
+}
+
+///
+@safe unittest
+{
+ int[] a = [ 1, 5 ];
+ int[] b = [ 9, 8 ];
+ int[] buf = new int[](a.length + b.length + 10);
+ auto rem = a.copy(buf); // copy a into buf
+ rem = b.copy(rem); // copy b into remainder of buf
+ assert(buf[0 .. a.length + b.length] == [1, 5, 9, 8]);
+ assert(rem.length == 10); // unused slots in buf
+}
+
+/**
+As long as the target range elements support assignment from source
+range elements, different types of ranges are accepted:
+*/
+@safe unittest
+{
+ float[] src = [ 1.0f, 5 ];
+ double[] dest = new double[src.length];
+ src.copy(dest);
+}
+
+/**
+To _copy at most $(D n) elements from a range, you may want to use
+$(REF take, std,range):
+*/
+@safe unittest
+{
+ import std.range;
+ int[] src = [ 1, 5, 8, 9, 10 ];
+ auto dest = new int[](3);
+ src.take(dest.length).copy(dest);
+ assert(dest == [ 1, 5, 8 ]);
+}
+
+/**
+To _copy just those elements from a range that satisfy a predicate,
+use $(LREF filter):
+*/
+@safe unittest
+{
+ import std.algorithm.iteration : filter;
+ int[] src = [ 1, 5, 8, 9, 10, 1, 2, 0 ];
+ auto dest = new int[src.length];
+ auto rem = src
+ .filter!(a => (a & 1) == 1)
+ .copy(dest);
+ assert(dest[0 .. $ - rem.length] == [ 1, 5, 9, 1 ]);
+}
+
+/**
+$(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
+{
+ import std.algorithm, std.range;
+ int[] src = [1, 2, 4];
+ int[] dest = [0, 0, 0, 0, 0];
+ src.retro.copy(dest.retro);
+ 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;
+
+ {
+ int[] a = [ 1, 5 ];
+ int[] b = [ 9, 8 ];
+ auto e = copy(filter!("a > 1")(a), b);
+ assert(b[0] == 5 && e.length == 1);
+ }
+
+ {
+ 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]);
+ }
+
+ { // Test for bug 7898
+ enum v =
+ {
+ import std.algorithm;
+ int[] arr1 = [10, 20, 30, 40, 50];
+ int[] arr2 = arr1.dup;
+ copy(arr1, arr2);
+ return 35;
+ }();
+ assert(v == 35);
+ }
+}
+
+@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).
+
+Params:
+ 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)(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;
+
+ static if (is(typeof(range[] = value)))
+ {
+ range[] = value;
+ }
+ else static if (is(typeof(range[] = T(value))))
+ {
+ range[] = T(value);
+ }
+ else
+ {
+ for ( ; !range.empty; range.popFront() )
+ {
+ range.front = value;
+ }
+ }
+}
+
+///
+@safe unittest
+{
+ int[] a = [ 1, 2, 3, 4 ];
+ fill(a, 5);
+ 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;
+ assert(chars.length == 0);
+ static assert(!__traits(compiles, fill(chars, 'c')));
+ wstring wchars;
+ assert(wchars.length == 0);
+ 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;
+
+ int[] a = [ 1, 2, 3 ];
+ fill(a, 6);
+ assert(a == [ 6, 6, 6 ], text(a));
+
+ void fun0()
+ {
+ foreach (i; 0 .. 1000)
+ {
+ foreach (ref e; a) e = 6;
+ }
+ }
+ void fun1() { foreach (i; 0 .. 1000) fill(a, 6); }
+
+ // fill should accept InputRange
+ alias InputRange = DummyRange!(ReturnBy.Reference, Length.No, RangeType.Input);
+ enum filler = uint.max;
+ InputRange range;
+ fill(range, filler);
+ foreach (value; range.arr)
+ assert(value == filler);
+}
+
+@safe unittest
+{
+ //ER8638_1 IS_NOT self assignable
+ static struct ER8638_1
+ {
+ void opAssign(int){}
+ }
+
+ //ER8638_1 IS self assignable
+ static struct ER8638_2
+ {
+ void opAssign(ER8638_2){}
+ void opAssign(int){}
+ }
+
+ auto er8638_1 = new ER8638_1[](10);
+ auto er8638_2 = new ER8638_2[](10);
+ er8638_1.fill(5); //generic case
+ er8638_2.fill(5); //opSlice(T.init) case
+}
+
+@safe unittest
+{
+ {
+ int[] a = [1, 2, 3];
+ immutable(int) b = 0;
+ a.fill(b);
+ assert(a == [0, 0, 0]);
+ }
+ {
+ double[] a = [1, 2, 3];
+ immutable(int) b = 0;
+ a.fill(b);
+ assert(a == [0, 0, 0]);
+ }
+}
+
+/**
+Fills $(D range) with a pattern copied from $(D filler). The length of
+$(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 $(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(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!ForwardRange)
+ {
+ //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);
+ }
+ else
+ {
+ //manual feed
+ for ( ; !range.empty; range.popFront(), filler.popFront())
+ {
+ range.front = filler.front;
+ }
+ }
+ }
+ else
+ {
+ import std.exception : enforce;
+
+ enforce(!filler.empty, "Cannot fill range with an empty filler");
+
+ static if (hasLength!InputRange && hasLength!ForwardRange
+ && is(typeof(range.length > filler.length)))
+ {
+ //Case we have access to length
+ immutable len = filler.length;
+ //Start by bulk copies
+ while (range.length > len)
+ {
+ range = copy(filler.save, range);
+ }
+
+ //and finally fill the partial range. No need to save here.
+ static if (hasSlicing!ForwardRange && is(typeof(filler[0 .. range.length])))
+ {
+ //use a quick copy
+ auto len2 = range.length;
+ range = copy(filler[0 .. len2], range);
+ }
+ else
+ {
+ //iterate. No need to check filler, it's length is longer than range's
+ for (; !range.empty; range.popFront(), filler.popFront())
+ {
+ range.front = filler.front;
+ }
+ }
+ }
+ else
+ {
+ //Most basic case.
+ auto bck = filler.save;
+ for (; !range.empty; range.popFront(), filler.popFront())
+ {
+ if (filler.empty) filler = bck.save;
+ range.front = filler.front;
+ }
+ }
+ }
+}
+
+///
+@safe unittest
+{
+ int[] a = [ 1, 2, 3, 4, 5 ];
+ int[] b = [ 8, 9 ];
+ fill(a, b);
+ assert(a == [ 8, 9, 8, 9, 8 ]);
+}
+
+@safe unittest
+{
+ import std.exception : assertThrown;
+ import std.internal.test.dummyrange;
+
+ int[] a = [ 1, 2, 3, 4, 5 ];
+ int[] b = [1, 2];
+ fill(a, b);
+ assert(a == [ 1, 2, 1, 2, 1 ]);
+
+ // fill should accept InputRange
+ alias InputRange = DummyRange!(ReturnBy.Reference, Length.No, RangeType.Input);
+ InputRange range;
+ fill(range,[1,2]);
+ foreach (i,value;range.arr)
+ assert(value == (i%2 == 0?1:2));
+
+ //test with a input being a "reference forward" range
+ fill(a, new ReferenceForwardRange!int([8, 9]));
+ assert(a == [8, 9, 8, 9, 8]);
+
+ //test with a input being an "infinite input" range
+ fill(a, new ReferenceInfiniteInputRange!int());
+ assert(a == [0, 1, 2, 3, 4]);
+
+ //empty filler test
+ assertThrown(fill(a, a[$..$]));
+}
+
+/**
+Initializes all elements of $(D range) with their $(D .init) value.
+Assumes that the elements of the range are uninitialized.
+
+Params:
+ 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)
+ */
+void initializeAll(Range)(Range range)
+if (isInputRange!Range && hasLvalueElements!Range && hasAssignableElements!Range)
+{
+ 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() )
+ {
+ 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);
+}
+
+/// ditto
+void initializeAll(Range)(Range range)
+if (is(Range == char[]) || is(Range == wchar[]))
+{
+ alias T = ElementEncodingType!Range;
+ range[] = T.init;
+}
+
+///
+@system unittest
+{
+ 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);
+}
+
+@system unittest
+{
+ import std.algorithm.iteration : filter;
+ import std.meta : AliasSeq;
+ import std.traits : hasElaborateAssign;
+
+ //Test strings:
+ //Must work on narrow strings.
+ //Must reject const
+ char[3] a = void;
+ a[].initializeAll();
+ assert(a[] == [char.init, char.init, char.init]);
+ string s;
+ assert(!__traits(compiles, s.initializeAll()));
+ assert(!__traits(compiles, s.initializeAll()));
+ assert(s.empty);
+
+ //Note: Cannot call uninitializedFill on narrow strings
+
+ enum e {e1, e2}
+ e[3] b1 = void;
+ b1[].initializeAll();
+ assert(b1[] == [e.e1, e.e1, e.e1]);
+ e[3] b2 = void;
+ b2[].uninitializedFill(e.e2);
+ assert(b2[] == [e.e2, e.e2, e.e2]);
+
+ static struct S1
+ {
+ int i;
+ }
+ static struct S2
+ {
+ int i = 1;
+ }
+ static struct S3
+ {
+ int i;
+ this(this){}
+ }
+ static struct S4
+ {
+ int i = 1;
+ this(this){}
+ }
+ 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; AliasSeq!(S1, S2, S3, S4))
+ {
+ //initializeAll
+ {
+ //Array
+ S[3] ss1 = void;
+ ss1[].initializeAll();
+ assert(ss1[] == [S.init, S.init, S.init]);
+
+ //Not array
+ S[3] ss2 = void;
+ auto sf = ss2[].filter!"true"();
+
+ sf.initializeAll();
+ assert(ss2[] == [S.init, S.init, S.init]);
+ }
+ //uninitializedFill
+ {
+ //Array
+ S[3] ss1 = void;
+ ss1[].uninitializedFill(S(2));
+ assert(ss1[] == [S(2), S(2), S(2)]);
+
+ //Not array
+ S[3] ss2 = void;
+ auto sf = ss2[].filter!"true"();
+ sf.uninitializedFill(S(2));
+ assert(ss2[] == [S(2), S(2), S(2)]);
+ }
+ }
+}
+
+// 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 `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.
+ 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)
+{
+ // test @safe destructible
+ static if (__traits(compiles, (T t) @safe {}))
+ trustedMoveImpl(source, target);
+ else
+ moveImpl(source, target);
+}
+
+/// For non-struct types, `move` just performs `target = source`:
+@safe unittest
+{
+ Object obj1 = new Object;
+ Object obj2 = obj1;
+ Object obj3;
+
+ move(obj2, obj3);
+ assert(obj3 is obj1);
+ // obj2 unchanged
+ assert(obj2 is obj1);
+}
+
+///
+pure nothrow @safe @nogc unittest
+{
+ // Structs without destructors are simply copied
+ struct S1
+ {
+ int a = 1;
+ int b = 2;
+ }
+ S1 s11 = { 10, 11 };
+ S1 s12;
+
+ move(s11, s12);
+
+ 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.
+ struct S2
+ {
+ int a = 1;
+ int b = 2;
+
+ ~this() pure nothrow @safe @nogc { }
+ }
+ S2 s21 = { 3, 4 };
+ S2 s22;
+
+ move(s21, s22);
+
+ assert(s21 == S2(1, 2));
+ assert(s22 == S2(3, 4));
+}
+
+@safe unittest
+{
+ import std.exception : assertCTFEable;
+ import std.traits;
+
+ assertCTFEable!((){
+ Object obj1 = new Object;
+ Object obj2 = obj1;
+ Object obj3;
+ move(obj2, obj3);
+ assert(obj3 is obj1);
+
+ static struct S1 { int a = 1, b = 2; }
+ S1 s11 = { 10, 11 };
+ S1 s12;
+ move(s11, s12);
+ assert(s11.a == 10 && s11.b == 11 && s12.a == 10 && s12.b == 11);
+
+ static struct S2 { int a = 1; int * b; }
+ S2 s21 = { 10, null };
+ s21.b = new int;
+ S2 s22;
+ move(s21, s22);
+ assert(s21 == s22);
+ });
+ // Issue 5661 test(1)
+ static struct S3
+ {
+ static struct X { int n = 0; ~this(){n = 0;} }
+ X x;
+ }
+ static assert(hasElaborateDestructor!S3);
+ S3 s31, s32;
+ s31.x.n = 1;
+ move(s31, s32);
+ assert(s31.x.n == 0);
+ assert(s32.x.n == 1);
+
+ // Issue 5661 test(2)
+ static struct S4
+ {
+ static struct X { int n = 0; this(this){n = 0;} }
+ X x;
+ }
+ static assert(hasElaborateCopyConstructor!S4);
+ S4 s41, s42;
+ s41.x.n = 1;
+ move(s41, s42);
+ assert(s41.x.n == 0);
+ assert(s42.x.n == 1);
+
+ // Issue 13990 test
+ class S5;
+
+ S5 s51;
+ S5 s52 = s51;
+ S5 s53;
+ move(s52, s53);
+ assert(s53 is s51);
+}
+
+/// Ditto
+T move(T)(ref T source)
+{
+ // test @safe destructible
+ static if (__traits(compiles, (T t) @safe {}))
+ return trustedMoveImpl(source);
+ else
+ return moveImpl(source);
+}
+
+/// Non-copyable structs can still be moved:
+pure nothrow @safe @nogc unittest
+{
+ struct S
+ {
+ int a = 1;
+ @disable this(this);
+ ~this() pure nothrow @safe @nogc {}
+ }
+ S s1;
+ s1.a = 2;
+ S s2 = move(s1);
+ assert(s1.a == 1);
+ assert(s2.a == 2);
+}
+
+private void trustedMoveImpl(T)(ref T source, ref T target) @trusted
+{
+ moveImpl(source, target);
+}
+
+private void moveImpl(T)(ref T source, ref T target)
+{
+ import std.traits : hasElaborateDestructor;
+
+ static if (is(T == struct))
+ {
+ 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;
+}
+
+@safe unittest
+{
+ import std.exception : assertCTFEable;
+ import std.traits;
+
+ assertCTFEable!((){
+ Object obj1 = new Object;
+ Object obj2 = obj1;
+ Object obj3 = move(obj2);
+ assert(obj3 is obj1);
+
+ static struct S1 { int a = 1, b = 2; }
+ S1 s11 = { 10, 11 };
+ S1 s12 = move(s11);
+ assert(s11.a == 10 && s11.b == 11 && s12.a == 10 && s12.b == 11);
+
+ static struct S2 { int a = 1; int * b; }
+ S2 s21 = { 10, null };
+ s21.b = new int;
+ S2 s22 = move(s21);
+ assert(s21 == s22);
+ });
+
+ // Issue 5661 test(1)
+ static struct S3
+ {
+ static struct X { int n = 0; ~this(){n = 0;} }
+ X x;
+ }
+ static assert(hasElaborateDestructor!S3);
+ S3 s31;
+ s31.x.n = 1;
+ S3 s32 = move(s31);
+ assert(s31.x.n == 0);
+ assert(s32.x.n == 1);
+
+ // Issue 5661 test(2)
+ static struct S4
+ {
+ static struct X { int n = 0; this(this){n = 0;} }
+ X x;
+ }
+ static assert(hasElaborateCopyConstructor!S4);
+ S4 s41;
+ s41.x.n = 1;
+ S4 s42 = move(s41);
+ assert(s41.x.n == 0);
+ assert(s42.x.n == 1);
+
+ // Issue 13990 test
+ class S5;
+
+ S5 s51;
+ 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);
+}
+
+@safe unittest//Issue 6217
+{
+ import std.algorithm.iteration : map;
+ auto x = map!"a"([1,2,3]);
+ x = move(x);
+}
+
+@safe unittest// Issue 8055
+{
+ static struct S
+ {
+ int x;
+ ~this()
+ {
+ assert(x == 0);
+ }
+ }
+ S foo(S s)
+ {
+ return move(s);
+ }
+ S a;
+ a.x = 0;
+ auto b = foo(a);
+ assert(b.x == 0);
+}
+
+@system unittest// Issue 8057
+{
+ int n = 10;
+ struct S
+ {
+ int x;
+ ~this()
+ {
+ // Access to enclosing scope
+ assert(n == 10);
+ }
+ }
+ S foo(S s)
+ {
+ // Move nested struct
+ return move(s);
+ }
+ S a;
+ a.x = 1;
+ auto b = foo(a);
+ assert(b.x == 1);
+
+ // Regression 8171
+ static struct Array(T)
+ {
+ // nested struct has no member
+ struct Payload
+ {
+ ~this() {}
+ }
+ }
+ Array!int.Payload x = void;
+ move(x);
+ move(x, x);
+}
+
+/**
+ * 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
+ */
+void moveEmplace(T)(ref T source, ref T target) @system
+{
+ import core.stdc.string : memcpy, memset;
+ 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))
+ {
+ 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)
+ {
+ // 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);
+ }
+ }
+ else
+ {
+ // Primitive data (including pointers and arrays) or class -
+ // assignment works great
+ target = source;
+ }
+}
+
+///
+pure nothrow @nogc @system unittest
+{
+ 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);
+}
+
+// moveAll
+/**
+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 $(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 portion of $(D tgt) after all elements from $(D src) have
+been moved.
+ */
+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;
+
+ static if (isRandomAccessRange!InputRange1 && hasLength!InputRange1 && hasLength!InputRange2
+ && hasSlicing!InputRange2 && isRandomAccessRange!InputRange2)
+ {
+ auto toMove = src.length;
+ assert(toMove <= tgt.length);
+ foreach (idx; 0 .. toMove)
+ moveOp(src[idx], tgt[idx]);
+ return tgt[toMove .. tgt.length];
+ }
+ 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);
+}
+
+///
+pure nothrow @safe @nogc unittest
+{
+ 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[]);
+ assert(res.length == 2);
+
+ 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
+elements in a range (such as partition and sort). The strategy
+concerns the swapping of elements that are not the core concern of the
+algorithm. For example, consider an algorithm that sorts $(D [ "abc",
+"b", "aBc" ]) according to $(D toUpper(a) < toUpper(b)). That
+algorithm might choose to swap the two equivalent strings $(D "abc")
+and $(D "aBc"). That does not affect the sorting since both $(D [
+"abc", "aBc", "b" ]) and $(D [ "aBc", "abc", "b" ]) are valid
+outcomes.
+
+Some situations require that the algorithm must NOT ever change the
+relative ordering of equivalent elements (in the example above, only
+$(D [ "abc", "aBc", "b" ]) would be the correct result). Such
+algorithms are called $(B stable). If the ordering algorithm may swap
+equivalent elements discretionarily, the ordering is called $(B
+unstable).
+
+Yet another class of algorithms may choose an intermediate tradeoff by
+being stable only on a well-defined subrange of the range. There is no
+established terminology for such behavior; this library calls it $(B
+semistable).
+
+Generally, the $(D stable) ordering strategy may be more costly in
+time and/or space than the other two because it imposes additional
+constraints. Similarly, $(D semistable) may be costlier than $(D
+unstable). As (semi-)stability is not needed very often, the ordering
+algorithms in this module parameterized by $(D SwapStrategy) all
+choose $(D SwapStrategy.unstable) as the default.
+*/
+
+enum SwapStrategy
+{
+ /**
+ Allows freely swapping of elements as long as the output
+ satisfies the algorithm's requirements.
+ */
+ unstable,
+ /**
+ In algorithms partitioning ranges in two, preserve relative
+ ordering of elements only to the left of the partition point.
+ */
+ semistable,
+ /**
+ Preserve the relative ordering of elements to the largest
+ extent allowed by the algorithm's requirements.
+ */
+ stable,
+}
+
+///
+@safe unittest
+{
+ import std.stdio;
+ import std.algorithm.sorting : partition;
+ int[] a = [0, 1, 2, 3];
+ assert(remove!(SwapStrategy.stable)(a, 1) == [0, 2, 3]);
+ a = [0, 1, 2, 3];
+ assert(remove!(SwapStrategy.unstable)(a, 1) == [0, 3, 2]);
+}
+
+///
+@safe unittest
+{
+ import std.algorithm.sorting : partition;
+
+ // Put stuff greater than 3 on the left
+ auto arr = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
+ assert(partition!(a => a > 3, SwapStrategy.stable)(arr) == [1, 2, 3]);
+ assert(arr == [4, 5, 6, 7, 8, 9, 10, 1, 2, 3]);
+
+ arr = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
+ assert(partition!(a => a > 3, SwapStrategy.semistable)(arr) == [2, 3, 1]);
+ assert(arr == [4, 5, 6, 7, 8, 9, 10, 2, 3, 1]);
+
+ arr = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
+ assert(partition!(a => a > 3, SwapStrategy.unstable)(arr) == [3, 2, 1]);
+ assert(arr == [10, 9, 8, 4, 5, 6, 7, 3, 2, 1]);
+}
+
+/**
+Eliminates elements at given offsets from `range` and returns the shortened
+range.
+
+For example, here is how to _remove a single element from an array:
+
+----
+string[] a = [ "a", "b", "c", "d" ];
+a = a.remove(1); // remove element at offset 1
+assert(a == [ "a", "c", "d"]);
+----
+
+Note that `remove` does not change the length of the original _range directly;
+instead, it returns the shortened _range. If its return value is not assigned to
+the original _range, the original _range will retain its original length, though
+its contents will have changed:
+
+----
+int[] a = [ 3, 5, 7, 8 ];
+assert(remove(a, 1) == [ 3, 7, 8 ]);
+assert(a == [ 3, 7, 8, 8 ]);
+----
+
+The element at _offset `1` has been removed and the rest of the elements have
+shifted up to fill its place, however, the original array remains of the same
+length. This is because all functions in `std.algorithm` only change $(I
+content), not $(I topology). The value `8` is repeated because $(LREF move) was
+invoked to rearrange elements, and on integers `move` simply copies the source
+to the destination. To replace `a` with the effect of the removal, simply
+assign the slice returned by `remove` to it, as shown in the first example.
+
+Multiple indices can be passed into $(D remove). In that case,
+elements at the respective indices are all removed. The indices must
+be passed in increasing order, otherwise an exception occurs.
+
+----
+int[] a = [ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 ];
+assert(remove(a, 1, 3, 5) ==
+ [ 0, 2, 4, 6, 7, 8, 9, 10 ]);
+----
+
+(Note that all indices refer to slots in the $(I original) array, not
+in the array as it is being progressively shortened.) Finally, any
+combination of integral offsets and tuples composed of two integral
+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, 5, 6, 7, 8, 10 ]);
+----
+
+In this case, the slots at positions 1, 3, 4, and 9 are removed from
+the array. The tuple passes in a range closed to the left and open to
+the right (consistent with built-in slices), e.g. $(D tuple(3, 5))
+means indices $(D 3) and $(D 4) but not $(D 5).
+
+If the need is to remove some elements in the range but the order of
+the remaining elements does not have to be preserved, you may want to
+pass $(D SwapStrategy.unstable) to $(D remove).
+
+----
+int[] a = [ 0, 1, 2, 3 ];
+assert(remove!(SwapStrategy.unstable)(a, 1) == [ 0, 3, 2 ]);
+----
+
+In the case above, the element at slot $(D 1) is removed, but replaced
+with the last element of the range. Taking advantage of the relaxation
+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 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
+end of the range, but time is spent on advancing between slots by repeated
+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
+ && hasLvalueElements!Range
+ && hasLength!Range
+ && Offset.length >= 1)
+{
+ Tuple!(size_t, "pos", size_t, "len")[offset.length] blackouts;
+ foreach (i, v; offset)
+ {
+ 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");
+ }
+ }
+
+ 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)
+ {
+ 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)
+ {
+ --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)
+ {
+ toMove = tailLen;
+ blackouts[left].pos += toMove;
+ blackouts[left].len -= toMove;
+ }
+ else
+ {
+ 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)
+ {
+ 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");
+
+ for (; pos < from; ++pos, src.popFront(), tgt.popFront())
+ {
+ move(src.front, tgt.front);
+ }
+ }
+ 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;
+}
+
+///
+@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
+{
+ import std.exception : assertThrown;
+ import std.range;
+
+ // http://d.puremagic.com/issues/show_bug.cgi?id=10173
+ int[] test = iota(0, 10).array();
+ assertThrown(remove!(SwapStrategy.stable)(test, tuple(2, 4), tuple(1, 3)));
+ assertThrown(remove!(SwapStrategy.unstable)(test, tuple(2, 4), tuple(1, 3)));
+ assertThrown(remove!(SwapStrategy.stable)(test, 2, 4, 1, 3));
+ assertThrown(remove!(SwapStrategy.unstable)(test, 2, 4, 1, 3));
+}
+
+@safe unittest
+{
+ import std.range;
+ int[] a = [ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 ];
+ 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 ]);
+
+ a = [ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 ];
+ assert(remove!(SwapStrategy.unstable)(a, 0, 10) ==
+ [ 9, 1, 2, 3, 4, 5, 6, 7, 8 ]);
+
+ a = [ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 ];
+ assert(remove!(SwapStrategy.unstable)(a, 0, tuple(9, 11)) ==
+ [ 8, 1, 2, 3, 4, 5, 6, 7 ]);
+ // http://d.puremagic.com/issues/show_bug.cgi?id=5224
+ a = [ 1, 2, 3, 4 ];
+ assert(remove!(SwapStrategy.unstable)(a, 2) ==
+ [ 1, 2, 4 ]);
+
+ 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 ];
+ 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 ];
+ assert(remove!(SwapStrategy.stable)(a, 1, tuple(3, 5))
+ == [ 0, 2, 5, 6, 7, 8, 9, 10]);
+
+ a = iota(0, 10).array();
+ assert(remove!(SwapStrategy.unstable)(a, tuple(1, 4), tuple(6, 7))
+ == [0, 9, 8, 7, 4, 5]);
+}
+
+@safe unittest
+{
+ // Issue 11576
+ auto arr = [1,2,3];
+ arr = arr.remove!(SwapStrategy.unstable)(2);
+ assert(arr == [1,2]);
+
+}
+
+@safe unittest
+{
+ import std.range;
+ // Bug# 12889
+ int[1][] arr = [[0], [1], [2], [3], [4], [5], [6]];
+ auto orig = arr.dup;
+ foreach (i; iota(arr.length))
+ {
+ assert(orig == arr.remove!(SwapStrategy.unstable)(tuple(i,i)));
+ assert(orig == arr.remove!(SwapStrategy.stable)(tuple(i,i)));
+ }
+}
+
+/**
+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)
+if (isBidirectionalRange!Range
+ && hasLvalueElements!Range)
+{
+ import std.functional : unaryFun;
+ auto result = range;
+ static if (s != SwapStrategy.stable)
+ {
+ for (;!range.empty;)
+ {
+ if (!unaryFun!pred(range.front))
+ {
+ range.popFront();
+ continue;
+ }
+ move(range.back, range.front);
+ range.popBack();
+ result.popBack();
+ }
+ }
+ else
+ {
+ auto tgt = range;
+ for (; !range.empty; range.popFront())
+ {
+ if (unaryFun!(pred)(range.front))
+ {
+ // yank this guy
+ result.popBack();
+ continue;
+ }
+ // keep this guy
+ move(range.front, tgt.front);
+ tgt.popFront();
+ }
+ }
+ return result;
+}
+
+///
+@safe unittest
+{
+ static immutable base = [1, 2, 3, 2, 4, 2, 5, 2];
+
+ int[] arr = base[].dup;
+
+ // using a string-based predicate
+ assert(remove!("a == 2")(arr) == [ 1, 3, 4, 5 ]);
+
+ // The original array contents have been modified,
+ // so we need to reset it to its original state.
+ // The length is unmodified however.
+ arr[] = base[];
+
+ // using a lambda predicate
+ assert(remove!(a => a == 2)(arr) == [ 1, 3, 4, 5 ]);
+}
+
+@safe unittest
+{
+ 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 ];
+ 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).
+Params:
+ r = a $(REF_ALTTEXT bidirectional range, isBidirectionalRange, std,range,primitives)
+ with swappable elements or a random access range with a length member
+
+See_Also:
+ $(HTTP sgi.com/tech/stl/_reverse.html, STL's _reverse), $(REF retro, std,range) for a lazy reversed range view
+*/
+void reverse(Range)(Range r)
+if (isBidirectionalRange!Range && !isRandomAccessRange!Range
+ && hasSwappableElements!Range)
+{
+ while (!r.empty)
+ {
+ swap(r.front, r.back);
+ r.popFront();
+ if (r.empty) break;
+ r.popBack();
+ }
+}
+
+///
+@safe unittest
+{
+ int[] arr = [ 1, 2, 3 ];
+ reverse(arr);
+ assert(arr == [ 3, 2, 1 ]);
+}
+
+///ditto
+void reverse(Range)(Range r)
+if (isRandomAccessRange!Range && hasLength!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);
+ }
+}
+
+@safe unittest
+{
+ int[] range = null;
+ reverse(range);
+ range = [ 1 ];
+ reverse(range);
+ assert(range == [1]);
+ range = [1, 2];
+ reverse(range);
+ assert(range == [2, 1]);
+ range = [1, 2, 3];
+ reverse(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.
+
+Params:
+ s = a narrow string
+
+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(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 = 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)
+ {
+ auto c = a.dup;
+ reverse(c);
+ assert(c == b, c ~ " != " ~ b);
+ }
+
+ test("a", "a");
+ test(" ", " ");
+ test("\u2029", "\u2029");
+ test("\u0100", "\u0100");
+ test("\u0430", "\u0430");
+ test("\U00010143", "\U00010143");
+ test("abcdefcdef", "fedcfedcba");
+ test("hello\U00010143\u0100\U00010143", "\U00010143\u0100\U00010143olleh");
+}
+
+/**
+ The strip group of functions allow stripping of either leading, trailing,
+ or both leading and trailing elements.
+
+ The $(D stripLeft) function will strip the $(D front) of the range,
+ the $(D stripRight) function will strip the $(D back) of the range,
+ while the $(D strip) function will strip both the $(D front) and $(D back)
+ of the range.
+
+ Note that the $(D strip) and $(D stripRight) functions require the range to
+ be a $(LREF BidirectionalRange) range.
+
+ All of these functions come in two varieties: one takes a target element,
+ 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))
+{
+ return range.stripLeft(element).stripRight(element);
+}
+
+/// ditto
+Range strip(alias pred, Range)(Range range)
+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))
+{
+ import std.algorithm.searching : find;
+ return find!((auto ref a) => a != element)(range);
+}
+
+/// ditto
+Range stripLeft(alias pred, Range)(Range range)
+if (isInputRange!Range && is(typeof(pred(range.front)) : bool))
+{
+ import std.algorithm.searching : find;
+ import std.functional : not;
+
+ return find!(not!pred)(range);
+}
+
+/// ditto
+Range stripRight(Range, E)(Range range, E element)
+if (isBidirectionalRange!Range && is(typeof(range.back == element) : bool))
+{
+ for (; !range.empty; range.popBack())
+ {
+ if (range.back != element)
+ break;
+ }
+ return range;
+}
+
+/// ditto
+Range stripRight(alias pred, Range)(Range range)
+if (isBidirectionalRange!Range && is(typeof(pred(range.back)) : bool))
+{
+ for (; !range.empty; range.popBack())
+ {
+ if (!pred(range.back))
+ break;
+ }
+ return range;
+}
+
+/// Strip leading and trailing elements equal to the target element.
+@safe pure unittest
+{
+ assert(" foobar ".strip(' ') == "foobar");
+ assert("00223.444500".strip('0') == "223.4445");
+ assert("ëëêéüŗōpéêëë".strip('ë') == "êéüŗōpéê");
+ assert([1, 1, 0, 1, 1].strip(1) == [0]);
+ assert([0.0, 0.01, 0.01, 0.0].strip(0).length == 2);
+}
+
+/// Strip leading and trailing elements while the predicate returns true.
+@safe pure unittest
+{
+ assert(" foobar ".strip!(a => a == ' ')() == "foobar");
+ assert("00223.444500".strip!(a => a == '0')() == "223.4445");
+ assert("ëëêéüŗōpéêëë".strip!(a => a == 'ë')() == "êéüŗōpéê");
+ assert([1, 1, 0, 1, 1].strip!(a => a == 1)() == [0]);
+ assert([0.0, 0.01, 0.5, 0.6, 0.01, 0.0].strip!(a => a < 0.4)().length == 2);
+}
+
+/// Strip leading elements equal to the target element.
+@safe pure unittest
+{
+ assert(" foobar ".stripLeft(' ') == "foobar ");
+ assert("00223.444500".stripLeft('0') == "223.444500");
+ assert("ůůűniçodêéé".stripLeft('ů') == "űniçodêéé");
+ assert([1, 1, 0, 1, 1].stripLeft(1) == [0, 1, 1]);
+ assert([0.0, 0.01, 0.01, 0.0].stripLeft(0).length == 3);
+}
+
+/// Strip leading elements while the predicate returns true.
+@safe pure unittest
+{
+ assert(" foobar ".stripLeft!(a => a == ' ')() == "foobar ");
+ assert("00223.444500".stripLeft!(a => a == '0')() == "223.444500");
+ assert("ůůűniçodêéé".stripLeft!(a => a == 'ů')() == "űniçodêéé");
+ assert([1, 1, 0, 1, 1].stripLeft!(a => a == 1)() == [0, 1, 1]);
+ assert([0.0, 0.01, 0.10, 0.5, 0.6].stripLeft!(a => a < 0.4)().length == 2);
+}
+
+/// Strip trailing elements equal to the target element.
+@safe pure unittest
+{
+ assert(" foobar ".stripRight(' ') == " foobar");
+ assert("00223.444500".stripRight('0') == "00223.4445");
+ assert("ùniçodêéé".stripRight('é') == "ùniçodê");
+ assert([1, 1, 0, 1, 1].stripRight(1) == [1, 1, 0]);
+ assert([0.0, 0.01, 0.01, 0.0].stripRight(0).length == 3);
+}
+
+/// Strip trailing elements while the predicate returns true.
+@safe pure unittest
+{
+ assert(" foobar ".stripRight!(a => a == ' ')() == " foobar");
+ assert("00223.444500".stripRight!(a => a == '0')() == "00223.4445");
+ assert("ùniçodêéé".stripRight!(a => a == 'é')() == "ùniçodê");
+ assert([1, 1, 0, 1, 1].stripRight!(a => a == 1)() == [1, 1, 0]);
+ assert([0.0, 0.01, 0.10, 0.5, 0.6].stripRight!(a => a > 0.4)().length == 3);
+}
+
+// swap
+/**
+Swaps $(D lhs) and $(D rhs). The instances $(D lhs) and $(D rhs) are moved in
+memory, without ever calling $(D opAssign), nor any other function. $(D T)
+need not be assignable at all to be swapped.
+
+If $(D lhs) and $(D rhs) reference the same instance, then nothing is done.
+
+$(D lhs) and $(D rhs) must be mutable. If $(D T) is a struct or union, then
+its fields must also all be (recursively) mutable.
+
+Params:
+ lhs = Data to be swapped with $(D rhs).
+ rhs = Data to be swapped with $(D lhs).
+*/
+void swap(T)(ref T lhs, ref T rhs) @trusted pure nothrow @nogc
+if (isBlitAssignable!T && !is(typeof(lhs.proxySwap(rhs))))
+{
+ import std.traits : hasAliasing, hasElaborateAssign, isAssignable,
+ isStaticArray;
+ static if (hasAliasing!T) if (!__ctfe)
+ {
+ import std.exception : doesPointTo;
+ assert(!doesPointTo(lhs, lhs), "Swap: lhs internal pointer.");
+ assert(!doesPointTo(rhs, rhs), "Swap: rhs internal pointer.");
+ assert(!doesPointTo(lhs, rhs), "Swap: lhs points to rhs.");
+ assert(!doesPointTo(rhs, lhs), "Swap: rhs points to lhs.");
+ }
+
+ static if (hasElaborateAssign!T || !isAssignable!T)
+ {
+ if (&lhs != &rhs)
+ {
+ // For structs with non-trivial assignment, move memory directly
+ ubyte[T.sizeof] t = void;
+ auto a = (cast(ubyte*) &lhs)[0 .. T.sizeof];
+ auto b = (cast(ubyte*) &rhs)[0 .. T.sizeof];
+ t[] = a[];
+ a[] = b[];
+ b[] = t[];
+ }
+ }
+ else
+ {
+ //Avoid assigning overlapping arrays. Dynamic arrays are fine, because
+ //it's their ptr and length properties which get assigned rather
+ //than their elements when assigning them, but static arrays are value
+ //types and therefore all of their elements get copied as part of
+ //assigning them, which would be assigning overlapping arrays if lhs
+ //and rhs were the same array.
+ static if (isStaticArray!T)
+ {
+ if (lhs.ptr == rhs.ptr)
+ return;
+ }
+
+ // For non-struct types, suffice to do the classic swap
+ auto tmp = lhs;
+ lhs = rhs;
+ rhs = tmp;
+ }
+}
+
+///
+@safe unittest
+{
+ // Swapping POD (plain old data) types:
+ int a = 42, b = 34;
+ swap(a, b);
+ assert(a == 34 && b == 42);
+
+ // Swapping structs with indirection:
+ static struct S { int x; char c; int[] y; }
+ S s1 = { 0, 'z', [ 1, 2 ] };
+ S s2 = { 42, 'a', [ 4, 6 ] };
+ swap(s1, s2);
+ assert(s1.x == 42);
+ assert(s1.c == 'a');
+ assert(s1.y == [ 4, 6 ]);
+
+ assert(s2.x == 0);
+ assert(s2.c == 'z');
+ assert(s2.y == [ 1, 2 ]);
+
+ // Immutables cannot be swapped:
+ immutable int imm1 = 1, imm2 = 2;
+ static assert(!__traits(compiles, swap(imm1, imm2)));
+
+ int c = imm1 + 0;
+ int d = imm2 + 0;
+ swap(c, d);
+ assert(c == 2);
+ assert(d == 1);
+}
+
+///
+@safe unittest
+{
+ // Non-copyable types can still be swapped.
+ static struct NoCopy
+ {
+ this(this) { assert(0); }
+ int n;
+ string s;
+ }
+ NoCopy nc1, nc2;
+ nc1.n = 127; nc1.s = "abc";
+ nc2.n = 513; nc2.s = "uvwxyz";
+
+ swap(nc1, nc2);
+ assert(nc1.n == 513 && nc1.s == "uvwxyz");
+ assert(nc2.n == 127 && nc2.s == "abc");
+
+ swap(nc1, nc1);
+ swap(nc2, nc2);
+ assert(nc1.n == 513 && nc1.s == "uvwxyz");
+ assert(nc2.n == 127 && nc2.s == "abc");
+
+ // Types containing non-copyable fields can also be swapped.
+ static struct NoCopyHolder
+ {
+ NoCopy noCopy;
+ }
+ NoCopyHolder h1, h2;
+ h1.noCopy.n = 31; h1.noCopy.s = "abc";
+ h2.noCopy.n = 65; h2.noCopy.s = null;
+
+ swap(h1, h2);
+ assert(h1.noCopy.n == 65 && h1.noCopy.s == null);
+ assert(h2.noCopy.n == 31 && h2.noCopy.s == "abc");
+
+ swap(h1, h1);
+ swap(h2, h2);
+ assert(h1.noCopy.n == 65 && h1.noCopy.s == null);
+ assert(h2.noCopy.n == 31 && h2.noCopy.s == "abc");
+
+ // Const types cannot be swapped.
+ const NoCopy const1, const2;
+ assert(const1.n == 0 && const2.n == 0);
+ static assert(!__traits(compiles, swap(const1, const2)));
+}
+
+@safe unittest
+{
+ //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
+{
+ static struct NoAssign
+ {
+ int i;
+ void opAssign(NoAssign) @disable;
+ }
+ auto s1 = NoAssign(1);
+ auto s2 = NoAssign(2);
+ swap(s1, s2);
+ assert(s1.i == 2);
+ assert(s2.i == 1);
+}
+
+@safe unittest
+{
+ struct S
+ {
+ const int i;
+ int i2 = 2;
+ int i3 = 3;
+ }
+ S s;
+ static assert(!__traits(compiles, swap(s, s)));
+ swap(s.i2, s.i3);
+ assert(s.i2 == 3);
+ assert(s.i3 == 2);
+}
+
+@safe unittest
+{
+ //11853
+ import std.traits : isAssignable;
+ alias T = Tuple!(int, double);
+ static assert(isAssignable!T);
+}
+
+@safe unittest
+{
+ // 12024
+ import std.datetime;
+ SysTime a, b;
+ swap(a, b);
+}
+
+@system unittest // 9975
+{
+ import std.exception : doesPointTo, mayPointTo;
+ static struct S2
+ {
+ union
+ {
+ size_t sz;
+ string s;
+ }
+ }
+ S2 a , b;
+ a.sz = -1;
+ assert(!doesPointTo(a, b));
+ assert( mayPointTo(a, b));
+ swap(a, b);
+
+ //Note: we can catch an error here, because there is no RAII in this test
+ import std.exception : assertThrown;
+ void* p, pp;
+ p = &p;
+ assertThrown!Error(move(p));
+ assertThrown!Error(move(p, pp));
+ assertThrown!Error(swap(p, pp));
+}
+
+@system unittest
+{
+ static struct A
+ {
+ int* x;
+ this(this) { x = new int; }
+ }
+ A a1, a2;
+ swap(a1, a2);
+
+ static struct B
+ {
+ int* x;
+ void opAssign(B) { x = new int; }
+ }
+ B b1, b2;
+ swap(b1, b2);
+}
+
+/// ditto
+void swap(T)(ref T lhs, ref T rhs)
+if (is(typeof(lhs.proxySwap(rhs))))
+{
+ lhs.proxySwap(rhs);
+}
+
+/**
+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))))
+ {
+ swap(r1.front, r2.front);
+ }
+ else
+ {
+ auto t1 = moveFront(r1), t2 = moveFront(r2);
+ r1.front = move(t2);
+ r2.front = move(t1);
+ }
+}
+
+// swapRanges
+/**
+Swaps all elements of $(D r1) with successive elements in $(D r2).
+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!(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())
+ {
+ swap(r1.front, r2.front);
+ }
+ return tuple(r1, 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]);
+ assert(c[0].empty && c[1].empty);
+ assert(a == [ 100, 2, 3, 103 ]);
+ assert(b == [ 0, 1, 101, 102 ]);
+}
+
+/**
+Initializes each element of $(D range) with $(D value).
+Assumes that the elements of the range are uninitialized.
+This is of interest for structs that
+define copy constructors (for all other types, $(LREF fill) and
+uninitializedFill are equivalent).
+
+Params:
+ 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)
+ */
+void uninitializedFill(Range, Value)(Range range, Value value)
+if (isInputRange!Range && hasLvalueElements!Range && is(typeof(range.front = value)))
+{
+ import std.traits : hasElaborateAssign;
+
+ alias T = ElementType!Range;
+ static if (hasElaborateAssign!T)
+ {
+ import std.conv : emplaceRef;
+
+ // Must construct stuff by the book
+ for (; !range.empty; range.popFront())
+ emplaceRef!T(range.front, value);
+ }
+ else
+ // Doesn't matter whether fill is initialized or not
+ 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/libphobos/src/std/algorithm/package.d b/libphobos/src/std/algorithm/package.d
new file mode 100644
index 0000000..4c9a72f
--- /dev/null
+++ b/libphobos/src/std/algorithm/package.d
@@ -0,0 +1,198 @@
+// Written in the D programming language.
+
+/**
+This package implements generic algorithms oriented towards the processing of
+sequences. Sequences processed by these functions define range-based
+interfaces. See also $(MREF_ALTTEXT Reference on ranges, std, range) and
+$(HTTP ddili.org/ders/d.en/ranges.html, tutorial on ranges).
+
+$(SCRIPT inhibitQuickIndex = 1;)
+
+Algorithms are categorized into the following submodules:
+
+$(DIVC quickindex,
+$(BOOKTABLE ,
+$(TR $(TH Submodule) $(TH Functions)
+)
+$(TR
+ $(TDNW $(SUBMODULE Searching, searching))
+ $(TD
+ $(SUBREF searching, all)
+ $(SUBREF searching, any)
+ $(SUBREF searching, balancedParens)
+ $(SUBREF searching, boyerMooreFinder)
+ $(SUBREF searching, canFind)
+ $(SUBREF searching, commonPrefix)
+ $(SUBREF searching, count)
+ $(SUBREF searching, countUntil)
+ $(SUBREF searching, endsWith)
+ $(SUBREF searching, find)
+ $(SUBREF searching, findAdjacent)
+ $(SUBREF searching, findAmong)
+ $(SUBREF searching, findSkip)
+ $(SUBREF searching, findSplit)
+ $(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 $(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)
+ $(SUBREF comparison, min)
+ $(SUBREF comparison, mismatch)
+ $(SUBREF comparison, predSwitch)
+ )
+)
+$(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 $(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)
+ $(SUBREF sorting, partialSort)
+ $(SUBREF sorting, partition)
+ $(SUBREF sorting, partition3)
+ $(SUBREF sorting, schwartzSort)
+ $(SUBREF sorting, sort)
+ $(SUBREF sorting, topN)
+ $(SUBREF sorting, topNCopy)
+ $(SUBREF sorting, topNIndex)
+ )
+)
+$(TR
+ $(TDNW Set operations $(BR)($(SUBMODULE setops, setops)))
+ $(TD
+ $(SUBREF setops, cartesianProduct)
+ $(SUBREF setops, largestPartialIntersection)
+ $(SUBREF setops, largestPartialIntersectionWeighted)
+ $(SUBREF setops, multiwayMerge)
+ $(SUBREF setops, multiwayUnion)
+ $(SUBREF setops, setDifference)
+ $(SUBREF setops, setIntersection)
+ $(SUBREF setops, setSymmetricDifference)
+ )
+)
+$(TR
+ $(TDNW $(SUBMODULE Mutation, mutation))
+ $(TD
+ $(SUBREF mutation, bringToFront)
+ $(SUBREF mutation, copy)
+ $(SUBREF mutation, fill)
+ $(SUBREF mutation, initializeAll)
+ $(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)
+ $(SUBREF mutation, stripLeft)
+ $(SUBREF mutation, stripRight)
+ $(SUBREF mutation, swap)
+ $(SUBREF mutation, swapRanges)
+ $(SUBREF mutation, uninitializedFill)
+ )
+)
+))
+
+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
+interfere with other homonym symbols in user code because they are
+evaluated in a different context. The default for all binary
+comparison predicates is $(D "a == b") for unordered operations and
+$(D "a < b") for ordered operations.
+
+Example:
+
+----
+int[] a = ...;
+static bool greater(int a, int b)
+{
+ return a > b;
+}
+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:
+SUBMODULE = $(MREF_ALTTEXT $1, std, algorithm, $2)
+SUBREF = $(REF_ALTTEXT $(TT $2), $2, std, algorithm, $1)$(NBSP)
+
+Copyright: Andrei Alexandrescu 2008-.
+
+License: $(HTTP boost.org/LICENSE_1_0.txt, Boost License 1.0).
+
+Authors: $(HTTP erdani.com, Andrei Alexandrescu)
+
+Source: $(PHOBOSSRC std/_algorithm/package.d)
+ */
+module std.algorithm;
+
+public import std.algorithm.comparison;
+public import std.algorithm.iteration;
+public import std.algorithm.mutation;
+public import std.algorithm.searching;
+public import std.algorithm.setops;
+public import std.algorithm.sorting;
+
+static import std.functional;
diff --git a/libphobos/src/std/algorithm/searching.d b/libphobos/src/std/algorithm/searching.d
new file mode 100644
index 0000000..6468a87
--- /dev/null
+++ b/libphobos/src/std/algorithm/searching.d
@@ -0,0 +1,4600 @@
+// Written in the D programming language.
+/**
+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)
+$(T2 any,
+ $(D any!"a > 0"([1, 2, -3, -4])) returns $(D true) because at least one
+ element is positive)
+$(T2 balancedParens,
+ $(D balancedParens("((1 + 1) / 2)")) returns $(D true) because the
+ string has balanced parentheses.)
+$(T2 boyerMooreFinder,
+ $(D find("hello world", boyerMooreFinder("or"))) returns $(D "orld")
+ 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,
+ Counts elements that are equal to a specified value or satisfy a
+ predicate. $(D count([1, 2, 1], 1)) returns $(D 2) and
+ $(D count!"a < 0"([1, -3, 0])) returns $(D 1).)
+$(T2 countUntil,
+ $(D countUntil(a, b)) returns the number of steps taken in $(D a) to
+ reach $(D b); for example, $(D countUntil("hello!", "o")) returns
+ $(D 4).)
+$(T2 commonPrefix,
+ $(D commonPrefix("parakeet", "parachute")) returns $(D "para").)
+$(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 $(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]).)
+$(T2 findAmong,
+ $(D findAmong("abcd", "qcx")) returns $(D "cd") because $(D 'c') is
+ 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 "de") and returns $(D true).)
+$(T2 findSplit,
+ $(D findSplit("abcdefg", "de")) returns the three ranges $(D "abc"),
+ $(D "de"), and $(D "fg").)
+$(T2 findSplitAfter,
+ $(D findSplitAfter("abcdefg", "de")) returns the two ranges
+ $(D "abcde") and $(D "fg").)
+$(T2 findSplitBefore,
+ $(D findSplitBefore("abcdefg", "de")) returns the two ranges $(D "abc")
+ 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").)
+$(T2 skipOver,
+ Assume $(D a = "blah"). Then $(D skipOver(a, "bi")) leaves $(D a)
+ unchanged and returns $(D false), whereas $(D skipOver(a, "bl"))
+ advances $(D a) to refer to $(D "ah") and returns $(D true).)
+$(T2 startsWith,
+ $(D startsWith("hello, world", "hello")) returns $(D true).)
+$(T2 until,
+ Lazily iterates a range until a specific value is found.)
+)
+
+Copyright: Andrei Alexandrescu 2008-.
+
+License: $(HTTP boost.org/LICENSE_1_0.txt, Boost License 1.0).
+
+Authors: $(HTTP erdani.com, Andrei Alexandrescu)
+
+Source: $(PHOBOSSRC std/algorithm/_searching.d)
+
+Macros:
+T2=$(TR $(TDNW $(LREF $1)) $(TD $+))
+ */
+module std.algorithm.searching;
+
+// FIXME
+import std.functional; // : unaryFun, binaryFun;
+import std.range.primitives;
+import std.traits;
+// FIXME
+import std.typecons; // : Tuple, Flag, Yes, No;
+
+/++
+Checks if $(I _all) of the elements verify $(D pred).
+ +/
+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).
+ Performs (at most) $(BIGOH range.length) evaluations of $(D pred).
+ +/
+ bool all(Range)(Range range)
+ if (isInputRange!Range && is(typeof(unaryFun!pred(range.front))))
+ {
+ import std.functional : not;
+
+ return find!(not!(unaryFun!pred))(range).empty;
+ }
+}
+
+///
+@safe unittest
+{
+ assert( all!"a & 1"([1, 3, 5, 7, 9]));
+ assert(!all!"a & 1"([1, 2, 3, 5, 7, 9]));
+}
+
+/++
+$(D all) can also be used without a predicate, if its items can be
+evaluated to true or false in a conditional statement. This can be a
+convenient way to quickly evaluate that $(I _all) of the elements of a range
+are true.
+ +/
+@safe unittest
+{
+ int[3] vals = [5, 3, 18];
+ assert( all(vals[]));
+}
+
+@safe unittest
+{
+ int x = 1;
+ assert(all!(a => a > x)([2, 3]));
+}
+
+/++
+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).
+ Performs (at most) $(BIGOH range.length) evaluations of $(D pred).
+ +/
+ bool any(Range)(Range range)
+ if (isInputRange!Range && is(typeof(unaryFun!pred(range.front))))
+ {
+ return !find!pred(range).empty;
+ }
+}
+
+///
+@safe unittest
+{
+ import std.ascii : isWhite;
+ assert( all!(any!isWhite)(["a a", "b b"]));
+ assert(!any!(all!isWhite)(["a a", "b b"]));
+}
+
+/++
+$(D any) can also be used without a predicate, if its items can be
+evaluated to true or false in a conditional statement. $(D !any) can be a
+convenient way to quickly test that $(I none) of the elements of a range
+evaluate to true.
+ +/
+@safe unittest
+{
+ int[3] vals1 = [0, 0, 0];
+ assert(!any(vals1[])); //none of vals1 evaluate to true
+
+ int[3] vals2 = [2, 0, 2];
+ assert( any(vals2[]));
+ assert(!all(vals2[]));
+
+ int[3] vals3 = [3, 3, 3];
+ assert( any(vals3[]));
+ assert( all(vals3[]));
+}
+
+@safe unittest
+{
+ auto a = [ 1, 2, 0, 4 ];
+ assert(any!"a == 2"(a));
+}
+
+// balancedParens
+/**
+Checks whether $(D r) has "balanced parentheses", i.e. all instances
+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)
+if (isInputRange!(Range) && is(typeof(r.front == lPar)))
+{
+ size_t count;
+ for (; !r.empty; r.popFront())
+ {
+ if (r.front == lPar)
+ {
+ if (count > maxNestingLevel) return false;
+ ++count;
+ }
+ else if (r.front == rPar)
+ {
+ if (!count) return false;
+ --count;
+ }
+ }
+ return count == 0;
+}
+
+///
+@safe unittest
+{
+ auto s = "1 + (2 * (3 + 1 / 2)";
+ assert(!balancedParens(s, '(', ')'));
+ s = "1 + (2 * (3 + 1) / 2)";
+ assert(balancedParens(s, '(', ')'));
+ s = "1 + (2 * (3 + 1) / 2)";
+ assert(!balancedParens(s, '(', ')', 0));
+ s = "1 + (2 * 3 + 1) / (2 - 5)";
+ assert(balancedParens(s, '(', ')', 0));
+}
+
+/**
+ * Sets up Boyer-Moore matching for use with $(D find) below.
+ * By default, elements are compared for equality.
+ *
+ * $(D BoyerMooreFinder) allocates GC memory.
+ *
+ * Params:
+ * pred = Predicate used to compare elements.
+ * needle = A random-access range with length and slicing.
+ *
+ * Returns:
+ * An instance of $(D BoyerMooreFinder) that can be used with $(D find()) to
+ * invoke the Boyer-Moore matching algorithm for finding of $(D needle) in a
+ * given haystack.
+ */
+struct BoyerMooreFinder(alias pred, Range)
+{
+private:
+ size_t[] skip; // GC allocated
+ ptrdiff_t[ElementType!(Range)] occ; // GC allocated
+ Range needle;
+
+ ptrdiff_t occurrence(ElementType!(Range) c)
+ {
+ auto p = c in occ;
+ return p ? *p : -1;
+ }
+
+/*
+This helper function checks whether the last "portion" bytes of
+"needle" (which is "nlen" bytes long) exist within the "needle" at
+offset "offset" (counted from the end of the string), and whether the
+character preceding "offset" is not a match. Notice that the range
+being checked may reach beyond the beginning of the string. Such range
+is ignored.
+ */
+ static bool needlematch(R)(R needle,
+ size_t portion, size_t offset)
+ {
+ import std.algorithm.comparison : equal;
+ ptrdiff_t virtual_begin = needle.length - offset - portion;
+ ptrdiff_t ignore = 0;
+ if (virtual_begin < 0)
+ {
+ ignore = -virtual_begin;
+ virtual_begin = 0;
+ }
+ if (virtual_begin > 0
+ && needle[virtual_begin - 1] == needle[$ - portion - 1])
+ return 0;
+
+ immutable delta = portion - ignore;
+ return equal(needle[needle.length - delta .. needle.length],
+ needle[virtual_begin .. virtual_begin + delta]);
+ }
+
+public:
+ ///
+ this(Range needle)
+ {
+ if (!needle.length) return;
+ this.needle = needle;
+ /* Populate table with the analysis of the needle */
+ /* But ignoring the last letter */
+ foreach (i, n ; needle[0 .. $ - 1])
+ {
+ this.occ[n] = i;
+ }
+ /* Preprocess #2: init skip[] */
+ /* Note: This step could be made a lot faster.
+ * A simple implementation is shown here. */
+ this.skip = new size_t[needle.length];
+ foreach (a; 0 .. needle.length)
+ {
+ size_t value = 0;
+ while (value < needle.length
+ && !needlematch(needle, a, value))
+ {
+ ++value;
+ }
+ this.skip[needle.length - a - 1] = value;
+ }
+ }
+
+ ///
+ Range beFound(Range haystack)
+ {
+ import std.algorithm.comparison : max;
+
+ if (!needle.length) return haystack;
+ if (needle.length > haystack.length) return haystack[$ .. $];
+ /* Search: */
+ immutable limit = haystack.length - needle.length;
+ for (size_t hpos = 0; hpos <= limit; )
+ {
+ size_t npos = needle.length - 1;
+ while (pred(needle[npos], haystack[npos+hpos]))
+ {
+ if (npos == 0) return haystack[hpos .. $];
+ --npos;
+ }
+ hpos += max(skip[npos], cast(sizediff_t) npos - occurrence(haystack[npos+hpos]));
+ }
+ 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.
+
+Params:
+ pred = The predicate to use in comparing elements for commonality. Defaults
+ to equality $(D "a == b").
+
+ r1 = A $(REF_ALTTEXT forward range, isForwardRange, std,range,primitives) 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,
+if the first argument is a string; otherwise, the same as the result of
+$(D takeExactly(r1, n)), where $(D n) is the number of elements in the common
+prefix of both ranges.
+
+See_Also:
+ $(REF takeExactly, std,range)
+ */
+auto commonPrefix(alias pred = "a == b", R1, R2)(R1 r1, R2 r2)
+if (isForwardRange!R1 && isInputRange!R2 &&
+ !isNarrowString!R1 &&
+ is(typeof(binaryFun!pred(r1.front, r2.front))))
+{
+ import std.algorithm.comparison : min;
+ static if (isRandomAccessRange!R1 && isRandomAccessRange!R2 &&
+ hasLength!R1 && hasLength!R2 &&
+ hasSlicing!R1)
+ {
+ immutable limit = min(r1.length, r2.length);
+ foreach (i; 0 .. limit)
+ {
+ if (!binaryFun!pred(r1[i], r2[i]))
+ {
+ return r1[0 .. i];
+ }
+ }
+ return r1[0 .. limit];
+ }
+ else
+ {
+ import std.range : takeExactly;
+ auto result = r1.save;
+ size_t i = 0;
+ for (;
+ !r1.empty && !r2.empty && binaryFun!pred(r1.front, r2.front);
+ ++i, r1.popFront(), r2.popFront())
+ {}
+ return takeExactly(result, i);
+ }
+}
+
+///
+@safe unittest
+{
+ assert(commonPrefix("hello, world", "hello, there") == "hello, ");
+}
+
+/// ditto
+auto commonPrefix(alias pred, R1, R2)(R1 r1, R2 r2)
+if (isNarrowString!R1 && isInputRange!R2 &&
+ is(typeof(binaryFun!pred(r1.front, r2.front))))
+{
+ import std.utf : decode;
+
+ auto result = r1.save;
+ immutable len = r1.length;
+ size_t i = 0;
+
+ for (size_t j = 0; i < len && !r2.empty; r2.popFront(), i = j)
+ {
+ immutable f = decode(r1, j);
+ if (!binaryFun!pred(f, r2.front))
+ break;
+ }
+
+ return result[0 .. i];
+}
+
+/// ditto
+auto commonPrefix(R1, R2)(R1 r1, R2 r2)
+if (isNarrowString!R1 && isInputRange!R2 && !isNarrowString!R2 &&
+ is(typeof(r1.front == r2.front)))
+{
+ return commonPrefix!"a == b"(r1, r2);
+}
+
+/// ditto
+auto commonPrefix(R1, R2)(R1 r1, R2 r2)
+if (isNarrowString!R1 && isNarrowString!R2)
+{
+ import std.algorithm.comparison : min;
+
+ static if (ElementEncodingType!R1.sizeof == ElementEncodingType!R2.sizeof)
+ {
+ import std.utf : stride, UTFException;
+
+ immutable limit = min(r1.length, r2.length);
+ for (size_t i = 0; i < limit;)
+ {
+ immutable codeLen = stride(r1, i);
+ size_t j = 0;
+
+ for (; j < codeLen && i < limit; ++i, ++j)
+ {
+ if (r1[i] != r2[i])
+ return r1[0 .. i - j];
+ }
+
+ if (i == limit && j < codeLen)
+ throw new UTFException("Invalid UTF-8 sequence", i);
+ }
+ return r1[0 .. limit];
+ }
+ else
+ return commonPrefix!"a == b"(r1, r2);
+}
+
+@safe unittest
+{
+ import std.algorithm.comparison : equal;
+ import std.algorithm.iteration : filter;
+ import std.conv : to;
+ import std.exception : assertThrown;
+ import std.meta : AliasSeq;
+ import std.range;
+ 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);
+
+ foreach (S; AliasSeq!(char[], const(char)[], string,
+ wchar[], const(wchar)[], wstring,
+ dchar[], const(dchar)[], 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);
+ assert(commonPrefix(to!S("hello"), to!T("")).empty);
+ assert(commonPrefix(to!S("hello, world"), to!T("hello, there")) == to!S("hello, "));
+ assert(commonPrefix(to!S("hello, there"), to!T("hello, world")) == to!S("hello, "));
+ assert(commonPrefix(to!S("hello, "), to!T("hello, world")) == to!S("hello, "));
+ assert(commonPrefix(to!S("hello, world"), to!T("hello, ")) == to!S("hello, "));
+ assert(commonPrefix(to!S("hello, world"), to!T("hello, world")) == to!S("hello, world"));
+
+ //Bug# 8890
+ assert(commonPrefix(to!S("Пиво"), to!T("Пони"))== to!S("П"));
+ assert(commonPrefix(to!S("Пони"), to!T("Пиво"))== to!S("П"));
+ assert(commonPrefix(to!S("Пиво"), to!T("Пиво"))== to!S("Пиво"));
+ assert(commonPrefix(to!S("\U0010FFFF\U0010FFFB\U0010FFFE"),
+ to!T("\U0010FFFF\U0010FFFB\U0010FFFC")) == to!S("\U0010FFFF\U0010FFFB"));
+ assert(commonPrefix(to!S("\U0010FFFF\U0010FFFB\U0010FFFC"),
+ to!T("\U0010FFFF\U0010FFFB\U0010FFFE")) == to!S("\U0010FFFF\U0010FFFB"));
+ assert(commonPrefix!"a != b"(to!S("Пиво"), to!T("онво")) == to!S("Пи"));
+ assert(commonPrefix!"a != b"(to!S("онво"), to!T("Пиво")) == to!S("он"));
+ }();
+
+ static assert(is(typeof(commonPrefix(to!S("Пиво"), filter!"true"("Пони"))) == S));
+ assert(equal(commonPrefix(to!S("Пиво"), filter!"true"("Пони")), to!S("П")));
+
+ static assert(is(typeof(commonPrefix(filter!"true"("Пиво"), to!S("Пони"))) ==
+ typeof(takeExactly(filter!"true"("П"), 1))));
+ assert(equal(commonPrefix(filter!"true"("Пиво"), to!S("Пони")), takeExactly(filter!"true"("П"), 1)));
+ }
+
+ assertThrown!UTFException(commonPrefix("\U0010FFFF\U0010FFFB", "\U0010FFFF\U0010FFFB"[0 .. $ - 1]));
+
+ assert(commonPrefix("12345"d, [49, 50, 51, 60, 60]) == "123"d);
+ assert(commonPrefix([49, 50, 51, 60, 60], "12345" ) == [49, 50, 51]);
+ assert(commonPrefix([49, 50, 51, 60, 60], "12345"d) == [49, 50, 51]);
+
+ assert(commonPrefix!"a == ('0' + b)"("12345" , [1, 2, 3, 9, 9]) == "123");
+ assert(commonPrefix!"a == ('0' + b)"("12345"d, [1, 2, 3, 9, 9]) == "123"d);
+ assert(commonPrefix!"('0' + a) == b"([1, 2, 3, 9, 9], "12345" ) == [1, 2, 3]);
+ assert(commonPrefix!"('0' + a) == b"([1, 2, 3, 9, 9], "12345"d) == [1, 2, 3]);
+}
+
+// count
+/**
+The first version counts the number of elements $(D x) in $(D r) for
+which $(D pred(x, value)) is $(D true). $(D pred) defaults to
+equality. Performs $(BIGOH haystack.length) evaluations of $(D pred).
+
+The second version returns the number of times $(D needle) occurs in
+$(D haystack). Throws an exception if $(D needle.empty), as the _count
+of the empty range in any range would be infinite. Overlapped counts
+are not considered, for example $(D count("aaa", "aa")) is $(D 1), not
+$(D 2).
+
+The third version counts the elements for which $(D pred(x)) is $(D
+true). Performs $(BIGOH haystack.length) evaluations of $(D pred).
+
+The fourth version counts the number of elements in a range. It is
+an optimization for the third version: if the given range has the
+`length` property the count is returned right away, otherwise
+performs $(BIGOH haystack.length) to walk the range.
+
+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))
+{
+ bool pred2(ElementType!Range a) { return binaryFun!pred(a, needle); }
+ return count!pred2(haystack);
+}
+
+///
+@safe unittest
+{
+ import std.uni : toLower;
+
+ // count elements in range
+ int[] a = [ 1, 2, 4, 3, 2, 5, 3, 2, 4 ];
+ assert(count(a) == 9);
+ assert(count(a, 2) == 3);
+ assert(count!("a > b")(a, 2) == 5);
+ // count range in range
+ assert(count("abcadfabf", "ab") == 2);
+ assert(count("ababab", "abab") == 1);
+ assert(count("ababab", "abx") == 0);
+ // fuzzy count range in range
+ assert(count!((a, b) => toLower(a) == toLower(b))("AbcAdFaBf", "ab") == 2);
+ // count predicate in range
+ assert(count!("a > 1")(a) == 8);
+}
+
+@safe unittest
+{
+ import std.conv : text;
+
+ 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)));
+
+ // check strings
+ assert(count("日本語") == 3);
+ assert(count("日本語"w) == 3);
+ assert(count("日本語"d) == 3);
+
+ assert(count!("a == '日'")("日本語") == 1);
+ assert(count!("a == '本'")("日本語"w) == 1);
+ assert(count!("a == '語'")("日本語"d) == 1);
+}
+
+@safe unittest
+{
+ string s = "This is a fofofof list";
+ string sub = "fof";
+ assert(count(s, sub) == 2);
+}
+
+/// 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))
+{
+ assert(!needle.empty, "Cannot count occurrences of an empty range");
+
+ static if (isInfinite!R2)
+ {
+ //Note: This is the special case of looking for an infinite inside a finite...
+ //"How many instances of the Fibonacci sequence can you count in [1, 2, 3]?" - "None."
+ return 0;
+ }
+ else
+ {
+ size_t result;
+ //Note: haystack is not saved, because findskip is designed to modify it
+ for ( ; findSkip!pred(haystack, needle.save) ; ++result)
+ {}
+ return result;
+ }
+}
+
+/// Ditto
+size_t count(alias pred, R)(R haystack)
+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
+ foreach (T elem; haystack)
+ if (unaryFun!pred(elem)) ++result;
+ return result;
+}
+
+/// Ditto
+size_t count(R)(R haystack)
+if (isInputRange!R && !isInfinite!R)
+{
+ return walkLength(haystack);
+}
+
+@safe unittest
+{
+ int[] a = [ 1, 2, 4, 3, 2, 5, 3, 2, 4 ];
+ assert(count!("a == 3")(a) == 2);
+ assert(count("日本語") == 3);
+}
+
+// Issue 11253
+@safe nothrow unittest
+{
+ assert([1, 2, 3].count([2, 3]) == 1);
+}
+
+/++
+ 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
+ $(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
+ $(D haystack) before reaching an element for which
+ $(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 .. $])))))
+{
+ typeof(return) result;
+
+ static if (needles.length == 1)
+ {
+ static if (hasLength!R) //Note: Narrow strings don't have length.
+ {
+ //We delegate to find because find is very efficient.
+ //We store the length of the haystack so we don't have to save it.
+ auto len = haystack.length;
+ auto r2 = find!pred(haystack, needles[0]);
+ if (!r2.empty)
+ return cast(typeof(return)) (len - r2.length);
+ }
+ else
+ {
+ import std.range : dropOne;
+
+ if (needles[0].empty)
+ return 0;
+
+ //Default case, slower route doing startsWith iteration
+ for ( ; !haystack.empty ; ++result )
+ {
+ //We compare the first elements of the ranges here before
+ //forwarding to startsWith. This avoids making useless saves to
+ //haystack/needle if they aren't even going to be mutated anyways.
+ //It also cuts down on the amount of pops on haystack.
+ if (binaryFun!pred(haystack.front, needles[0].front))
+ {
+ //Here, we need to save the needle before popping it.
+ //haystack we pop in all paths, so we do that, and then save.
+ haystack.popFront();
+ if (startsWith!pred(haystack.save, needles[0].save.dropOne()))
+ return result;
+ }
+ else
+ haystack.popFront();
+ }
+ }
+ }
+ else
+ {
+ foreach (i, Ri; Rs)
+ {
+ static if (isForwardRange!Ri)
+ {
+ if (needles[i].empty)
+ return 0;
+ }
+ }
+ Tuple!Rs t;
+ foreach (i, Ri; Rs)
+ {
+ static if (!isForwardRange!Ri)
+ {
+ t[i] = needles[i];
+ }
+ }
+ for (; !haystack.empty ; ++result, haystack.popFront())
+ {
+ foreach (i, Ri; Rs)
+ {
+ static if (isForwardRange!Ri)
+ {
+ t[i] = needles[i].save;
+ }
+ }
+ if (startsWith!pred(haystack.save, t.expand))
+ {
+ return result;
+ }
+ }
+ }
+
+ //Because of @@@8804@@@: Avoids both "unreachable code" or "no return statement"
+ static if (isInfinite!R) assert(0);
+ else return -1;
+}
+
+/// 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))
+{
+ bool pred2(ElementType!R a) { return binaryFun!pred(a, needle); }
+ return countUntil!pred2(haystack);
+}
+
+///
+@safe unittest
+{
+ assert(countUntil("hello world", "world") == 6);
+ assert(countUntil("hello world", 'r') == 8);
+ assert(countUntil("hello world", "programming") == -1);
+ assert(countUntil("日本語", "本語") == 1);
+ assert(countUntil("日本語", '語') == 2);
+ assert(countUntil("日本語", "五") == -1);
+ assert(countUntil("日本語", '五') == -1);
+ assert(countUntil([0, 7, 12, 22, 9], [12, 22]) == 2);
+ assert(countUntil([0, 7, 12, 22, 9], 9) == 4);
+ assert(countUntil!"a > b"([0, 7, 12, 22, 9], 20) == 3);
+}
+
+@safe unittest
+{
+ import std.algorithm.iteration : filter;
+ import std.internal.test.dummyrange;
+
+ assert(countUntil("日本語", "") == 0);
+ assert(countUntil("日本語"d, "") == 0);
+
+ assert(countUntil("", "") == 0);
+ assert(countUntil("".filter!"true"(), "") == 0);
+
+ auto rf = [0, 20, 12, 22, 9].filter!"true"();
+ assert(rf.countUntil!"a > b"((int[]).init) == 0);
+ assert(rf.countUntil!"a > b"(20) == 3);
+ assert(rf.countUntil!"a > b"([20, 8]) == 3);
+ assert(rf.countUntil!"a > b"([20, 10]) == -1);
+ assert(rf.countUntil!"a > b"([20, 8, 0]) == -1);
+
+ auto r = new ReferenceForwardRange!int([0, 1, 2, 3, 4, 5, 6]);
+ auto r2 = new ReferenceForwardRange!int([3, 4]);
+ auto r3 = new ReferenceForwardRange!int([3, 5]);
+ assert(r.save.countUntil(3) == 3);
+ assert(r.save.countUntil(r2) == 3);
+ assert(r.save.countUntil(7) == -1);
+ assert(r.save.countUntil(r3) == -1);
+}
+
+@safe unittest
+{
+ assert(countUntil("hello world", "world", "asd") == 6);
+ assert(countUntil("hello world", "world", "ello") == 1);
+ assert(countUntil("hello world", "world", "") == 0);
+ assert(countUntil("hello world", "world", 'l') == 2);
+}
+
+/++
+ Similar to the previous overload of $(D countUntil), except that this one
+ evaluates only the predicate $(D pred).
+
+ Params:
+ pred = Predicate to when to stop counting.
+ 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))
+{
+ typeof(return) i;
+ static if (isRandomAccessRange!R)
+ {
+ //Optimized RA implementation. Since we want to count *and* iterate at
+ //the same time, it is more efficient this way.
+ static if (hasLength!R)
+ {
+ immutable len = cast(typeof(return)) haystack.length;
+ for ( ; i < len ; ++i )
+ if (unaryFun!pred(haystack[i])) return i;
+ }
+ else //if (isInfinite!R)
+ {
+ for ( ; ; ++i )
+ if (unaryFun!pred(haystack[i])) return i;
+ }
+ }
+ else static if (hasLength!R)
+ {
+ //For those odd ranges that have a length, but aren't RA.
+ //It is faster to quick find, and then compare the lengths
+ auto r2 = find!pred(haystack.save);
+ if (!r2.empty) return cast(typeof(return)) (haystack.length - r2.length);
+ }
+ else //Everything else
+ {
+ alias T = ElementType!R; //For narrow strings forces dchar iteration
+ foreach (T elem; haystack)
+ {
+ if (unaryFun!pred(elem)) return i;
+ ++i;
+ }
+ }
+
+ //Because of @@@8804@@@: Avoids both "unreachable code" or "no return statement"
+ static if (isInfinite!R) assert(0);
+ else return -1;
+}
+
+///
+@safe unittest
+{
+ import std.ascii : isDigit;
+ import std.uni : isWhite;
+
+ assert(countUntil!(std.uni.isWhite)("hello world") == 5);
+ assert(countUntil!(std.ascii.isDigit)("hello world") == -1);
+ assert(countUntil!"a > 20"([0, 7, 12, 22, 9]) == 3);
+}
+
+@safe unittest
+{
+ import std.internal.test.dummyrange;
+
+ // References
+ {
+ // input
+ ReferenceInputRange!int r;
+ r = new ReferenceInputRange!int([0, 1, 2, 3, 4, 5, 6]);
+ assert(r.countUntil(3) == 3);
+ r = new ReferenceInputRange!int([0, 1, 2, 3, 4, 5, 6]);
+ assert(r.countUntil(7) == -1);
+ }
+ {
+ // forward
+ auto r = new ReferenceForwardRange!int([0, 1, 2, 3, 4, 5, 6]);
+ assert(r.save.countUntil([3, 4]) == 3);
+ assert(r.save.countUntil(3) == 3);
+ assert(r.save.countUntil([3, 7]) == -1);
+ assert(r.save.countUntil(7) == -1);
+ }
+ {
+ // infinite forward
+ auto r = new ReferenceInfiniteForwardRange!int(0);
+ assert(r.save.countUntil([3, 4]) == 3);
+ assert(r.save.countUntil(3) == 3);
+ }
+}
+
+/**
+Checks if the given range ends with (one of) the given needle(s).
+The reciprocal of $(D startsWith).
+
+Params:
+ pred = The predicate to use for comparing elements between the range and
+ the needle(s).
+
+ doesThisEnd = The
+ $(REF_ALTTEXT bidirectional range, isBidirectionalRange, std,range,primitives)
+ to check.
+
+ withOneOfThese = The needles to check against, which may be single
+ elements, or bidirectional ranges of elements.
+
+ withThis = The single element to check.
+
+Returns:
+0 if the needle(s) do not occur at the end of the given range;
+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) &&
+ is(typeof(.endsWith!pred(doesThisEnd, withOneOfThese[1 .. $])) : uint))
+{
+ alias haystack = doesThisEnd;
+ alias needles = withOneOfThese;
+
+ // Make one pass looking for empty ranges in needles
+ foreach (i, Unused; Needles)
+ {
+ // Empty range matches everything
+ static if (!is(typeof(binaryFun!pred(haystack.back, needles[i])) : bool))
+ {
+ if (needles[i].empty) return i + 1;
+ }
+ }
+
+ for (; !haystack.empty; haystack.popBack())
+ {
+ foreach (i, Unused; Needles)
+ {
+ static if (is(typeof(binaryFun!pred(haystack.back, needles[i])) : bool))
+ {
+ // Single-element
+ if (binaryFun!pred(haystack.back, needles[i]))
+ {
+ // found, but continue to account for one-element
+ // range matches (consider endsWith("ab", "b",
+ // 'b') should return 1, not 2).
+ continue;
+ }
+ }
+ else
+ {
+ if (binaryFun!pred(haystack.back, needles[i].back))
+ continue;
+ }
+
+ // This code executed on failure to match
+ // Out with this guy, check for the others
+ uint result = endsWith!pred(haystack, needles[0 .. i], needles[i + 1 .. $]);
+ if (result > i) ++result;
+ return result;
+ }
+
+ // If execution reaches this point, then the back matches for all
+ // needles ranges. What we need to do now is to lop off the back of
+ // all ranges involved and recurse.
+ foreach (i, Unused; Needles)
+ {
+ static if (is(typeof(binaryFun!pred(haystack.back, needles[i])) : bool))
+ {
+ // Test has passed in the previous loop
+ return i + 1;
+ }
+ else
+ {
+ needles[i].popBack();
+ if (needles[i].empty) return i + 1;
+ }
+ }
+ }
+ return 0;
+}
+
+/// Ditto
+bool endsWith(alias pred = "a == b", R1, R2)(R1 doesThisEnd, R2 withThis)
+if (isBidirectionalRange!R1 &&
+ isBidirectionalRange!R2 &&
+ is(typeof(binaryFun!pred(doesThisEnd.back, withThis.back)) : bool))
+{
+ alias haystack = doesThisEnd;
+ alias needle = withThis;
+
+ static if (is(typeof(pred) : string))
+ enum isDefaultPred = pred == "a == b";
+ else
+ enum isDefaultPred = false;
+
+ static if (isDefaultPred && isArray!R1 && isArray!R2 &&
+ is(Unqual!(ElementEncodingType!R1) == Unqual!(ElementEncodingType!R2)))
+ {
+ if (haystack.length < needle.length) return false;
+
+ return haystack[$ - needle.length .. $] == needle;
+ }
+ else
+ {
+ import std.range : retro;
+ return startsWith!pred(retro(doesThisEnd), retro(withThis));
+ }
+}
+
+/// Ditto
+bool endsWith(alias pred = "a == b", R, E)(R doesThisEnd, E withThis)
+if (isBidirectionalRange!R &&
+ is(typeof(binaryFun!pred(doesThisEnd.back, withThis)) : bool))
+{
+ if (doesThisEnd.empty)
+ return false;
+
+ alias predFunc = binaryFun!pred;
+
+ // auto-decoding special case
+ static if (isNarrowString!R)
+ {
+ // specialize for ASCII as to not change previous behavior
+ if (withThis <= 0x7F)
+ return predFunc(doesThisEnd[$ - 1], withThis);
+ else
+ return predFunc(doesThisEnd.back, withThis);
+ }
+ else
+ {
+ return predFunc(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);
+ assert(endsWith("abc", "c", "a") == 1);
+ assert(endsWith("abc", "c", "c") == 1);
+ assert(endsWith("abc", "bc", "c") == 2);
+ assert(endsWith("abc", "x", "c", "b") == 2);
+ assert(endsWith("abc", "x", "aa", "bc") == 3);
+ assert(endsWith("abc", "x", "aaa", "sab") == 0);
+ assert(endsWith("abc", "x", "aaa", 'c', "sab") == 3);
+}
+
+@safe unittest
+{
+ import std.algorithm.iteration : filterBidirectional;
+ import std.conv : to;
+ import std.meta : AliasSeq;
+
+ foreach (S; AliasSeq!(char[], wchar[], dchar[], string, wstring, dstring))
+ {
+ assert(!endsWith(to!S("abc"), 'a'));
+ assert(endsWith(to!S("abc"), 'a', 'c') == 2);
+ assert(!endsWith(to!S("abc"), 'x', 'n', 'b'));
+ assert(endsWith(to!S("abc"), 'x', 'n', 'c') == 3);
+ assert(endsWith(to!S("abc\uFF28"), 'a', '\uFF28', 'c') == 2);
+
+ 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("")));
+ assert(!endsWith(to!S("abc"), to!T("a")));
+ assert(!endsWith(to!S("abc"), to!T("b")));
+ assert(endsWith(to!S("abc"), to!T("bc"), 'c') == 2);
+ assert(endsWith(to!S("abc"), to!T("a"), "c") == 2);
+ assert(endsWith(to!S("abc"), to!T("c"), "a") == 1);
+ assert(endsWith(to!S("abc"), to!T("c"), "c") == 1);
+ assert(endsWith(to!S("abc"), to!T("x"), 'c', "b") == 2);
+ assert(endsWith(to!S("abc"), 'x', to!T("aa"), "bc") == 3);
+ assert(endsWith(to!S("abc"), to!T("x"), "aaa", "sab") == 0);
+ assert(endsWith(to!S("abc"), to!T("x"), "aaa", "c", "sab") == 3);
+ assert(endsWith(to!S("\uFF28el\uFF4co"), to!T("l\uFF4co")));
+ assert(endsWith(to!S("\uFF28el\uFF4co"), to!T("lo"), to!T("l\uFF4co")) == 2);
+
+ //Unicode
+ assert(endsWith(to!S("\uFF28el\uFF4co"), to!T("l\uFF4co")));
+ assert(endsWith(to!S("\uFF28el\uFF4co"), to!T("lo"), to!T("l\uFF4co")) == 2);
+ assert(endsWith(to!S("日本語"), to!T("本語")));
+ assert(endsWith(to!S("日本語"), to!T("日本語")));
+ assert(!endsWith(to!S("本語"), to!T("日本語")));
+
+ //Empty
+ assert(endsWith(to!S(""), T.init));
+ assert(!endsWith(to!S(""), 'a'));
+ assert(endsWith(to!S("a"), T.init));
+ assert(endsWith(to!S("a"), T.init, "") == 1);
+ assert(endsWith(to!S("a"), T.init, 'a') == 1);
+ assert(endsWith(to!S("a"), 'a', T.init) == 2);
+ }();
+ }
+
+ 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, 0));
+ assert(!endsWith(arr, 4));
+ assert(endsWith(arr, 5));
+ assert(endsWith(arr, 0, 4, 5) == 3);
+ assert(endsWith(arr, [5]));
+ assert(endsWith(arr, [4, 5]));
+ assert(endsWith(arr, [4, 5], 7) == 1);
+ assert(!endsWith(arr, [2, 4, 5]));
+ assert(endsWith(arr, [2, 4, 5], [3, 4, 5]) == 2);
+
+ //Normal input range
+ assert(!endsWith(filterBidirectional!"true"(arr), 4));
+ assert(endsWith(filterBidirectional!"true"(arr), 5));
+ assert(endsWith(filterBidirectional!"true"(arr), [5]));
+ assert(endsWith(filterBidirectional!"true"(arr), [4, 5]));
+ assert(endsWith(filterBidirectional!"true"(arr), [4, 5], 7) == 1);
+ assert(!endsWith(filterBidirectional!"true"(arr), [2, 4, 5]));
+ assert(endsWith(filterBidirectional!"true"(arr), [2, 4, 5], [3, 4, 5]) == 2);
+ assert(endsWith(arr, filterBidirectional!"true"([4, 5])));
+ assert(endsWith(arr, filterBidirectional!"true"([4, 5]), 7) == 1);
+ assert(!endsWith(arr, filterBidirectional!"true"([2, 4, 5])));
+ assert(endsWith(arr, [2, 4, 5], filterBidirectional!"true"([3, 4, 5])) == 2);
+
+ //Non-default pred
+ assert(endsWith!("a%10 == b%10")(arr, [14, 15]));
+ assert(!endsWith!("a%10 == b%10")(arr, [15, 14]));
+ }
+}
+
+/**
+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
+haystack) are compared with $(D needle) by using predicate $(D
+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 $(REF retro, std,range).
+
+Params:
+
+pred = The predicate for comparing each element with the needle, defaulting to
+$(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 $(REF_ALTTEXT input range, isInputRange, std,range,primitives)
+searched in.
+
+needle = The element searched for.
+
+Constraints:
+
+$(D isInputRange!InputRange && is(typeof(binaryFun!pred(haystack.front, needle)
+: bool)))
+
+Returns:
+
+$(D haystack) advanced such that the front element is the one searched for;
+that is, until $(D binaryFun!pred(haystack.front, needle)) is $(D true). If no
+such position exists, returns an empty $(D haystack).
+
+See_Also:
+ $(HTTP sgi.com/tech/stl/_find.html, STL's _find)
+ */
+InputRange find(alias pred = "a == b", InputRange, Element)(InputRange haystack, scope Element needle)
+if (isInputRange!InputRange &&
+ is (typeof(binaryFun!pred(haystack.front, needle)) : bool))
+{
+ alias R = InputRange;
+ alias E = Element;
+ alias predFun = binaryFun!pred;
+ static if (is(typeof(pred == "a == b")))
+ enum isDefaultPred = pred == "a == b";
+ else
+ enum isDefaultPred = false;
+ enum isIntegralNeedle = isSomeChar!E || isIntegral!E || isBoolean!E;
+
+ alias EType = ElementType!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;
+
+ //These are two special cases which can search without decoding the UTF stream.
+ static if (isDefaultPred && isIntegralNeedle)
+ {
+ import std.utf : canSearchInCodeUnits;
+
+ //This special case deals with UTF8 search, when the needle
+ //is represented by a single code point.
+ //Note: "needle <= 0x7F" properly handles sign via unsigned promotion
+ static if (is(UEEType == char))
+ {
+ if (!__ctfe && canSearchInCodeUnits!char(needle))
+ {
+ static R trustedMemchr(ref R haystack, ref E needle) @trusted nothrow pure
+ {
+ import core.stdc.string : memchr;
+ auto ptr = memchr(haystack.ptr, needle, haystack.length);
+ return ptr ?
+ haystack[cast(char*) ptr - haystack.ptr .. $] :
+ haystack[$ .. $];
+ }
+ return trustedMemchr(haystack, needle);
+ }
+ }
+
+ //Ditto, but for UTF16
+ static if (is(UEEType == wchar))
+ {
+ if (canSearchInCodeUnits!wchar(needle))
+ {
+ foreach (i, ref EEType e; haystack)
+ {
+ if (e == needle)
+ return haystack[i .. $];
+ }
+ return haystack[$ .. $];
+ }
+ }
+ }
+
+ //Previous conditonal optimizations did not succeed. Fallback to
+ //unconditional implementations
+ static if (isDefaultPred)
+ {
+ import std.utf : encode;
+
+ //In case of default pred, it is faster to do string/string search.
+ UEEType[is(UEEType == char) ? 4 : 2] buf;
+
+ size_t len = encode(buf, needle);
+ return find(haystack, buf[0 .. len]);
+ }
+ else
+ {
+ import std.utf : decode;
+
+ //Explicit pred: we must test each character by the book.
+ //We choose a manual decoding approach, because it is faster than
+ //the built-in foreach, or doing a front/popFront for-loop.
+ immutable len = haystack.length;
+ size_t i = 0, next = 0;
+ while (next < len)
+ {
+ if (predFun(decode(haystack, next), needle))
+ return haystack[i .. $];
+ i = next;
+ }
+ return haystack[$ .. $];
+ }
+ }
+ else static if (isArray!R)
+ {
+ //10403 optimization
+ static if (isDefaultPred && isIntegral!EType && EType.sizeof == 1 && isIntegralNeedle)
+ {
+ import std.algorithm.comparison : max, min;
+
+ R findHelper(ref R haystack, ref E needle) @trusted nothrow pure
+ {
+ import core.stdc.string : memchr;
+
+ EType* ptr = null;
+ //Note: we use "min/max" to handle sign mismatch.
+ if (min(EType.min, needle) == EType.min &&
+ max(EType.max, needle) == EType.max)
+ {
+ ptr = cast(EType*) memchr(haystack.ptr, needle,
+ haystack.length);
+ }
+
+ return ptr ?
+ haystack[ptr - haystack.ptr .. $] :
+ haystack[$ .. $];
+ }
+
+ if (!__ctfe)
+ return findHelper(haystack, needle);
+ }
+
+ //Default implementation.
+ foreach (i, ref e; haystack)
+ if (predFun(e, needle))
+ return haystack[i .. $];
+ return haystack[$ .. $];
+ }
+ else
+ {
+ //Everything else. Walk.
+ for ( ; !haystack.empty; haystack.popFront() )
+ {
+ if (predFun(haystack.front, needle))
+ break;
+ }
+ return haystack;
+ }
+}
+
+///
+@safe unittest
+{
+ import std.algorithm.comparison : equal;
+ import std.container : SList;
+ import std.range;
+ import std.range.primitives : empty;
+
+ 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) == []);
+ assert(equal(find(SList!int(1, 2, 3, 4, 5)[], 4), SList!int(4, 5)[]));
+ assert(find!"a > b"([1, 2, 3, 5], 2) == [3, 5]);
+
+ auto a = [ 1, 2, 3 ];
+ assert(find(a, 5).empty); // not found
+ assert(!find(a, 2).empty); // found
+
+ // Case-insensitive find of a string
+ string[] s = [ "Hello", "world", "!" ];
+ assert(!find!("toLower(a) == b")(s, "hello").empty);
+}
+
+@safe unittest
+{
+ import std.algorithm.comparison : equal;
+ import std.container : SList;
+
+ auto lst = SList!int(1, 2, 5, 7, 3);
+ assert(lst.front == 1);
+ auto r = find(lst[], 5);
+ assert(equal(r, SList!int(5, 7, 3)[]));
+ assert(find([1, 2, 3, 5], 4).empty);
+ assert(equal(find!"a > b"("hello", 'k'), "llo"));
+}
+
+@safe pure nothrow unittest
+{
+ assert(!find ([1, 2, 3], 2).empty);
+ assert(!find!((a,b)=>a == b)([1, 2, 3], 2).empty);
+ assert(!find ([1, 2, 3], 2).empty);
+ assert(!find!((a,b)=>a == b)([1, 2, 3], 2).empty);
+}
+
+@safe pure unittest
+{
+ import std.meta : AliasSeq;
+ foreach (R; AliasSeq!(string, wstring, dstring))
+ {
+ foreach (E; AliasSeq!(char, wchar, dchar))
+ {
+ assert(find ("hello world", 'w') == "world");
+ assert(find!((a,b)=>a == b)("hello world", 'w') == "world");
+ assert(find ("日c語", 'c') == "c語");
+ assert(find!((a,b)=>a == b)("日c語", 'c') == "c語");
+ assert(find ("0123456789", 'A').empty);
+ static if (E.sizeof >= 2)
+ {
+ assert(find ("日本語", '本') == "本語");
+ assert(find!((a,b)=>a == b)("日本語", '本') == "本語");
+ }
+ }
+ }
+}
+
+@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 ([1, 2, 3], 2));
+ static assert(find!((a,b)=>a == b)([1, 2, 3], 2));
+ static assert(find ([1, 2, 3], 2));
+ static assert(find!((a,b)=>a == b)([1, 2, 3], 2));
+}
+
+@safe unittest
+{
+ import std.exception : assertCTFEable;
+ import std.meta : AliasSeq;
+
+ void dg() @safe pure nothrow
+ {
+ byte[] sarr = [1, 2, 3, 4];
+ ubyte[] uarr = [1, 2, 3, 4];
+ foreach (arr; AliasSeq!(sarr, uarr))
+ {
+ foreach (T; AliasSeq!(byte, ubyte, int, uint))
+ {
+ assert(find(arr, cast(T) 3) == arr[2 .. $]);
+ assert(find(arr, cast(T) 9) == arr[$ .. $]);
+ }
+ assert(find(arr, 256) == arr[$ .. $]);
+ }
+ }
+ dg();
+ assertCTFEable!dg;
+}
+
+@safe unittest
+{
+ // Bugzilla 11603
+ enum Foo : ubyte { A }
+ assert([Foo.A].find(Foo.A).empty == false);
+
+ ubyte x = 0;
+ assert([x].find(x).empty == false);
+}
+
+/**
+Advances the input range $(D haystack) by calling $(D haystack.popFront)
+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
+$(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 $(REF_ALTTEXT input range, isInputRange, std,range,primitives) to
+search in.
+
+Returns:
+
+$(D haystack) advanced such that the front element is the one searched for;
+that is, until $(D binaryFun!pred(haystack.front, needle)) is $(D true). If no
+such position exists, returns an empty $(D haystack).
+
+See_Also:
+ $(HTTP sgi.com/tech/stl/find_if.html, STL's find_if)
+*/
+InputRange find(alias pred, InputRange)(InputRange haystack)
+if (isInputRange!InputRange)
+{
+ alias R = InputRange;
+ alias predFun = unaryFun!pred;
+ static if (isNarrowString!R)
+ {
+ import std.utf : decode;
+
+ immutable len = haystack.length;
+ size_t i = 0, next = 0;
+ while (next < len)
+ {
+ if (predFun(decode(haystack, next)))
+ return haystack[i .. $];
+ i = next;
+ }
+ return haystack[$ .. $];
+ }
+ else
+ {
+ //standard range
+ for ( ; !haystack.empty; haystack.popFront() )
+ {
+ if (predFun(haystack.front))
+ break;
+ }
+ return haystack;
+ }
+}
+
+///
+@safe unittest
+{
+ auto arr = [ 1, 2, 3, 4, 1 ];
+ assert(find!("a > 2")(arr) == [ 3, 4, 1 ]);
+
+ // with predicate alias
+ bool pred(int x) { return x + 1 > 1.5; }
+ assert(find!(pred)(arr) == arr);
+}
+
+@safe pure unittest
+{
+ int[] r = [ 1, 2, 3 ];
+ assert(find!(a=>a > 2)(r) == [3]);
+ bool pred(int x) { return x + 1 > 1.5; }
+ assert(find!(pred)(r) == r);
+
+ assert(find!(a=>a > 'v')("hello world") == "world");
+ assert(find!(a=>a%4 == 0)("日本語") == "本語");
+}
+
+/**
+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 $(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 $(REF_ALTTEXT forward range, isForwardRange, std,range,primitives)
+searched in.
+
+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, scope R2 needle)
+if (isForwardRange!R1 && isForwardRange!R2
+ && is(typeof(binaryFun!pred(haystack.front, needle.front)) : bool))
+{
+ static if (!isRandomAccessRange!R1)
+ {
+ 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));
+ // 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; }
+ return force!R1(.find!(pred, Representation, Representation)
+ (force!Representation(haystack), force!Representation(needle)));
+ }
+ else
+ {
+ return simpleMindedFind!pred(haystack, needle);
+ }
+ }
+ else static if (!isBidirectionalRange!R2 || !hasSlicing!R1)
+ {
+ static if (!is(ElementType!R1 == ElementType!R2))
+ {
+ return simpleMindedFind!pred(haystack, needle);
+ }
+ else
+ {
+ // Prepare the search with needle's first element
+ if (needle.empty)
+ return haystack;
+
+ haystack = .find!pred(haystack, needle.front);
+
+ static if (hasLength!R1 && hasLength!R2 && is(typeof(takeNone(haystack)) == R1))
+ {
+ if (needle.length > haystack.length)
+ return takeNone(haystack);
+ }
+ else
+ {
+ if (haystack.empty)
+ return haystack;
+ }
+
+ needle.popFront();
+ size_t matchLen = 1;
+
+ // Loop invariant: haystack[0 .. matchLen] matches everything in
+ // the initial needle that was popped out of needle.
+ for (;;)
+ {
+ // Extend matchLength as much as possible
+ for (;;)
+ {
+ import std.range : takeNone;
+
+ if (needle.empty || haystack.empty)
+ return haystack;
+
+ static if (hasLength!R1 && is(typeof(takeNone(haystack)) == R1))
+ {
+ if (matchLen == haystack.length)
+ return takeNone(haystack);
+ }
+
+ if (!binaryFun!pred(haystack[matchLen], needle.front))
+ break;
+
+ ++matchLen;
+ needle.popFront();
+ }
+
+ auto bestMatch = haystack[0 .. matchLen];
+ haystack.popFront();
+ haystack = .find!pred(haystack, bestMatch);
+ }
+ }
+ }
+ else // static if (hasSlicing!R1 && isBidirectionalRange!R2)
+ {
+ if (needle.empty) return haystack;
+
+ static if (hasLength!R2)
+ {
+ immutable needleLength = needle.length;
+ }
+ else
+ {
+ immutable needleLength = walkLength(needle.save);
+ }
+ if (needleLength > haystack.length)
+ {
+ return haystack[haystack.length .. haystack.length];
+ }
+ // 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.algorithm.comparison : mismatch;
+ import std.range : SortedRange;
+ 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 .. $];
+ }
+ else static if (isRandomAccessRange!R2)
+ {
+ immutable lastIndex = needleLength - 1;
+ auto last = needle[lastIndex];
+ size_t j = lastIndex, skip = 0;
+ for (; j < 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;
+ }
+ }
+ 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)
+ {
+ }
+ // Stage 2: linear find
+ size_t scout = needleLength - 1;
+ for (;;)
+ {
+ 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;
+ }
+ }
+ return haystack[haystack.length .. haystack.length];
+ }
+}
+
+///
+@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");
+ assert([1, 2, 3, 4].find(SList!int(2, 3)[]) == [2, 3, 4]);
+ alias C = Tuple!(int, "x", int, "y");
+ auto a = [C(1,0), C(2,0), C(3,1), C(4,0)];
+ assert(a.find!"a.x == b"([2, 3]) == [C(2,0), C(3,1), C(4,0)]);
+ assert(a[1 .. $].find!"a.x == b"([2, 3]) == [C(2,0), C(3,1), C(4,0)]);
+}
+
+@safe unittest
+{
+ import std.container : SList;
+ alias C = Tuple!(int, "x", int, "y");
+ assert([C(1,0), C(2,0), C(3,1), C(4,0)].find!"a.x == b"(SList!int(2, 3)[]) == [C(2,0), C(3,1), C(4,0)]);
+}
+
+@safe unittest
+{
+ import std.algorithm.comparison : equal;
+ import std.container : SList;
+
+ auto lst = SList!int(1, 2, 5, 7, 3);
+ static assert(isForwardRange!(int[]));
+ static assert(isForwardRange!(typeof(lst[])));
+ auto r = find(lst[], [2, 5]);
+ assert(equal(r, SList!int(2, 5, 7, 3)[]));
+}
+
+@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
+{
+ import std.algorithm.comparison : equal;
+ // @@@BUG@@@ removing static below makes unittest fail
+ static struct BiRange
+ {
+ int[] payload;
+ @property bool empty() { return payload.empty; }
+ @property BiRange save() { return this; }
+ @property ref int front() { return payload[0]; }
+ @property ref int back() { return payload[$ - 1]; }
+ void popFront() { return payload.popFront(); }
+ void popBack() { return payload.popBack(); }
+ }
+ auto r = BiRange([1, 2, 3, 10, 11, 4]);
+ assert(equal(find(r, [10, 11]), [10, 11, 4]));
+}
+
+@safe unittest
+{
+ import std.container : SList;
+
+ assert(find([ 1, 2, 3 ], SList!int(2, 3)[]) == [ 2, 3 ]);
+ assert(find([ 1, 2, 1, 2, 3, 3 ], SList!int(2, 3)[]) == [ 2, 3, 3 ]);
+}
+
+//Bug# 8334
+@safe unittest
+{
+ import std.algorithm.iteration : filter;
+ import std.range;
+
+ auto haystack = [1, 2, 3, 4, 1, 9, 12, 42];
+ auto needle = [12, 42, 27];
+
+ //different overload of find, but it's the base case.
+ assert(find(haystack, needle).empty);
+
+ assert(find(haystack, takeExactly(filter!"true"(needle), 3)).empty);
+ assert(find(haystack, filter!"true"(needle)).empty);
+}
+
+// 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;
+
+ static if (hasLength!R1)
+ {
+ static if (!hasLength!R2)
+ size_t estimatedNeedleLength = 0;
+ else
+ immutable size_t estimatedNeedleLength = needle.length;
+ }
+
+ bool haystackTooShort()
+ {
+ static if (estimateNeedleLength)
+ {
+ return haystack.length < estimatedNeedleLength;
+ }
+ else
+ {
+ return haystack.empty;
+ }
+ }
+
+ searching:
+ for (;; haystack.popFront())
+ {
+ if (haystackTooShort())
+ {
+ // Failed search
+ static if (hasLength!R1)
+ {
+ static if (is(typeof(haystack[haystack.length ..
+ haystack.length]) : R1))
+ return haystack[haystack.length .. haystack.length];
+ else
+ return R1.init;
+ }
+ else
+ {
+ assert(haystack.empty);
+ return haystack;
+ }
+ }
+ static if (estimateNeedleLength)
+ size_t matchLength = 0;
+ for (auto h = haystack.save, n = needle.save;
+ !n.empty;
+ h.popFront(), n.popFront())
+ {
+ if (h.empty || !binaryFun!pred(h.front, n.front))
+ {
+ // Failed searching n in h
+ static if (estimateNeedleLength)
+ {
+ if (estimatedNeedleLength < matchLength)
+ estimatedNeedleLength = matchLength;
+ }
+ continue searching;
+ }
+ static if (estimateNeedleLength)
+ ++matchLength;
+ }
+ break;
+ }
+ return haystack;
+}
+
+@safe unittest
+{
+ // Test simpleMindedFind for the case where both haystack and needle have
+ // length.
+ struct CustomString
+ {
+ @safe:
+ string _impl;
+
+ // This is what triggers issue 7992.
+ @property size_t length() const { return _impl.length; }
+ @property void length(size_t len) { _impl.length = len; }
+
+ // This is for conformance to the forward range API (we deliberately
+ // make it non-random access so that we will end up in
+ // simpleMindedFind).
+ @property bool empty() const { return _impl.empty; }
+ @property dchar front() const { return _impl.front; }
+ void popFront() { _impl.popFront(); }
+ @property CustomString save() { return this; }
+ }
+
+ // If issue 7992 occurs, this will throw an exception from calling
+ // popFront() on an empty range.
+ auto r = find(CustomString("a"), CustomString("b"));
+ assert(r.empty);
+}
+
+/**
+Finds two or more $(D needles) into a $(D haystack). The predicate $(D
+pred) is used throughout to compare elements. By default, elements are
+compared for equality.
+
+Params:
+
+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
+$(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
+be either comparable to one element in $(D haystack), or be itself a
+forward range with elements comparable with elements in
+$(D haystack).
+
+Returns:
+
+A tuple containing $(D haystack) positioned to match one of the
+needles and also the 1-based index of the matching element in $(D
+needles) (0 if none of $(D needles) matched, 1 if $(D needles[0])
+matched, 2 if $(D needles[1]) matched...). The first needle to be found
+will be the one that matches. If multiple needles are found at the
+same spot in the range, then the shortest one is the one which matches
+(if multiple needles of the same length are found at the same spot (e.g
+$(D "a") and $(D 'a')), then the left-most of them in the argument list
+matches).
+
+The relationship between $(D haystack) and $(D needles) simply means
+that one can e.g. search for individual $(D int)s or arrays of $(D
+int)s in an array of $(D int)s. In addition, if elements are
+individually comparable, searches of heterogeneous types are allowed
+as well: a $(D double[]) can be searched for an $(D int) or a $(D
+short[]), and conversely a $(D long) can be searched for a $(D float)
+or a $(D double[]). This makes for efficient searches without the need
+to coerce one side of the comparison into the other's side type.
+
+The complexity of the search is $(BIGOH haystack.length *
+max(needles.length)). (For needles that are individual items, length
+is considered to be 1.) The strategy used in searching several
+subranges at once maximizes cache usage by moving in $(D haystack) as
+few times as possible.
+ */
+Tuple!(Range, size_t) find(alias pred = "a == b", Range, Ranges...)
+(Range haystack, Ranges needles)
+if (Ranges.length > 1 && is(typeof(startsWith!pred(haystack, needles))))
+{
+ for (;; haystack.popFront())
+ {
+ size_t r = startsWith!pred(haystack, needles);
+ if (r || haystack.empty)
+ {
+ return tuple(haystack, r);
+ }
+ }
+}
+
+///
+@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 ]);
+ assert(find(a, [ 1, 3 ], 4) == tuple([ 4, 2, 3 ], 2));
+ // Mixed types allowed if comparable
+ assert(find(a, 5, [ 1.2, 3.5 ], 2.0) == tuple([ 2, 3 ], 3));
+}
+
+@safe unittest
+{
+ auto s1 = "Mary has a little lamb";
+ 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));
+ assert(find("abc", "bc").length == 2);
+}
+
+@safe unittest
+{
+ import std.algorithm.internal : rndstuff;
+ import std.meta : AliasSeq;
+ import std.uni : toUpper;
+
+ int[] a = [ 1, 2, 3 ];
+ assert(find(a, 5).empty);
+ assert(find(a, 2) == [2, 3]);
+
+ foreach (T; AliasSeq!(int, double))
+ {
+ auto b = rndstuff!(T)();
+ if (!b.length) continue;
+ b[$ / 2] = 200;
+ b[$ / 4] = 200;
+ assert(find(b, 200).length == b.length - b.length / 4);
+ }
+
+ // Case-insensitive find of a string
+ string[] s = [ "Hello", "world", "!" ];
+ assert(find!("toUpper(a) == toUpper(b)")(s, "hello").length == 3);
+
+ static bool f(string a, string b) { return toUpper(a) == toUpper(b); }
+ assert(find!(f)(s, "hello").length == 3);
+}
+
+@safe unittest
+{
+ import std.algorithm.comparison : equal;
+ import std.algorithm.internal : rndstuff;
+ import std.meta : AliasSeq;
+ import std.range : retro;
+
+ 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; AliasSeq!(int, double))
+ {
+ auto b = rndstuff!(T)();
+ if (!b.length) continue;
+ b[$ / 2] = 200;
+ b[$ / 4] = 200;
+ assert(find(retro(b), 200).length ==
+ b.length - (b.length - 1) / 2);
+ }
+}
+
+@safe unittest
+{
+ import std.algorithm.comparison : equal;
+ import std.internal.test.dummyrange;
+
+ 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)
+ {
+ DummyType d;
+ auto findRes = find(d, 5);
+ assert(equal(findRes, [5,6,7,8,9,10]));
+ }
+}
+
+/**
+ * Finds $(D needle) in $(D haystack) efficiently using the
+ * $(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.
+ * needle = A $(LREF BoyerMooreFinder).
+ *
+ * Returns:
+ * $(D haystack) advanced such that $(D needle) is a prefix of it (if no
+ * such position exists, returns $(D haystack) advanced to termination).
+ */
+RandomAccessRange find(RandomAccessRange, alias pred, InputRange)(
+ RandomAccessRange haystack, scope BoyerMooreFinder!(pred, InputRange) needle)
+{
+ return needle.beFound(haystack);
+}
+
+@safe unittest
+{
+ 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)
+ {
+ auto p = find(h, boyerMooreFinder(n));
+ assert(!p.empty);
+ }
+}
+
+///
+@safe unittest
+{
+ import std.range.primitives : empty;
+ int[] a = [ -1, 0, 1, 2, 3, 4, 5 ];
+ int[] b = [ 1, 2, 3 ];
+
+ assert(find(a, boyerMooreFinder(b)) == [ 1, 2, 3, 4, 5 ]);
+ assert(find(b, boyerMooreFinder(a)).empty);
+}
+
+@safe unittest
+{
+ auto bm = boyerMooreFinder("for");
+ auto match = find("Moor", bm);
+ assert(match.empty);
+}
+
+// canFind
+/++
+Convenience function. Like find, but only returns whether or not the search
+was successful.
+
+See_Also:
+$(LREF among) for checking a value against multiple possibilities.
+ +/
+template canFind(alias pred="a == b")
+{
+ import std.meta : allSatisfy;
+
+ /++
+ Returns $(D true) if and only if any value $(D v) found in the
+ input range $(D range) satisfies the predicate $(D pred).
+ Performs (at most) $(BIGOH haystack.length) evaluations of $(D pred).
+ +/
+ bool canFind(Range)(Range haystack)
+ if (is(typeof(find!pred(haystack))))
+ {
+ return any!pred(haystack);
+ }
+
+ /++
+ 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, scope Element needle)
+ if (is(typeof(find!pred(haystack, needle))))
+ {
+ return !find!pred(haystack, needle).empty;
+ }
+
+ /++
+ Returns the 1-based index of the first needle found in $(D haystack). If no
+ needle is found, then $(D 0) is returned.
+
+ So, if used directly in the condition of an if statement or loop, the result
+ will be $(D true) if one of the needles is found and $(D false) if none are
+ found, whereas if the result is used elsewhere, it can either be cast to
+ $(D bool) for the same effect or used to get which needle was found first
+ without having to deal with the tuple that $(D LREF find) returns for the
+ same operation.
+ +/
+ size_t canFind(Range, Ranges...)(Range haystack, scope Ranges needles)
+ if (Ranges.length > 1 &&
+ allSatisfy!(isForwardRange, Ranges) &&
+ is(typeof(find!pred(haystack, needles))))
+ {
+ return find!pred(haystack, needles)[1];
+ }
+}
+
+///
+@safe unittest
+{
+ assert(canFind([0, 1, 2, 3], 2) == true);
+ assert(canFind([0, 1, 2, 3], [1, 2], [2, 3]));
+ assert(canFind([0, 1, 2, 3], [1, 2], [2, 3]) == 1);
+ assert(canFind([0, 1, 2, 3], [1, 7], [2, 3]));
+ assert(canFind([0, 1, 2, 3], [1, 7], [2, 3]) == 2);
+
+ assert(canFind([0, 1, 2, 3], 4) == false);
+ assert(!canFind([0, 1, 2, 3], [1, 3], [2, 4]));
+ 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;
+
+ auto a = rndstuff!(int)();
+ if (a.length)
+ {
+ auto b = a[a.length / 2];
+ assert(canFind(a, b));
+ }
+}
+
+@safe unittest
+{
+ import std.algorithm.comparison : equal;
+ assert(equal!(canFind!"a < b")([[1, 2, 3], [7, 8, 9]], [2, 8]));
+}
+
+// findAdjacent
+/**
+Advances $(D r) until it finds the first two adjacent elements $(D a),
+$(D b) that satisfy $(D pred(a, b)). Performs $(BIGOH r.length)
+evaluations of $(D pred).
+
+Params:
+ pred = The predicate to satisfy.
+ 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
+the given predicate. If there are no such two elements, returns $(D r) advanced
+until empty.
+
+See_Also:
+ $(HTTP sgi.com/tech/stl/adjacent_find.html, STL's adjacent_find)
+*/
+Range findAdjacent(alias pred = "a == b", Range)(Range r)
+if (isForwardRange!(Range))
+{
+ auto ahead = r.save;
+ if (!ahead.empty)
+ {
+ for (ahead.popFront(); !ahead.empty; r.popFront(), ahead.popFront())
+ {
+ if (binaryFun!(pred)(r.front, ahead.front)) return r;
+ }
+ }
+ static if (!isInfinite!Range)
+ return ahead;
+}
+
+///
+@safe unittest
+{
+ int[] a = [ 11, 10, 10, 9, 8, 8, 7, 8, 9 ];
+ auto r = findAdjacent(a);
+ assert(r == [ 10, 10, 9, 8, 8, 7, 8, 9 ]);
+ auto p = findAdjacent!("a < b")(a);
+ assert(p == [ 7, 8, 9 ]);
+
+}
+
+@safe unittest
+{
+ import std.algorithm.comparison : equal;
+ import std.internal.test.dummyrange;
+ import std.range;
+
+ 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 ]);
+ p = findAdjacent!("a < b")(a);
+ assert(p == [7, 8, 9]);
+ // empty
+ a = [];
+ p = findAdjacent(a);
+ assert(p.empty);
+ // not found
+ a = [ 1, 2, 3, 4, 5 ];
+ p = findAdjacent(a);
+ assert(p.empty);
+ p = findAdjacent!"a > b"(a);
+ assert(p.empty);
+ ReferenceForwardRange!int rfr = new ReferenceForwardRange!int([1, 2, 3, 2, 2, 3]);
+ assert(equal(findAdjacent(rfr), [2, 2, 3]));
+
+ // Issue 9350
+ assert(!repeat(1).findAdjacent().empty);
+}
+
+// findAmong
+/**
+Searches the given range for an element that matches one of the given choices.
+
+Advances $(D seq) by calling $(D seq.popFront) until either
+$(D find!(pred)(choices, seq.front)) is $(D true), or $(D seq) becomes empty.
+Performs $(BIGOH seq.length * choices.length) evaluations of $(D pred).
+
+Params:
+ pred = The predicate to use for determining a match.
+ 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:
+ $(HTTP sgi.com/tech/stl/find_first_of.html, STL's find_first_of)
+*/
+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())
+ {
+ }
+ return seq;
+}
+
+///
+@safe unittest
+{
+ int[] a = [ -1, 0, 1, 2, 3, 4, 5 ];
+ int[] b = [ 3, 1, 2 ];
+ assert(findAmong(a, b) == a[2 .. $]);
+}
+
+@safe unittest
+{
+ 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 ]);
+ assert(findAmong(b, [ 4, 6, 7 ][]).empty);
+ assert(findAmong!("a == b")(a, b).length == a.length - 2);
+ assert(findAmong!("a == b")(b, [ 4, 6, 7 ][]).empty);
+}
+
+// findSkip
+/**
+ * Finds $(D needle) in $(D haystack) and positions $(D haystack)
+ * right after the first occurrence of $(D needle).
+ *
+ * Params:
+ * 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
+ * $(D false), leaving $(D haystack) untouched.
+ */
+bool findSkip(alias pred = "a == b", R1, R2)(ref R1 haystack, R2 needle)
+if (isForwardRange!R1 && isForwardRange!R2
+ && is(typeof(binaryFun!pred(haystack.front, needle.front))))
+{
+ auto parts = findSplit!pred(haystack, needle);
+ if (parts[1].empty) return false;
+ // found
+ haystack = parts[2];
+ return true;
+}
+
+///
+@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";
+ assert(findSkip(s, "cd") && s == "ef");
+
+ // Needle is not found; s is left untouched.
+ s = "abcdef";
+ assert(!findSkip(s, "cxd") && s == "abcdef");
+
+ // If the needle occurs at the end of the range, the range is left empty.
+ s = "abcdef";
+ assert(findSkip(s, "def") && s.empty);
+}
+
+/**
+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 `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).
+
+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 && 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 Result!(typeof(haystack[0 .. pos1]),
+ typeof(haystack[pos2 .. haystack.length]))(haystack[0 .. pos1],
+ haystack[pos1 .. pos2],
+ haystack[pos2 .. haystack.length]);
+ }
+ else
+ {
+ import std.range : takeExactly;
+ auto original = haystack.save;
+ auto h = haystack.save;
+ auto n = needle.save;
+ size_t pos1, pos2;
+ while (!n.empty && !h.empty)
+ {
+ if (binaryFun!pred(h.front, n.front))
+ {
+ h.popFront();
+ n.popFront();
+ ++pos2;
+ }
+ else
+ {
+ haystack.popFront();
+ n = needle.save;
+ h = haystack.save;
+ pos2 = ++pos1;
+ }
+ }
+ return Result!(typeof(takeExactly(original, pos1)),
+ typeof(h))(takeExactly(original, pos1),
+ takeExactly(haystack, pos2 - pos1),
+ h);
+ }
+}
+
+/// Ditto
+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!R1 && hasSlicing!R1 && hasLength!R2))
+ {
+ auto balance = find!pred(haystack, needle);
+ immutable pos = haystack.length - balance.length;
+ return Result!(typeof(haystack[0 .. pos]),
+ typeof(haystack[pos .. haystack.length]))(haystack[0 .. pos],
+ haystack[pos .. haystack.length]);
+ }
+ else
+ {
+ import std.range : takeExactly;
+ auto original = haystack.save;
+ auto h = haystack.save;
+ auto n = needle.save;
+ size_t pos;
+ while (!n.empty && !h.empty)
+ {
+ if (binaryFun!pred(h.front, n.front))
+ {
+ h.popFront();
+ n.popFront();
+ }
+ else
+ {
+ haystack.popFront();
+ n = needle.save;
+ h = haystack.save;
+ ++pos;
+ }
+ }
+ return Result!(typeof(takeExactly(original, pos)),
+ typeof(haystack))(takeExactly(original, pos),
+ haystack);
+ }
+}
+
+/// Ditto
+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!R1 && hasSlicing!R1 && hasLength!R2)
+ {
+ auto balance = find!pred(haystack, needle);
+ immutable pos = balance.empty ? 0 : haystack.length - balance.length + needle.length;
+ return Result!(typeof(haystack[0 .. pos]),
+ typeof(haystack[pos .. haystack.length]))(haystack[0 .. pos],
+ haystack[pos .. haystack.length]);
+ }
+ else
+ {
+ import std.range : takeExactly;
+ auto original = haystack.save;
+ auto h = haystack.save;
+ auto n = needle.save;
+ size_t pos1, pos2;
+ while (!n.empty)
+ {
+ if (h.empty)
+ {
+ // Failed search
+ return Result!(typeof(takeExactly(original, 0)),
+ typeof(original))(takeExactly(original, 0),
+ original);
+ }
+ if (binaryFun!pred(h.front, n.front))
+ {
+ h.popFront();
+ n.popFront();
+ ++pos2;
+ }
+ else
+ {
+ haystack.popFront();
+ n = needle.save;
+ h = haystack.save;
+ pos2 = ++pos1;
+ }
+ }
+ return Result!(typeof(takeExactly(original, pos2)),
+ typeof(h))(takeExactly(original, pos2),
+ h);
+ }
+}
+
+///
+@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);
+ r = findSplit(a, " ");
+ assert(r[0] == "Carl");
+ assert(r[1] == " ");
+ assert(r[2] == "Sagan Memorial Station");
+ auto r1 = findSplitBefore(a, "Sagan");
+ 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");
+}
+
+/// 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 .. $]);
+
+ 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 pure nothrow unittest
+{
+ import std.algorithm.comparison : equal;
+ import std.algorithm.iteration : filter;
+
+ 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 .. $]));
+
+ 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 .. $]));
+}
+
+@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))))
+{
+ import std.algorithm.internal : algoFormat;
+ import std.exception : enforce;
+
+ alias T = ElementType!Range;
+ alias UT = Unqual!T;
+ alias RetType = Tuple!(T, size_t);
+
+ 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));
+
+ enforce(!range.empty, "Can't count elements from an empty range");
+ size_t occurrences = 1;
+
+ static if (isForwardRange!Range)
+ {
+ Range least = range.save;
+ for (range.popFront(); !range.empty; range.popFront())
+ {
+ 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
+ least = range.save;
+ occurrences = 1;
+ }
+ else
+ ++occurrences;
+ }
+ return RetType(least.front, occurrences);
+ }
+ else static if (isAssignable!(UT, T) || (!hasElaborateAssign!UT && isAssignable!UT))
+ {
+ UT v = UT.init;
+ static if (isAssignable!(UT, T)) v = range.front;
+ else v = cast(UT) range.front;
+
+ for (range.popFront(); !range.empty; range.popFront())
+ {
+ if (binaryFun!pred(*cast(T*)&v, range.front)) continue;
+ if (binaryFun!pred(range.front, *cast(T*)&v))
+ {
+ // change the min
+ static if (isAssignable!(UT, T)) v = range.front;
+ else v = cast(UT) range.front; //Safe because !hasElaborateAssign!UT
+ occurrences = 1;
+ }
+ else
+ ++occurrences;
+ }
+ return RetType(*cast(T*)&v, occurrences);
+ }
+ else static if (hasLvalueElements!Range)
+ {
+ import std.algorithm.internal : addressOf;
+ T* p = addressOf(range.front);
+ for (range.popFront(); !range.empty; range.popFront())
+ {
+ if (binaryFun!pred(*p, range.front)) continue;
+ if (binaryFun!pred(range.front, *p))
+ {
+ // change the min
+ p = addressOf(range.front);
+ occurrences = 1;
+ }
+ else
+ ++occurrences;
+ }
+ return RetType(*p, occurrences);
+ }
+ else
+ static assert(false,
+ algoFormat("Sorry, can't find the minCount of a %s: Don't know how "~
+ "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));
+}
+
+///
+@safe unittest
+{
+ import std.conv : text;
+ import std.typecons : tuple;
+
+ int[] a = [ 2, 3, 4, 1, 2, 4, 1, 1, 2 ];
+ // Minimum is 1 and occurs 3 times
+ assert(a.minCount == tuple(1, 3));
+ // Maximum is 4 and occurs 2 times
+ assert(a.maxCount == tuple(4, 2));
+}
+
+@system unittest
+{
+ import std.conv : text;
+ import std.exception : assertThrown;
+ import std.internal.test.dummyrange;
+
+ int[][] b = [ [4], [2, 4], [4], [4] ];
+ auto c = minCount!("a[0] < b[0]")(b);
+ assert(c == tuple([2, 4], 1), text(c[0]));
+
+ //Test empty range
+ assertThrown(minCount(b[$..$]));
+
+ //test with reference ranges. Test both input and forward.
+ assert(minCount(new ReferenceInputRange!int([1, 2, 1, 0, 2, 0])) == tuple(0, 2));
+ assert(minCount(new ReferenceForwardRange!int([1, 2, 1, 0, 2, 0])) == tuple(0, 2));
+}
+
+@system unittest
+{
+ import std.conv : text;
+ import std.meta : AliasSeq;
+
+ static struct R(T) //input range
+ {
+ T[] arr;
+ alias arr this;
+ }
+
+ immutable a = [ 2, 3, 4, 1, 2, 4, 1, 1, 2 ];
+ R!(immutable int) b = R!(immutable int)(a);
+
+ assert(minCount(a) == tuple(1, 3));
+ assert(minCount(b) == tuple(1, 3));
+ assert(minCount!((ref immutable int a, ref immutable int b) => (a > b))(a) == tuple(4, 2));
+ assert(minCount!((ref immutable int a, ref immutable int b) => (a > b))(b) == tuple(4, 2));
+
+ immutable(int[])[] c = [ [4], [2, 4], [4], [4] ];
+ assert(minCount!("a[0] < b[0]")(c) == tuple([2, 4], 1), text(c[0]));
+
+ static struct S1
+ {
+ int i;
+ }
+ alias IS1 = immutable(S1);
+ static assert( isAssignable!S1);
+ static assert( isAssignable!(S1, IS1));
+
+ static struct S2
+ {
+ int* p;
+ this(ref immutable int i) immutable {p = &i;}
+ this(ref int i) {p = &i;}
+ @property ref inout(int) i() inout {return *p;}
+ bool opEquals(const S2 other) const {return i == other.i;}
+ }
+ alias IS2 = immutable(S2);
+ static assert( isAssignable!S2);
+ static assert(!isAssignable!(S2, IS2));
+ static assert(!hasElaborateAssign!S2);
+
+ static struct S3
+ {
+ int i;
+ void opAssign(ref S3 other) @disable;
+ }
+ static assert(!isAssignable!S3);
+
+ foreach (Type; AliasSeq!(S1, IS1, S2, IS2, S3))
+ {
+ static if (is(Type == immutable)) alias V = immutable int;
+ else alias V = int;
+ V one = 1, two = 2;
+ auto r1 = [Type(two), Type(one), Type(one)];
+ auto r2 = R!Type(r1);
+ assert(minCount!"a.i < b.i"(r1) == tuple(Type(one), 2));
+ assert(minCount!"a.i < b.i"(r2) == tuple(Type(one), 2));
+ assert(one == 1 && two == 2);
+ }
+}
+
+/**
+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
+/**
+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))))
+{
+ static if (hasSlicing!Range && isRandomAccessRange!Range && hasLength!Range)
+ {
+ // Prefer index-based access
+ size_t pos = 0;
+ foreach (i; 1 .. range.length)
+ {
+ if (binaryFun!pred(range[i], range[pos]))
+ {
+ pos = i;
+ }
+ }
+ return range[pos .. range.length];
+ }
+ 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));
+}
+
+///
+@safe unittest
+{
+ int[] a = [ 2, 3, 4, 1, 2, 4, 1, 1, 2 ];
+ // Minimum is 1 and first occurs in position 3
+ assert(a.minPos == [ 1, 2, 4, 1, 1, 2 ]);
+ // Maximum is 4 and first occurs in position 2
+ assert(a.maxPos == [ 4, 1, 2, 4, 1, 1, 2 ]);
+}
+
+@safe unittest
+{
+ import std.algorithm.comparison : equal;
+ import std.internal.test.dummyrange;
+
+ int[] a = [ 2, 3, 4, 1, 2, 4, 1, 1, 2 ];
+ //Test that an empty range works
+ int[] b = a[$..$];
+ assert(equal(minPos(b), b));
+
+ //test with reference range.
+ assert( equal( minPos(new ReferenceForwardRange!int([1, 2, 1, 0, 2, 0])), [0, 2, 0] ) );
+}
+
+@system unittest
+{
+ //Rvalue range
+ import std.algorithm.comparison : equal;
+ import std.container : Array;
+
+ assert(Array!int(2, 3, 4, 1, 2, 4, 1, 1, 2)
+ []
+ .minPos()
+ .equal([ 1, 2, 4, 1, 1, 2 ]));
+}
+
+@safe unittest
+{
+ //BUG 9299
+ 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 ]);
+ // Maximum is 4 and first occurs in position 5
+ assert(minPos!("a > b")(a) == [ 4, 1, 2, 4, 1, 1, 2 ]);
+
+ immutable(int[])[] b = [ [4], [2, 4], [4], [4] ];
+ 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 if no second range is given skip over the elements that fullfil pred.
+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 $(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) or $(D pred) evaluates to true,
+and $(D r1) has been 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)))
+{
+ static if (is(typeof(r1[0 .. $] == r2) : bool)
+ && is(typeof(r2.length > r1.length) : bool)
+ && is(typeof(r1 = r1[r2.length .. $])))
+ {
+ if (r2.length > r1.length || r1[0 .. r2.length] != r2)
+ {
+ return false;
+ }
+ r1 = r1[r2.length .. $];
+ return true;
+ }
+ else
+ {
+ return skipOver!((a, b) => a == b)(r1, 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)
+{
+ static if (hasLength!R1 && hasLength!R2)
+ {
+ // Shortcut opportunity!
+ if (r2.length > r1.length)
+ return false;
+ }
+ auto r = r1.save;
+ while (!r2.empty && !r.empty && binaryFun!pred(r.front, r2.front))
+ {
+ r.popFront();
+ r2.popFront();
+ }
+ if (r2.empty)
+ r1 = r;
+ return r2.empty;
+}
+
+/// Ditto
+bool skipOver(alias pred, R)(ref R r1)
+if (isForwardRange!R &&
+ ifTestable!(typeof(r1.front), unaryFun!pred))
+{
+ if (r1.empty || !unaryFun!pred(r1.front))
+ return false;
+
+ do
+ r1.popFront();
+ while (!r1.empty && unaryFun!pred(r1.front));
+ return true;
+}
+
+///
+@safe unittest
+{
+ import std.algorithm.comparison : equal;
+
+ auto s1 = "Hello world";
+ assert(!skipOver(s1, "Ha"));
+ assert(s1 == "Hello world");
+ assert(skipOver(s1, "Hell") && s1 == "o world");
+
+ string[] r1 = ["abc", "def", "hij"];
+ dstring[] r2 = ["abc"d];
+ assert(!skipOver!((a, b) => a.equal(b))(r1, ["def"d]));
+ assert(r1 == ["abc", "def", "hij"]);
+ assert(skipOver!((a, b) => a.equal(b))(r1, r2));
+ assert(r1 == ["def", "hij"]);
+
+ import std.ascii : isWhite;
+ import std.range.primitives : empty;
+
+ auto s2 = "\t\tvalue";
+ auto s3 = "";
+ auto s4 = "\t\t\t";
+ assert(s2.skipOver!isWhite && s2 == "value");
+ assert(!s3.skipOver!isWhite);
+ assert(s4.skipOver!isWhite && s3.empty);
+}
+
+/**
+Skip over the first element of the given range if it matches the given element,
+otherwise do nothing.
+
+Params:
+ pred = The predicate that determines whether an element from the range
+ matches the given element.
+
+ r = The $(REF_ALTTEXT input range, isInputRange, std,range,primitives) to skip
+ over.
+
+ e = The element to match.
+
+Returns:
+true if the first element matches the given element according to the given
+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))
+{
+ 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 (r.empty || !binaryFun!pred(r.front, e))
+ return false;
+ r.popFront();
+ return true;
+}
+
+///
+@safe unittest
+{
+ import std.algorithm.comparison : equal;
+
+ auto s1 = "Hello world";
+ assert(!skipOver(s1, 'a'));
+ assert(s1 == "Hello world");
+ assert(skipOver(s1, 'H') && s1 == "ello world");
+
+ string[] r = ["abc", "def", "hij"];
+ dstring e = "abc"d;
+ assert(!skipOver!((a, b) => a.equal(b))(r, "def"d));
+ assert(r == ["abc", "def", "hij"]);
+ assert(skipOver!((a, b) => a.equal(b))(r, e));
+ assert(r == ["def", "hij"]);
+
+ auto s2 = "";
+ assert(!s2.skipOver('a'));
+}
+
+/**
+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). Mandatory if no needles are given.
+
+ doesThisStart = The input range to check.
+
+ withOneOfThese = The needles against which the range is to be checked,
+ which may be individual elements or input ranges of elements.
+
+ withThis = The single needle to check, which may be either a single element
+ or an input range of elements.
+
+Returns:
+
+0 if the needle(s) do not occur at the beginning of the given range;
+otherwise the position of the matching needle, that is, 1 if the range starts
+with $(D withOneOfThese[0]), 2 if it starts with $(D withOneOfThese[1]), and so
+on.
+
+In the case where $(D doesThisStart) starts with multiple of the ranges or
+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 &&
+ is(typeof(.startsWith!pred(doesThisStart, withOneOfThese[0])) : bool ) &&
+ is(typeof(.startsWith!pred(doesThisStart, withOneOfThese[1 .. $])) : uint))
+{
+ alias haystack = doesThisStart;
+ alias needles = withOneOfThese;
+
+ // Make one pass looking for empty ranges in needles
+ foreach (i, Unused; Needles)
+ {
+ // Empty range matches everything
+ static if (!is(typeof(binaryFun!pred(haystack.front, needles[i])) : bool))
+ {
+ if (needles[i].empty) return i + 1;
+ }
+ }
+
+ for (; !haystack.empty; haystack.popFront())
+ {
+ foreach (i, Unused; Needles)
+ {
+ static if (is(typeof(binaryFun!pred(haystack.front, needles[i])) : bool))
+ {
+ // Single-element
+ if (binaryFun!pred(haystack.front, needles[i]))
+ {
+ // found, but instead of returning, we just stop searching.
+ // This is to account for one-element
+ // range matches (consider startsWith("ab", "a",
+ // 'a') should return 1, not 2).
+ break;
+ }
+ }
+ else
+ {
+ if (binaryFun!pred(haystack.front, needles[i].front))
+ {
+ continue;
+ }
+ }
+
+ // This code executed on failure to match
+ // Out with this guy, check for the others
+ uint result = startsWith!pred(haystack, needles[0 .. i], needles[i + 1 .. $]);
+ if (result > i) ++result;
+ return result;
+ }
+
+ // If execution reaches this point, then the front matches for all
+ // needle ranges, or a needle element has been matched.
+ // What we need to do now is iterate, lopping off the front of
+ // the range and checking if the result is empty, or finding an
+ // element needle and returning.
+ // If neither happens, we drop to the end and loop.
+ foreach (i, Unused; Needles)
+ {
+ static if (is(typeof(binaryFun!pred(haystack.front, needles[i])) : bool))
+ {
+ // Test has passed in the previous loop
+ return i + 1;
+ }
+ else
+ {
+ needles[i].popFront();
+ if (needles[i].empty) return i + 1;
+ }
+ }
+ }
+ return 0;
+}
+
+/// Ditto
+bool startsWith(alias pred = "a == b", R1, R2)(R1 doesThisStart, R2 withThis)
+if (isInputRange!R1 &&
+ isInputRange!R2 &&
+ is(typeof(binaryFun!pred(doesThisStart.front, withThis.front)) : bool))
+{
+ alias haystack = doesThisStart;
+ alias needle = withThis;
+
+ static if (is(typeof(pred) : string))
+ enum isDefaultPred = pred == "a == b";
+ else
+ enum isDefaultPred = false;
+
+ //Note: While narrow strings don't have a "true" length, for a narrow string to start with another
+ //narrow string *of the same type*, it must have *at least* as many code units.
+ static if ((hasLength!R1 && hasLength!R2) ||
+ (isNarrowString!R1 && isNarrowString!R2 && ElementEncodingType!R1.sizeof == ElementEncodingType!R2.sizeof))
+ {
+ if (haystack.length < needle.length)
+ return false;
+ }
+
+ static if (isDefaultPred && isArray!R1 && isArray!R2 &&
+ is(Unqual!(ElementEncodingType!R1) == Unqual!(ElementEncodingType!R2)))
+ {
+ //Array slice comparison mode
+ return haystack[0 .. needle.length] == needle;
+ }
+ else static if (isRandomAccessRange!R1 && isRandomAccessRange!R2 && hasLength!R2)
+ {
+ //RA dual indexing mode
+ foreach (j; 0 .. needle.length)
+ {
+ if (!binaryFun!pred(haystack[j], needle[j]))
+ // not found
+ return false;
+ }
+ // found!
+ return true;
+ }
+ else
+ {
+ //Standard input range mode
+ if (needle.empty) return true;
+ static if (hasLength!R1 && hasLength!R2)
+ {
+ //We have previously checked that haystack.length > needle.length,
+ //So no need to check haystack.empty during iteration
+ for ( ; ; haystack.popFront() )
+ {
+ if (!binaryFun!pred(haystack.front, needle.front)) break;
+ needle.popFront();
+ if (needle.empty) return true;
+ }
+ }
+ else
+ {
+ for ( ; !haystack.empty ; haystack.popFront() )
+ {
+ if (!binaryFun!pred(haystack.front, needle.front)) break;
+ needle.popFront();
+ if (needle.empty) return true;
+ }
+ }
+ return false;
+ }
+}
+
+/// Ditto
+bool startsWith(alias pred = "a == b", R, E)(R doesThisStart, E withThis)
+if (isInputRange!R &&
+ is(typeof(binaryFun!pred(doesThisStart.front, withThis)) : bool))
+{
+ if (doesThisStart.empty)
+ return false;
+
+ alias predFunc = binaryFun!pred;
+
+ // auto-decoding special case
+ static if (isNarrowString!R)
+ {
+ // specialize for ASCII as to not change previous behavior
+ if (withThis <= 0x7F)
+ return predFunc(doesThisStart[0], withThis);
+ else
+ return predFunc(doesThisStart.front, withThis);
+ }
+ else
+ {
+ return predFunc(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"));
+ assert(startsWith("abc", 'a', "b") == 1);
+ assert(startsWith("abc", "b", "a") == 2);
+ assert(startsWith("abc", "a", "a") == 1);
+ assert(startsWith("abc", "ab", "a") == 2);
+ assert(startsWith("abc", "x", "a", "b") == 2);
+ 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);
+}
+
+@safe unittest
+{
+ import std.algorithm.iteration : filter;
+ import std.conv : to;
+ import std.meta : AliasSeq;
+ import std.range;
+
+ foreach (S; AliasSeq!(char[], wchar[], dchar[], string, wstring, dstring))
+ {
+ assert(!startsWith(to!S("abc"), 'c'));
+ assert(startsWith(to!S("abc"), 'a', 'c') == 1);
+ assert(!startsWith(to!S("abc"), 'x', 'n', 'b'));
+ assert(startsWith(to!S("abc"), 'x', 'n', 'a') == 3);
+ assert(startsWith(to!S("\uFF28abc"), 'a', '\uFF28', 'c') == 2);
+
+ 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("")));
+ assert(startsWith(to!S("ab"), to!T("a")));
+ assert(startsWith(to!S("abc"), to!T("a")));
+ assert(!startsWith(to!S("abc"), to!T("b")));
+ assert(!startsWith(to!S("abc"), to!T("b"), "bc", "abcd", "xyz"));
+ assert(startsWith(to!S("abc"), to!T("ab"), 'a') == 2);
+ assert(startsWith(to!S("abc"), to!T("a"), "b") == 1);
+ assert(startsWith(to!S("abc"), to!T("b"), "a") == 2);
+ assert(startsWith(to!S("abc"), to!T("a"), 'a') == 1);
+ assert(startsWith(to!S("abc"), 'a', to!T("a")) == 1);
+ assert(startsWith(to!S("abc"), to!T("x"), "a", "b") == 2);
+ assert(startsWith(to!S("abc"), to!T("x"), "aa", "ab") == 3);
+ assert(startsWith(to!S("abc"), to!T("x"), "aaa", "sab") == 0);
+ assert(startsWith(to!S("abc"), 'a'));
+ assert(!startsWith(to!S("abc"), to!T("sab")));
+ assert(startsWith(to!S("abc"), 'x', to!T("aaa"), 'a', "sab") == 3);
+
+ //Unicode
+ assert(startsWith(to!S("\uFF28el\uFF4co"), to!T("\uFF28el")));
+ assert(startsWith(to!S("\uFF28el\uFF4co"), to!T("Hel"), to!T("\uFF28el")) == 2);
+ assert(startsWith(to!S("日本語"), to!T("日本")));
+ assert(startsWith(to!S("日本語"), to!T("日本語")));
+ assert(!startsWith(to!S("日本"), to!T("日本語")));
+
+ //Empty
+ assert(startsWith(to!S(""), T.init));
+ assert(!startsWith(to!S(""), 'a'));
+ assert(startsWith(to!S("a"), T.init));
+ assert(startsWith(to!S("a"), T.init, "") == 1);
+ assert(startsWith(to!S("a"), T.init, 'a') == 1);
+ assert(startsWith(to!S("a"), 'a', T.init) == 2);
+ }();
+ }
+
+ //Length but no RA
+ assert(!startsWith("abc".takeExactly(3), "abcd".takeExactly(4)));
+ assert(startsWith("abc".takeExactly(3), "abcd".takeExactly(3)));
+ assert(startsWith("abc".takeExactly(3), "abcd".takeExactly(1)));
+
+ 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, 5));
+ assert(!startsWith(arr, 1));
+ assert(startsWith(arr, 0));
+ assert(startsWith(arr, 5, 0, 1) == 2);
+ assert(startsWith(arr, [0]));
+ assert(startsWith(arr, [0, 1]));
+ assert(startsWith(arr, [0, 1], 7) == 1);
+ assert(!startsWith(arr, [0, 1, 7]));
+ assert(startsWith(arr, [0, 1, 7], [0, 1, 2]) == 2);
+
+ //Normal input range
+ assert(!startsWith(filter!"true"(arr), 1));
+ assert(startsWith(filter!"true"(arr), 0));
+ assert(startsWith(filter!"true"(arr), [0]));
+ assert(startsWith(filter!"true"(arr), [0, 1]));
+ assert(startsWith(filter!"true"(arr), [0, 1], 7) == 1);
+ assert(!startsWith(filter!"true"(arr), [0, 1, 7]));
+ assert(startsWith(filter!"true"(arr), [0, 1, 7], [0, 1, 2]) == 2);
+ assert(startsWith(arr, filter!"true"([0, 1])));
+ assert(startsWith(arr, filter!"true"([0, 1]), 7) == 1);
+ assert(!startsWith(arr, filter!"true"([0, 1, 7])));
+ assert(startsWith(arr, [0, 1, 7], filter!"true"([0, 1, 2])) == 2);
+
+ //Non-default pred
+ assert(startsWith!("a%10 == b%10")(arr, [10, 11]));
+ assert(!startsWith!("a%10 == b%10")(arr, [10, 12]));
+ }
+}
+
+/* (Not yet documented.)
+Consume all elements from $(D r) that are equal to one of the elements
+$(D 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:
+ for (; !r.empty; r.popFront())
+ {
+ foreach (i, E; Es)
+ {
+ if (binaryFun!pred(r.front, es[i]))
+ {
+ continue loop;
+ }
+ }
+ break;
+ }
+}
+
+@safe unittest
+{
+ auto s1 = "Hello world";
+ skipAll(s1, 'H', 'e');
+ assert(s1 == "llo world");
+}
+
+/**
+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.
+ */
+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)
+{
+ return typeof(return)(range, openRight);
+}
+
+/// ditto
+struct Until(alias pred, Range, Sentinel)
+if (isInputRange!Range)
+{
+ private Range _input;
+ static if (!is(Sentinel == void))
+ private Sentinel _sentinel;
+ private OpenRight _openRight;
+ private bool _done;
+
+ static if (!is(Sentinel == void))
+ ///
+ this(Range input, Sentinel sentinel,
+ OpenRight openRight = Yes.openRight)
+ {
+ _input = input;
+ _sentinel = sentinel;
+ _openRight = openRight;
+ _done = _input.empty || openRight && predSatisfied();
+ }
+ else
+ ///
+ 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);
+ return _input.front;
+ }
+
+ private bool predSatisfied()
+ {
+ static if (is(Sentinel == void))
+ return cast(bool) unaryFun!pred(_input.front);
+ else
+ return cast(bool) startsWith!pred(_input, _sentinel);
+ }
+
+ ///
+ void popFront()
+ {
+ assert(!empty);
+ if (!_openRight)
+ {
+ _done = predSatisfied();
+ _input.popFront();
+ _done = _done || _input.empty;
+ }
+ else
+ {
+ _input.popFront();
+ _done = _input.empty || predSatisfied();
+ }
+ }
+
+ static if (isForwardRange!Range)
+ {
+ static if (!is(Sentinel == void))
+ ///
+ @property Until save()
+ {
+ Until result = this;
+ result._input = _input.save;
+ result._sentinel = _sentinel;
+ result._openRight = _openRight;
+ result._done = _done;
+ return result;
+ }
+ else
+ ///
+ @property Until save()
+ {
+ Until result = this;
+ result._input = _input.save;
+ result._openRight = _openRight;
+ result._done = _done;
+ return result;
+ }
+ }
+}
+
+///
+@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, No.openRight), [1, 2, 4, 7]));
+}
+
+@safe unittest
+{
+ import std.algorithm.comparison : equal;
+ 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, 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, No.openRight), [1, 2, 4, 7]));
+ assert(equal(until!"a == 2"(a, No.openRight), [1, 2]));
+}
+
+@system unittest // bugzilla 13171
+{
+ import std.algorithm.comparison : equal;
+ import std.range;
+ auto a = [1, 2, 3, 4];
+ assert(equal(refRange(&a).until(3, No.openRight), [1, 2, 3]));
+ assert(a == [4]);
+}
+
+@safe unittest // Issue 10460
+{
+ import std.algorithm.comparison : equal;
+ auto a = [1, 2, 3, 4];
+ foreach (ref e; a.until(3))
+ e = 0;
+ assert(equal(a, [0, 0, 3, 4]));
+}
+
+@safe unittest // Issue 13124
+{
+ import std.algorithm.comparison : among, equal;
+ auto s = "hello how\nare you";
+ assert(equal(s.until!(c => c.among!('\n', '\r')), "hello how"));
+}
diff --git a/libphobos/src/std/algorithm/setops.d b/libphobos/src/std/algorithm/setops.d
new file mode 100644
index 0000000..05a6e7e
--- /dev/null
+++ b/libphobos/src/std/algorithm/setops.d
@@ -0,0 +1,1521 @@
+// Written in the D programming language.
+/**
+This is a submodule of $(MREF std, algorithm).
+It contains generic algorithms that implement set operations.
+
+The functions $(LREF multiwayMerge), $(LREF multiwayUnion), $(LREF setDifference),
+$(LREF setIntersection), $(LREF setSymmetricDifference) expect a range of sorted
+ranges as input.
+
+All algorithms are generalized to accept as input not only sets but also
+$(HTTP https://en.wikipedia.org/wiki/Multiset, multisets). Each algorithm
+documents behaviour in the presence of duplicated inputs.
+
+$(SCRIPT inhibitQuickIndex = 1;)
+$(BOOKTABLE Cheat Sheet,
+$(TR $(TH Function Name) $(TH Description))
+$(T2 cartesianProduct,
+ Computes Cartesian product of two ranges.)
+$(T2 largestPartialIntersection,
+ Copies out the values that occur most frequently in a range of ranges.)
+$(T2 largestPartialIntersectionWeighted,
+ Copies out the values that occur most frequently (multiplied by
+ per-value weights) in a range of ranges.)
+$(T2 multiwayMerge,
+ Merges a range of sorted ranges.)
+$(T2 multiwayUnion,
+ Computes the union of a range of sorted ranges.)
+$(T2 setDifference,
+ Lazily computes the set difference of two or more sorted ranges.)
+$(T2 setIntersection,
+ Lazily computes the intersection of two or more sorted ranges.)
+$(T2 setSymmetricDifference,
+ Lazily computes the symmetric set difference of two or more sorted
+ ranges.)
+)
+
+Copyright: Andrei Alexandrescu 2008-.
+
+License: $(HTTP boost.org/LICENSE_1_0.txt, Boost License 1.0).
+
+Authors: $(HTTP erdani.com, Andrei Alexandrescu)
+
+Source: $(PHOBOSSRC std/algorithm/_setops.d)
+
+Macros:
+T2=$(TR $(TDNW $(LREF $1)) $(TD $+))
+ */
+module std.algorithm.setops;
+
+import std.range.primitives;
+
+// FIXME
+import std.functional; // : unaryFun, binaryFun;
+import std.traits;
+// FIXME
+import std.meta; // : AliasSeq, staticMap, allSatisfy, anySatisfy;
+
+import std.algorithm.sorting; // : Merge;
+import std.typecons : No;
+
+// cartesianProduct
+/**
+Lazily computes the Cartesian product of two or more ranges. The product is a
+_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
+$(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.
+
+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))
+{
+ import std.algorithm.iteration : map, joiner;
+
+ static if (isInfinite!R1 && isInfinite!R2)
+ {
+ static if (isForwardRange!R1 && isForwardRange!R2)
+ {
+ import std.range : zip, repeat, take, chain, sequence;
+
+ // This algorithm traverses the cartesian product by alternately
+ // 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,
+ repeat(range1), repeat(range2))
+ .map!(function(a) => chain(
+ zip(repeat(a[1]), take(a[4].save, a[0])),
+ zip(take(a[3].save, a[0]+1), repeat(a[2]))
+ ))()
+ .joiner();
+ }
+ else static assert(0, "cartesianProduct of infinite ranges requires "~
+ "forward ranges");
+ }
+ else static if (isInputRange!R1 && isForwardRange!R2 && !isInfinite!R2)
+ {
+ import std.range : zip, repeat;
+ return joiner(map!((ElementType!R1 a) => zip(repeat(a), range2.save))
+ (range1));
+ }
+ else static if (isInputRange!R2 && isForwardRange!R1 && !isInfinite!R1)
+ {
+ import std.range : zip, repeat;
+ return joiner(map!((ElementType!R2 a) => zip(range1.save, repeat(a)))
+ (range2));
+ }
+ else static assert(0, "cartesianProduct involving finite ranges must "~
+ "have at least one finite forward range");
+}
+
+///
+@safe unittest
+{
+ import std.algorithm.searching : canFind;
+ import std.range;
+ import std.typecons : tuple;
+
+ auto N = sequence!"n"(0); // the range of natural numbers
+ auto N2 = cartesianProduct(N, N); // the range of all pairs of natural numbers
+
+ // Various arbitrary number pairs can be found in the range in finite time.
+ assert(canFind(N2, tuple(0, 0)));
+ assert(canFind(N2, tuple(123, 321)));
+ assert(canFind(N2, tuple(11, 35)));
+ assert(canFind(N2, tuple(279, 172)));
+}
+
+///
+@safe unittest
+{
+ import std.algorithm.searching : canFind;
+ import std.typecons : tuple;
+
+ auto B = [ 1, 2, 3 ];
+ auto C = [ 4, 5, 6 ];
+ auto BC = cartesianProduct(B, C);
+
+ foreach (n; [[1, 4], [2, 4], [3, 4], [1, 5], [2, 5], [3, 5], [1, 6],
+ [2, 6], [3, 6]])
+ {
+ assert(canFind(BC, tuple(n[0], n[1])));
+ }
+}
+
+@safe unittest
+{
+ // Test cartesian product of two infinite ranges
+ import std.algorithm.searching : canFind;
+ import std.range;
+ import std.typecons : tuple;
+
+ auto Even = sequence!"2*n"(0);
+ auto Odd = sequence!"2*n+1"(0);
+ auto EvenOdd = cartesianProduct(Even, Odd);
+
+ foreach (pair; [[0, 1], [2, 1], [0, 3], [2, 3], [4, 1], [4, 3], [0, 5],
+ [2, 5], [4, 5], [6, 1], [6, 3], [6, 5]])
+ {
+ assert(canFind(EvenOdd, tuple(pair[0], pair[1])));
+ }
+
+ // This should terminate in finite time
+ assert(canFind(EvenOdd, tuple(124, 73)));
+ assert(canFind(EvenOdd, tuple(0, 97)));
+ assert(canFind(EvenOdd, tuple(42, 1)));
+}
+
+@safe unittest
+{
+ // Test cartesian product of an infinite input range and a finite forward
+ // range.
+ import std.algorithm.searching : canFind;
+ import std.range;
+ import std.typecons : tuple;
+
+ auto N = sequence!"n"(0);
+ auto M = [100, 200, 300];
+ auto NM = cartesianProduct(N,M);
+
+ foreach (pair; [[0, 100], [0, 200], [0, 300], [1, 100], [1, 200], [1, 300],
+ [2, 100], [2, 200], [2, 300], [3, 100], [3, 200],
+ [3, 300]])
+ {
+ assert(canFind(NM, tuple(pair[0], pair[1])));
+ }
+
+ // We can't solve the halting problem, so we can only check a finite
+ // initial segment here.
+ assert(!canFind(NM.take(100), tuple(100, 0)));
+ assert(!canFind(NM.take(100), tuple(1, 1)));
+ assert(!canFind(NM.take(100), tuple(100, 200)));
+
+ auto MN = cartesianProduct(M,N);
+ foreach (pair; [[100, 0], [200, 0], [300, 0], [100, 1], [200, 1], [300, 1],
+ [100, 2], [200, 2], [300, 2], [100, 3], [200, 3],
+ [300, 3]])
+ {
+ assert(canFind(MN, tuple(pair[0], pair[1])));
+ }
+
+ // We can't solve the halting problem, so we can only check a finite
+ // initial segment here.
+ assert(!canFind(MN.take(100), tuple(0, 100)));
+ assert(!canFind(MN.take(100), tuple(0, 1)));
+ assert(!canFind(MN.take(100), tuple(100, 200)));
+}
+
+@safe unittest
+{
+ import std.algorithm.searching : canFind;
+ import std.typecons : tuple;
+
+ // Test cartesian product of two finite ranges.
+ auto X = [1, 2, 3];
+ auto Y = [4, 5, 6];
+ auto XY = cartesianProduct(X, Y);
+ auto Expected = [[1, 4], [1, 5], [1, 6], [2, 4], [2, 5], [2, 6], [3, 4],
+ [3, 5], [3, 6]];
+
+ // Verify Expected ⊆ XY
+ foreach (pair; Expected)
+ {
+ assert(canFind(XY, tuple(pair[0], pair[1])));
+ }
+
+ // Verify XY ⊆ Expected
+ foreach (pair; XY)
+ {
+ assert(canFind(Expected, [pair[0], pair[1]]));
+ }
+
+ // And therefore, by set comprehension, XY == Expected
+}
+
+@safe unittest
+{
+ import std.algorithm.comparison : equal;
+ import std.algorithm.iteration : map;
+ import std.algorithm.searching : canFind;
+ import std.typecons : tuple;
+
+ import std.range;
+ auto N = sequence!"n"(0);
+
+ // To force the template to fall to the second case, we wrap N in a struct
+ // that doesn't allow bidirectional access.
+ struct FwdRangeWrapper(R)
+ {
+ R impl;
+
+ // Input range API
+ @property auto front() { return impl.front; }
+ void popFront() { impl.popFront(); }
+ static if (isInfinite!R)
+ enum empty = false;
+ else
+ @property bool empty() { return impl.empty; }
+
+ // Forward range API
+ @property auto save() { return typeof(this)(impl.save); }
+ }
+ auto fwdWrap(R)(R range) { return FwdRangeWrapper!R(range); }
+
+ // General test: two infinite bidirectional ranges
+ auto N2 = cartesianProduct(N, N);
+
+ assert(canFind(N2, tuple(0, 0)));
+ assert(canFind(N2, tuple(123, 321)));
+ assert(canFind(N2, tuple(11, 35)));
+ assert(canFind(N2, tuple(279, 172)));
+
+ // Test first case: forward range with bidirectional range
+ auto fwdN = fwdWrap(N);
+ auto N2_a = cartesianProduct(fwdN, N);
+
+ assert(canFind(N2_a, tuple(0, 0)));
+ assert(canFind(N2_a, tuple(123, 321)));
+ assert(canFind(N2_a, tuple(11, 35)));
+ assert(canFind(N2_a, tuple(279, 172)));
+
+ // Test second case: bidirectional range with forward range
+ auto N2_b = cartesianProduct(N, fwdN);
+
+ assert(canFind(N2_b, tuple(0, 0)));
+ assert(canFind(N2_b, tuple(123, 321)));
+ assert(canFind(N2_b, tuple(11, 35)));
+ assert(canFind(N2_b, tuple(279, 172)));
+
+ // Test third case: finite forward range with (infinite) input range
+ static struct InpRangeWrapper(R)
+ {
+ R impl;
+
+ // Input range API
+ @property auto front() { return impl.front; }
+ void popFront() { impl.popFront(); }
+ static if (isInfinite!R)
+ enum empty = false;
+ else
+ @property bool empty() { return impl.empty; }
+ }
+ auto inpWrap(R)(R r) { return InpRangeWrapper!R(r); }
+
+ auto inpN = inpWrap(N);
+ auto B = [ 1, 2, 3 ];
+ auto fwdB = fwdWrap(B);
+ auto BN = cartesianProduct(fwdB, inpN);
+
+ assert(equal(map!"[a[0],a[1]]"(BN.take(10)), [[1, 0], [2, 0], [3, 0],
+ [1, 1], [2, 1], [3, 1], [1, 2], [2, 2], [3, 2], [1, 3]]));
+
+ // Test fourth case: (infinite) input range with finite forward range
+ auto NB = cartesianProduct(inpN, fwdB);
+
+ assert(equal(map!"[a[0],a[1]]"(NB.take(10)), [[0, 1], [0, 2], [0, 3],
+ [1, 1], [1, 2], [1, 3], [2, 1], [2, 2], [2, 3], [3, 1]]));
+
+ // General finite range case
+ auto C = [ 4, 5, 6 ];
+ auto BC = cartesianProduct(B, C);
+
+ foreach (n; [[1, 4], [2, 4], [3, 4], [1, 5], [2, 5], [3, 5], [1, 6],
+ [2, 6], [3, 6]])
+ {
+ assert(canFind(BC, tuple(n[0], n[1])));
+ }
+}
+
+// Issue 13091
+pure nothrow @safe @nogc unittest
+{
+ 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))
+{
+ // This overload uses a much less template-heavy implementation when
+ // all ranges are finite forward ranges, which is the most common use
+ // case, so that we don't run out of resources too quickly.
+ //
+ // For infinite ranges or non-forward ranges, we fall back to the old
+ // implementation which expands an exponential number of templates.
+ import std.typecons : tuple;
+
+ static struct Result
+ {
+ RR ranges;
+ RR current;
+ bool empty = true;
+
+ this(RR _ranges)
+ {
+ ranges = _ranges;
+ empty = false;
+ foreach (i, r; ranges)
+ {
+ current[i] = r.save;
+ if (current[i].empty)
+ empty = true;
+ }
+ }
+ @property auto front()
+ {
+ import std.algorithm.internal : algoFormat;
+ import std.range : iota;
+ return mixin(algoFormat("tuple(%(current[%d].front%|,%))",
+ iota(0, current.length)));
+ }
+ void popFront()
+ {
+ foreach_reverse (i, ref r; current)
+ {
+ r.popFront();
+ if (!r.empty) break;
+
+ static if (i == 0)
+ empty = true;
+ else
+ r = ranges[i].save; // rollover
+ }
+ }
+ @property Result save()
+ {
+ Result copy = this;
+ foreach (i, r; ranges)
+ {
+ copy.ranges[i] = r.save;
+ copy.current[i] = current[i].save;
+ }
+ return copy;
+ }
+ }
+ static assert(isForwardRange!Result);
+
+ return Result(ranges);
+}
+
+@safe unittest
+{
+ // Issue 10693: cartesian product of empty ranges should be empty.
+ int[] a, b, c, d, e;
+ auto cprod = cartesianProduct(a,b,c,d,e);
+ assert(cprod.empty);
+ foreach (_; cprod) {} // should not crash
+
+ // Test case where only one of the ranges is empty: the result should still
+ // be empty.
+ int[] p=[1], q=[];
+ auto cprod2 = cartesianProduct(p,p,p,q,p);
+ assert(cprod2.empty);
+ foreach (_; cprod2) {} // should not crash
+}
+
+@safe unittest
+{
+ // .init value of cartesianProduct should be empty
+ auto cprod = cartesianProduct([0,0], [1,1], [2,2]);
+ assert(!cprod.empty);
+ assert(cprod.init.empty);
+}
+
+@safe unittest
+{
+ // Issue 13393
+ assert(!cartesianProduct([0],[0],[0]).save.empty);
+}
+
+/// ditto
+auto cartesianProduct(R1, R2, RR...)(R1 range1, R2 range2, RR otherRanges)
+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
+ * one level of tuples so that a ternary cartesian product, for example,
+ * returns 3-element tuples instead of nested 2-element tuples.
+ */
+ import std.algorithm.internal : algoFormat;
+ import std.algorithm.iteration : map;
+ import std.range : iota;
+
+ enum string denest = algoFormat("tuple(a[0], %(a[1][%d]%|,%))",
+ iota(0, otherRanges.length+1));
+ return map!denest(
+ cartesianProduct(range1, cartesianProduct(range2, otherRanges))
+ );
+}
+
+@safe unittest
+{
+ import std.algorithm.searching : canFind;
+ import std.range;
+ import std.typecons : tuple, Tuple;
+
+ auto N = sequence!"n"(0);
+ auto N3 = cartesianProduct(N, N, N);
+
+ // Check that tuples are properly denested
+ assert(is(ElementType!(typeof(N3)) == Tuple!(size_t,size_t,size_t)));
+
+ assert(canFind(N3, tuple(0, 27, 7)));
+ assert(canFind(N3, tuple(50, 23, 71)));
+ assert(canFind(N3, tuple(9, 3, 0)));
+}
+
+@safe unittest
+{
+ import std.algorithm.searching : canFind;
+ import std.range;
+ import std.typecons : tuple, Tuple;
+
+ auto N = sequence!"n"(0);
+ auto N4 = cartesianProduct(N, N, N, N);
+
+ // Check that tuples are properly denested
+ assert(is(ElementType!(typeof(N4)) == Tuple!(size_t,size_t,size_t,size_t)));
+
+ assert(canFind(N4, tuple(1, 2, 3, 4)));
+ assert(canFind(N4, tuple(4, 3, 2, 1)));
+ assert(canFind(N4, tuple(10, 31, 7, 12)));
+}
+
+// Issue 9878
+///
+@safe unittest
+{
+ import std.algorithm.comparison : equal;
+ import std.typecons : tuple;
+
+ auto A = [ 1, 2, 3 ];
+ auto B = [ 'a', 'b', 'c' ];
+ auto C = [ "x", "y", "z" ];
+ auto ABC = cartesianProduct(A, B, C);
+
+ assert(ABC.equal([
+ tuple(1, 'a', "x"), tuple(1, 'a', "y"), tuple(1, 'a', "z"),
+ tuple(1, 'b', "x"), tuple(1, 'b', "y"), tuple(1, 'b', "z"),
+ tuple(1, 'c', "x"), tuple(1, 'c', "y"), tuple(1, 'c', "z"),
+ tuple(2, 'a', "x"), tuple(2, 'a', "y"), tuple(2, 'a', "z"),
+ tuple(2, 'b', "x"), tuple(2, 'b', "y"), tuple(2, 'b', "z"),
+ tuple(2, 'c', "x"), tuple(2, 'c', "y"), tuple(2, 'c', "z"),
+ tuple(3, 'a', "x"), tuple(3, 'a', "y"), tuple(3, 'a', "z"),
+ tuple(3, 'b', "x"), tuple(3, 'b', "y"), tuple(3, 'b', "z"),
+ tuple(3, 'c', "x"), tuple(3, 'c', "y"), tuple(3, 'c', "z")
+ ]));
+}
+
+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)));
+
+ C.popFront();
+ auto front1 = C.front;
+ auto D = C.save;
+ C.popFront();
+ assert(D.front == front1);
+}
+
+// Issue 13935
+@safe unittest
+{
+ import std.algorithm.iteration : map;
+ auto seq = [1, 2].map!(x => x);
+ foreach (pair; cartesianProduct(seq, seq)) {}
+}
+
+// largestPartialIntersection
+/**
+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.
+
+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 $(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
+array of the occurrences and then selecting its top items, and also
+requires less memory ($(D largestPartialIntersection) builds its
+result directly in $(D tgt) and requires no extra memory).
+
+If at least one of the ranges is a multiset, then all occurences
+of a duplicate element are taken into account. The result is
+equivalent to merging all ranges and picking the most frequent
+$(D tgt.length) elements.
+
+Warning: Because $(D largestPartialIntersection) does not allocate
+extra memory, it will leave $(D ror) modified. Namely, $(D
+largestPartialIntersection) assumes ownership of $(D ror) and
+discretionarily swaps and advances elements of it. If you want $(D
+ror) to preserve its contents after the call, you may want to pass a
+duplicate to $(D largestPartialIntersection) (and perhaps cache the
+duplicate in between calls).
+ */
+void largestPartialIntersection
+(alias less = "a < b", RangeOfRanges, Range)
+(RangeOfRanges ror, Range tgt, SortOutput sorted = No.sortOutput)
+{
+ struct UnitWeights
+ {
+ static int opIndex(ElementType!(ElementType!RangeOfRanges)) { return 1; }
+ }
+ return largestPartialIntersectionWeighted!less(ror, tgt, UnitWeights(),
+ 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
+
+ // multiset
+ double[][] x =
+ [
+ [1, 1, 1, 1, 4, 7, 8],
+ [1, 7],
+ [1, 7, 8],
+ [4, 7],
+ [7]
+ ];
+ auto y = new Tuple!(double, uint)[2];
+ largestPartialIntersection(x.dup, y);
+ // 7.0 occurs 5 times
+ assert(y[0] == tuple(7.0, 5u));
+ // 1.0 occurs 6 times
+ assert(y[1] == tuple(1.0, 6u));
+}
+
+import std.algorithm.sorting : SortOutput; // FIXME
+
+// largestPartialIntersectionWeighted
+/**
+Similar to $(D largestPartialIntersection), but associates a weight
+with each distinct element in the intersection.
+
+If at least one of the ranges is a multiset, then all occurences
+of a duplicate element are taken into account. The result
+is equivalent to merging all input ranges and picking the highest
+$(D tgt.length), weight-based ranking elements.
+
+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 = No.sortOutput)
+{
+ import std.algorithm.iteration : group;
+ import std.algorithm.sorting : topNCopy;
+
+ if (tgt.empty) return;
+ alias InfoType = ElementType!Range;
+ bool heapComp(InfoType a, InfoType b)
+ {
+ return weights[a[0]] * a[1] > weights[b[0]] * b[1];
+ }
+ topNCopy!heapComp(group(multiwayMerge!less(ror)), tgt, sorted);
+}
+
+///
+@system unittest
+{
+ import std.typecons : tuple, Tuple;
+
+ // 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)
+
+ // multiset
+ double[][] x =
+ [
+ [ 1, 1, 1, 4, 7, 8 ],
+ [ 1, 7 ],
+ [ 1, 7, 8],
+ [ 4 ],
+ [ 7 ],
+ ];
+ auto y = new Tuple!(double, uint)[1];
+ largestPartialIntersectionWeighted(x, y, weights);
+ assert(y[0] == tuple(1.0, 5u));
+ // 1.0 occurs 5 times -> 1.2 * 5 = 6
+}
+
+@system unittest
+{
+ import std.conv : text;
+ import std.typecons : tuple, Tuple, Yes;
+
+ double[][] a =
+ [
+ [ 1, 4, 7, 8 ],
+ [ 1, 7 ],
+ [ 1, 7, 8],
+ [ 4 ],
+ [ 7 ],
+ ];
+ auto b = new Tuple!(double, uint)[2];
+ largestPartialIntersection(a, b, Yes.sortOutput);
+ assert(b == [ tuple(7.0, 4u), tuple(1.0, 3u) ][], text(b));
+ assert(a[0].empty);
+}
+
+@system unittest
+{
+ import std.conv : text;
+ import std.typecons : tuple, Tuple, Yes;
+
+ string[][] a =
+ [
+ [ "1", "4", "7", "8" ],
+ [ "1", "7" ],
+ [ "1", "7", "8"],
+ [ "4" ],
+ [ "7" ],
+ ];
+ auto b = new Tuple!(string, uint)[2];
+ largestPartialIntersection(a, b, Yes.sortOutput);
+ assert(b == [ tuple("7", 4u), tuple("1", 3u) ][], text(b));
+}
+
+@system unittest
+{
+ import std.typecons : tuple, Tuple;
+
+ // 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));
+}
+
+@system unittest
+{
+ import std.container : Array;
+ import std.typecons : Tuple;
+
+ alias T = Tuple!(uint, uint);
+ const Array!T arrayOne = Array!T( [ T(1,2), T(3,4) ] );
+ const Array!T arrayTwo = Array!T([ T(1,2), T(3,4) ] );
+
+ assert(arrayOne == arrayTwo);
+}
+
+// MultiwayMerge
+/**
+Merges multiple sets. The input sets are passed as a
+range of ranges and each is assumed to be sorted by $(D
+less). Computation is done lazily, one union element at a time. The
+complexity of one $(D popFront) operation is $(BIGOH
+log(ror.length)). However, the length of $(D ror) decreases as ranges
+in it are exhausted, so the complexity of a full pass through $(D
+MultiwayMerge) is dependent on the distribution of the lengths of ranges
+contained within $(D ror). If all ranges have the same length $(D n)
+(worst case scenario), the complexity of a full pass through $(D
+MultiwayMerge) 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).
+
+The length of the resulting range is the sum of all lengths of
+the ranges passed as input. This means that all elements (duplicates
+included) are transferred to the resulting range.
+
+For backward compatibility, `multiwayMerge` is available under
+the name `nWayUnion` and `MultiwayMerge` under the name of `NWayUnion` .
+Future code should use `multiwayMerge` and `MultiwayMerge` as `nWayUnion`
+and `NWayUnion` will be deprecated.
+
+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 MultiwayMerge) does not allocate extra memory, it
+will leave $(D ror) modified. Namely, $(D MultiwayMerge) assumes ownership
+of $(D ror) and discretionarily swaps and advances elements of it. If
+you want $(D ror) to preserve its contents after the call, you may
+want to pass a duplicate to $(D MultiwayMerge) (and perhaps cache the
+duplicate in between calls).
+ */
+struct MultiwayMerge(alias less, RangeOfRanges)
+{
+ import std.container : BinaryHeap;
+
+ 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);
+ }
+ private BinaryHeap!(RangeOfRanges, compFront) _heap;
+
+ ///
+ this(RangeOfRanges ror)
+ {
+ import std.algorithm.mutation : remove, SwapStrategy;
+
+ // Preemptively get rid of all empty ranges in the input
+ // No need for stability either
+ _ror = remove!("a.empty", SwapStrategy.unstable)(ror);
+ //Build the heap across the range
+ _heap.acquire(_ror);
+ }
+
+ ///
+ @property bool empty() { return _ror.empty; }
+
+ ///
+ @property auto ref front()
+ {
+ return _heap.front.front;
+ }
+
+ ///
+ void popFront()
+ {
+ _heap.removeFront();
+ // let's look at the guy just popped
+ _ror.back.popFront();
+ if (_ror.back.empty)
+ {
+ _ror.popBack();
+ // nothing else to do: the empty range is not in the
+ // heap and not in _ror
+ return;
+ }
+ // Put the popped range back in the heap
+ _heap.conditionalInsert(_ror.back) || assert(false);
+ }
+}
+
+/// Ditto
+MultiwayMerge!(less, RangeOfRanges) multiwayMerge
+(alias less = "a < b", RangeOfRanges)
+(RangeOfRanges ror)
+{
+ return typeof(return)(ror);
+}
+
+///
+@system unittest
+{
+ import std.algorithm.comparison : equal;
+
+ double[][] a =
+ [
+ [ 1, 4, 7, 8 ],
+ [ 1, 7 ],
+ [ 1, 7, 8],
+ [ 4 ],
+ [ 7 ],
+ ];
+ auto witness = [
+ 1, 1, 1, 4, 4, 7, 7, 7, 7, 8, 8
+ ];
+ assert(equal(multiwayMerge(a), witness));
+
+ double[][] b =
+ [
+ // range with duplicates
+ [ 1, 1, 4, 7, 8 ],
+ [ 7 ],
+ [ 1, 7, 8],
+ [ 4 ],
+ [ 7 ],
+ ];
+ // duplicates are propagated to the resulting range
+ assert(equal(multiwayMerge(b), witness));
+}
+
+alias nWayUnion = multiwayMerge;
+alias NWayUnion = MultiwayMerge;
+
+/**
+Computes the union of multiple ranges. The input ranges are passed
+as a range of ranges and each is assumed to be sorted by $(D
+less). Computation is done lazily, one union element at a time.
+`multiwayUnion(ror)` is functionally equivalent to `multiwayMerge(ror).uniq`.
+
+"The output of multiwayUnion has no duplicates even when its inputs contain duplicates."
+
+Params:
+ less = Predicate the given ranges are sorted by.
+ ror = A range of ranges sorted by `less` to compute the intersection for.
+
+Returns:
+ A range of the union of the ranges in `ror`.
+
+See also: $(LREF multiwayMerge)
+ */
+auto multiwayUnion(alias less = "a < b", RangeOfRanges)(RangeOfRanges ror)
+{
+ import std.algorithm.iteration : uniq;
+ return ror.multiwayMerge.uniq;
+}
+
+///
+@system unittest
+{
+ import std.algorithm.comparison : equal;
+
+ // sets
+ double[][] a =
+ [
+ [ 1, 4, 7, 8 ],
+ [ 1, 7 ],
+ [ 1, 7, 8],
+ [ 4 ],
+ [ 7 ],
+ ];
+
+ auto witness = [1, 4, 7, 8];
+ assert(equal(multiwayUnion(a), witness));
+
+ // multisets
+ double[][] b =
+ [
+ [ 1, 1, 1, 4, 7, 8 ],
+ [ 1, 7 ],
+ [ 1, 7, 7, 8],
+ [ 4 ],
+ [ 7 ],
+ ];
+ assert(equal(multiwayUnion(b), witness));
+}
+
+/**
+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.
+
+
+In the case of multisets, considering that element `a` appears `x`
+times in $(D r1) and `y` times and $(D r2), the number of occurences
+of `a` in the resulting range is going to be `x-y` if x > y or 0 othwerise.
+
+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))
+{
+private:
+ R1 r1;
+ R2 r2;
+ alias comp = binaryFun!(less);
+
+ void adjustPosition()
+ {
+ while (!r1.empty)
+ {
+ if (r2.empty || comp(r1.front, r2.front)) break;
+ if (comp(r2.front, r1.front))
+ {
+ r2.popFront();
+ }
+ else
+ {
+ // both are equal
+ r1.popFront();
+ r2.popFront();
+ }
+ }
+ }
+
+public:
+ ///
+ this(R1 r1, R2 r2)
+ {
+ this.r1 = r1;
+ this.r2 = r2;
+ // position to the first element
+ adjustPosition();
+ }
+
+ ///
+ void popFront()
+ {
+ r1.popFront();
+ adjustPosition();
+ }
+
+ ///
+ @property auto ref front()
+ {
+ assert(!empty);
+ return r1.front;
+ }
+
+ static if (isForwardRange!R1 && isForwardRange!R2)
+ {
+ ///
+ @property typeof(this) save()
+ {
+ auto ret = this;
+ ret.r1 = r1.save;
+ ret.r2 = r2.save;
+ return ret;
+ }
+ }
+
+ ///
+ @property bool empty() { return r1.empty; }
+}
+
+/// Ditto
+SetDifference!(less, R1, R2) setDifference(alias less = "a < b", R1, R2)
+(R1 r1, R2 r2)
+{
+ return typeof(return)(r1, r2);
+}
+
+///
+@safe unittest
+{
+ import std.algorithm.comparison : equal;
+ import std.range.primitives : isForwardRange;
+
+ //sets
+ int[] a = [ 1, 2, 4, 5, 7, 9 ];
+ int[] b = [ 0, 1, 2, 4, 7, 8 ];
+ assert(equal(setDifference(a, b), [5, 9]));
+ static assert(isForwardRange!(typeof(setDifference(a, b))));
+
+ // multisets
+ int[] x = [1, 1, 1, 2, 3];
+ int[] y = [1, 1, 2, 4, 5];
+ auto r = setDifference(x, y);
+ assert(equal(r, [1, 3]));
+ assert(setDifference(r, x).empty);
+}
+
+@safe unittest // Issue 10460
+{
+ import std.algorithm.comparison : equal;
+
+ int[] a = [1, 2, 3, 4, 5];
+ int[] b = [2, 4];
+ foreach (ref e; setDifference(a, b))
+ e = 0;
+ assert(equal(a, [0, 2, 0, 4, 0]));
+}
+
+/**
+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.
+
+In the case of multisets, the range with the minimum number of
+occurences of a given element, propagates the number of
+occurences of this element to the resulting range.
+
+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))
+{
+private:
+ Rs _input;
+ alias comp = binaryFun!less;
+ alias ElementType = CommonType!(staticMap!(.ElementType, Rs));
+
+ // Positions to the first elements that are all equal
+ void adjustPosition()
+ {
+ if (empty) return;
+
+ size_t done = Rs.length;
+ static if (Rs.length > 1) while (true)
+ {
+ foreach (i, ref r; _input)
+ {
+ alias next = _input[(i + 1) % Rs.length];
+
+ if (comp(next.front, r.front))
+ {
+ do
+ {
+ next.popFront();
+ if (next.empty) return;
+ } while (comp(next.front, r.front));
+ done = Rs.length;
+ }
+ if (--done == 0) return;
+ }
+ }
+ }
+
+public:
+ ///
+ this(Rs input)
+ {
+ this._input = input;
+ // position to the first element
+ adjustPosition();
+ }
+
+ ///
+ @property bool empty()
+ {
+ foreach (ref r; _input)
+ {
+ if (r.empty) return true;
+ }
+ return false;
+ }
+
+ ///
+ void popFront()
+ {
+ assert(!empty);
+ static if (Rs.length > 1) foreach (i, ref r; _input)
+ {
+ alias next = _input[(i + 1) % Rs.length];
+ assert(!comp(r.front, next.front));
+ }
+
+ foreach (ref r; _input)
+ {
+ r.popFront();
+ }
+ adjustPosition();
+ }
+
+ ///
+ @property ElementType front()
+ {
+ assert(!empty);
+ return _input[0].front;
+ }
+
+ static if (allSatisfy!(isForwardRange, Rs))
+ {
+ ///
+ @property SetIntersection save()
+ {
+ auto ret = this;
+ foreach (i, ref r; _input)
+ {
+ ret._input[i] = r.save;
+ }
+ return ret;
+ }
+ }
+}
+
+/// 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))
+{
+ return typeof(return)(ranges);
+}
+
+///
+@safe unittest
+{
+ import std.algorithm.comparison : equal;
+
+ // sets
+ int[] a = [ 1, 2, 4, 5, 7, 9 ];
+ int[] b = [ 0, 1, 2, 4, 7, 8 ];
+ int[] c = [ 0, 1, 4, 5, 7, 8 ];
+ assert(equal(setIntersection(a, a), a));
+ assert(equal(setIntersection(a, b), [1, 2, 4, 7]));
+ assert(equal(setIntersection(a, b, c), [1, 4, 7]));
+
+ // multisets
+ int[] d = [ 1, 1, 2, 2, 7, 7 ];
+ int[] e = [ 1, 1, 1, 7];
+ assert(equal(setIntersection(a, d), [1, 2, 7]));
+ assert(equal(setIntersection(d, e), [1, 1, 7]));
+}
+
+@safe unittest
+{
+ import std.algorithm.comparison : equal;
+ import std.algorithm.iteration : filter;
+
+ int[] a = [ 1, 2, 4, 5, 7, 9 ];
+ int[] b = [ 0, 1, 2, 4, 7, 8 ];
+ int[] c = [ 0, 1, 4, 5, 7, 8 ];
+ int[] d = [ 1, 3, 4 ];
+ int[] e = [ 4, 5 ];
+
+ assert(equal(setIntersection(a, a), a));
+ assert(equal(setIntersection(a, a, a), a));
+ assert(equal(setIntersection(a, b), [1, 2, 4, 7]));
+ assert(equal(setIntersection(a, b, c), [1, 4, 7]));
+ assert(equal(setIntersection(a, b, c, d), [1, 4]));
+ assert(equal(setIntersection(a, b, c, d, e), [4]));
+
+ auto inpA = a.filter!(_ => true), inpB = b.filter!(_ => true);
+ auto inpC = c.filter!(_ => true), inpD = d.filter!(_ => true);
+ assert(equal(setIntersection(inpA, inpB, inpC, inpD), [1, 4]));
+
+ assert(equal(setIntersection(a, b, b, a), [1, 2, 4, 7]));
+ assert(equal(setIntersection(a, c, b), [1, 4, 7]));
+ assert(equal(setIntersection(b, a, c), [1, 4, 7]));
+ assert(equal(setIntersection(b, c, a), [1, 4, 7]));
+ assert(equal(setIntersection(c, a, b), [1, 4, 7]));
+ assert(equal(setIntersection(c, b, a), [1, 4, 7]));
+}
+
+/**
+Lazily computes the symmetric difference of $(D r1) and $(D r2),
+i.e. the elements that are present in exactly one of $(D r1) and $(D
+r2). The two ranges are assumed to be sorted by $(D less), and the
+output is also sorted by $(D less). The element types of the two
+ranges must have a common type.
+
+If both ranges are sets (without duplicated elements), the resulting
+range is going to be a set. If at least one of the ranges is a multiset,
+the number of occurences of an element `x` in the resulting range is `abs(a-b)`
+where `a` is the number of occurences of `x` in $(D r1), `b` is the number of
+occurences of `x` in $(D r2), and `abs` is the absolute value.
+
+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))
+{
+private:
+ R1 r1;
+ R2 r2;
+ //bool usingR2;
+ alias comp = binaryFun!(less);
+
+ void adjustPosition()
+ {
+ while (!r1.empty && !r2.empty)
+ {
+ if (comp(r1.front, r2.front) || comp(r2.front, r1.front))
+ {
+ break;
+ }
+ // equal, pop both
+ r1.popFront();
+ r2.popFront();
+ }
+ }
+
+public:
+ ///
+ this(R1 r1, R2 r2)
+ {
+ this.r1 = r1;
+ this.r2 = r2;
+ // position to the first element
+ adjustPosition();
+ }
+
+ ///
+ void popFront()
+ {
+ assert(!empty);
+ if (r1.empty) r2.popFront();
+ else if (r2.empty) r1.popFront();
+ else
+ {
+ // neither is empty
+ if (comp(r1.front, r2.front))
+ {
+ r1.popFront();
+ }
+ else
+ {
+ assert(comp(r2.front, r1.front));
+ r2.popFront();
+ }
+ }
+ adjustPosition();
+ }
+
+ ///
+ @property auto ref front()
+ {
+ assert(!empty);
+ 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;
+ ret.r1 = r1.save;
+ ret.r2 = r2.save;
+ return ret;
+ }
+ }
+
+ ///
+ ref auto opSlice() { return this; }
+
+ ///
+ @property bool empty() { return r1.empty && r2.empty; }
+}
+
+/// Ditto
+SetSymmetricDifference!(less, R1, R2)
+setSymmetricDifference(alias less = "a < b", R1, R2)
+(R1 r1, R2 r2)
+{
+ return typeof(return)(r1, r2);
+}
+
+///
+@safe unittest
+{
+ import std.algorithm.comparison : equal;
+ import std.range.primitives : isForwardRange;
+
+ // sets
+ int[] a = [ 1, 2, 4, 5, 7, 9 ];
+ int[] b = [ 0, 1, 2, 4, 7, 8 ];
+ assert(equal(setSymmetricDifference(a, b), [0, 5, 8, 9][]));
+ static assert(isForwardRange!(typeof(setSymmetricDifference(a, b))));
+
+ //mutisets
+ int[] c = [1, 1, 1, 1, 2, 2, 2, 4, 5, 6];
+ int[] d = [1, 1, 2, 2, 2, 2, 4, 7, 9];
+ assert(equal(setSymmetricDifference(c, d), setSymmetricDifference(d, c)));
+ assert(equal(setSymmetricDifference(c, d), [1, 1, 2, 5, 6, 7, 9]));
+}
+
+@safe unittest // Issue 10460
+{
+ import std.algorithm.comparison : equal;
+
+ int[] a = [1, 2];
+ double[] b = [2.0, 3.0];
+ int[] c = [2, 3];
+
+ alias R1 = typeof(setSymmetricDifference(a, b));
+ static assert(is(ElementType!R1 == double));
+ static assert(!hasLvalueElements!R1);
+
+ alias R2 = typeof(setSymmetricDifference(a, c));
+ static assert(is(ElementType!R2 == int));
+ static assert(hasLvalueElements!R2);
+
+ assert(equal(setSymmetricDifference(a, b), [1.0, 3.0]));
+ assert(equal(setSymmetricDifference(a, c), [1, 3]));
+}
+
+/++
+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
+unique. The element types of all ranges must have a common type.
+
+Params:
+ less = Predicate the given ranges are sorted by.
+ rs = The ranges to compute the union for.
+
+Returns:
+ A range containing the unique union of the given ranges.
+
+See_Also:
+ $(REF merge, std,algorithm,sorting)
+ */
+auto setUnion(alias less = "a < b", Rs...)
+(Rs rs)
+{
+ import std.algorithm.iteration : uniq;
+ import std.algorithm.sorting : merge;
+ return merge!(less, Rs)(rs).uniq;
+}
+
+///
+@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;
+
+ int[] a = [ 1, 2, 4, 5, 7, 9 ];
+ int[] b = [ 0, 1, 2, 4, 7, 8 ];
+ double[] c = [ 10.5 ];
+
+ assert(equal(setUnion(a, b), [0, 1, 2, 4, 5, 7, 8, 9][]));
+ assert(equal(setUnion(a, c, b),
+ [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/libphobos/src/std/algorithm/sorting.d b/libphobos/src/std/algorithm/sorting.d
new file mode 100644
index 0000000..2400bca
--- /dev/null
+++ b/libphobos/src/std/algorithm/sorting.d
@@ -0,0 +1,4468 @@
+// Written in the D programming language.
+/**
+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 $(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.)
+$(T2 nextPermutation,
+ Computes the next lexicographically greater permutation of a range
+ in-place.)
+$(T2 partialSort,
+ If $(D a = [5, 4, 3, 2, 1]), then $(D partialSort(a, 3)) leaves
+ $(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 unary predicate.)
+$(T2 partition3,
+ 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 $(LINK2 https://en.wikipedia.org/wiki/Schwartzian_transform, Schwartzian transform).)
+$(T2 sort,
+ Sorts.)
+$(T2 topN,
+ Separates the top elements in a range.)
+$(T2 topNCopy,
+ Copies out the top elements of a range.)
+$(T2 topNIndex,
+ Builds an index of the top elements of a range.)
+)
+
+Copyright: Andrei Alexandrescu 2008-.
+
+License: $(HTTP boost.org/LICENSE_1_0.txt, Boost License 1.0).
+
+Authors: $(HTTP erdani.com, Andrei Alexandrescu)
+
+Source: $(PHOBOSSRC std/algorithm/_sorting.d)
+
+Macros:
+T2=$(TR $(TDNW $(LREF $1)) $(TD $+))
+ */
+module std.algorithm.sorting;
+
+import std.algorithm.mutation : SwapStrategy;
+import std.functional; // : unaryFun, binaryFun;
+import std.range.primitives;
+import std.typecons : Flag;
+// FIXME
+import std.meta; // : allSatisfy;
+import std.range; // : SortedRange;
+import std.traits;
+
+/**
+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.
+ */
+alias SortOutput = Flag!"sortOutput";
+
+// completeSort
+/**
+Sorts the random-access range $(D chain(lhs, rhs)) according to
+predicate $(D less). The left-hand side of the range $(D lhs) is
+assumed to be already sorted; $(D rhs) is assumed to be unsorted. The
+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,
+ RandomAccessRange1, RandomAccessRange2)(SortedRange!(RandomAccessRange1, less) lhs, RandomAccessRange2 rhs)
+if (hasLength!(RandomAccessRange2) && hasSlicing!(RandomAccessRange2))
+{
+ import std.algorithm.mutation : bringToFront;
+ import std.range : chain, assumeSorted;
+ // Probably this algorithm can be optimized by using in-place
+ // merge
+ auto lhsOriginal = lhs.release();
+ foreach (i; 0 .. rhs.length)
+ {
+ auto sortedSoFar = chain(lhsOriginal, rhs[0 .. i]);
+ auto ub = assumeSorted!less(sortedSoFar).upperBound(rhs[i]);
+ if (!ub.length) continue;
+ bringToFront(ub.release(), rhs[i .. i + 1]);
+ }
+}
+
+///
+@safe unittest
+{
+ import std.range : assumeSorted;
+ int[] a = [ 1, 2, 3 ];
+ int[] b = [ 4, 0, 6, 5 ];
+ completeSort(assumeSorted(a), b);
+ assert(a == [ 0, 1, 2 ]);
+ assert(b == [ 3, 4, 5, 6 ]);
+}
+
+// isSorted
+/**
+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))
+{
+ if (r.empty) return true;
+
+ static if (isRandomAccessRange!Range && hasLength!Range)
+ {
+ immutable limit = r.length - 1;
+ foreach (i; 0 .. limit)
+ {
+ if (!binaryFun!less(r[i + 1], r[i])) continue;
+ assert(
+ !binaryFun!less(r[i], r[i + 1]),
+ "Predicate for isSorted is not antisymmetric. Both" ~
+ " pred(a, b) and pred(b, a) are true for certain values.");
+ return false;
+ }
+ }
+ else
+ {
+ auto ahead = r.save;
+ ahead.popFront();
+ size_t i;
+
+ for (; !ahead.empty; ahead.popFront(), r.popFront(), ++i)
+ {
+ if (!binaryFun!less(ahead.front, r.front)) continue;
+ // Check for antisymmetric predicate
+ assert(
+ !binaryFun!less(r.front, ahead.front),
+ "Predicate for isSorted is not antisymmetric. Both" ~
+ " pred(a, b) and pred(b, a) are true for certain values.");
+ return false;
+ }
+ }
+ return true;
+}
+
+///
+@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));
+ assert(isStrictlyMonotonic(arr));
+}
+
+@safe unittest
+{
+ import std.conv : to;
+
+ // Issue 9457
+ auto x = "abcd";
+ assert(isSorted(x));
+ auto y = "acbd";
+ assert(!isSorted(y));
+
+ int[] a = [1, 2, 3];
+ assert(isSorted(a));
+ 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);
+ assert(isSorted(ds)); // random-access
+ 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 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
+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 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 $(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, Range)(Range r)
+if (ss == SwapStrategy.stable && isRandomAccessRange!(Range) && hasLength!Range && hasSlicing!Range)
+{
+ import std.algorithm.mutation : bringToFront;
+
+ alias pred = unaryFun!(predicate);
+ if (r.empty) return r;
+
+ 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 .. 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
+ if (pred(r.front)) continue;
+ // hit the first "bad" element
+ auto result = r;
+ for (r.popFront(); !r.empty; r.popFront())
+ {
+ if (!pred(r.front)) continue;
+ swap(result.front, r.front);
+ result.popFront();
+ }
+ return result;
+ }
+
+ return r;
+ }
+ else
+ {
+ // Inspired from www.stepanovpapers.com/PAM3-partition_notes.pdf,
+ // section "Bidirectional Partition Algorithm (Hoare)"
+ 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 (;;)
+ {
+ 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--);
+ }
+ }
+ else
+ {
+ import std.algorithm.mutation : swap;
+ auto result = r;
+ for (;;)
+ {
+ 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();
+ }
+ }
+ }
+}
+
+///
+@safe unittest
+{
+ import std.algorithm.mutation : SwapStrategy;
+ import std.algorithm.searching : count, find;
+ import std.conv : text;
+ import std.range.primitives : empty;
+
+ auto Arr = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
+ auto arr = Arr.dup;
+ static bool even(int a) { return (a & 1) == 0; }
+ // Partition arr such that even numbers come first
+ auto r = partition!(even)(arr);
+ // Now arr is separated in evens and odds.
+ // Numbers may have become shuffled due to instability
+ assert(r == arr[5 .. $]);
+ assert(count!(even)(arr[0 .. 5]) == 5);
+ assert(find!(even)(r).empty);
+
+ // Can also specify the predicate as a string.
+ // Use 'a' as the predicate argument name
+ arr[] = Arr[];
+ r = partition!(q{(a & 1) == 0})(arr);
+ assert(r == arr[5 .. $]);
+
+ // Now for a stable partition:
+ arr[] = Arr[];
+ r = partition!(q{(a & 1) == 0}, SwapStrategy.stable)(arr);
+ // Now arr is [2 4 6 8 10 1 3 5 7 9], and r points to 1
+ assert(arr == [2, 4, 6, 8, 10, 1, 3, 5, 7, 9] && r == arr[5 .. $]);
+
+ // In case the predicate needs to hold its own state, use a delegate:
+ arr[] = Arr[];
+ int x = 3;
+ // Put stuff greater than 3 on the left
+ bool fun(int a) { return a > x; }
+ r = partition!(fun, SwapStrategy.semistable)(arr);
+ // Now arr is [4 5 6 7 8 9 10 2 3 1] and r points to 2
+ assert(arr == [4, 5, 6, 7, 8, 9, 10, 2, 3, 1] && r == arr[7 .. $]);
+}
+
+@safe unittest
+{
+ import std.algorithm.internal : rndstuff;
+ static bool even(int a) { return (a & 1) == 0; }
+
+ // test with random data
+ auto a = rndstuff!int();
+ partition!even(a);
+ assert(isPartitioned!even(a));
+ auto b = rndstuff!string();
+ partition!`a.length < 5`(b);
+ 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.algorithm.iteration : map;
+ import std.random;
+ 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;
+}
+
+/**
+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))
+{
+ for (; !r.empty; r.popFront())
+ {
+ if (unaryFun!(pred)(r.front)) continue;
+ for (r.popFront(); !r.empty; r.popFront())
+ {
+ if (unaryFun!(pred)(r.front)) return false;
+ }
+ break;
+ }
+ return true;
+}
+
+///
+@safe unittest
+{
+ int[] r = [ 1, 3, 5, 7, 8, 2, 4, ];
+ assert(isPartitioned!"a & 1"(r));
+}
+
+// partition3
+/**
+Rearranges elements in $(D r) in three adjacent ranges and returns
+them. The first and leftmost range only contains elements in $(D r)
+less than $(D pivot). The second and middle range only contains
+elements in $(D r) that are equal to $(D pivot). Finally, the third
+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 && 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))
+{
+ // The algorithm is described in "Engineering a sort function" by
+ // Jon Bentley et al, pp 1257.
+
+ import std.algorithm.comparison : min;
+ import std.algorithm.mutation : swap, swapAt, swapRanges;
+ import std.typecons : tuple;
+
+ alias lessFun = binaryFun!less;
+ size_t i, j, k = r.length, l = k;
+
+ bigloop:
+ for (;;)
+ {
+ for (;; ++j)
+ {
+ if (j == k) break bigloop;
+ assert(j < r.length);
+ if (lessFun(r[j], pivot)) continue;
+ if (lessFun(pivot, r[j])) break;
+ r.swapAt(i++, j);
+ }
+ assert(j < k);
+ for (;;)
+ {
+ assert(k > 0);
+ if (!lessFun(pivot, r[--k]))
+ {
+ if (lessFun(r[k], pivot)) break;
+ r.swapAt(k, --l);
+ }
+ if (j == k) break bigloop;
+ }
+ // Here we know r[j] > pivot && r[k] < pivot
+ r.swapAt(j++, k);
+ }
+
+ // Swap the equal ranges from the extremes into the middle
+ auto strictlyLess = j - i, strictlyGreater = l - k;
+ auto swapLen = min(i, strictlyLess);
+ swapRanges(r[0 .. swapLen], r[j - swapLen .. j]);
+ swapLen = min(r.length - l, strictlyGreater);
+ swapRanges(r[k .. k + swapLen], r[r.length - swapLen .. r.length]);
+ return tuple(r[0 .. strictlyLess],
+ r[strictlyLess .. r.length - strictlyGreater],
+ r[r.length - strictlyGreater .. r.length]);
+}
+
+///
+@safe unittest
+{
+ auto a = [ 8, 3, 4, 1, 4, 7, 4 ];
+ auto pieces = partition3(a, 4);
+ assert(pieces[0] == [ 1, 3 ]);
+ assert(pieces[1] == [ 4, 4, 4 ]);
+ assert(pieces[2] == [ 8, 7 ]);
+}
+
+@safe unittest
+{
+ import std.random : Random, uniform, unpredictableSeed;
+
+ immutable uint[] seeds = [3923355730, 1927035882, unpredictableSeed];
+ foreach (s; seeds)
+ {
+ auto r = Random(s);
+ auto a = new int[](uniform(0, 100, r));
+ foreach (ref e; a)
+ {
+ e = uniform(0, 50, r);
+ }
+ 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);
+ }
+ }
+}
+
+// makeIndex
+/**
+Computes an index for $(D r) based on the comparison $(D less). The
+index is a sorted array of pointers or indices into the original
+range. This technique is similar to sorting, but it is more flexible
+because (1) it allows "sorting" of immutable collections, (2) allows
+binary search even if the original collection does not offer random
+access, (3) allows multiple indexes, each on a different predicate,
+and (4) may be faster when dealing with large objects. However, using
+an index may also be slower under certain circumstances due to the
+extra indirection, and is always larger than a sorting-based solution
+because it needs space for the index in addition to the original
+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
+$(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
+index. The index-based version returns $(D void) because the ordering
+relation involves not only $(D index) but also $(D r).
+
+Throws: If the second argument's length is less than that of the range
+indexed, an exception is thrown.
+*/
+SortedRange!(RangeIndex, (a, b) => binaryFun!less(*a, *b))
+makeIndex(
+ alias less = "a < b",
+ SwapStrategy ss = SwapStrategy.unstable,
+ Range,
+ RangeIndex)
+(Range r, RangeIndex index)
+if (isForwardRange!(Range) && isRandomAccessRange!(RangeIndex)
+ && is(ElementType!(RangeIndex) : ElementType!(Range)*))
+{
+ import std.algorithm.internal : addressOf;
+ import std.exception : enforce;
+
+ // assume collection already ordered
+ size_t i;
+ for (; !r.empty; r.popFront(), ++i)
+ index[i] = addressOf(r.front);
+ enforce(index.length == i);
+ // sort the index
+ sort!((a, b) => binaryFun!less(*a, *b), ss)(index);
+ return typeof(return)(index);
+}
+
+/// Ditto
+void makeIndex(
+ alias less = "a < b",
+ SwapStrategy ss = SwapStrategy.unstable,
+ Range,
+ RangeIndex)
+(Range r, RangeIndex index)
+if (isRandomAccessRange!Range && !isInfinite!Range &&
+ isRandomAccessRange!RangeIndex && !isInfinite!RangeIndex &&
+ isIntegral!(ElementType!RangeIndex))
+{
+ import std.conv : to;
+ import std.exception : enforce;
+
+ alias IndexType = Unqual!(ElementType!RangeIndex);
+ enforce(r.length == index.length,
+ "r and index must be same length for makeIndex.");
+ static if (IndexType.sizeof < size_t.sizeof)
+ {
+ enforce(r.length <= size_t(1) + IndexType.max, "Cannot create an index with " ~
+ "element type " ~ IndexType.stringof ~ " with length " ~
+ to!string(r.length) ~ ".");
+ }
+
+ // Use size_t as loop index to avoid overflow on ++i,
+ // e.g. when squeezing 256 elements into a ubyte index.
+ foreach (size_t i; 0 .. r.length)
+ index[i] = cast(IndexType) i;
+
+ // sort the index
+ sort!((a, b) => binaryFun!less(r[cast(size_t) a], r[cast(size_t) b]), ss)
+ (index);
+}
+
+///
+@system unittest
+{
+ immutable(int[]) arr = [ 2, 3, 1, 5, 0 ];
+ // index using pointers
+ auto index1 = new immutable(int)*[arr.length];
+ makeIndex!("a < b")(arr, index1);
+ assert(isSorted!("*a < *b")(index1));
+ // index using offsets
+ auto index2 = new size_t[arr.length];
+ makeIndex!("a < b")(arr, index2);
+ assert(isSorted!
+ ((size_t a, size_t b){ return arr[a] < arr[b];})
+ (index2));
+}
+
+@system unittest
+{
+ immutable(int)[] arr = [ 2, 3, 1, 5, 0 ];
+ // index using pointers
+ auto index1 = new immutable(int)*[arr.length];
+ alias ImmRange = typeof(arr);
+ alias ImmIndex = typeof(index1);
+ static assert(isForwardRange!(ImmRange));
+ static assert(isRandomAccessRange!(ImmIndex));
+ static assert(!isIntegral!(ElementType!(ImmIndex)));
+ static assert(is(ElementType!(ImmIndex) : ElementType!(ImmRange)*));
+ makeIndex!("a < b")(arr, index1);
+ assert(isSorted!("*a < *b")(index1));
+
+ // index using offsets
+ auto index2 = new long[arr.length];
+ makeIndex(arr, index2);
+ assert(isSorted!
+ ((long a, long b){
+ return arr[cast(size_t) a] < arr[cast(size_t) b];
+ })(index2));
+
+ // index strings using offsets
+ string[] arr1 = ["I", "have", "no", "chocolate"];
+ auto index3 = new byte[arr1.length];
+ makeIndex(arr1, index3);
+ assert(isSorted!
+ ((byte a, byte b){ return arr1[a] < arr1[b];})
+ (index3));
+}
+
+@safe unittest
+{
+ import std.algorithm.comparison : equal;
+
+ ubyte[256] index = void;
+ iota(256).makeIndex(index[]);
+ assert(index[].equal(iota(256)));
+ byte[128] sindex = void;
+ iota(128).makeIndex(sindex[]);
+ assert(sindex[].equal(iota(128)));
+
+ auto index2 = new uint[10];
+ 10.iota.makeIndex(index2);
+ assert(index2.equal(10.iota));
+}
+
+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)
+ enum validPredicates = true;
+ else static if (less.length == 1 && is(typeof(less[0]) == SwapStrategy))
+ enum validPredicates = true;
+ else
+ enum validPredicates =
+ is(typeof((E a, E b){ bool r = binaryFun!(less[0])(a, b); }))
+ && validPredicates!(E, less[1 .. $]);
+}
+
+/**
+$(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",
+"a.date > b.date")(r)) sorts the range $(D r) by $(D id) ascending,
+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)
+{
+ auto multiSort(Range)(Range r)
+ if (validPredicates!(ElementType!Range, less))
+ {
+ import std.meta : AliasSeq;
+ import std.range : assumeSorted;
+ static if (is(typeof(less[$ - 1]) == SwapStrategy))
+ {
+ enum ss = less[$ - 1];
+ alias funs = less[0 .. $ - 1];
+ }
+ else
+ {
+ enum ss = SwapStrategy.unstable;
+ alias funs = less;
+ }
+
+ 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
+ {
+ multiSortImpl!(Range, ss, funs)(r);
+ return assumeSorted!(multiSortPredFun!(Range, funs))(r);
+ }
+ }
+}
+
+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)
+ {
+ 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) ];
+ multiSort!("a.x < b.x", "a.y < b.y", SwapStrategy.unstable)(pts1);
+ assert(pts1 == pts2);
+}
+
+@safe unittest
+{
+ import std.algorithm.comparison : equal;
+ import std.range;
+
+ static struct Point { int x, y; }
+ auto pts1 = [ Point(5, 6), Point(1, 0), Point(5, 7), Point(1, 1), Point(1, 2), Point(0, 1) ];
+ auto pts2 = [ Point(0, 1), Point(1, 0), Point(1, 1), Point(1, 2), Point(5, 6), Point(5, 7) ];
+ static assert(validPredicates!(Point, "a.x < b.x", "a.y < b.y"));
+ multiSort!("a.x < b.x", "a.y < b.y", SwapStrategy.unstable)(pts1);
+ assert(pts1 == pts2);
+
+ auto pts3 = indexed(pts1, iota(pts1.length));
+ 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)
+{
+ static struct A
+ {
+ int x;
+ int y;
+ }
+
+ static bool byX(const ref A lhs, const ref A rhs)
+ {
+ return lhs.x < rhs.x;
+ }
+
+ static bool byY(const ref A lhs, const ref A rhs)
+ {
+ return lhs.y < rhs.y;
+ }
+
+ auto points = [ A(4, 1), A(2, 4)];
+ multiSort!(byX, byY)(points);
+ assert(points[0] == A(2, 4));
+ assert(points[1] == A(4, 1));
+}
+
+@safe unittest // issue 16179 (cannot access frame of function)
+{
+ 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]]);
+}
+
+@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]);
+}
+
+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;
+}
+
+/*
+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);
+
+ switch (r.length)
+ {
+ 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;
+ }
+
+ 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)
+ {
+ 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];
+ if (pred(r[j], temp))
+ {
+ do
+ {
+ r[j - 1] = r[j];
+ ++j;
+ }
+ while (j < r.length && pred(r[j], temp));
+ r[j - 1] = temp;
+ }
+ }
+ else
+ {
+ size_t j = i;
+ while (pred(r[j + 1], r[j]))
+ {
+ r.swapAt(j, j + 1);
+ if (j == maxJ) break;
+ ++j;
+ }
+ }
+ if (i == 0) break;
+ }
+}
+
+@safe unittest
+{
+ import std.random : Random, uniform;
+
+ auto rnd = Random(1);
+ auto a = new int[uniform(100, 200, rnd)];
+ foreach (ref e; a)
+ {
+ e = uniform(-100, 100, rnd);
+ }
+
+ 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). 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.
+
+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
+others) when not compiled in release mode, due to the cursory $(D assumeSorted)
+check. Specifically, $(D sort) expects $(D less(a,b) && less(b,c)) to imply
+$(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: $(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
+or more allocations per call. Both algorithms have $(BIGOH n log n) worst-case
+time complexity.
+
+See_Also:
+ $(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)
+ /+ Unstable sorting uses the quicksort algorithm, which uses swapAt,
+ which either uses swap(...), requiring swappable elements, or just
+ swaps using assignment.
+ Stable sorting uses TimSort, which needs to copy elements into a buffer,
+ requiring assignable elements. +/
+{
+ import std.range : assumeSorted;
+ alias lessFun = binaryFun!(less);
+ alias LessRet = typeof(lessFun(r.front, r.front)); // instantiate lessFun
+ static if (is(LessRet == bool))
+ {
+ static if (ss == SwapStrategy.unstable)
+ quickSortImpl!(lessFun)(r, r.length);
+ else //use Tim Sort for semistable & stable
+ TimSortImpl!(lessFun, Range).sort(r, null);
+
+ assert(isSorted!lessFun(r), "Failed to sort range of type " ~ Range.stringof);
+ }
+ else
+ {
+ static assert(false, "Invalid predicate passed to sort: " ~ less.stringof);
+ }
+ return assumeSorted!less(r);
+}
+
+///
+@safe pure nothrow unittest
+{
+ int[] array = [ 1, 2, 3, 4 ];
+
+ // sort in descending order
+ array.sort!("a > b");
+ assert(array == [ 4, 3, 2, 1 ]);
+
+ // sort in ascending order
+ array.sort();
+ assert(array == [ 1, 2, 3, 4 ]);
+
+ // sort with reusable comparator and chain
+ alias myComp = (x, y) => x > y;
+ assert(array.sort!(myComp).release == [ 4, 3, 2, 1 ]);
+}
+
+///
+@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" ]);
+}
+
+///
+@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.algorithm.comparison : equal;
+ import std.math : cmp, isIdentical;
+
+ 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.algorithm.iteration, std.algorithm.mutation, std.random;
+ 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.mutation : swapRanges;
+ import std.random : Random, unpredictableSeed, uniform;
+ import std.uni : toUpper;
+
+ // sort using delegate
+ auto a = new int[100];
+ auto rnd = Random(unpredictableSeed);
+ foreach (ref e; a)
+ {
+ e = uniform(-100, 100, rnd);
+ }
+
+ int i = 0;
+ bool greater2(int a, int b) @safe { return a + i > b + i; }
+ auto greater = &greater2;
+ sort!(greater)(a);
+ assert(isSorted!(greater)(a));
+
+ // sort using string
+ sort!("a < b")(a);
+ assert(isSorted!("a < b")(a));
+
+ // sort using function; all elements equal
+ foreach (ref e; a)
+ {
+ e = 5;
+ }
+ static bool less(int a, int b) { return a < b; }
+ sort!(less)(a);
+ assert(isSorted!(less)(a));
+
+ string[] words = [ "aBc", "a", "abc", "b", "ABC", "c" ];
+ bool lessi(string a, string b) { return toUpper(a) < toUpper(b); }
+ sort!(lessi, SwapStrategy.stable)(words);
+ assert(words == [ "a", "aBc", "abc", "ABC", "b", "c" ]);
+
+ // sort using ternary predicate
+ //sort!("b - a")(a);
+ //assert(isSorted!(less)(a));
+
+ a = rndstuff!(int)();
+ sort(a);
+ assert(isSorted(a));
+ auto b = rndstuff!(string)();
+ sort!("toLower(a) < toLower(b)")(b);
+ assert(isSorted!("toUpper(a) < toUpper(b)")(b));
+
+ {
+ // Issue 10317
+ enum E_10317 { a, b }
+ auto a_10317 = new E_10317[10];
+ sort(a_10317);
+ }
+
+ {
+ // Issue 7767
+ // Unstable sort should complete without an excessive number of predicate calls
+ // This would suggest it's running in quadratic time
+
+ // Compilation error if predicate is not static, i.e. a nested function
+ static uint comp;
+ static bool pred(size_t a, size_t b)
+ {
+ ++comp;
+ return a < b;
+ }
+
+ size_t[] arr;
+ arr.length = 1024;
+
+ foreach (k; 0 .. arr.length) arr[k] = k;
+ swapRanges(arr[0..$/2], arr[$/2..$]);
+
+ sort!(pred, SwapStrategy.unstable)(arr);
+ assert(comp < 25_000);
+ }
+
+ {
+ import std.algorithm.mutation : swap;
+
+ bool proxySwapCalled;
+ struct S
+ {
+ int i;
+ alias i this;
+ void proxySwap(ref S other) { swap(i, other.i); proxySwapCalled = true; }
+ @disable void opAssign(S value);
+ }
+
+ alias R = S[];
+ R r = [S(3), S(2), S(1)];
+ static assert(hasSwappableElements!R);
+ static assert(!hasAssignableElements!R);
+ r.sort();
+ assert(proxySwapCalled);
+ }
+}
+
+private void quickSortImpl(alias less, Range)(Range r, size_t depth)
+{
+ import std.algorithm.comparison : min, max;
+ import std.algorithm.mutation : swap, swapAt;
+
+ alias Elem = ElementType!(Range);
+ enum size_t shortSortGetsBetter = max(32, 1024 / Elem.sizeof);
+ static assert(shortSortGetsBetter >= 1);
+
+ // partition
+ while (r.length > shortSortGetsBetter)
+ {
+ if (depth == 0)
+ {
+ HeapOps!(less, Range).heapSort(r);
+ return;
+ }
+ depth = depth >= depth.max / 2 ? (depth / 3) * 2 : (depth * 2) / 3;
+
+ const pivotIdx = getPivot!(less)(r);
+ auto pivot = r[pivotIdx];
+
+ // partition
+ r.swapAt(pivotIdx, r.length - 1);
+ size_t lessI = size_t.max, greaterI = r.length - 1;
+
+ outer: for (;;)
+ {
+ alias pred = binaryFun!less;
+ while (pred(r[++lessI], pivot)) {}
+ assert(lessI <= greaterI, "sort: invalid comparison function.");
+ for (;;)
+ {
+ if (greaterI == lessI) break outer;
+ if (!pred(pivot, r[--greaterI])) break;
+ }
+ assert(lessI <= greaterI, "sort: invalid comparison function.");
+ if (lessI == greaterI) break;
+ r.swapAt(lessI, greaterI);
+ }
+
+ 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);
+ }
+ .quickSortImpl!(less, Range)(right, depth);
+ r = left;
+ }
+ // residual sort
+ static if (shortSortGetsBetter > 1)
+ {
+ shortSort!(less, Range)(r);
+ }
+}
+
+// Heap operations for random-access ranges
+package(std) template HeapOps(alias less, Range)
+{
+ import std.algorithm.mutation : swapAt;
+
+ static assert(isRandomAccessRange!Range);
+ static assert(hasLength!Range);
+ static assert(hasSwappableElements!Range || hasAssignableElements!Range);
+
+ alias lessFun = binaryFun!less;
+
+ //template because of @@@12410@@@
+ void heapSort()(Range r)
+ {
+ // If true, there is nothing to do
+ if (r.length < 2) return;
+ // Build Heap
+ buildHeap(r);
+ // Sort
+ 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 (;;)
+ {
+ 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 percolate()(Range r, size_t parent, immutable size_t end)
+ {
+ immutable root = parent;
+
+ // Sift down
+ for (;;)
+ {
+ auto child = (parent + 1) * 2;
+
+ if (child >= end)
+ {
+ if (child == end)
+ {
+ // Leftover left node.
+ --child;
+ r.swapAt(parent, child);
+ parent = child;
+ }
+ break;
+ }
+
+ auto leftChild = child - 1;
+ if (lessFun(r[child], r[leftChild])) child = leftChild;
+ r.swapAt(parent, child);
+ parent = child;
+ }
+
+ // Sift up
+ for (auto child = parent; child > root; child = parent)
+ {
+ parent = (child - 1) / 2;
+ if (!lessFun(r[parent], r[child])) break;
+ r.swapAt(parent, child);
+ }
+ }
+}
+
+// Tim Sort implementation
+private template TimSortImpl(alias pred, R)
+{
+ import core.bitop : bsr;
+ import std.array : uninitializedArray;
+
+ static assert(isRandomAccessRange!R);
+ static assert(hasLength!R);
+ static assert(hasSlicing!R);
+ static assert(hasAssignableElements!R);
+
+ alias T = ElementType!R;
+
+ alias less = binaryFun!pred;
+ 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;
+ enum minimalStorage = 256;
+ enum stackSize = 40;
+
+ struct Slice{ size_t base, length; }
+
+ // Entry point for tim sort
+ void sort()(R range, T[] temp)
+ {
+ import std.algorithm.comparison : min;
+
+ // Do insertion sort on small range
+ if (range.length <= minimalMerge)
+ {
+ binaryInsertionSort(range);
+ return;
+ }
+
+ immutable minRun = minRunLength(range.length);
+ immutable minTemp = min(range.length / 2, minimalStorage);
+ size_t minGallop = minimalGallop;
+ Slice[stackSize] stack = void;
+ size_t stackLen = 0;
+
+ // Allocate temporary memory if not provided by user
+ if (temp.length < minTemp) temp = () @trusted { return uninitializedArray!(T[])(minTemp); }();
+
+ for (size_t i = 0; i < range.length; )
+ {
+ // Find length of first run in list
+ size_t runLen = firstRun(range[i .. range.length]);
+
+ // If run has less than minRun elements, extend using insertion sort
+ if (runLen < minRun)
+ {
+ // Do not run farther than the length of the range
+ immutable force = range.length - i > minRun ? minRun : range.length - i;
+ binaryInsertionSort(range[i .. i + force], runLen);
+ runLen = force;
+ }
+
+ // Push run onto stack
+ stack[stackLen++] = Slice(i, runLen);
+ i += runLen;
+
+ // Collapse stack so that (e1 > e2 + e3 && e2 > e3)
+ // STACK is | ... e1 e2 e3 >
+ while (stackLen > 1)
+ {
+ immutable run4 = stackLen - 1;
+ immutable run3 = stackLen - 2;
+ immutable run2 = stackLen - 3;
+ immutable run1 = stackLen - 4;
+
+ 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;
+ mergeAt(range, stack[0 .. stackLen], at, minGallop, temp);
+ }
+ 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)
+ {
+ assert(stack[k - 2].length > stack[k - 1].length + stack[k].length);
+ assert(stack[k - 1].length > stack[k].length);
+ }
+ }
+ }
+ }
+
+ // Force collapse stack until there is only one run left
+ while (stackLen > 1)
+ {
+ immutable run3 = stackLen - 1;
+ immutable run2 = stackLen - 2;
+ immutable run1 = stackLen - 3;
+ immutable at = stackLen >= 3 && stack[run1].length <= stack[run3].length
+ ? run1 : run2;
+ mergeAt(range, stack[0 .. stackLen], at, minGallop, temp);
+ --stackLen;
+ }
+ }
+
+ // Calculates optimal value for minRun:
+ // take first 6 bits of n and add 1 if any lower bits are set
+ size_t minRunLength()(size_t n)
+ {
+ immutable shift = bsr(n)-5;
+ auto result = (n >> shift) + !!(n & ~((1 << shift)-1));
+ return result;
+ }
+
+ // Returns length of first run in range
+ size_t firstRun()(R range)
+ out(ret)
+ {
+ assert(ret <= range.length);
+ }
+ body
+ {
+ import std.algorithm.mutation : reverse;
+
+ if (range.length < 2) return range.length;
+
+ size_t i = 2;
+ if (lessEqual(range[0], range[1]))
+ {
+ while (i < range.length && lessEqual(range[i-1], range[i])) ++i;
+ }
+ else
+ {
+ while (i < range.length && greater(range[i-1], range[i])) ++i;
+ reverse(range[0 .. i]);
+ }
+ return i;
+ }
+
+ // A binary insertion sort for building runs up to minRun length
+ void binaryInsertionSort()(R range, size_t sortedLen = 1)
+ out
+ {
+ if (!__ctfe) assert(isSorted!pred(range));
+ }
+ body
+ {
+ import std.algorithm.mutation : move;
+
+ for (; sortedLen < range.length; ++sortedLen)
+ {
+ T item = range.moveAt(sortedLen);
+ size_t lower = 0;
+ size_t upper = sortedLen;
+ while (upper != lower)
+ {
+ size_t center = (lower + upper) / 2;
+ if (less(item, range[center])) upper = center;
+ else lower = center + 1;
+ }
+ //Currently (DMD 2.061) moveAll+retro is slightly less
+ //efficient then stright 'for' loop
+ //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] = 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)
+ in
+ {
+ assert(stack.length >= 2);
+ assert(stack.length - at == 2 || stack.length - at == 3);
+ }
+ body
+ {
+ immutable base = stack[at].base;
+ immutable mid = stack[at].length;
+ immutable len = stack[at + 1].length + mid;
+
+ // Pop run from stack
+ stack[at] = Slice(base, len);
+ if (stack.length - at == 3) stack[$ - 2] = stack[$ - 1];
+
+ // Merge runs (at, at + 1)
+ return merge(range[base .. base + len], mid, minGallop, temp);
+ }
+
+ // 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)
+ in
+ {
+ if (!__ctfe)
+ {
+ assert(isSorted!pred(range[0 .. mid]));
+ assert(isSorted!pred(range[mid .. range.length]));
+ }
+ }
+ body
+ {
+ assert(mid < range.length);
+
+ // Reduce range of elements
+ immutable firstElement = gallopForwardUpper(range[0 .. mid], range[mid]);
+ immutable lastElement = gallopReverseLower(range[mid .. range.length], range[mid - 1]) + mid;
+ range = range[firstElement .. lastElement];
+ mid -= firstElement;
+
+ if (mid == 0 || mid == range.length) return;
+
+ // Call function which will copy smaller run into temporary memory
+ if (mid <= range.length / 2)
+ {
+ temp = ensureCapacity(mid, temp);
+ minGallop = mergeLo(range, mid, minGallop, temp);
+ }
+ else
+ {
+ temp = ensureCapacity(range.length - mid, temp);
+ minGallop = mergeHi(range, mid, minGallop, temp);
+ }
+ }
+
+ // Enlarge size of temporary memory if needed
+ T[] ensureCapacity()(size_t minCapacity, T[] temp)
+ out(ret)
+ {
+ assert(ret.length >= minCapacity);
+ }
+ body
+ {
+ if (temp.length < minCapacity)
+ {
+ size_t newSize = 1<<(bsr(minCapacity)+1);
+ //Test for overflow
+ if (newSize < minCapacity) newSize = minCapacity;
+
+ if (__ctfe) temp.length = 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)
+ out
+ {
+ if (!__ctfe) assert(isSorted!pred(range));
+ }
+ body
+ {
+ import std.algorithm.mutation : copy;
+
+ assert(mid <= range.length);
+ assert(temp.length >= mid);
+
+ // Copy run into temporary memory
+ temp = temp[0 .. mid];
+ copy(range[0 .. mid], temp);
+
+ // Move first element into place
+ range[0] = range[mid];
+
+ size_t i = 1, lef = 0, rig = mid + 1;
+ size_t count_lef, count_rig;
+ immutable lef_end = temp.length - 1;
+
+ if (lef < lef_end && rig < range.length)
+ outer: while (true)
+ {
+ count_lef = 0;
+ count_rig = 0;
+
+ // Linear merge
+ while ((count_lef | count_rig) < minGallop)
+ {
+ if (lessEqual(temp[lef], range[rig]))
+ {
+ range[i++] = temp[lef++];
+ if (lef >= lef_end) break outer;
+ ++count_lef;
+ count_rig = 0;
+ }
+ else
+ {
+ range[i++] = range[rig++];
+ if (rig >= range.length) break outer;
+ count_lef = 0;
+ ++count_rig;
+ }
+ }
+
+ // Gallop merge
+ do
+ {
+ count_lef = gallopForwardUpper(temp[lef .. $], range[rig]);
+ foreach (j; 0 .. count_lef) range[i++] = temp[lef++];
+ 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)
+ {
+ range[i++] = temp[lef++];
+ if (lef >= temp.length) break outer;
+ }
+
+ if (minGallop > 0) --minGallop;
+ }
+ while (count_lef >= minimalGallop || count_rig >= minimalGallop);
+
+ minGallop += 2;
+ }
+
+ // Move remaining elements from right
+ while (rig < range.length)
+ range[i++] = range[rig++];
+
+ // Move remaining elements from left
+ while (lef < temp.length)
+ range[i++] = temp[lef++];
+
+ return minGallop > 0 ? minGallop : 1;
+ }
+
+ // 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)
+ out
+ {
+ if (!__ctfe) assert(isSorted!pred(range));
+ }
+ body
+ {
+ import std.algorithm.mutation : copy;
+
+ assert(mid <= range.length);
+ assert(temp.length >= range.length - mid);
+
+ // Copy run into temporary memory
+ temp = temp[0 .. range.length - mid];
+ copy(range[mid .. range.length], temp);
+
+ // Move first element into place
+ range[range.length - 1] = range[mid - 1];
+
+ size_t i = range.length - 2, lef = mid - 2, rig = temp.length - 1;
+ size_t count_lef, count_rig;
+
+ outer:
+ while (true)
+ {
+ count_lef = 0;
+ count_rig = 0;
+
+ // Linear merge
+ while ((count_lef | count_rig) < minGallop)
+ {
+ if (greaterEqual(temp[rig], range[lef]))
+ {
+ range[i--] = temp[rig];
+ if (rig == 1)
+ {
+ // Move remaining elements from left
+ while (true)
+ {
+ range[i--] = range[lef];
+ if (lef == 0) break;
+ --lef;
+ }
+
+ // Move last element into place
+ range[i] = temp[0];
+
+ break outer;
+ }
+ --rig;
+ count_lef = 0;
+ ++count_rig;
+ }
+ else
+ {
+ range[i--] = range[lef];
+ if (lef == 0) while (true)
+ {
+ range[i--] = temp[rig];
+ if (rig == 0) break outer;
+ --rig;
+ }
+ --lef;
+ ++count_lef;
+ count_rig = 0;
+ }
+ }
+
+ // Gallop merge
+ do
+ {
+ count_rig = rig - gallopReverseLower(temp[0 .. rig], range[lef]);
+ foreach (j; 0 .. count_rig)
+ {
+ range[i--] = temp[rig];
+ if (rig == 0) break outer;
+ --rig;
+ }
+
+ count_lef = lef - gallopReverseUpper(range[0 .. lef], temp[rig]);
+ foreach (j; 0 .. count_lef)
+ {
+ range[i--] = range[lef];
+ if (lef == 0) while (true)
+ {
+ range[i--] = temp[rig];
+ if (rig == 0) break outer;
+ --rig;
+ }
+ --lef;
+ }
+
+ if (minGallop > 0) --minGallop;
+ }
+ while (count_lef >= minimalGallop || count_rig >= minimalGallop);
+
+ minGallop += 2;
+ }
+
+ return minGallop > 0 ? minGallop : 1;
+ }
+
+ // false = forward / lower, true = reverse / upper
+ template gallopSearch(bool forwardReverse, bool lowerUpper)
+ {
+ // Gallop search on range according to attributes forwardReverse and lowerUpper
+ size_t gallopSearch(R)(R range, T value)
+ out(ret)
+ {
+ assert(ret <= range.length);
+ }
+ body
+ {
+ size_t lower = 0, center = 1, upper = range.length;
+ alias gap = center;
+
+ static if (forwardReverse)
+ {
+ static if (!lowerUpper) alias comp = lessEqual; // reverse lower
+ static if (lowerUpper) alias comp = less; // reverse upper
+
+ // Gallop Search Reverse
+ while (gap <= upper)
+ {
+ if (comp(value, range[upper - gap]))
+ {
+ upper -= gap;
+ gap *= 2;
+ }
+ else
+ {
+ lower = upper - gap;
+ break;
+ }
+ }
+
+ // Binary Search Reverse
+ while (upper != lower)
+ {
+ center = lower + (upper - lower) / 2;
+ if (comp(value, range[center])) upper = center;
+ else lower = center + 1;
+ }
+ }
+ else
+ {
+ static if (!lowerUpper) alias comp = greater; // forward lower
+ static if (lowerUpper) alias comp = greaterEqual; // forward upper
+
+ // Gallop Search Forward
+ while (lower + gap < upper)
+ {
+ if (comp(value, range[lower + gap]))
+ {
+ lower += gap;
+ gap *= 2;
+ }
+ else
+ {
+ upper = lower + gap;
+ break;
+ }
+ }
+
+ // Binary Search Forward
+ while (lower != upper)
+ {
+ center = lower + (upper - lower) / 2;
+ if (comp(value, range[center])) lower = center + 1;
+ else upper = center;
+ }
+ }
+
+ return lower;
+ }
+ }
+
+ alias gallopForwardLower = gallopSearch!(false, false);
+ alias gallopForwardUpper = gallopSearch!(false, true);
+ alias gallopReverseLower = gallopSearch!( true, false);
+ alias gallopReverseUpper = gallopSearch!( true, true);
+}
+
+@safe unittest
+{
+ import std.random : Random, uniform, randomShuffle;
+
+ // Element type with two fields
+ static struct E
+ {
+ size_t value, index;
+ }
+
+ // Generates data especially for testing sorting with Timsort
+ static E[] genSampleData(uint seed) @safe
+ {
+ import std.algorithm.mutation : swap, swapRanges;
+
+ auto rnd = Random(seed);
+
+ E[] arr;
+ arr.length = 64 * 64;
+
+ // We want duplicate values for testing stability
+ 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);
+ swapRanges(arr[0 .. mid], arr[mid .. $]);
+
+ // Shuffle last 1/8 of the array (test insertion sort and linear merge)
+ randomShuffle(arr[$ / 8 * 7 .. $], rnd);
+
+ // Swap few random elements (test galloping mode)
+ foreach (i; 0 .. arr.length / 64)
+ {
+ immutable a = uniform(0, arr.length, rnd), b = uniform(0, arr.length, rnd);
+ swap(arr[a], arr[b]);
+ }
+
+ // 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;
+
+ return arr;
+ }
+
+ // Tests the Timsort function for correctness and stability
+ static bool testSort(uint seed)
+ {
+ auto arr = genSampleData(seed);
+
+ // Now sort the array!
+ static bool comp(E a, E b)
+ {
+ return a.value < b.value;
+ }
+
+ sort!(comp, SwapStrategy.stable)(arr);
+
+ // Test that the array was sorted correctly
+ assert(isSorted!comp(arr));
+
+ // Test that the array was sorted stably
+ foreach (i; 0 .. arr.length - 1)
+ {
+ if (arr[i].value == arr[i + 1].value) assert(arr[i].index < arr[i + 1].index);
+ }
+
+ return true;
+ }
+
+ enum seed = 310614065;
+ testSort(seed);
+
+ enum result = testSort(seed);
+ assert(result == true);
+}
+
+@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,
+ 97, 14, 33, 10, 101, 102, 103, 26, 105, 106, 107, 6]
+ )));
+
+}
+
+@safe unittest
+{
+ //test stable sort + zip
+ import std.range;
+ auto x = [10, 50, 60, 60, 20];
+ dchar[] y = "abcde"d.dup;
+
+ sort!("a[0] < b[0]", SwapStrategy.stable)(zip(x, y));
+ assert(x == [10, 20, 50, 60, 60]);
+ assert(y == "aebcd"d);
+}
+
+@safe unittest
+{
+ // Issue 14223
+ import std.array, std.range;
+ auto arr = chain(iota(0, 384), iota(0, 256), iota(0, 80), iota(0, 64), iota(0, 96)).array;
+ sort!("a < b", SwapStrategy.stable)(arr);
+}
+
+// schwartzSort
+/**
+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 = ...;
+// Sort strings by hash, slow
+sort!((a, b) => hashFun(a) < hashFun(b))(array);
+// Sort strings by hash, fast (only computes arr.length hashes):
+schwartzSort!(hashFun, "a < b")(array);
+----
+
+The $(D schwartzSort) function might require less temporary data and
+be faster than the Perl idiom or the decorate-sort-undecorate idiom
+present in Python and Lisp. This is because sorting is done in-place
+and only minimal extra data (one array of transformed elements) is
+created.
+
+To check whether an array was sorted and benefit of the speedup of
+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))).
+ */
+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)
+{
+ import std.conv : emplace;
+ import std.range : zip, SortedRange;
+ import std.string : representation;
+
+ alias T = typeof(unaryFun!transform(r.front));
+ static trustedMalloc(size_t len) @trusted
+ {
+ import core.checkedint : mulu;
+ import core.stdc.stdlib : malloc;
+ 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)
+ {
+ static if (hasElaborateDestructor!T)
+ {
+ foreach (i; 0 .. length) collectException(destroy(xform1[i]));
+ }
+ static void trustedFree(T[] p) @trusted
+ {
+ import core.stdc.stdlib : free;
+ free(p.ptr);
+ }
+ trustedFree(xform1);
+ }
+ for (; length != r.length; ++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.
+ static if (isNarrowString!(typeof(xform1)))
+ {
+ auto xform = xform1.representation();
+ }
+ else
+ {
+ alias xform = xform1;
+ }
+ zip(xform, r).sort!((a, b) => binaryFun!less(a[0], b[0]), ss)();
+ return typeof(return)(r);
+}
+
+///
+@safe unittest
+{
+ import std.algorithm.iteration : map;
+ import std.numeric : entropy;
+
+ auto lowEnt = [ 1.0, 0, 0 ],
+ midEnt = [ 0.1, 0.1, 0.8 ],
+ highEnt = [ 0.31, 0.29, 0.4 ];
+ auto arr = new double[][3];
+ arr[0] = midEnt;
+ arr[1] = lowEnt;
+ arr[2] = highEnt;
+
+ schwartzSort!(entropy, "a > b")(arr);
+
+ assert(arr[0] == highEnt);
+ assert(arr[1] == midEnt);
+ assert(arr[2] == lowEnt);
+ assert(isSorted!("a > b")(map!(entropy)(arr)));
+}
+
+@safe unittest
+{
+ import std.algorithm.iteration : map;
+ import std.numeric : entropy;
+
+ auto lowEnt = [ 1.0, 0, 0 ],
+ midEnt = [ 0.1, 0.1, 0.8 ],
+ highEnt = [ 0.31, 0.29, 0.4 ];
+ auto arr = new double[][3];
+ arr[0] = midEnt;
+ arr[1] = lowEnt;
+ arr[2] = highEnt;
+
+ 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
+.. mid]) is the same as if the entire $(D r) were sorted, and leaves
+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))
+{
+ partialSort!(less, ss)(r[0 .. n], r[n .. $]);
+}
+
+///
+@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
+to the element that would fall there if the range were fully
+sorted. In addition, it also partitions $(D r) such that all elements
+$(D e1) from $(D r[0]) to $(D r[nth]) satisfy $(D !less(r[nth], e1)),
+and all elements $(D e2) from $(D r[nth]) to $(D r[r.length]) satisfy
+$(D !less(e2, r[nth])). Effectively, it finds the nth smallest
+(according to $(D less)) elements in $(D r). Performs an expected
+$(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 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),
+ $(HTTP sgi.com/tech/stl/nth_element.html, STL's nth_element)
+
+BUGS:
+
+Stable topN has not been implemented yet.
+*/
+auto topN(alias less = "a < b",
+ SwapStrategy ss = SwapStrategy.unstable,
+ Range)(Range r, size_t nth)
+if (isRandomAccessRange!(Range) && hasLength!Range && hasSlicing!Range)
+{
+ static assert(ss == SwapStrategy.unstable,
+ "Stable topN not yet implemented");
+ 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.
+ binaryFun!less(r[0], r[r.length - 1]);
+ import std.algorithm.mutation : swapAt;
+ r.swapAt(size_t(0), size_t(0));
+ static assert(is(typeof(r.length) == 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 (;;)
+ {
+ 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 (n + 1 == r.length)
+ {
+ 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
+ {
+ 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];
+ }
+ }
+}
+
+///
+@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
+ 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.algorithm.iteration : map;
+ import std.random : unpredictableSeed, Random, uniform;
+ 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;
+
+ int[] v = [ 7, 6, 5, 4, 3, 2, 1, 0 ];
+ ptrdiff_t n = 3;
+ topN!("a < b")(v, n);
+ assert(reduce!max(v[0 .. n]) <= v[n]);
+ assert(reduce!min(v[n + 1 .. $]) >= v[n]);
+ //
+ v = [3, 4, 5, 6, 7, 2, 3, 4, 5, 6, 1, 2, 3, 4, 5];
+ n = 3;
+ topN(v, n);
+ assert(reduce!max(v[0 .. n]) <= v[n]);
+ assert(reduce!min(v[n + 1 .. $]) >= v[n]);
+ //
+ v = [3, 4, 5, 6, 7, 2, 3, 4, 5, 6, 1, 2, 3, 4, 5];
+ n = 1;
+ topN(v, n);
+ assert(reduce!max(v[0 .. n]) <= v[n]);
+ assert(reduce!min(v[n + 1 .. $]) >= v[n]);
+ //
+ v = [3, 4, 5, 6, 7, 2, 3, 4, 5, 6, 1, 2, 3, 4, 5];
+ n = v.length - 1;
+ topN(v, n);
+ assert(v[n] == 7);
+ //
+ v = [3, 4, 5, 6, 7, 2, 3, 4, 5, 6, 1, 2, 3, 4, 5];
+ n = 0;
+ topN(v, n);
+ assert(v[n] == 1);
+
+ double[][] v1 = [[-10, -5], [-10, -3], [-10, -5], [-10, -4],
+ [-10, -5], [-9, -5], [-9, -3], [-9, -5],];
+
+ // double[][] v1 = [ [-10, -5], [-10, -4], [-9, -5], [-9, -5],
+ // [-10, -5], [-10, -3], [-10, -5], [-9, -3],];
+ double[]*[] idx = [ &v1[0], &v1[1], &v1[2], &v1[3], &v1[4], &v1[5], &v1[6],
+ &v1[7], ];
+
+ auto mid = v1.length / 2;
+ topN!((a, b){ return (*a)[1] < (*b)[1]; })(idx, mid);
+ foreach (e; idx[0 .. mid]) assert((*e)[1] <= (*idx[mid])[1]);
+ foreach (e; idx[mid .. $]) assert((*e)[1] >= (*idx[mid])[1]);
+}
+
+@safe unittest
+{
+ import std.algorithm.comparison : max, min;
+ import std.algorithm.iteration : reduce;
+ import std.random : Random, uniform, unpredictableSeed;
+
+ immutable uint[] seeds = [90027751, 2709791795, 1374631933, 995751648, 3541495258, 984840953, unpredictableSeed];
+ foreach (s; seeds)
+ {
+ 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.
+ */
+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) &&
+ hasLvalueElements!Range1 && hasLvalueElements!Range2)
+{
+ import std.container : BinaryHeap;
+
+ static assert(ss == SwapStrategy.unstable,
+ "Stable topN not yet implemented");
+
+ auto heap = BinaryHeap!(Range1, less)(r1);
+ foreach (ref e; r2)
+ {
+ heap.conditionalSwap(e);
+ }
+
+ return r1;
+}
+
+///
+@system unittest
+{
+ int[] a = [ 5, 7, 2, 6, 7 ];
+ int[] b = [ 2, 1, 5, 6, 7, 3, 0 ];
+ topN(a, b);
+ sort(a);
+ 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
+$(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 $(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 = No.sortOutput)
+if (isInputRange!(SRange) && isRandomAccessRange!(TRange)
+ && hasLength!(TRange) && hasSlicing!(TRange))
+{
+ import std.container : BinaryHeap;
+
+ if (target.empty) return target;
+ auto heap = BinaryHeap!(TRange, less)(target, 0);
+ foreach (e; source) heap.conditionalInsert(e);
+ auto result = target[0 .. heap.length];
+ if (sorted == Yes.sortOutput)
+ {
+ while (!heap.empty) heap.removeFront();
+ }
+ return result;
+}
+
+///
+@system unittest
+{
+ import std.typecons : Yes;
+
+ int[] a = [ 10, 16, 2, 3, 1, 5, 0 ];
+ int[] b = new int[3];
+ topNCopy(a, b, Yes.sortOutput);
+ assert(b == [ 0, 1, 2 ]);
+}
+
+@system unittest
+{
+ import std.random : Random, unpredictableSeed, uniform, randomShuffle;
+ import std.typecons : Yes;
+
+ auto r = Random(unpredictableSeed);
+ ptrdiff_t[] a = new ptrdiff_t[uniform(1, 1000, r)];
+ foreach (i, ref e; a) e = i;
+ randomShuffle(a, r);
+ auto n = uniform(0, a.length, r);
+ ptrdiff_t[] b = new ptrdiff_t[n];
+ topNCopy!(binaryFun!("a < b"))(a, b, Yes.sortOutput);
+ assert(isSorted!(binaryFun!("a < b"))(b));
+}
+
+/**
+Given a range of elements, constructs an index of its top $(I n) elements
+(i.e., the first $(I n) elements if the range were sorted).
+
+Similar to $(LREF topN), except that the range is not modified.
+
+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
+ $(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
+ constructed index will consist of zero-based numerical indices into
+ $(D r); or it can have pointers to the element type of $(D r), in which
+ case the constructed index will be pointers to the top elements in
+ $(D r).
+ sorted = Determines whether to sort the index by the elements they refer
+ to.
+
+See_also: $(LREF topN), $(LREF topNCopy).
+
+BUGS:
+The swapping strategy parameter is not implemented yet; currently it is
+ignored.
+*/
+void topNIndex(alias less = "a < b", SwapStrategy ss = SwapStrategy.unstable,
+ Range, RangeIndex)
+ (Range r, RangeIndex index, SortOutput sorted = No.sortOutput)
+if (isRandomAccessRange!Range &&
+ isRandomAccessRange!RangeIndex &&
+ hasAssignableElements!RangeIndex)
+{
+ static assert(ss == SwapStrategy.unstable,
+ "Stable swap strategy not implemented yet.");
+
+ import std.container.binaryheap : BinaryHeap;
+ if (index.empty) return;
+
+ static if (isIntegral!(ElementType!(RangeIndex)))
+ {
+ import std.exception : enforce;
+
+ enforce(ElementType!(RangeIndex).max >= index.length,
+ "Index type too small");
+ bool indirectLess(ElementType!(RangeIndex) a, ElementType!(RangeIndex) b)
+ {
+ return binaryFun!(less)(r[a], r[b]);
+ }
+ auto heap = BinaryHeap!(RangeIndex, indirectLess)(index, 0);
+ foreach (i; 0 .. r.length)
+ {
+ heap.conditionalInsert(cast(ElementType!RangeIndex) i);
+ }
+
+ }
+ else static if (is(ElementType!(RangeIndex) == ElementType!(Range)*))
+ {
+ static bool indirectLess(const ElementType!(RangeIndex) a,
+ const ElementType!(RangeIndex) b)
+ {
+ return binaryFun!less(*a, *b);
+ }
+ auto heap = BinaryHeap!(RangeIndex, indirectLess)(index, 0);
+ foreach (i; 0 .. r.length)
+ {
+ heap.conditionalInsert(&r[i]);
+ }
+ }
+ else static assert(0, "Invalid ElementType");
+
+ if (sorted == Yes.sortOutput)
+ {
+ while (!heap.empty) heap.removeFront();
+ }
+}
+
+///
+@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, 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, Yes.sortOutput);
+ assert(ptrIndex == [ &a[5], &a[1], &a[3] ]);
+}
+
+@system unittest
+{
+ import std.conv : text;
+
+ {
+ int[] a = [ 10, 8, 9, 2, 4, 6, 7, 1, 3, 5 ];
+ int*[] b = new int*[5];
+ 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, 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
+ * permutation.
+ *
+ * The predicate $(D less) defines the lexicographical ordering to be used on
+ * the range.
+ *
+ * If the range is currently the lexicographically greatest permutation, it is
+ * permuted back to the least permutation and false is returned. Otherwise,
+ * true is returned. One can thus generate all permutations of a range by
+ * sorting it according to $(D less), which produces the lexicographically
+ * least permutation, and then calling nextPermutation until it returns false.
+ * This is guaranteed to generate all distinct permutations of the range
+ * exactly once. If there are $(I N) elements in the range and all of them are
+ * unique, then $(I N)! permutations will be generated. Otherwise, if there are
+ * some duplicated elements, fewer permutations will be produced.
+----
+// Enumerate all permutations
+int[] a = [1,2,3,4,5];
+do
+{
+ // use the current permutation and
+ // 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)
+{
+ 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;
+
+ auto i = retro(range);
+ auto last = i.save;
+
+ // Find last occurring increasing pair of elements
+ size_t n = 1;
+ for (i.popFront(); !i.empty; i.popFront(), last.popFront(), n++)
+ {
+ if (binaryFun!less(i.front, last.front))
+ break;
+ }
+
+ if (i.empty)
+ {
+ // Entire range is decreasing: it's lexicographically the greatest. So
+ // wrap it around.
+ range.reverse();
+ return false;
+ }
+
+ // Find last element greater than i.front.
+ auto j = find!((a) => binaryFun!less(i.front, a))(
+ takeExactly(retro(range), n));
+
+ assert(!j.empty); // shouldn't happen since i.front < last.front
+ swap(i.front, j.front);
+ reverse(takeExactly(retro(range), n));
+
+ return true;
+}
+
+///
+@safe unittest
+{
+ // Step through all permutations of a sorted array in lexicographic order
+ int[] a = [1,2,3];
+ assert(nextPermutation(a) == true);
+ assert(a == [1,3,2]);
+ assert(nextPermutation(a) == true);
+ assert(a == [2,1,3]);
+ assert(nextPermutation(a) == true);
+ assert(a == [2,3,1]);
+ assert(nextPermutation(a) == true);
+ assert(a == [3,1,2]);
+ assert(nextPermutation(a) == true);
+ assert(a == [3,2,1]);
+ assert(nextPermutation(a) == false);
+ assert(a == [1,2,3]);
+}
+
+///
+@safe unittest
+{
+ // Step through permutations of an array containing duplicate elements:
+ int[] a = [1,1,2];
+ assert(nextPermutation(a) == true);
+ assert(a == [1,2,1]);
+ assert(nextPermutation(a) == true);
+ assert(a == [2,1,1]);
+ assert(nextPermutation(a) == false);
+ assert(a == [1,1,2]);
+}
+
+@safe unittest
+{
+ // Boundary cases: arrays of 0 or 1 element.
+ int[] a1 = [];
+ assert(!nextPermutation(a1));
+ assert(a1 == []);
+
+ int[] a2 = [1];
+ assert(!nextPermutation(a2));
+ assert(a2 == [1]);
+}
+
+@safe unittest
+{
+ import std.algorithm.comparison : equal;
+
+ auto a1 = [1, 2, 3, 4];
+
+ assert(nextPermutation(a1));
+ assert(equal(a1, [1, 2, 4, 3]));
+
+ assert(nextPermutation(a1));
+ assert(equal(a1, [1, 3, 2, 4]));
+
+ assert(nextPermutation(a1));
+ assert(equal(a1, [1, 3, 4, 2]));
+
+ assert(nextPermutation(a1));
+ assert(equal(a1, [1, 4, 2, 3]));
+
+ assert(nextPermutation(a1));
+ assert(equal(a1, [1, 4, 3, 2]));
+
+ assert(nextPermutation(a1));
+ assert(equal(a1, [2, 1, 3, 4]));
+
+ assert(nextPermutation(a1));
+ assert(equal(a1, [2, 1, 4, 3]));
+
+ assert(nextPermutation(a1));
+ assert(equal(a1, [2, 3, 1, 4]));
+
+ assert(nextPermutation(a1));
+ assert(equal(a1, [2, 3, 4, 1]));
+
+ assert(nextPermutation(a1));
+ assert(equal(a1, [2, 4, 1, 3]));
+
+ assert(nextPermutation(a1));
+ assert(equal(a1, [2, 4, 3, 1]));
+
+ assert(nextPermutation(a1));
+ assert(equal(a1, [3, 1, 2, 4]));
+
+ assert(nextPermutation(a1));
+ assert(equal(a1, [3, 1, 4, 2]));
+
+ assert(nextPermutation(a1));
+ assert(equal(a1, [3, 2, 1, 4]));
+
+ assert(nextPermutation(a1));
+ assert(equal(a1, [3, 2, 4, 1]));
+
+ assert(nextPermutation(a1));
+ assert(equal(a1, [3, 4, 1, 2]));
+
+ assert(nextPermutation(a1));
+ assert(equal(a1, [3, 4, 2, 1]));
+
+ assert(nextPermutation(a1));
+ assert(equal(a1, [4, 1, 2, 3]));
+
+ assert(nextPermutation(a1));
+ assert(equal(a1, [4, 1, 3, 2]));
+
+ assert(nextPermutation(a1));
+ assert(equal(a1, [4, 2, 1, 3]));
+
+ assert(nextPermutation(a1));
+ assert(equal(a1, [4, 2, 3, 1]));
+
+ assert(nextPermutation(a1));
+ assert(equal(a1, [4, 3, 1, 2]));
+
+ assert(nextPermutation(a1));
+ assert(equal(a1, [4, 3, 2, 1]));
+
+ assert(!nextPermutation(a1));
+ assert(equal(a1, [1, 2, 3, 4]));
+}
+
+@safe unittest
+{
+ // Test with non-default sorting order
+ int[] a = [3,2,1];
+ assert(nextPermutation!"a > b"(a) == true);
+ assert(a == [3,1,2]);
+ assert(nextPermutation!"a > b"(a) == true);
+ assert(a == [2,3,1]);
+ assert(nextPermutation!"a > b"(a) == true);
+ assert(a == [2,1,3]);
+ assert(nextPermutation!"a > b"(a) == true);
+ assert(a == [1,3,2]);
+ assert(nextPermutation!"a > b"(a) == true);
+ assert(a == [1,2,3]);
+ assert(nextPermutation!"a > b"(a) == false);
+ assert(a == [3,2,1]);
+}
+
+// Issue 13594
+@safe unittest
+{
+ int[3] a = [1,2,3];
+ assert(nextPermutation(a[]));
+ assert(a == [1,3,2]);
+}
+
+// nextEvenPermutation
+/**
+ * Permutes $(D range) in-place to the next lexicographically greater $(I even)
+ * permutation.
+ *
+ * The predicate $(D less) defines the lexicographical ordering to be used on
+ * the range.
+ *
+ * An even permutation is one which is produced by swapping an even number of
+ * pairs of elements in the original range. The set of $(I even) permutations
+ * is distinct from the set of $(I all) permutations only when there are no
+ * duplicate elements in the range. If the range has $(I N) unique elements,
+ * then there are exactly $(I N)!/2 even permutations.
+ *
+ * If the range is already the lexicographically greatest even permutation, it
+ * is permuted back to the least even permutation and false is returned.
+ * Otherwise, true is returned, and the range is modified in-place to be the
+ * lexicographically next even permutation.
+ *
+ * One can thus generate the even permutations of a range with unique elements
+ * by starting with the lexicographically smallest permutation, and repeatedly
+ * calling nextEvenPermutation until it returns false.
+----
+// Enumerate even permutations
+int[] a = [1,2,3,4,5];
+do
+{
+ // use the current permutation and
+ // proceed to the next even permutation of the array.
+} while (nextEvenPermutation(a));
+----
+ * One can also generate the $(I odd) permutations of a range by noting that
+ * permutations obey the rule that even + even = even, and odd + even = odd.
+ * Thus, by swapping the last two elements of a lexicographically least range,
+ * it is turned into the first odd permutation. Then calling
+ * nextEvenPermutation on this first odd permutation will generate the next
+ * even permutation relative to this odd permutation, which is actually the
+ * next odd permutation of the original range. Thus, by repeatedly calling
+ * nextEvenPermutation until it returns false, one enumerates the odd
+ * permutations of the original range.
+----
+// Enumerate odd permutations
+int[] a = [1,2,3,4,5];
+swap(a[$-2], a[$-1]); // a is now the first odd permutation of [1,2,3,4,5]
+do
+{
+ // use the current permutation and
+ // proceed to the next odd permutation of the original array
+ // (which is an even permutation of the first odd permutation).
+} while (nextEvenPermutation(a));
+----
+ *
+ * Warning: Since even permutations are only distinct from all permutations
+ * when the range elements are unique, this function assumes that there are no
+ * duplicate elements under the specified ordering. If this is not _true, some
+ * 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)
+{
+ 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;
+
+ bool oddParity = false;
+ bool ret = true;
+ do
+ {
+ auto i = retro(range);
+ auto last = i.save;
+
+ // Find last occurring increasing pair of elements
+ size_t n = 1;
+ for (i.popFront(); !i.empty;
+ i.popFront(), last.popFront(), n++)
+ {
+ if (binaryFun!less(i.front, last.front))
+ break;
+ }
+
+ if (!i.empty)
+ {
+ // Find last element greater than i.front.
+ auto j = find!((a) => binaryFun!less(i.front, a))(
+ takeExactly(retro(range), n));
+
+ // shouldn't happen since i.front < last.front
+ assert(!j.empty);
+
+ swap(i.front, j.front);
+ oddParity = !oddParity;
+ }
+ else
+ {
+ // Entire range is decreasing: it's lexicographically
+ // the greatest.
+ ret = false;
+ }
+
+ reverse(takeExactly(retro(range), n));
+ if ((n / 2) % 2 == 1)
+ oddParity = !oddParity;
+ } while (oddParity);
+
+ return ret;
+}
+
+///
+@safe unittest
+{
+ // Step through even permutations of a sorted array in lexicographic order
+ int[] a = [1,2,3];
+ assert(nextEvenPermutation(a) == true);
+ assert(a == [2,3,1]);
+ assert(nextEvenPermutation(a) == true);
+ assert(a == [3,1,2]);
+ assert(nextEvenPermutation(a) == false);
+ assert(a == [1,2,3]);
+}
+
+@safe unittest
+{
+ auto a3 = [ 1, 2, 3, 4 ];
+ int count = 1;
+ while (nextEvenPermutation(a3)) count++;
+ assert(count == 12);
+}
+
+@safe unittest
+{
+ // Test with non-default sorting order
+ auto a = [ 3, 2, 1 ];
+
+ assert(nextEvenPermutation!"a > b"(a) == true);
+ assert(a == [ 2, 1, 3 ]);
+ assert(nextEvenPermutation!"a > b"(a) == true);
+ assert(a == [ 1, 3, 2 ]);
+ assert(nextEvenPermutation!"a > b"(a) == false);
+ assert(a == [ 3, 2, 1 ]);
+}
+
+@safe unittest
+{
+ // Test various cases of rollover
+ auto a = [ 3, 1, 2 ];
+ assert(nextEvenPermutation(a) == false);
+ assert(a == [ 1, 2, 3 ]);
+
+ auto b = [ 3, 2, 1 ];
+ assert(nextEvenPermutation(b) == false);
+ assert(b == [ 1, 3, 2 ]);
+}
+
+@safe unittest
+{
+ // Issue 13594
+ int[3] a = [1,2,3];
+ assert(nextEvenPermutation(a[]));
+ assert(a == [2,3,1]);
+}
+
+/**
+Even permutations are useful for generating coordinates of certain geometric
+shapes. Here's a non-trivial example:
+*/
+@safe unittest
+{
+ import std.math : sqrt;
+
+ // Print the 60 vertices of a uniform truncated icosahedron (soccer ball)
+ enum real Phi = (1.0 + sqrt(5.0)) / 2.0; // Golden ratio
+ real[][] seeds = [
+ [0.0, 1.0, 3.0*Phi],
+ [1.0, 2.0+Phi, 2.0*Phi],
+ [Phi, 2.0, Phi^^3]
+ ];
+ size_t n;
+ foreach (seed; seeds)
+ {
+ // Loop over even permutations of each seed
+ do
+ {
+ // Loop over all sign changes of each permutation
+ size_t i;
+ do
+ {
+ // Generate all possible sign changes
+ for (i=0; i < seed.length; i++)
+ {
+ if (seed[i] != 0.0)
+ {
+ seed[i] = -seed[i];
+ if (seed[i] < 0.0)
+ break;
+ }
+ }
+ n++;
+ } while (i < seed.length);
+ } while (nextEvenPermutation(seed));
+ }
+ assert(n == 60);
+}
diff --git a/libphobos/src/std/array.d b/libphobos/src/std/array.d
new file mode 100644
index 0000000..13677a3
--- /dev/null
+++ b/libphobos/src/std/array.d
@@ -0,0 +1,3775 @@
+// Written in the D programming language.
+/**
+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 $(LREF _array))
+ $(TD Returns a copy of the input in a newly allocated dynamic _array.
+ ))
+ $(TR $(TD $(LREF appender))
+ $(TD Returns a new $(LREF Appender) or $(LREF RefAppender) initialized with a given _array.
+ ))
+ $(TR $(TD $(LREF assocArray))
+ $(TD Returns a newly allocated associative _array from a range of key/value tuples.
+ ))
+ $(TR $(TD $(LREF byPair))
+ $(TD Construct a range iterating over an associative _array by key/value tuples.
+ ))
+ $(TR $(TD $(LREF insertInPlace))
+ $(TD Inserts into an existing _array at a given position.
+ ))
+ $(TR $(TD $(LREF join))
+ $(TD Concatenates a range of ranges into one _array.
+ ))
+ $(TR $(TD $(LREF minimallyInitializedArray))
+ $(TD Returns a new _array of type $(D T).
+ ))
+ $(TR $(TD $(LREF replace))
+ $(TD Returns a new _array with all occurrences of a certain subrange replaced.
+ ))
+ $(TR $(TD $(LREF replaceFirst))
+ $(TD Returns a new _array with the first occurrence of a certain subrange replaced.
+ ))
+ $(TR $(TD $(LREF replaceInPlace))
+ $(TD Replaces all occurrences of a certain subrange and puts the result into a given _array.
+ ))
+ $(TR $(TD $(LREF replaceInto))
+ $(TD Replaces all occurrences of a certain subrange and puts the result into an output range.
+ ))
+ $(TR $(TD $(LREF replaceLast))
+ $(TD Returns a new _array with the last occurrence of a certain subrange replaced.
+ ))
+ $(TR $(TD $(LREF replaceSlice))
+ $(TD Returns a new _array with a given slice replaced.
+ ))
+ $(TR $(TD $(LREF replicate))
+ $(TD Creates a new _array out of several copies of an input _array or range.
+ ))
+ $(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 $(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: $(HTTP boost.org/LICENSE_1_0.txt, Boost License 1.0).
+
+Authors: $(HTTP erdani.org, Andrei Alexandrescu) and Jonathan M Davis
+
+Source: $(PHOBOSSRC std/_array.d)
+*/
+module std.array;
+
+static import std.algorithm.iteration; // FIXME, remove with alias of splitter
+import std.functional;
+import std.meta;
+import std.traits;
+
+import std.range.primitives;
+public import std.range.primitives : save, empty, popFront, popBack, front, back;
+
+/**
+ * Allocates an array and initializes it with copies of the elements
+ * of range $(D r).
+ *
+ * Narrow strings are handled as a special case in an overload.
+ *
+ * Params:
+ * r = range (or aggregate with $(D opApply) function) whose elements are copied into the allocated array
+ * Returns:
+ * allocated and initialized array
+ */
+ForeachType!Range[] array(Range)(Range r)
+if (isIterable!Range && !isNarrowString!Range && !isInfinite!Range)
+{
+ if (__ctfe)
+ {
+ // Compile-time version to avoid memcpy calls.
+ // Also used to infer attributes of array().
+ typeof(return) result;
+ foreach (e; r)
+ result ~= e;
+ return result;
+ }
+
+ alias E = ForeachType!Range;
+ static if (hasLength!Range)
+ {
+ auto length = r.length;
+ if (length == 0)
+ return null;
+
+ import std.conv : emplaceRef;
+
+ auto result = (() @trusted => uninitializedArray!(Unqual!E[])(length))();
+
+ // Every element of the uninitialized array must be initialized
+ size_t i;
+ foreach (e; r)
+ {
+ emplaceRef!E(result[i], e);
+ ++i;
+ }
+ return (() @trusted => cast(E[]) result)();
+ }
+ else
+ {
+ auto a = appender!(E[])();
+ foreach (e; r)
+ {
+ a.put(e);
+ }
+ return a.data;
+ }
+}
+
+///
+@safe pure nothrow unittest
+{
+ auto a = array([1, 2, 3, 4, 5][]);
+ assert(a == [ 1, 2, 3, 4, 5 ]);
+}
+
+@safe pure nothrow unittest
+{
+ import std.algorithm.comparison : equal;
+ struct Foo
+ {
+ int a;
+ }
+ auto a = array([Foo(1), Foo(2), Foo(3), Foo(4), Foo(5)][]);
+ assert(equal(a, [Foo(1), Foo(2), Foo(3), Foo(4), Foo(5)]));
+}
+
+@system unittest
+{
+ import std.algorithm.comparison : equal;
+ struct Foo
+ {
+ int a;
+ void opAssign(Foo foo)
+ {
+ assert(0);
+ }
+ auto opEquals(Foo foo)
+ {
+ return a == foo.a;
+ }
+ }
+ auto a = array([Foo(1), Foo(2), Foo(3), Foo(4), Foo(5)][]);
+ assert(equal(a, [Foo(1), Foo(2), Foo(3), Foo(4), Foo(5)]));
+}
+
+@safe unittest
+{
+ // Issue 12315
+ static struct Bug12315 { immutable int i; }
+ enum bug12315 = [Bug12315(123456789)].array();
+ static assert(bug12315[0].i == 123456789);
+}
+
+@safe unittest
+{
+ import std.range;
+ static struct S{int* p;}
+ auto a = array(immutable(S).init.repeat(5));
+}
+
+/**
+Convert a narrow string to an array type that fully supports random access.
+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.
+*/
+@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;
+}
+
+///
+@safe unittest
+{
+ import std.range.primitives : isRandomAccessRange;
+
+ assert("Hello D".array == "Hello D"d);
+ static assert(isRandomAccessRange!string == false);
+
+ assert("Hello D"w.array == "Hello D"d);
+ static assert(isRandomAccessRange!dstring == true);
+}
+
+@system unittest
+{
+ // @system due to array!string
+ import std.conv : to;
+
+ static struct TestArray { int x; string toString() @safe { return to!string(x); } }
+
+ static struct OpAssign
+ {
+ uint num;
+ this(uint num) { this.num = num; }
+
+ // Templating opAssign to make sure the bugs with opAssign being
+ // templated are fixed.
+ void opAssign(T)(T rhs) { this.num = rhs.num; }
+ }
+
+ static struct OpApply
+ {
+ int opApply(scope int delegate(ref int) dg)
+ {
+ int res;
+ foreach (i; 0 .. 10)
+ {
+ res = dg(i);
+ if (res) break;
+ }
+
+ return res;
+ }
+ }
+
+ auto a = array([1, 2, 3, 4, 5][]);
+ //writeln(a);
+ assert(a == [ 1, 2, 3, 4, 5 ]);
+
+ auto b = array([TestArray(1), TestArray(2)][]);
+ //writeln(b);
+
+ class C
+ {
+ int x;
+ this(int y) { x = y; }
+ override string toString() const @safe { return to!string(x); }
+ }
+ auto c = array([new C(1), new C(2)][]);
+ //writeln(c);
+
+ auto d = array([1.0, 2.2, 3][]);
+ assert(is(typeof(d) == double[]));
+ //writeln(d);
+
+ auto e = [OpAssign(1), OpAssign(2)];
+ auto f = array(e);
+ assert(e == f);
+
+ assert(array(OpApply.init) == [0,1,2,3,4,5,6,7,8,9]);
+ assert(array("ABC") == "ABC"d);
+ assert(array("ABC".dup) == "ABC"d.dup);
+}
+
+//Bug# 8233
+@safe unittest
+{
+ assert(array("hello world"d) == "hello world"d);
+ immutable a = [1, 2, 3, 4, 5];
+ assert(array(a) == a);
+ const b = a;
+ assert(array(b) == a);
+
+ //To verify that the opAssign branch doesn't get screwed up by using Unqual.
+ //EDIT: array no longer calls opAssign.
+ struct S
+ {
+ ref S opAssign(S)(const ref S rhs)
+ {
+ assert(0);
+ }
+
+ int i;
+ }
+
+ foreach (T; AliasSeq!(S, const S, immutable S))
+ {
+ auto arr = [T(1), T(2), T(3), T(4)];
+ assert(array(arr) == arr);
+ }
+}
+
+@safe unittest
+{
+ //9824
+ static struct S
+ {
+ @disable void opAssign(S);
+ int i;
+ }
+ auto arr = [S(0), S(1), S(2)];
+ arr.array();
+}
+
+// Bugzilla 10220
+@safe unittest
+{
+ import std.algorithm.comparison : equal;
+ import std.exception;
+ import std.range : repeat;
+
+ static struct S
+ {
+ int val;
+
+ @disable this();
+ this(int v) { val = v; }
+ }
+ assertCTFEable!(
+ {
+ auto r = S(1).repeat(2).array();
+ assert(equal(r, [S(1), S(1)]));
+ });
+}
+
+@safe unittest
+{
+ //Turn down infinity:
+ static assert(!is(typeof(
+ repeat(1).array()
+ )));
+}
+
+/**
+Returns a newly allocated associative _array from a range of key/value tuples.
+
+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). 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)
+{
+ import std.typecons : isTuple;
+
+ alias E = ElementType!Range;
+ static assert(isTuple!E, "assocArray: argument must be a range of tuples");
+ static assert(E.length == 2, "assocArray: tuple dimension must be 2");
+ alias KeyType = E.Types[0];
+ alias ValueType = E.Types[1];
+ static assert(isMutable!ValueType, "assocArray: value type must be mutable");
+
+ ValueType[KeyType] aa;
+ foreach (t; r)
+ aa[t[0]] = t[1];
+ return aa;
+}
+
+///
+@safe pure /*nothrow*/ unittest
+{
+ import std.range;
+ import std.typecons;
+ 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"]);
+
+ auto b = assocArray([ tuple("foo", "bar"), tuple("baz", "quux") ]);
+ assert(is(typeof(b) == string[string]));
+ assert(b == ["foo":"bar", "baz":"quux"]);
+}
+
+// @@@11053@@@ - Cannot be version (unittest) - recursive instantiation error
+@safe unittest
+{
+ import std.typecons;
+ static assert(!__traits(compiles, [ tuple("foo", "bar", "baz") ].assocArray()));
+ static assert(!__traits(compiles, [ tuple("foo") ].assocArray()));
+ assert([ tuple("foo", "bar") ].assocArray() == ["foo": "bar"]);
+}
+
+// Issue 13909
+@safe unittest
+{
+ import std.typecons;
+ auto a = [tuple!(const string, string)("foo", "bar")];
+ auto b = [tuple!(string, const string)("foo", "bar")];
+ assert(assocArray(a) == [cast(const(string)) "foo": "bar"]);
+ static assert(!__traits(compiles, assocArray(b)));
+}
+
+/**
+Construct a range iterating over an associative array by key/value tuples.
+
+Params: aa = The associative array to iterate over.
+
+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(AA : Value[Key], Value, Key)(AA aa)
+{
+ import std.algorithm.iteration : map;
+ import std.typecons : tuple;
+
+ return aa.byKeyValue.map!(pair => tuple(pair.key, pair.value));
+}
+
+///
+@system unittest
+{
+ import std.algorithm.sorting : sort;
+ import std.typecons : tuple, Tuple;
+
+ auto aa = ["a": 1, "b": 2, "c": 3];
+ Tuple!(string, int)[] pairs;
+
+ // Iteration over key/value pairs.
+ foreach (pair; aa.byPair)
+ {
+ pairs ~= pair;
+ }
+
+ // Iteration order is implementation-dependent, so we should sort it to get
+ // a fixed order.
+ sort(pairs);
+ assert(pairs == [
+ tuple("a", 1),
+ tuple("b", 2),
+ tuple("c", 3)
+ ]);
+}
+
+@system unittest
+{
+ import std.typecons : tuple, Tuple;
+
+ auto aa = ["a":2];
+ auto pairs = aa.byPair();
+
+ static assert(is(typeof(pairs.front) == Tuple!(string,int)));
+ static assert(isForwardRange!(typeof(pairs)));
+
+ assert(!pairs.empty);
+ assert(pairs.front == tuple("a", 2));
+
+ auto savedPairs = pairs.save;
+
+ pairs.popFront();
+ assert(pairs.empty);
+ assert(!savedPairs.empty);
+ assert(savedPairs.front == tuple("a", 2));
+}
+
+// Issue 17711
+@system unittest
+{
+ const(int[string]) aa = [ "abc": 123 ];
+
+ // Ensure that byKeyValue is usable with a const AA.
+ auto kv = aa.byKeyValue;
+ assert(!kv.empty);
+ assert(kv.front.key == "abc" && kv.front.value == 123);
+ kv.popFront();
+ assert(kv.empty);
+
+ // Ensure byPair is instantiable with const AA.
+ auto r = aa.byPair;
+ static assert(isInputRange!(typeof(r)));
+ assert(!r.empty && r.front[0] == "abc" && r.front[1] == 123);
+ r.popFront();
+ assert(r.empty);
+}
+
+private template blockAttribute(T)
+{
+ import core.memory;
+ static if (hasIndirections!(T) || is(T == void))
+ {
+ enum blockAttribute = 0;
+ }
+ else
+ {
+ enum blockAttribute = GC.BlkAttr.NO_SCAN;
+ }
+}
+version (unittest)
+{
+ import core.memory : UGC = GC;
+ static assert(!(blockAttribute!void & UGC.BlkAttr.NO_SCAN));
+}
+
+// Returns the number of dimensions in an array T.
+private template nDimensions(T)
+{
+ static if (isArray!T)
+ {
+ enum nDimensions = 1 + nDimensions!(typeof(T.init[0]));
+ }
+ else
+ {
+ enum nDimensions = 0;
+ }
+}
+
+version (unittest)
+{
+ static assert(nDimensions!(uint[]) == 1);
+ static assert(nDimensions!(float[][]) == 2);
+}
+
+/++
+Returns a new array of type $(D T) allocated on the garbage collected heap
+without initializing its elements. This can be a useful optimization if every
+element will be immediately initialized. $(D T) may be a multidimensional
+array. In this case sizes may be specified for any number of dimensions from 0
+to the number in $(D T).
+
+uninitializedArray is nothrow and weakly pure.
+
+uninitializedArray is @system if the uninitialized element type has pointers.
++/
+auto uninitializedArray(T, I...)(I sizes) nothrow @system
+if (isDynamicArray!T && allSatisfy!(isIntegral, I) && hasIndirections!(ElementEncodingType!T))
+{
+ enum isSize_t(E) = is (E : size_t);
+ alias toSize_t(E) = size_t;
+
+ static assert(allSatisfy!(isSize_t, I),
+ "Argument types in "~I.stringof~" are not all convertible to size_t: "
+ ~Filter!(templateNot!(isSize_t), I).stringof);
+
+ //Eagerlly transform non-size_t into size_t to avoid template bloat
+ alias ST = staticMap!(toSize_t, I);
+
+ return arrayAllocImpl!(false, T, ST)(sizes);
+}
+
+/// ditto
+auto uninitializedArray(T, I...)(I sizes) nothrow @trusted
+if (isDynamicArray!T && allSatisfy!(isIntegral, I) && !hasIndirections!(ElementEncodingType!T))
+{
+ enum isSize_t(E) = is (E : size_t);
+ alias toSize_t(E) = size_t;
+
+ static assert(allSatisfy!(isSize_t, I),
+ "Argument types in "~I.stringof~" are not all convertible to size_t: "
+ ~Filter!(templateNot!(isSize_t), I).stringof);
+
+ //Eagerlly transform non-size_t into size_t to avoid template bloat
+ alias ST = staticMap!(toSize_t, I);
+
+ return arrayAllocImpl!(false, T, ST)(sizes);
+}
+///
+@system nothrow pure unittest
+{
+ double[] arr = uninitializedArray!(double[])(100);
+ assert(arr.length == 100);
+
+ double[][] matrix = uninitializedArray!(double[][])(42, 31);
+ assert(matrix.length == 42);
+ assert(matrix[0].length == 31);
+
+ char*[] ptrs = uninitializedArray!(char*[])(100);
+ assert(ptrs.length == 100);
+}
+
+/++
+Returns a new array of type $(D T) allocated on the garbage collected heap.
+
+Partial initialization is done for types with indirections, for preservation
+of memory safety. Note that elements will only be initialized to 0, but not
+necessarily the element type's $(D .init).
+
+minimallyInitializedArray is nothrow and weakly pure.
++/
+auto minimallyInitializedArray(T, I...)(I sizes) nothrow @trusted
+if (isDynamicArray!T && allSatisfy!(isIntegral, I))
+{
+ enum isSize_t(E) = is (E : size_t);
+ alias toSize_t(E) = size_t;
+
+ static assert(allSatisfy!(isSize_t, I),
+ "Argument types in "~I.stringof~" are not all convertible to size_t: "
+ ~Filter!(templateNot!(isSize_t), I).stringof);
+ //Eagerlly transform non-size_t into size_t to avoid template bloat
+ alias ST = staticMap!(toSize_t, I);
+
+ return arrayAllocImpl!(true, T, ST)(sizes);
+}
+
+///
+@safe pure nothrow unittest
+{
+ import std.algorithm.comparison : equal;
+ import std.range : repeat;
+
+ auto arr = minimallyInitializedArray!(int[])(42);
+ assert(arr.length == 42);
+ // Elements aren't necessarily initialized to 0
+ assert(!arr.equal(0.repeat(42)));
+}
+
+@safe pure nothrow unittest
+{
+ 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)
+ {
+ assert(elem.ptr is null);
+ }
+}
+
+private auto arrayAllocImpl(bool minimallyInitialized, T, I...)(I sizes) nothrow
+{
+ static assert(I.length <= nDimensions!T,
+ I.length.stringof~"dimensions specified for a "~nDimensions!T.stringof~" dimensional array.");
+
+ alias E = ElementEncodingType!T;
+
+ E[] ret;
+
+ static if (I.length != 0)
+ {
+ static assert(is(I[0] == size_t));
+ alias size = sizes[0];
+ }
+
+ static if (I.length == 1)
+ {
+ if (__ctfe)
+ {
+ static if (__traits(compiles, new E[](size)))
+ ret = new E[](size);
+ else static if (__traits(compiles, ret ~= E.init))
+ {
+ try
+ {
+ //Issue: if E has an impure postblit, then all of arrayAllocImpl
+ //Will be impure, even during non CTFE.
+ foreach (i; 0 .. size)
+ ret ~= E.init;
+ }
+ catch (Exception e)
+ throw new Error(e.msg);
+ }
+ else
+ assert(0, "No postblit nor default init on " ~ E.stringof ~
+ ": At least one is required for CTFE.");
+ }
+ else
+ {
+ import core.memory : GC;
+ import core.stdc.string : memset;
+
+ 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, nbytes);
+ ret = ptr[0 .. size];
+ }
+ }
+ else static if (I.length > 1)
+ {
+ ret = arrayAllocImpl!(false, E[])(size);
+ foreach (ref elem; ret)
+ elem = arrayAllocImpl!(minimallyInitialized, E)(sizes[1..$]);
+ }
+
+ return ret;
+}
+
+@safe nothrow pure unittest
+{
+ auto s1 = uninitializedArray!(int[])();
+ auto s2 = minimallyInitializedArray!(int[])();
+ assert(s1.length == 0);
+ assert(s2.length == 0);
+}
+
+@safe nothrow pure unittest //@@@9803@@@
+{
+ auto a = minimallyInitializedArray!(int*[])(1);
+ assert(a[0] == null);
+ auto b = minimallyInitializedArray!(int[][])(1);
+ assert(b[0].empty);
+ auto c = minimallyInitializedArray!(int*[][])(1, 1);
+ assert(c[0][0] == null);
+}
+
+@safe unittest //@@@10637@@@
+{
+ static struct S
+ {
+ static struct I{int i; alias i this;}
+ int* p;
+ this() @disable;
+ this(int i)
+ {
+ p = &(new I(i)).i;
+ }
+ this(this)
+ {
+ p = &(new I(*p)).i;
+ }
+ ~this()
+ {
+ assert(p != null);
+ }
+ }
+ auto a = minimallyInitializedArray!(S[])(1);
+ assert(a[0].p == null);
+ enum b = minimallyInitializedArray!(S[])(1);
+}
+
+@safe nothrow unittest
+{
+ static struct S1
+ {
+ this() @disable;
+ this(this) @disable;
+ }
+ auto a1 = minimallyInitializedArray!(S1[][])(2, 2);
+ //enum b1 = minimallyInitializedArray!(S1[][])(2, 2);
+ static struct S2
+ {
+ this() @disable;
+ //this(this) @disable;
+ }
+ auto a2 = minimallyInitializedArray!(S2[][])(2, 2);
+ enum b2 = minimallyInitializedArray!(S2[][])(2, 2);
+ static struct S3
+ {
+ //this() @disable;
+ this(this) @disable;
+ }
+ auto a3 = minimallyInitializedArray!(S3[][])(2, 2);
+ enum b3 = minimallyInitializedArray!(S3[][])(2, 2);
+}
+
+// overlap
+/*
+NOTE: Undocumented for now, overlap does not yet work with ctfe.
+Returns the overlapping portion, if any, of two arrays. Unlike $(D
+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.
+*/
+auto overlap(T, U)(T[] r1, U[] r2) @trusted pure nothrow
+if (is(typeof(r1.ptr < r2.ptr) == bool))
+{
+ 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;
+}
+
+///
+@safe pure /*nothrow*/ unittest
+{
+ int[] a = [ 10, 11, 12, 13, 14 ];
+ int[] b = a[1 .. 3];
+ assert(overlap(a, b) == [ 11, 12 ]);
+ b = b.dup;
+ // overlap disappears even though the content is the same
+ assert(overlap(a, b).empty);
+}
+
+@safe /*nothrow*/ unittest
+{
+ static void test(L, R)(L l, R r)
+ {
+ import std.stdio;
+ scope(failure) writeln("Types: L %s R %s", L.stringof, R.stringof);
+
+ assert(overlap(l, r) == [ 100, 12 ]);
+
+ assert(overlap(l, l[0 .. 2]) is l[0 .. 2]);
+ assert(overlap(l, l[3 .. 5]) is l[3 .. 5]);
+ assert(overlap(l[0 .. 2], l) is l[0 .. 2]);
+ assert(overlap(l[3 .. 5], l) is l[3 .. 5]);
+ }
+
+ int[] a = [ 10, 11, 12, 13, 14 ];
+ int[] b = a[1 .. 3];
+ a[1] = 100;
+
+ immutable int[] c = a.idup;
+ immutable int[] d = c[1 .. 3];
+
+ test(a, b);
+ assert(overlap(a, b.dup).empty);
+ test(c, d);
+ assert(overlap(c, d.idup).empty);
+}
+
+@safe pure nothrow unittest // bugzilla 9836
+{
+ // range primitives for array should work with alias this types
+ struct Wrapper
+ {
+ int[] data;
+ alias data this;
+
+ @property Wrapper save() { return this; }
+ }
+ auto w = Wrapper([1,2,3,4]);
+ std.array.popFront(w); // should work
+
+ static assert(isInputRange!Wrapper);
+ static assert(isForwardRange!Wrapper);
+ static assert(isBidirectionalRange!Wrapper);
+ static assert(isRandomAccessRange!Wrapper);
+}
+
+private void copyBackwards(T)(T[] src, T[] dest)
+{
+ import core.stdc.string : memmove;
+
+ assert(src.length == dest.length);
+
+ if (!__ctfe || hasElaborateCopyConstructor!T)
+ {
+ /* insertInPlace relies on dest being uninitialized, so no postblits allowed,
+ * as this is a MOVE that overwrites the destination, not a COPY.
+ * BUG: insertInPlace will not work with ctfe and postblits
+ */
+ memmove(dest.ptr, src.ptr, src.length * T.sizeof);
+ }
+ else
+ {
+ immutable len = src.length;
+ for (size_t i = len; i-- > 0;)
+ {
+ dest[i] = src[i];
+ }
+ }
+}
+
+/++
+ 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)
+{
+ static if (allSatisfy!(isInputRangeWithLengthOrConvertible!T, U))
+ {
+ import std.conv : emplaceRef;
+
+ immutable oldLen = array.length;
+
+ size_t to_insert = 0;
+ foreach (i, E; U)
+ {
+ static if (is(E : T)) //a single convertible value, not a range
+ to_insert += 1;
+ else
+ to_insert += stuff[i].length;
+ }
+ if (to_insert)
+ {
+ array.length += to_insert;
+
+ // Takes arguments array, pos, stuff
+ // Spread apart array[] at pos by moving elements
+ (() @trusted { copyBackwards(array[pos .. oldLen], array[pos+to_insert..$]); })();
+
+ // Initialize array[pos .. pos+to_insert] with stuff[]
+ auto j = 0;
+ foreach (i, E; U)
+ {
+ static if (is(E : T))
+ {
+ emplaceRef!T(array[pos + j++], stuff[i]);
+ }
+ else
+ {
+ foreach (v; stuff[i])
+ {
+ emplaceRef!T(array[pos + j++], v);
+ }
+ }
+ }
+ }
+ }
+ else
+ {
+ // stuff has some InputRanges in it that don't have length
+ // assume that stuff to be inserted is typically shorter
+ // then the array that can be arbitrary big
+ // TODO: needs a better implementation as there is no need to build an _array_
+ // a singly-linked list of memory blocks (rope, etc.) will do
+ auto app = appender!(T[])();
+ foreach (i, E; U)
+ app.put(stuff[i]);
+ insertInPlace(array, pos, app.data);
+ }
+}
+
+/// Ditto
+void insertInPlace(T, U...)(ref T[] array, size_t pos, U stuff)
+if (isSomeString!(T[]) && allSatisfy!(isCharOrStringOrDcharRange, U))
+{
+ static if (is(Unqual!T == T)
+ && allSatisfy!(isInputRangeWithLengthOrConvertible!dchar, U))
+ {
+ import std.utf : codeLength;
+ // mutable, can do in place
+ //helper function: re-encode dchar to Ts and store at *ptr
+ static T* putDChar(T* ptr, dchar ch)
+ {
+ static if (is(T == dchar))
+ {
+ *ptr++ = ch;
+ return ptr;
+ }
+ else
+ {
+ import std.utf : encode;
+ T[dchar.sizeof/T.sizeof] buf;
+ immutable len = encode(buf, ch);
+ final switch (len)
+ {
+ static if (T.sizeof == char.sizeof)
+ {
+ case 4:
+ ptr[3] = buf[3];
+ goto case;
+ case 3:
+ ptr[2] = buf[2];
+ goto case;
+ }
+ case 2:
+ ptr[1] = buf[1];
+ goto case;
+ case 1:
+ ptr[0] = buf[0];
+ }
+ ptr += len;
+ return ptr;
+ }
+ }
+ size_t to_insert = 0;
+ //count up the number of *codeunits* to insert
+ foreach (i, E; U)
+ to_insert += codeLength!T(stuff[i]);
+ array.length += to_insert;
+
+ @trusted static void moveToRight(T[] arr, size_t gap)
+ {
+ static assert(!hasElaborateCopyConstructor!T);
+ import core.stdc.string : memmove;
+ if (__ctfe)
+ {
+ for (size_t i = arr.length - gap; i; --i)
+ arr[gap + i - 1] = arr[i - 1];
+ }
+ else
+ memmove(arr.ptr + gap, arr.ptr, (arr.length - gap) * T.sizeof);
+ }
+ moveToRight(array[pos .. $], to_insert);
+ auto ptr = array.ptr + pos;
+ foreach (i, E; U)
+ {
+ static if (is(E : dchar))
+ {
+ ptr = putDChar(ptr, stuff[i]);
+ }
+ else
+ {
+ foreach (dchar ch; stuff[i])
+ ptr = putDChar(ptr, ch);
+ }
+ }
+ assert(ptr == array.ptr + pos + to_insert, "(ptr == array.ptr + pos + to_insert) is false");
+ }
+ else
+ {
+ // immutable/const, just construct a new array
+ auto app = appender!(T[])();
+ app.put(array[0 .. pos]);
+ foreach (i, E; U)
+ app.put(stuff[i]);
+ app.put(array[pos..$]);
+ array = app.data;
+ }
+}
+
+///
+@safe pure unittest
+{
+ int[] a = [ 1, 2, 3, 4 ];
+ a.insertInPlace(2, [ 1, 2 ]);
+ assert(a == [ 1, 2, 1, 2, 3, 4 ]);
+ a.insertInPlace(3, 10u, 11);
+ assert(a == [ 1, 2, 1, 10, 11, 2, 3, 4]);
+}
+
+//constraint helpers
+private template isInputRangeWithLengthOrConvertible(E)
+{
+ template isInputRangeWithLengthOrConvertible(R)
+ {
+ //hasLength not defined for char[], wchar[] and dchar[]
+ enum isInputRangeWithLengthOrConvertible =
+ (isInputRange!R && is(typeof(R.init.length))
+ && is(ElementType!R : E)) || is(R : E);
+ }
+}
+
+//ditto
+private template isCharOrStringOrDcharRange(T)
+{
+ enum isCharOrStringOrDcharRange = isSomeString!T || isSomeChar!T ||
+ (isInputRange!T && is(ElementType!T : dchar));
+}
+
+//ditto
+private template isInputRangeOrConvertible(E)
+{
+ template isInputRangeOrConvertible(R)
+ {
+ enum isInputRangeOrConvertible =
+ (isInputRange!R && is(ElementType!R : E)) || is(R : E);
+ }
+}
+
+@system unittest
+{
+ // @system due to insertInPlace
+ import core.exception;
+ import std.algorithm.comparison : equal;
+ import std.algorithm.iteration : filter;
+ import std.conv : to;
+ import std.exception;
+
+
+ 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)))
+ auto a = orig.dup;
+ else
+ auto a = orig.idup;
+
+ a.insertInPlace(pos, toInsert);
+ if (!equal(a, result))
+ return false;
+ }
+
+ static if (isInputRange!U)
+ {
+ orig.insertInPlace(pos, filter!"true"(toInsert));
+ return equal(orig, result);
+ }
+ else
+ return true;
+ }
+
+
+ assert(test([1, 2, 3, 4], 0, [6, 7], [6, 7, 1, 2, 3, 4]));
+ assert(test([1, 2, 3, 4], 2, [8, 9], [1, 2, 8, 9, 3, 4]));
+ assert(test([1, 2, 3, 4], 4, [10, 11], [1, 2, 3, 4, 10, 11]));
+
+ assert(test([1, 2, 3, 4], 0, 22, [22, 1, 2, 3, 4]));
+ 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]));
+
+ void testStr(T, U)(string file = __FILE__, size_t line = __LINE__)
+ {
+
+ auto l = to!T("hello");
+ auto r = to!U(" વિશ્વ");
+
+ enforce(test(l, 0, r, " વિશ્વhello"),
+ new AssertError("testStr failure 1", file, line));
+ enforce(test(l, 3, r, "hel વિશ્વlo"),
+ new AssertError("testStr failure 2", file, line));
+ enforce(test(l, l.length, r, "hello વિશ્વ"),
+ new AssertError("testStr failure 3", file, line));
+ }
+
+ foreach (T; AliasSeq!(char, wchar, dchar,
+ immutable(char), immutable(wchar), immutable(dchar)))
+ {
+ foreach (U; AliasSeq!(char, wchar, dchar,
+ immutable(char), immutable(wchar), immutable(dchar)))
+ {
+ testStr!(T[], U[])();
+ }
+
+ }
+
+ // variadic version
+ bool testVar(T, U...)(T orig, size_t pos, U args)
+ {
+ 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 (!equal(a, result))
+ return false;
+ return true;
+ }
+ assert(testVar([1, 2, 3, 4], 0, 6, 7u, [6, 7, 1, 2, 3, 4]));
+ assert(testVar([1L, 2, 3, 4], 2, 8, 9L, [1, 2, 8, 9, 3, 4]));
+ assert(testVar([1L, 2, 3, 4], 4, 10L, 11, [1, 2, 3, 4, 10, 11]));
+ assert(testVar([1L, 2, 3, 4], 4, [10, 11], 40L, 42L,
+ [1, 2, 3, 4, 10, 11, 40, 42]));
+ assert(testVar([1L, 2, 3, 4], 4, 10, 11, [40L, 42],
+ [1, 2, 3, 4, 10, 11, 40, 42]));
+ assert(testVar("t".idup, 1, 'e', 's', 't', "test"));
+ assert(testVar("!!"w.idup, 1, "\u00e9ll\u00f4", 'x', "TTT"w, 'y',
+ "!\u00e9ll\u00f4xTTTy!"));
+ assert(testVar("flipflop"d.idup, 4, '_',
+ "xyz"w, '\U00010143', '_', "abc"d, "__",
+ "flip_xyz\U00010143_abc__flop"));
+}
+
+@system unittest
+{
+ import std.algorithm.comparison : equal;
+ // insertInPlace interop with postblit
+ static struct Int
+ {
+ int* payload;
+ this(int k)
+ {
+ payload = new int;
+ *payload = k;
+ }
+ this(this)
+ {
+ int* np = new int;
+ *np = *payload;
+ payload = np;
+ }
+ ~this()
+ {
+ if (payload)
+ *payload = 0; //'destroy' it
+ }
+ @property int getPayload(){ return *payload; }
+ alias getPayload this;
+ }
+
+ Int[] arr = [Int(1), Int(4), Int(5)];
+ assert(arr[0] == 1);
+ insertInPlace(arr, 1, Int(2), Int(3));
+ assert(equal(arr, [1, 2, 3, 4, 5])); //check it works with postblit
+
+ version (none) // illustrates that insertInPlace() will not work with CTFE and postblit
+ {
+ static bool testctfe()
+ {
+ Int[] arr = [Int(1), Int(4), Int(5)];
+ assert(arr[0] == 1);
+ insertInPlace(arr, 1, Int(2), Int(3));
+ return equal(arr, [1, 2, 3, 4, 5]); //check it works with postblit
+ }
+ enum E = testctfe();
+ }
+}
+
+@safe unittest
+{
+ import std.exception;
+ assertCTFEable!(
+ {
+ int[] a = [1, 2];
+ a.insertInPlace(2, 3);
+ a.insertInPlace(0, -1, 0);
+ return a == [-1, 0, 1, 2, 3];
+ });
+}
+
+@system unittest // bugzilla 6874
+{
+ import core.memory;
+ // allocate some space
+ byte[] a;
+ a.length = 1;
+
+ // fill it
+ a.length = a.capacity;
+
+ // write beyond
+ byte[] b = a[$ .. $];
+ b.insertInPlace(0, a);
+
+ // make sure that reallocation has happened
+ assert(GC.addrOf(&b[0]) == GC.addrOf(&b[$-1]));
+}
+
+
+/++
+ Returns whether the $(D front)s of $(D lhs) and $(D rhs) both refer to the
+ same place in memory, making one of the arrays a slice of the other which
+ starts at index $(D 0).
+ +/
+@safe
+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
+ same place in memory, making one of the arrays a slice of the other which
+ end at index $(D $).
+ +/
+@trusted
+pure nothrow bool sameTail(T)(in T[] lhs, in T[] rhs)
+{
+ return lhs.ptr + lhs.length == rhs.ptr + rhs.length;
+}
+
+///
+@safe pure nothrow unittest
+{
+ 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;
+ T c = a[1 .. $];
+ T d = a[0 .. 1];
+ T e = null;
+
+ assert(sameHead(a, a));
+ assert(sameHead(a, b));
+ assert(!sameHead(a, c));
+ assert(sameHead(a, d));
+ assert(!sameHead(a, e));
+
+ assert(sameTail(a, a));
+ assert(sameTail(a, b));
+ assert(sameTail(a, c));
+ assert(!sameTail(a, d));
+ assert(!sameTail(a, e));
+
+ //verifies R-value compatibilty
+ assert(a.sameHead(a[0 .. 0]));
+ assert(a.sameTail(a[$ .. $]));
+ }
+}
+
+/**
+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)
+{
+ alias RetType = ElementEncodingType!S[];
+
+ // Optimization for return join(std.range.repeat(s, n));
+ if (n == 0)
+ return RetType.init;
+ if (n == 1)
+ return cast(RetType) s;
+ auto r = new Unqual!(typeof(s[0]))[n * s.length];
+ if (s.length == 1)
+ r[] = s[0];
+ else
+ {
+ immutable len = s.length, nlen = n * len;
+ for (size_t i = 0; i < nlen; i += len)
+ {
+ r[i .. i + len] = s[];
+ }
+ }
+ return r;
+}
+
+/// ditto
+ElementType!S[] replicate(S)(S s, size_t n)
+if (isInputRange!S && !isDynamicArray!S)
+{
+ import std.range : repeat;
+ return join(std.range.repeat(s, n));
+}
+
+
+///
+@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;
+
+ foreach (S; AliasSeq!(string, wstring, dstring, char[], wchar[], dchar[]))
+ {
+ S s;
+ immutable S t = "abc";
+
+ assert(replicate(to!S("1234"), 0) is null);
+ assert(replicate(to!S("1234"), 0) is null);
+ assert(replicate(to!S("1234"), 1) == "1234");
+ assert(replicate(to!S("1234"), 2) == "12341234");
+ assert(replicate(to!S("1"), 4) == "1111");
+ assert(replicate(t, 3) == "abcabcabc");
+ assert(replicate(cast(S) null, 4) is null);
+ }
+}
+
+/++
+Eagerly split the string $(D s) into an array of words, using whitespace as
+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
+
+Returns:
+ An array of each word in `s`
+
+See_Also:
+$(REF splitter, std,algorithm,iteration) for a version that splits using any
+separator.
+
+$(REF splitter, std,regex) for a version that splits using a regular
+expression defined separator.
++/
+S[] split(S)(S s) @safe pure
+if (isSomeString!S)
+{
+ size_t istart;
+ bool inword = false;
+ S[] result;
+
+ foreach (i, dchar c ; s)
+ {
+ import std.uni : isWhite;
+ if (isWhite(c))
+ {
+ if (inword)
+ {
+ result ~= s[istart .. i];
+ inword = false;
+ }
+ }
+ else
+ {
+ if (!inword)
+ {
+ istart = i;
+ inword = true;
+ }
+ }
+ }
+ if (inword)
+ result ~= s[istart .. $];
+ return result;
+}
+
+///
+@safe unittest
+{
+ string str = "Hello World!";
+ assert(str.split == ["Hello", "World!"]);
+
+ string str2 = "Hello\t\tWorld\t!";
+ assert(str2.split == ["Hello", "World", "!"]);
+}
+
+/**
+ * `split` allocates memory, so the same effect can be achieved lazily
+ * using $(REF splitter, std,algorithm,iteration).
+ */
+@safe unittest
+{
+ import std.ascii : isWhite;
+ import std.algorithm.comparison : equal;
+
+ string str = "Hello World!";
+ assert(str.splitter!(isWhite).equal(["Hello", "World!"]));
+}
+
+@safe unittest
+{
+ import std.conv : to;
+ import std.format;
+ import std.typecons;
+
+ static auto makeEntry(S)(string l, string[] r)
+ {return tuple(l.to!S(), r.to!(S[])());}
+
+ foreach (S; AliasSeq!(string, wstring, dstring,))
+ {
+ auto entries =
+ [
+ makeEntry!S("", []),
+ makeEntry!S(" ", []),
+ makeEntry!S("hello", ["hello"]),
+ makeEntry!S(" hello ", ["hello"]),
+ makeEntry!S(" h e l l o ", ["h", "e", "l", "l", "o"]),
+ makeEntry!S("peter\t\npaul\rjerry", ["peter", "paul", "jerry"]),
+ makeEntry!S(" \t\npeter paul\tjerry \n", ["peter", "paul", "jerry"]),
+ makeEntry!S("\u2000日\u202F本\u205F語\u3000", ["日", "本", "語"]),
+ makeEntry!S("  哈・郎博尔德}    ___一个", ["哈・郎博尔德}", "___一个"])
+ ];
+ foreach (entry; entries)
+ assert(entry[0].split() == entry[1], format("got: %s, expected: %s.", entry[0].split(), entry[1]));
+ }
+
+ //Just to test that an immutable is split-able
+ immutable string s = " \t\npeter paul\tjerry \n";
+ assert(split(s) == ["peter", "paul", "jerry"]);
+}
+
+@safe unittest //purity, ctfe ...
+{
+ import std.exception;
+ void dg() @safe pure {
+ assert(split("hello world"c) == ["hello"c, "world"c]);
+ assert(split("hello world"w) == ["hello"w, "world"w]);
+ assert(split("hello world"d) == ["hello"d, "world"d]);
+ }
+ dg();
+ assertCTFEable!dg;
+}
+
+///
+@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
+ $(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.
+
+ 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.
+ sep = a value of the same type as the elements of $(D range) or another
+ forward range.
+
+ Returns:
+ An array containing the divided parts of $(D range).
+
+ See_Also:
+ $(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.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)))
+{
+ 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.iteration : splitter;
+ return range.splitter!isTerminator.array;
+}
+
+///
+@safe unittest
+{
+ import std.uni : isWhite;
+ assert("Learning,D,is,fun".split(",") == ["Learning", "D", "is", "fun"]);
+ assert("Learning D is fun".split!isWhite == ["Learning", "D", "is", "fun"]);
+ assert("Learning D is fun".split(" D ") == ["Learning", "is fun"]);
+}
+
+@safe unittest
+{
+ import std.algorithm.comparison : cmp;
+ import std.conv;
+
+ foreach (S; AliasSeq!(string, wstring, dstring,
+ immutable(string), immutable(wstring), immutable(dstring),
+ char[], wchar[], dchar[],
+ const(char)[], const(wchar)[], const(dchar)[],
+ const(char[]), immutable(char[])))
+ {
+ S s = to!S(",peter,paul,jerry,");
+
+ auto words = split(s, ",");
+ assert(words.length == 5, text(words.length));
+ assert(cmp(words[0], "") == 0);
+ assert(cmp(words[1], "peter") == 0);
+ assert(cmp(words[2], "paul") == 0);
+ assert(cmp(words[3], "jerry") == 0);
+ assert(cmp(words[4], "") == 0);
+
+ auto s1 = s[0 .. s.length - 1]; // lop off trailing ','
+ words = split(s1, ",");
+ assert(words.length == 4);
+ assert(cmp(words[3], "jerry") == 0);
+
+ auto s2 = s1[1 .. s1.length]; // lop off leading ','
+ words = split(s2, ",");
+ assert(words.length == 3);
+ assert(cmp(words[0], "peter") == 0);
+
+ auto s3 = to!S(",,peter,,paul,,jerry,,");
+
+ words = split(s3, ",,");
+ assert(words.length == 5);
+ assert(cmp(words[0], "") == 0);
+ assert(cmp(words[1], "peter") == 0);
+ assert(cmp(words[2], "paul") == 0);
+ assert(cmp(words[3], "jerry") == 0);
+ assert(cmp(words[4], "") == 0);
+
+ auto s4 = s3[0 .. s3.length - 2]; // lop off trailing ',,'
+ words = split(s4, ",,");
+ assert(words.length == 4);
+ assert(cmp(words[3], "jerry") == 0);
+
+ auto s5 = s4[2 .. s4.length]; // lop off leading ',,'
+ words = split(s5, ",,");
+ assert(words.length == 3);
+ assert(cmp(words[0], "peter") == 0);
+ }
+}
+
+/++
+ Conservative heuristic to determine if a range can be iterated cheaply.
+ Used by $(D join) in decision to do an extra iteration of the range to
+ compute the resultant length. If iteration is not cheap then precomputing
+ length could be more expensive than using $(D Appender).
+
+ For now, we only assume arrays are cheap to iterate.
+ +/
+private enum bool hasCheapIteration(R) = isArray!R;
+
+/++
+ Eagerly concatenates all of the ranges in `ror` together (with the GC)
+ into one array using `sep` as the separator if present.
+
+ Params:
+ 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 array of elements
+
+ See_Also:
+ For a lazy version, see $(REF joiner, std,algorithm,iteration)
+ +/
+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);
+ alias RoRElem = ElementType!RoR;
+
+ if (ror.empty)
+ return RetType.init;
+
+ // Constraint only requires input range for sep.
+ // This converts sep to an array (forward range) if it isn't one,
+ // and makes sure it has the same string encoding for string types.
+ static if (isSomeString!RetType &&
+ !is(RetTypeElement == Unqual!(ElementEncodingType!R)))
+ {
+ import std.conv : to;
+ auto sepArr = to!RetType(sep);
+ }
+ else static if (!isArray!R)
+ auto sepArr = array(sep);
+ else
+ alias sepArr = sep;
+
+ 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)
+ {
+ length += r.length;
+ ++rorLength;
+ }
+ if (!rorLength)
+ return null;
+ length += (rorLength - 1) * sepArr.length;
+
+ auto result = (() @trusted => uninitializedArray!(RetTypeElement[])(length))();
+ size_t len;
+ foreach (e; ror.front)
+ emplaceRef(result[len++], e);
+ ror.popFront();
+ foreach (r; ror)
+ {
+ foreach (e; sepArr)
+ emplaceRef(result[len++], e);
+ foreach (e; r)
+ emplaceRef(result[len++], e);
+ }
+ assert(len == result.length);
+ return (() @trusted => cast(RetType) result)();
+ }
+ else
+ {
+ auto result = appender!RetType();
+ put(result, ror.front);
+ ror.popFront();
+ for (; !ror.empty; ror.popFront())
+ {
+ put(result, sep);
+ put(result, ror.front);
+ }
+ return result.data;
+ }
+}
+
+@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, scope E sep)
+if (isInputRange!RoR &&
+ isInputRange!(Unqual!(ElementType!RoR)) &&
+ is(E : ElementType!(ElementType!RoR)))
+{
+ alias RetType = typeof(return);
+ alias RetTypeElement = Unqual!(ElementEncodingType!RetType);
+ alias RoRElem = ElementType!RoR;
+
+ if (ror.empty)
+ return RetType.init;
+
+ 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]);
+ }
+ else
+ {
+ import std.conv : emplaceRef;
+ size_t length;
+ size_t rorLength;
+ foreach (r; ror.save)
+ {
+ length += r.length;
+ ++rorLength;
+ }
+ if (!rorLength)
+ return null;
+ length += rorLength - 1;
+ auto result = uninitializedArray!(RetTypeElement[])(length);
+
+
+ size_t len;
+ foreach (e; ror.front)
+ emplaceRef(result[len++], e);
+ ror.popFront();
+ foreach (r; ror)
+ {
+ emplaceRef(result[len++], sep);
+ foreach (e; r)
+ emplaceRef(result[len++], e);
+ }
+ assert(len == result.length);
+ return (() @trusted => cast(RetType) result)();
+ }
+ }
+ else
+ {
+ auto result = appender!RetType();
+ put(result, ror.front);
+ ror.popFront();
+ for (; !ror.empty; ror.popFront())
+ {
+ put(result, sep);
+ put(result, ror.front);
+ }
+ return result.data;
+ }
+}
+
+@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");
+}
+
+/// Ditto
+ElementEncodingType!(ElementType!RoR)[] join(RoR)(RoR ror)
+if (isInputRange!RoR &&
+ isInputRange!(Unqual!(ElementType!RoR)))
+{
+ alias RetType = typeof(return);
+ alias RetTypeElement = Unqual!(ElementEncodingType!RetType);
+ alias RoRElem = ElementType!RoR;
+
+ if (ror.empty)
+ return RetType.init;
+
+ static if (hasCheapIteration!RoR && (hasLength!RoRElem || isNarrowString!RoRElem))
+ {
+ import std.conv : emplaceRef;
+ size_t length;
+ foreach (r; ror.save)
+ length += r.length;
+
+ auto result = (() @trusted => uninitializedArray!(RetTypeElement[])(length))();
+ size_t len;
+ foreach (r; ror)
+ foreach (e; r)
+ emplaceRef(result[len++], e);
+ assert(len == result.length);
+ return (() @trusted => cast(RetType) result)();
+ }
+ else
+ {
+ auto result = appender!RetType();
+ for (; !ror.empty; ror.popFront())
+ put(result, ror.front);
+ return result.data;
+ }
+}
+
+///
+@safe pure nothrow unittest
+{
+ assert(join(["hello", "silly", "world"], " ") == "hello silly world");
+ assert(join(["hello", "silly", "world"]) == "hellosillyworld");
+
+ assert(join([[1, 2, 3], [4, 5]], [72, 73]) == [1, 2, 3, 72, 73, 4, 5]);
+ assert(join([[1, 2, 3], [4, 5]]) == [1, 2, 3, 4, 5]);
+
+ const string[] arr = ["apple", "banana"];
+ assert(arr.join(",") == "apple,banana");
+ assert(arr.join() == "applebanana");
+}
+
+@safe pure unittest
+{
+ import std.conv : to;
+
+ foreach (T; AliasSeq!(string,wstring,dstring))
+ {
+ auto arr2 = "Здравствуй Мир Unicode".to!(T);
+ auto arr = ["Здравствуй", "Мир", "Unicode"].to!(T[]);
+ assert(join(arr) == "ЗдравствуйМирUnicode");
+ foreach (S; AliasSeq!(char,wchar,dchar))
+ {
+ auto jarr = arr.join(to!S(' '));
+ static assert(is(typeof(jarr) == T));
+ assert(jarr == arr2);
+ }
+ foreach (S; AliasSeq!(string,wstring,dstring))
+ {
+ auto jarr = arr.join(to!S(" "));
+ static assert(is(typeof(jarr) == T));
+ assert(jarr == arr2);
+ }
+ }
+
+ foreach (T; AliasSeq!(string,wstring,dstring))
+ {
+ auto arr2 = "Здравствуй\u047CМир\u047CUnicode".to!(T);
+ auto arr = ["Здравствуй", "Мир", "Unicode"].to!(T[]);
+ foreach (S; AliasSeq!(wchar,dchar))
+ {
+ auto jarr = arr.join(to!S('\u047C'));
+ static assert(is(typeof(jarr) == T));
+ assert(jarr == arr2);
+ }
+ }
+
+ const string[] arr = ["apple", "banana"];
+ assert(arr.join(',') == "apple,banana");
+}
+
+@system unittest
+{
+ import std.algorithm;
+ import std.conv : to;
+ import std.range;
+
+ foreach (R; AliasSeq!(string, wstring, dstring))
+ {
+ R word1 = "日本語";
+ R word2 = "paul";
+ R word3 = "jerry";
+ R[] words = [word1, word2, word3];
+
+ auto filteredWord1 = filter!"true"(word1);
+ auto filteredLenWord1 = takeExactly(filteredWord1, word1.walkLength());
+ auto filteredWord2 = filter!"true"(word2);
+ auto filteredLenWord2 = takeExactly(filteredWord2, word2.walkLength());
+ auto filteredWord3 = filter!"true"(word3);
+ auto filteredLenWord3 = takeExactly(filteredWord3, word3.walkLength());
+ auto filteredWordsArr = [filteredWord1, filteredWord2, filteredWord3];
+ auto filteredLenWordsArr = [filteredLenWord1, filteredLenWord2, filteredLenWord3];
+ auto filteredWords = filter!"true"(filteredWordsArr);
+
+ 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");
+ assert(join(filteredWordsArr, to!(ElementType!(S))(',')) == "日本語,paul,jerry");
+ assert(join(filteredWordsArr, to!S(", ")) == "日本語, paul, jerry");
+ assert(join(filteredWordsArr, to!(ElementType!(S))(',')) == "日本語,paul,jerry");
+ assert(join(filteredLenWordsArr, to!S(", ")) == "日本語, paul, jerry");
+ assert(join(filter!"true"(words), to!S(", ")) == "日本語, paul, jerry");
+ assert(join(words, to!S(", ")) == "日本語, paul, jerry");
+
+ assert(join(filteredWords, to!S("")) == "日本語pauljerry");
+ assert(join(filteredWordsArr, to!S("")) == "日本語pauljerry");
+ assert(join(filteredLenWordsArr, to!S("")) == "日本語pauljerry");
+ assert(join(filter!"true"(words), to!S("")) == "日本語pauljerry");
+ assert(join(words, to!S("")) == "日本語pauljerry");
+
+ assert(join(filter!"true"([word1]), to!S(", ")) == "日本語");
+ assert(join([filteredWord1], to!S(", ")) == "日本語");
+ assert(join([filteredLenWord1], to!S(", ")) == "日本語");
+ assert(join(filter!"true"([filteredWord1]), to!S(", ")) == "日本語");
+ assert(join([word1], to!S(", ")) == "日本語");
+
+ assert(join(filteredWords, to!S(word1)) == "日本語日本語paul日本語jerry");
+ assert(join(filteredWordsArr, to!S(word1)) == "日本語日本語paul日本語jerry");
+ assert(join(filteredLenWordsArr, to!S(word1)) == "日本語日本語paul日本語jerry");
+ assert(join(filter!"true"(words), to!S(word1)) == "日本語日本語paul日本語jerry");
+ assert(join(words, to!S(word1)) == "日本語日本語paul日本語jerry");
+
+ auto filterComma = filter!"true"(to!S(", "));
+ assert(join(filteredWords, filterComma) == "日本語, paul, jerry");
+ assert(join(filteredWordsArr, filterComma) == "日本語, paul, jerry");
+ assert(join(filteredLenWordsArr, filterComma) == "日本語, paul, jerry");
+ assert(join(filter!"true"(words), filterComma) == "日本語, paul, jerry");
+ assert(join(words, filterComma) == "日本語, paul, jerry");
+ }();
+
+ assert(join(filteredWords) == "日本語pauljerry");
+ assert(join(filteredWordsArr) == "日本語pauljerry");
+ assert(join(filteredLenWordsArr) == "日本語pauljerry");
+ assert(join(filter!"true"(words)) == "日本語pauljerry");
+ assert(join(words) == "日本語pauljerry");
+
+ assert(join(filteredWords, filter!"true"(", ")) == "日本語, paul, jerry");
+ assert(join(filteredWordsArr, filter!"true"(", ")) == "日本語, paul, jerry");
+ assert(join(filteredLenWordsArr, filter!"true"(", ")) == "日本語, paul, jerry");
+ assert(join(filter!"true"(words), filter!"true"(", ")) == "日本語, paul, jerry");
+ assert(join(words, filter!"true"(", ")) == "日本語, paul, jerry");
+
+ assert(join(filter!"true"(cast(typeof(filteredWordsArr))[]), ", ").empty);
+ assert(join(cast(typeof(filteredWordsArr))[], ", ").empty);
+ assert(join(cast(typeof(filteredLenWordsArr))[], ", ").empty);
+ assert(join(filter!"true"(cast(R[])[]), ", ").empty);
+ assert(join(cast(R[])[], ", ").empty);
+
+ assert(join(filter!"true"(cast(typeof(filteredWordsArr))[])).empty);
+ assert(join(cast(typeof(filteredWordsArr))[]).empty);
+ assert(join(cast(typeof(filteredLenWordsArr))[]).empty);
+
+ assert(join(filter!"true"(cast(R[])[])).empty);
+ assert(join(cast(R[])[]).empty);
+ }
+
+ assert(join([[1, 2], [41, 42]], [5, 6]) == [1, 2, 5, 6, 41, 42]);
+ assert(join([[1, 2], [41, 42]], cast(int[])[]) == [1, 2, 41, 42]);
+ assert(join([[1, 2]], [5, 6]) == [1, 2]);
+ assert(join(cast(int[][])[], [5, 6]).empty);
+
+ assert(join([[1, 2], [41, 42]]) == [1, 2, 41, 42]);
+ assert(join(cast(int[][])[]).empty);
+
+ alias f = filter!"true";
+ assert(join([[1, 2], [41, 42]], [5, 6]) == [1, 2, 5, 6, 41, 42]);
+ assert(join(f([[1, 2], [41, 42]]), [5, 6]) == [1, 2, 5, 6, 41, 42]);
+ assert(join([f([1, 2]), f([41, 42])], [5, 6]) == [1, 2, 5, 6, 41, 42]);
+ assert(join(f([f([1, 2]), f([41, 42])]), [5, 6]) == [1, 2, 5, 6, 41, 42]);
+ assert(join([[1, 2], [41, 42]], f([5, 6])) == [1, 2, 5, 6, 41, 42]);
+ assert(join(f([[1, 2], [41, 42]]), f([5, 6])) == [1, 2, 5, 6, 41, 42]);
+ assert(join([f([1, 2]), f([41, 42])], f([5, 6])) == [1, 2, 5, 6, 41, 42]);
+ assert(join(f([f([1, 2]), f([41, 42])]), f([5, 6])) == [1, 2, 5, 6, 41, 42]);
+}
+
+// Issue 10683
+@safe unittest
+{
+ import std.range : join;
+ import std.typecons : tuple;
+ assert([[tuple(1)]].join == [tuple(1)]);
+ assert([[tuple("x")]].join == [tuple("x")]);
+}
+
+// Issue 13877
+@safe unittest
+{
+ // Test that the range is iterated only once.
+ import std.algorithm.iteration : map;
+ int c = 0;
+ auto j1 = [1, 2, 3].map!(_ => [c++]).join;
+ assert(c == 3);
+ assert(j1 == [0, 1, 2]);
+
+ c = 0;
+ auto j2 = [1, 2, 3].map!(_ => [c++]).join(9);
+ assert(c == 3);
+ assert(j2 == [0, 9, 1, 9, 2]);
+
+ c = 0;
+ auto j3 = [1, 2, 3].map!(_ => [c++]).join([9]);
+ assert(c == 3);
+ assert(j3 == [0, 9, 1, 9, 2]);
+}
+
+
+/++
+ 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 = find(subject, from.save);
+ if (balance.empty)
+ return subject;
+
+ auto app = appender!(E[])();
+ app.put(subject[0 .. subject.length - balance.length]);
+ app.put(to.save);
+ replaceInto(app, balance[from.length .. $], from, to);
+
+ return app.data;
+}
+
+///
+@safe unittest
+{
+ assert("Hello Wörld".replace("o Wö", "o Wo") == "Hello World");
+ assert("Hello Wörld".replace("l", "h") == "Hehho Wörhd");
+}
+
+/// 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);
+ return;
+ }
+ for (;;)
+ {
+ auto balance = find(subject, from.save);
+ if (balance.empty)
+ {
+ sink.put(subject);
+ break;
+ }
+ sink.put(subject[0 .. subject.length - balance.length]);
+ sink.put(to.save);
+ subject = balance[from.length .. $];
+ }
+}
+
+///
+@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.algorithm.comparison : cmp;
+ import std.conv : to;
+
+ foreach (S; AliasSeq!(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");
+ auto into = to!S("silly");
+ S r;
+ int i;
+
+ r = replace(s, from, into);
+ i = cmp(r, "This is a silly silly list");
+ assert(i == 0);
+
+ r = replace(s, to!S(""), into);
+ i = cmp(r, "This is a foo foo list");
+ assert(i == 0);
+
+ assert(replace(r, to!S("won't find this"), to!S("whatever")) is r);
+ }();
+ }
+
+ immutable s = "This is a foo foo list";
+ assert(replace(s, "foo", "silly") == "This is a silly silly list");
+}
+
+@safe unittest
+{
+ import std.algorithm.searching : skipOver;
+ import std.conv : to;
+
+ struct CheckOutput(C)
+ {
+ C[] desired;
+ this(C[] arr){ desired = arr; }
+ void put(C[] part){ assert(skipOver(desired, part)); }
+ }
+ foreach (S; AliasSeq!(string, wstring, dstring, char[], wchar[], dchar[]))
+ {
+ alias Char = ElementEncodingType!S;
+ S s = to!S("yet another dummy text, yet another ...");
+ S from = to!S("yet another");
+ S into = to!S("some");
+ replaceInto(CheckOutput!(Char)(to!S("some dummy text, some ..."))
+ , s, from, into);
+ }
+}
+
+/++
+ 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)))
+{
+ static if (hasLength!Range && is(ElementEncodingType!Range : T))
+ {
+ 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)
+ copy(stuff, retval[from .. from + stuff.length]);
+
+ retval[from + stuff.length .. $] = subject[to .. $];
+ return cast(T[]) retval;
+ }
+ else
+ {
+ auto app = appender!(T[])();
+ app.put(subject[0 .. from]);
+ app.put(stuff);
+ app.put(subject[to .. $]);
+ return app.data;
+ }
+}
+
+///
+@safe unittest
+{
+ auto a = [ 1, 2, 3, 4 ];
+ auto b = a.replace(1, 3, [ 9, 9, 9 ]);
+ assert(a == [ 1, 2, 3, 4 ]);
+ assert(b == [ 1, 9, 9, 9, 4 ]);
+}
+
+@system unittest
+{
+ import core.exception;
+ import std.algorithm.iteration : filter;
+ import std.conv : to;
+ import std.exception;
+
+
+ auto a = [ 1, 2, 3, 4 ];
+ assert(replace(a, 0, 0, [5, 6, 7]) == [5, 6, 7, 1, 2, 3, 4]);
+ assert(replace(a, 0, 2, cast(int[])[]) == [3, 4]);
+ assert(replace(a, 0, 4, [5, 6, 7]) == [5, 6, 7]);
+ assert(replace(a, 0, 2, [5, 6, 7]) == [5, 6, 7, 3, 4]);
+ assert(replace(a, 2, 4, [5, 6, 7]) == [1, 2, 5, 6, 7]);
+
+ assert(replace(a, 0, 0, filter!"true"([5, 6, 7])) == [5, 6, 7, 1, 2, 3, 4]);
+ assert(replace(a, 0, 2, filter!"true"(cast(int[])[])) == [3, 4]);
+ assert(replace(a, 0, 4, filter!"true"([5, 6, 7])) == [5, 6, 7]);
+ assert(replace(a, 0, 2, filter!"true"([5, 6, 7])) == [5, 6, 7, 3, 4]);
+ assert(replace(a, 2, 4, filter!"true"([5, 6, 7])) == [1, 2, 5, 6, 7]);
+ assert(a == [ 1, 2, 3, 4 ]);
+
+ void testStr(T, U)(string file = __FILE__, size_t line = __LINE__)
+ {
+
+ auto l = to!T("hello");
+ auto r = to!U(" world");
+
+ enforce(replace(l, 0, 0, r) == " worldhello",
+ new AssertError("testStr failure 1", file, line));
+ enforce(replace(l, 0, 3, r) == " worldlo",
+ new AssertError("testStr failure 2", file, line));
+ enforce(replace(l, 3, l.length, r) == "hel world",
+ new AssertError("testStr failure 3", file, line));
+ enforce(replace(l, 0, l.length, r) == " world",
+ new AssertError("testStr failure 4", file, line));
+ enforce(replace(l, l.length, l.length, r) == "hello world",
+ new AssertError("testStr failure 5", 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)();
+
+ enum s = "0123456789";
+ enum w = "⁰¹²³⁴⁵⁶⁷⁸⁹"w;
+ enum d = "⁰¹²³⁴⁵⁶⁷⁸⁹"d;
+
+ assert(replace(s, 0, 0, "***") == "***0123456789");
+ assert(replace(s, 10, 10, "***") == "0123456789***");
+ assert(replace(s, 3, 8, "1012") == "012101289");
+ assert(replace(s, 0, 5, "43210") == "4321056789");
+ assert(replace(s, 5, 10, "43210") == "0123443210");
+
+ assert(replace(w, 0, 0, "***"w) == "***⁰¹²³⁴⁵⁶⁷⁸⁹"w);
+ assert(replace(w, 10, 10, "***"w) == "⁰¹²³⁴⁵⁶⁷⁸⁹***"w);
+ assert(replace(w, 3, 8, "¹⁰¹²"w) == "⁰¹²¹⁰¹²⁸⁹"w);
+ assert(replace(w, 0, 5, "⁴³²¹⁰"w) == "⁴³²¹⁰⁵⁶⁷⁸⁹"w);
+ assert(replace(w, 5, 10, "⁴³²¹⁰"w) == "⁰¹²³⁴⁴³²¹⁰"w);
+
+ assert(replace(d, 0, 0, "***"d) == "***⁰¹²³⁴⁵⁶⁷⁸⁹"d);
+ assert(replace(d, 10, 10, "***"d) == "⁰¹²³⁴⁵⁶⁷⁸⁹***"d);
+ assert(replace(d, 3, 8, "¹⁰¹²"d) == "⁰¹²¹⁰¹²⁸⁹"d);
+ assert(replace(d, 0, 5, "⁴³²¹⁰"d) == "⁴³²¹⁰⁵⁶⁷⁸⁹"d);
+ assert(replace(d, 5, 10, "⁴³²¹⁰"d) == "⁰¹²³⁴⁴³²¹⁰"d);
+}
+
+/++
+ 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 (is(typeof(replace(array, from, to, stuff))))
+{
+ static if (isDynamicArray!Range &&
+ is(Unqual!(ElementEncodingType!Range) == T) &&
+ !isNarrowString!(T[]))
+ {
+ // 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
+ {
+ // default implementation, just do what replace does.
+ array = replace(array, from, to, stuff);
+ }
+}
+
+///
+@safe unittest
+{
+ int[] a = [1, 4, 5];
+ replaceInPlace(a, 1u, 2u, [2, 3, 4]);
+ assert(a == [1, 2, 3, 4, 5]);
+ replaceInPlace(a, 1u, 2u, cast(int[])[]);
+ assert(a == [1, 3, 4, 5]);
+ replaceInPlace(a, 1u, 3u, a[2 .. 4]);
+ assert(a == [1, 4, 5, 5]);
+}
+
+@safe unittest
+{
+ // Bug# 12889
+ int[1][] arr = [[0], [1], [2], [3], [4], [5], [6]];
+ int[1][] stuff = [[0], [1]];
+ replaceInPlace(arr, 4, 6, stuff);
+ assert(arr == [[0], [1], [2], [3], [0], [1], [6]]);
+}
+
+@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.algorithm.comparison : equal;
+ import std.conv;
+ 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 core.exception;
+ import std.algorithm.comparison : equal;
+ import std.algorithm.iteration : filter;
+ import std.conv : to;
+ import std.exception;
+
+
+ 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)))
+ auto a = orig.dup;
+ else
+ auto a = orig.idup;
+
+ a.replaceInPlace(from, to, toReplace);
+ if (!equal(a, result))
+ return false;
+ }
+
+ static if (isInputRange!U)
+ {
+ orig.replaceInPlace(from, to, filter!"true"(toReplace));
+ return equal(orig, result);
+ }
+ else
+ return true;
+ }
+
+ assert(test([1, 2, 3, 4], 0, 0, [5, 6, 7], [5, 6, 7, 1, 2, 3, 4]));
+ assert(test([1, 2, 3, 4], 0, 2, cast(int[])[], [3, 4]));
+ assert(test([1, 2, 3, 4], 0, 4, [5, 6, 7], [5, 6, 7]));
+ assert(test([1, 2, 3, 4], 0, 2, [5, 6, 7], [5, 6, 7, 3, 4]));
+ assert(test([1, 2, 3, 4], 2, 4, [5, 6, 7], [1, 2, 5, 6, 7]));
+
+ assert(test([1, 2, 3, 4], 0, 0, filter!"true"([5, 6, 7]), [5, 6, 7, 1, 2, 3, 4]));
+ assert(test([1, 2, 3, 4], 0, 2, filter!"true"(cast(int[])[]), [3, 4]));
+ assert(test([1, 2, 3, 4], 0, 4, filter!"true"([5, 6, 7]), [5, 6, 7]));
+ 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]));
+
+ void testStr(T, U)(string file = __FILE__, size_t line = __LINE__)
+ {
+
+ auto l = to!T("hello");
+ auto r = to!U(" world");
+
+ enforce(test(l, 0, 0, r, " worldhello"),
+ new AssertError("testStr failure 1", file, line));
+ enforce(test(l, 0, 3, r, " worldlo"),
+ new AssertError("testStr failure 2", file, line));
+ enforce(test(l, 3, l.length, r, "hel world"),
+ new AssertError("testStr failure 3", file, line));
+ enforce(test(l, 0, l.length, r, " world"),
+ new AssertError("testStr failure 4", file, line));
+ enforce(test(l, l.length, l.length, r, "hello world"),
+ new AssertError("testStr failure 5", 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)();
+}
+
+/++
+ 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]))))
+{
+ if (from.empty) return subject;
+ static if (isSomeString!(E[]))
+ {
+ import std.string : indexOf;
+ immutable idx = subject.indexOf(from);
+ }
+ else
+ {
+ import std.algorithm.searching : countUntil;
+ immutable idx = subject.countUntil(from);
+ }
+ if (idx == -1)
+ return subject;
+
+ auto app = appender!(E[])();
+ app.put(subject[0 .. idx]);
+ app.put(to);
+
+ static if (isSomeString!(E[]) && isSomeString!R1)
+ {
+ import std.utf : codeLength;
+ immutable fromLength = codeLength!(Unqual!E, R1)(from);
+ }
+ else
+ immutable fromLength = from.length;
+
+ app.put(subject[idx + fromLength .. $]);
+
+ return app.data;
+}
+
+///
+@safe unittest
+{
+ auto a = [1, 2, 2, 3, 4, 5];
+ auto b = a.replaceFirst([2], [1337]);
+ assert(b == [1, 1337, 2, 3, 4, 5]);
+
+ auto s = "This is a foo foo list";
+ auto r = s.replaceFirst("foo", "silly");
+ assert(r == "This is a silly foo list");
+}
+
+@safe unittest
+{
+ import std.algorithm.comparison : cmp;
+ import std.conv : to;
+
+ foreach (S; AliasSeq!(string, wstring, dstring, char[], wchar[], dchar[],
+ const(char[]), immutable(char[])))
+ {
+ 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");
+ auto s2 = to!S("Thüs is a ßöö foo list");
+ auto from = to!T("foo");
+ auto from2 = to!T("ßöö");
+ auto into = to!T("silly");
+ auto into2 = to!T("sälly");
+
+ S r1 = replaceFirst(s, from, into);
+ assert(cmp(r1, "This is a silly foo list") == 0);
+
+ S r11 = replaceFirst(s2, from2, into2);
+ assert(cmp(r11, "Thüs is a sälly foo list") == 0,
+ to!string(r11) ~ " : " ~ S.stringof ~ " " ~ T.stringof);
+
+ S r2 = replaceFirst(r1, from, into);
+ assert(cmp(r2, "This is a silly silly list") == 0);
+
+ S r3 = replaceFirst(s, to!T(""), into);
+ assert(cmp(r3, "This is a foo foo list") == 0);
+
+ assert(replaceFirst(r3, to!T("won't find"), to!T("whatever")) is r3);
+ }();
+ }
+}
+
+//Bug# 8187
+@safe unittest
+{
+ auto res = ["a", "a"];
+ assert(replace(res, "a", "b") == ["b", "b"]);
+ assert(replaceFirst(res, "a", "b") == ["b", "a"]);
+}
+
+/++
+ 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[]) &&
+ isForwardRange!R1 && is(typeof(appender!(E[])().put(from[0 .. 1]))) &&
+ isForwardRange!R2 && is(typeof(appender!(E[])().put(to[0 .. 1]))))
+{
+ import std.range : retro;
+ if (from.empty) return subject;
+ static if (isSomeString!(E[]))
+ {
+ import std.string : lastIndexOf;
+ auto idx = subject.lastIndexOf(from);
+ }
+ else
+ {
+ import std.algorithm.searching : countUntil;
+ auto idx = retro(subject).countUntil(retro(from));
+ }
+
+ if (idx == -1)
+ return subject;
+
+ static if (isSomeString!(E[]) && isSomeString!R1)
+ {
+ import std.utf : codeLength;
+ auto fromLength = codeLength!(Unqual!E, R1)(from);
+ }
+ else
+ auto fromLength = from.length;
+
+ auto app = appender!(E[])();
+ static if (isSomeString!(E[]))
+ app.put(subject[0 .. idx]);
+ else
+ app.put(subject[0 .. $ - idx - fromLength]);
+
+ app.put(to);
+
+ static if (isSomeString!(E[]))
+ app.put(subject[idx+fromLength .. $]);
+ else
+ app.put(subject[$ - idx .. $]);
+
+ return app.data;
+}
+
+///
+@safe unittest
+{
+ auto a = [1, 2, 2, 3, 4, 5];
+ auto b = a.replaceLast([2], [1337]);
+ assert(b == [1, 2, 1337, 3, 4, 5]);
+
+ auto s = "This is a foo foo list";
+ auto r = s.replaceLast("foo", "silly");
+ assert(r == "This is a foo silly list", r);
+}
+
+@safe unittest
+{
+ import std.algorithm.comparison : cmp;
+ import std.conv : to;
+
+ foreach (S; AliasSeq!(string, wstring, dstring, char[], wchar[], dchar[],
+ const(char[]), immutable(char[])))
+ {
+ 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");
+ auto s2 = to!S("Thüs is a ßöö ßöö list");
+ auto from = to!T("foo");
+ auto from2 = to!T("ßöö");
+ auto into = to!T("silly");
+ auto into2 = to!T("sälly");
+
+ S r1 = replaceLast(s, from, into);
+ assert(cmp(r1, "This is a foo silly list") == 0, to!string(r1));
+
+ S r11 = replaceLast(s2, from2, into2);
+ assert(cmp(r11, "Thüs is a ßöö sälly list") == 0,
+ to!string(r11) ~ " : " ~ S.stringof ~ " " ~ T.stringof);
+
+ S r2 = replaceLast(r1, from, into);
+ assert(cmp(r2, "This is a silly silly list") == 0);
+
+ S r3 = replaceLast(s, to!T(""), into);
+ assert(cmp(r3, "This is a foo foo list") == 0);
+
+ assert(replaceLast(r3, to!T("won't find"), to!T("whatever")) is r3);
+ }();
+ }
+}
+
+/++
+ 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
+{
+ // Verify that slice[] really is a slice of s[]
+ assert(overlap(s, slice) is slice);
+}
+body
+{
+ auto result = new T[s.length - slice.length + replacement.length];
+ immutable so = slice.ptr - s.ptr;
+ result[0 .. so] = s[0 .. so];
+ result[so .. so + replacement.length] = replacement[];
+ result[so + replacement.length .. result.length] =
+ s[so + slice.length .. s.length];
+
+ return cast(inout(T)[]) result;
+}
+
+///
+@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.comparison : cmp;
+
+ string s = "hello";
+ string slice = s[2 .. 4];
+
+ auto r = replaceSlice(s, slice, "bar");
+ int i;
+ i = cmp(r, "hebaro");
+ assert(i == 0);
+}
+
+/**
+Implements an output range that appends data to an array. This is
+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 : GC;
+
+ private alias T = ElementEncodingType!A;
+
+ private struct Data
+ {
+ size_t capacity;
+ Unqual!T[] arr;
+ bool canExtend = false;
+ }
+
+ private Data* _data;
+
+ /**
+ * 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(A arr) @trusted pure nothrow
+ {
+ // initialize to a given array.
+ _data = new Data;
+ _data.arr = cast(Unqual!T[]) arr; //trusted
+
+ if (__ctfe)
+ return;
+
+ // We want to use up as much of the block the array is in as possible.
+ // if we consume all the block that we can, then array appending is
+ // safe WRT built-in append, and we can use the entire block.
+ // We only do this for mutable types that can be extended.
+ static if (isMutable!T && is(typeof(arr.length = size_t.max)))
+ {
+ immutable cap = arr.capacity; //trusted
+ // Replace with "GC.setAttr( Not Appendable )" once pure (and fixed)
+ if (cap > arr.length)
+ arr.length = cap;
+ }
+ _data.capacity = arr.length;
+ }
+
+ /**
+ * Reserve at least newCapacity elements for appending. Note that more elements
+ * may be reserved than requested. If `newCapacity <= capacity`, then nothing is
+ * done.
+ */
+ void reserve(size_t newCapacity) @safe pure nothrow
+ {
+ if (_data)
+ {
+ if (newCapacity > _data.capacity)
+ ensureAddable(newCapacity - _data.arr.length);
+ }
+ else
+ {
+ ensureAddable(newCapacity);
+ }
+ }
+
+ /**
+ * 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
+ {
+ return _data ? _data.capacity : 0;
+ }
+
+ /**
+ * Returns: The managed array.
+ */
+ @property inout(ElementEncodingType!A)[] data() inout @trusted pure nothrow
+ {
+ /* @trusted operation:
+ * casting Unqual!T[] to inout(T)[]
+ */
+ return cast(typeof(return))(_data ? _data.arr : null);
+ }
+
+ // ensure we can add nelems elements, resizing as necessary
+ private void ensureAddable(size_t nelems) @trusted pure nothrow
+ {
+ if (!_data)
+ _data = new Data;
+ immutable len = _data.arr.length;
+ immutable reqlen = len + nelems;
+
+ if (_data.capacity >= reqlen)
+ return;
+
+ // need to increase capacity
+ if (__ctfe)
+ {
+ static if (__traits(compiles, new Unqual!T[1]))
+ {
+ _data.arr.length = reqlen;
+ }
+ else
+ {
+ // avoid restriction of @disable this()
+ _data.arr = _data.arr[0 .. _data.capacity];
+ foreach (i; _data.capacity .. reqlen)
+ _data.arr ~= Unqual!T.init;
+ }
+ _data.arr = _data.arr[0 .. len];
+ _data.capacity = reqlen;
+ }
+ else
+ {
+ // Time to reallocate.
+ // We need to almost duplicate what's in druntime, except we
+ // have better access to the capacity field.
+ auto newlen = appenderNewCapacity!(T.sizeof)(_data.capacity, reqlen);
+ // first, try extending the current block
+ if (_data.canExtend)
+ {
+ immutable u = GC.extend(_data.arr.ptr, nelems * T.sizeof, (newlen - len) * T.sizeof);
+ if (u)
+ {
+ // extend worked, update the capacity
+ _data.capacity = u / T.sizeof;
+ return;
+ }
+ }
+
+
+ // didn't work, must reallocate
+ 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.canExtend = true;
+ // leave the old data, for safety reasons
+ }
+ }
+
+ private template canPutItem(U)
+ {
+ enum bool canPutItem =
+ isImplicitlyConvertible!(U, T) ||
+ isSomeChar!T && isSomeChar!U;
+ }
+ private template canPutConstRange(Range)
+ {
+ enum bool canPutConstRange =
+ isInputRange!(Unqual!Range) &&
+ !isInputRange!Range &&
+ is(typeof(Appender.init.put(Range.init.front)));
+ }
+ private template canPutRange(Range)
+ {
+ enum bool canPutRange =
+ isInputRange!Range &&
+ is(typeof(Appender.init.put(Range.init.front)));
+ }
+
+ /**
+ * Appends `item` to the managed array.
+ */
+ void put(U)(U item) if (canPutItem!U)
+ {
+ static if (isSomeChar!T && isSomeChar!U && T.sizeof < U.sizeof)
+ {
+ /* may throwable operation:
+ * - std.utf.encode
+ */
+ // must do some transcoding around here
+ import std.utf : encode;
+ Unqual!T[T.sizeof == 1 ? 4 : 2] encoded;
+ auto len = encode(encoded, item);
+ put(encoded[0 .. len]);
+ }
+ else
+ {
+ import std.conv : emplaceRef;
+
+ ensureAddable(1);
+ immutable len = _data.arr.length;
+
+ auto bigData = (() @trusted => _data.arr.ptr[0 .. len + 1])();
+ emplaceRef!(Unqual!T)(bigData[len], cast(Unqual!T) item);
+ //We do this at the end, in case of exceptions
+ _data.arr = bigData;
+ }
+ }
+
+ // Const fixing hack.
+ void put(Range)(Range items) if (canPutConstRange!Range)
+ {
+ alias p = put!(Unqual!Range);
+ p(items);
+ }
+
+ /**
+ * Appends an entire range to the managed array.
+ */
+ void put(Range)(Range items) if (canPutRange!Range)
+ {
+ // note, we disable this branch for appending one type of char to
+ // another because we can't trust the length portion.
+ static if (!(isSomeChar!T && isSomeChar!(ElementType!Range) &&
+ !is(immutable Range == immutable T[])) &&
+ is(typeof(items.length) == size_t))
+ {
+ // optimization -- if this type is something other than a string,
+ // and we are adding exactly one element, call the version for one
+ // element.
+ static if (!isSomeChar!T)
+ {
+ if (items.length == 1)
+ {
+ put(items.front);
+ return;
+ }
+ }
+
+ // make sure we have enough space, then add the items
+ @trusted auto bigDataFun(size_t extra)
+ {
+ ensureAddable(extra);
+ return _data.arr.ptr[0 .. _data.arr.length + extra];
+ }
+ auto bigData = bigDataFun(items.length);
+
+ immutable len = _data.arr.length;
+ immutable newlen = bigData.length;
+
+ alias UT = Unqual!T;
+
+ static if (is(typeof(_data.arr[] = items[])) &&
+ !hasElaborateAssign!UT && isAssignable!(UT, ElementEncodingType!Range))
+ {
+ bigData[len .. newlen] = items[];
+ }
+ else
+ {
+ import std.conv : emplaceRef;
+ foreach (ref it ; bigData[len .. newlen])
+ {
+ emplaceRef!T(it, items.front);
+ items.popFront();
+ }
+ }
+
+ //We do this at the end, in case of exceptions
+ _data.arr = bigData;
+ }
+ else
+ {
+ //pragma(msg, Range.stringof);
+ // Generic input range
+ for (; !items.empty; items.popFront())
+ {
+ put(items.front);
+ }
+ }
+ }
+
+ /**
+ * Appends `rhs` to the managed array.
+ * Params:
+ * rhs = Element or range.
+ */
+ void opOpAssign(string op : "~", U)(U rhs)
+ if (__traits(compiles, put(rhs)))
+ {
+ put(rhs);
+ }
+
+ // only allow overwriting data on non-immutable and non-const data
+ static if (isMutable!T)
+ {
+ /**
+ * Clears the managed array. This allows the elements of the array to be reused
+ * for appending.
+ *
+ * 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
+ {
+ if (_data)
+ {
+ _data.arr = _data.arr.ptr[0 .. 0];
+ }
+ }
+
+ /**
+ * 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, "Attempting to shrink Appender with newlength > length");
+ _data.arr = _data.arr.ptr[0 .. newlength];
+ }
+ else
+ enforce(newlength == 0, "Attempting to shrink empty Appender with non-zero newlength");
+ }
+ }
+
+ void toString(Writer)(scope Writer w)
+ {
+ import std.format : formattedWrite;
+ w.formattedWrite(typeof(this).stringof ~ "(%s)", data);
+ }
+}
+
+///
+@safe unittest
+{
+ auto app = appender!string();
+ string b = "abcdefg";
+ foreach (char c; b)
+ app.put(c);
+ assert(app.data == "abcdefg");
+
+ int[] a = [ 1, 2 ];
+ auto app2 = appender(a);
+ app2.put(3);
+ app2.put([ 4, 5, 6 ]);
+ assert(app2.data == [ 1, 2, 3, 4, 5, 6 ]);
+}
+
+@safe unittest
+{
+ import std.format : format;
+ auto app = appender!(int[])();
+ app.put(1);
+ app.put(2);
+ app.put(3);
+ 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
+//arg reqLen: The length as requested by the user
+//ret sugLen: A suggested growth.
+private size_t appenderNewCapacity(size_t TSizeOf)(size_t curLen, size_t reqLen) @safe pure nothrow
+{
+ import core.bitop : bsr;
+ 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)
+ mult = 200;
+ auto sugLen = cast(size_t)((curLen * mult + 99) / 100);
+ return max(reqLen, sugLen);
+}
+
+/**
+ * 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
+ {
+ Appender!A impl;
+ A* arr;
+ }
+
+ /**
+ * 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.
+ *
+ * Params:
+ * arr = Pointer to an array. Must not be _null.
+ */
+ this(A* arr)
+ {
+ impl = Appender!A(*arr);
+ this.arr = arr;
+ }
+
+ /** 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);");
+ }
+
+ /**
+ * Appends `rhs` to the managed array.
+ * Params:
+ * rhs = Element or range.
+ */
+ void opOpAssign(string op : "~", U)(U rhs)
+ if (__traits(compiles, (Appender!A a){ a.put(rhs); }))
+ {
+ scope(exit) *this.arr = impl.data;
+ impl.put(rhs);
+ }
+
+ /**
+ * 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).
+ */
+ @property size_t capacity() const
+ {
+ return impl.capacity;
+ }
+
+ /**
+ * Returns the managed array.
+ */
+ @property inout(ElementEncodingType!A)[] data() inout
+ {
+ return impl.data;
+ }
+}
+
+///
+@system pure nothrow
+unittest
+{
+ int[] a = [1, 2];
+ auto app2 = appender(&a);
+ assert(app2.data == [1, 2]);
+ assert(a == [1, 2]);
+ app2 ~= 3;
+ app2 ~= [4, 5, 6];
+ assert(app2.data == [1, 2, 3, 4, 5, 6]);
+ assert(a == [1, 2, 3, 4, 5, 6]);
+
+ app2.reserve(5);
+ assert(app2.capacity >= 5);
+}
+
+/++
+ Convenience function that returns an $(LREF Appender) instance,
+ optionally initialized with $(D array).
+ +/
+Appender!A appender(A)()
+if (isDynamicArray!A)
+{
+ return Appender!A(null);
+}
+/// ditto
+Appender!(E[]) appender(A : E[], E)(auto ref A array)
+{
+ static assert(!isStaticArray!A || __traits(isRef, array),
+ "Cannot create Appender from an rvalue static array");
+
+ return Appender!(E[])(array);
+}
+
+@safe pure nothrow unittest
+{
+ import std.exception;
+ {
+ auto app = appender!(char[])();
+ string b = "abcdefg";
+ foreach (char c; b) app.put(c);
+ assert(app.data == "abcdefg");
+ }
+ {
+ auto app = appender!(char[])();
+ string b = "abcdefg";
+ foreach (char c; b) app ~= c;
+ assert(app.data == "abcdefg");
+ }
+ {
+ int[] a = [ 1, 2 ];
+ auto app2 = appender(a);
+ assert(app2.data == [ 1, 2 ]);
+ app2.put(3);
+ app2.put([ 4, 5, 6 ][]);
+ assert(app2.data == [ 1, 2, 3, 4, 5, 6 ]);
+ app2.put([ 7 ]);
+ assert(app2.data == [ 1, 2, 3, 4, 5, 6, 7 ]);
+ }
+
+ int[] a = [ 1, 2 ];
+ auto app2 = appender(a);
+ assert(app2.data == [ 1, 2 ]);
+ app2 ~= 3;
+ app2 ~= [ 4, 5, 6 ][];
+ assert(app2.data == [ 1, 2, 3, 4, 5, 6 ]);
+ app2 ~= [ 7 ];
+ assert(app2.data == [ 1, 2, 3, 4, 5, 6, 7 ]);
+
+ app2.reserve(5);
+ assert(app2.capacity >= 5);
+
+ try // shrinkTo may throw
+ {
+ app2.shrinkTo(3);
+ }
+ catch (Exception) assert(0);
+ assert(app2.data == [ 1, 2, 3 ]);
+ assertThrown(app2.shrinkTo(5));
+
+ const app3 = app2;
+ assert(app3.capacity >= 3);
+ assert(app3.data == [1, 2, 3]);
+
+ auto app4 = appender([]);
+ try // shrinkTo may throw
+ {
+ app4.shrinkTo(0);
+ }
+ catch (Exception) assert(0);
+
+ // Issue 5663 & 9725 tests
+ foreach (S; AliasSeq!(char[], const(char)[], string))
+ {
+ {
+ Appender!S app5663i;
+ assertNotThrown(app5663i.put("\xE3"));
+ assert(app5663i.data == "\xE3");
+
+ Appender!S app5663c;
+ assertNotThrown(app5663c.put(cast(const(char)[])"\xE3"));
+ assert(app5663c.data == "\xE3");
+
+ Appender!S app5663m;
+ assertNotThrown(app5663m.put("\xE3".dup));
+ assert(app5663m.data == "\xE3");
+ }
+ // ditto for ~=
+ {
+ Appender!S app5663i;
+ assertNotThrown(app5663i ~= "\xE3");
+ assert(app5663i.data == "\xE3");
+
+ Appender!S app5663c;
+ assertNotThrown(app5663c ~= cast(const(char)[])"\xE3");
+ assert(app5663c.data == "\xE3");
+
+ Appender!S app5663m;
+ assertNotThrown(app5663m ~= "\xE3".dup);
+ assert(app5663m.data == "\xE3");
+ }
+ }
+
+ static struct S10122
+ {
+ int val;
+
+ @disable this();
+ this(int v) @safe pure nothrow { val = v; }
+ }
+ assertCTFEable!(
+ {
+ auto w = appender!(S10122[])();
+ w.put(S10122(1));
+ assert(w.data.length == 1 && w.data[0].val == 1);
+ });
+}
+
+///
+@safe pure nothrow
+unittest
+{
+ auto w = appender!string;
+ // pre-allocate space for at least 10 elements (this avoids costly reallocations)
+ w.reserve(10);
+ assert(w.capacity >= 10);
+
+ w.put('a'); // single elements
+ w.put("bc"); // multiple elements
+
+ // use the append syntax
+ w ~= 'd';
+ w ~= "ef";
+
+ assert(w.data == "abcdef");
+}
+
+@safe pure nothrow unittest
+{
+ {
+ auto w = appender!string();
+ w.reserve(4);
+ cast(void) w.capacity;
+ cast(void) w.data;
+ try
+ {
+ wchar wc = 'a';
+ dchar dc = 'a';
+ w.put(wc); // decoding may throw
+ w.put(dc); // decoding may throw
+ }
+ catch (Exception) assert(0);
+ }
+ {
+ auto w = appender!(int[])();
+ w.reserve(4);
+ cast(void) w.capacity;
+ cast(void) w.data;
+ w.put(10);
+ w.put([10]);
+ w.clear();
+ try
+ {
+ w.shrinkTo(0);
+ }
+ catch (Exception) assert(0);
+
+ struct N
+ {
+ int payload;
+ alias payload this;
+ }
+ w.put(N(1));
+ w.put([N(2)]);
+
+ struct S(T)
+ {
+ @property bool empty() { return true; }
+ @property T front() { return T.init; }
+ void popFront() {}
+ }
+ S!int r;
+ w.put(r);
+ }
+}
+
+@safe unittest
+{
+ import std.algorithm;
+ import std.typecons;
+ //10690
+ [tuple(1)].filter!(t => true).array; // No error
+ [tuple("A")].filter!(t => true).array; // error
+}
+
+@system unittest
+{
+ import std.range;
+ //Coverage for put(Range)
+ struct S1
+ {
+ }
+ struct S2
+ {
+ void opAssign(S2){}
+ }
+ auto a1 = Appender!(S1[])();
+ auto a2 = Appender!(S2[])();
+ auto au1 = Appender!(const(S1)[])();
+ auto au2 = Appender!(const(S2)[])();
+ a1.put(S1().repeat().take(10));
+ a2.put(S2().repeat().take(10));
+ auto sc1 = const(S1)();
+ auto sc2 = const(S2)();
+ au1.put(sc1.repeat().take(10));
+ au2.put(sc2.repeat().take(10));
+}
+
+@system unittest
+{
+ struct S
+ {
+ int* p;
+ }
+
+ auto a0 = Appender!(S[])();
+ auto a1 = Appender!(const(S)[])();
+ auto a2 = Appender!(immutable(S)[])();
+ auto s0 = S(null);
+ auto s1 = const(S)(null);
+ auto s2 = immutable(S)(null);
+ a1.put(s0);
+ a1.put(s1);
+ a1.put(s2);
+ a1.put([s0]);
+ a1.put([s1]);
+ a1.put([s2]);
+ a0.put(s0);
+ static assert(!is(typeof(a0.put(a1))));
+ static assert(!is(typeof(a0.put(a2))));
+ a0.put([s0]);
+ static assert(!is(typeof(a0.put([a1]))));
+ static assert(!is(typeof(a0.put([a2]))));
+ static assert(!is(typeof(a2.put(a0))));
+ static assert(!is(typeof(a2.put(a1))));
+ a2.put(s2);
+ static assert(!is(typeof(a2.put([a0]))));
+ static assert(!is(typeof(a2.put([a1]))));
+ a2.put([s2]);
+}
+
+@safe unittest
+{
+ //9528
+ const(E)[] fastCopy(E)(E[] src) {
+ auto app = appender!(const(E)[])();
+ foreach (i, e; src)
+ app.put(e);
+ return app.data;
+ }
+
+ class C {}
+ struct S { const(C) c; }
+ S[] s = [ S(new C) ];
+
+ auto t = fastCopy(s); // Does not compile
+}
+
+@safe unittest
+{
+ import std.algorithm.iteration : map;
+ //10753
+ struct Foo {
+ immutable dchar d;
+ }
+ struct Bar {
+ immutable int x;
+ }
+ "12".map!Foo.array;
+ [1, 2].map!Bar.array;
+}
+
+@safe unittest
+{
+ //New appender signature tests
+ alias mutARR = int[];
+ alias conARR = const(int)[];
+ alias immARR = immutable(int)[];
+
+ mutARR mut;
+ conARR con;
+ immARR imm;
+
+ {auto app = Appender!mutARR(mut);} //Always worked. Should work. Should not create a warning.
+ static assert(!is(typeof(Appender!mutARR(con)))); //Never worked. Should not work.
+ static assert(!is(typeof(Appender!mutARR(imm)))); //Never worked. Should not work.
+
+ {auto app = Appender!conARR(mut);} //Always worked. Should work. Should not create a warning.
+ {auto app = Appender!conARR(con);} //Didn't work. Now works. Should not create a warning.
+ {auto app = Appender!conARR(imm);} //Didn't work. Now works. Should not create a warning.
+
+ //{auto app = Appender!immARR(mut);} //Worked. Will cease to work. Creates warning.
+ //static assert(!is(typeof(Appender!immARR(mut)))); //Worked. Will cease to work. Uncomment me after full deprecation.
+ static assert(!is(typeof(Appender!immARR(con)))); //Never worked. Should not work.
+ {auto app = Appender!immARR(imm);} //Didn't work. Now works. Should not create a warning.
+
+ //Deprecated. Please uncomment and make sure this doesn't work:
+ //char[] cc;
+ //static assert(!is(typeof(Appender!string(cc))));
+
+ //This should always work:
+ {auto app = appender!string(null);}
+ {auto app = appender!(const(char)[])(null);}
+ {auto app = appender!(char[])(null);}
+}
+
+@safe unittest //Test large allocations (for GC.extend)
+{
+ import std.algorithm.comparison : equal;
+ import std.range;
+ Appender!(char[]) app;
+ app.reserve(1); //cover reserve on non-initialized
+ foreach (_; 0 .. 100_000)
+ app.put('a');
+ assert(equal(app.data, 'a'.repeat(100_000)));
+}
+
+@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;
+ auto app = appender(arr[0 .. 0]);
+ app.reserve(1); //This should not trigger a call to extend
+ app.put(ubyte(1)); //Don't clobber arr
+ assert(reference[] == arr[]);
+}
+
+@safe unittest // clear method is supported only for mutable element types
+{
+ Appender!string app;
+ static assert(!__traits(compiles, app.clear()));
+}
+
+@safe unittest
+{
+ static struct D//dynamic
+ {
+ int[] i;
+ alias i this;
+ }
+ static struct S//static
+ {
+ int[5] i;
+ alias i this;
+ }
+ static assert(!is(Appender!(char[5])));
+ static assert(!is(Appender!D));
+ static assert(!is(Appender!S));
+
+ enum int[5] a = [];
+ int[5] b;
+ D d;
+ S s;
+ int[5] foo(){return a;}
+
+ static assert(!is(typeof(appender(a))));
+ static assert( is(typeof(appender(b))));
+ static assert( is(typeof(appender(d))));
+ static assert( is(typeof(appender(s))));
+ static assert(!is(typeof(appender(foo()))));
+}
+
+@system unittest
+{
+ // Issue 13077
+ static class A {}
+
+ // reduced case
+ auto w = appender!(shared(A)[])();
+ w.put(new shared A());
+
+ // original case
+ import std.range;
+ InputRange!(shared A) foo()
+ {
+ return [new shared A].inputRangeObject;
+ }
+ auto res = foo.array;
+}
+
+/++
+ 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(P : E[]*, E)(P arrayPtr)
+{
+ return RefAppender!(E[])(arrayPtr);
+}
+
+///
+@system pure nothrow
+unittest
+{
+ int[] a = [1, 2];
+ auto app2 = appender(&a);
+ assert(app2.data == [1, 2]);
+ assert(a == [1, 2]);
+ app2 ~= 3;
+ app2 ~= [4, 5, 6];
+ assert(app2.data == [1, 2, 3, 4, 5, 6]);
+ assert(a == [1, 2, 3, 4, 5, 6]);
+
+ app2.reserve(5);
+ assert(app2.capacity >= 5);
+}
+
+@system unittest
+{
+ import std.exception;
+ {
+ auto arr = new char[0];
+ auto app = appender(&arr);
+ string b = "abcdefg";
+ foreach (char c; b) app.put(c);
+ assert(app.data == "abcdefg");
+ assert(arr == "abcdefg");
+ }
+ {
+ auto arr = new char[0];
+ auto app = appender(&arr);
+ string b = "abcdefg";
+ foreach (char c; b) app ~= c;
+ assert(app.data == "abcdefg");
+ assert(arr == "abcdefg");
+ }
+ {
+ int[] a = [ 1, 2 ];
+ auto app2 = appender(&a);
+ assert(app2.data == [ 1, 2 ]);
+ assert(a == [ 1, 2 ]);
+ app2.put(3);
+ app2.put([ 4, 5, 6 ][]);
+ assert(app2.data == [ 1, 2, 3, 4, 5, 6 ]);
+ assert(a == [ 1, 2, 3, 4, 5, 6 ]);
+ }
+
+ int[] a = [ 1, 2 ];
+ auto app2 = appender(&a);
+ assert(app2.data == [ 1, 2 ]);
+ assert(a == [ 1, 2 ]);
+ app2 ~= 3;
+ app2 ~= [ 4, 5, 6 ][];
+ assert(app2.data == [ 1, 2, 3, 4, 5, 6 ]);
+ assert(a == [ 1, 2, 3, 4, 5, 6 ]);
+
+ app2.reserve(5);
+ assert(app2.capacity >= 5);
+
+ try // shrinkTo may throw
+ {
+ app2.shrinkTo(3);
+ }
+ catch (Exception) assert(0);
+ assert(app2.data == [ 1, 2, 3 ]);
+ assertThrown(app2.shrinkTo(5));
+
+ const app3 = app2;
+ assert(app3.capacity >= 3);
+ assert(app3.data == [1, 2, 3]);
+}
+
+@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];
+ app.put(range);
+ assert(app.data == [1, 2, 3]);
+}
+
+@safe unittest
+{
+ string s = "hello".idup;
+ char[] a = "hello".dup;
+ auto appS = appender(s);
+ auto appA = appender(a);
+ put(appS, 'w');
+ put(appA, 'w');
+ s ~= 'a'; //Clobbers here?
+ a ~= 'a'; //Clobbers here?
+ assert(appS.data == "hellow");
+ assert(appA.data == "hellow");
+}
diff --git a/libphobos/src/std/ascii.d b/libphobos/src/std/ascii.d
new file mode 100644
index 0000000..b430114
--- /dev/null
+++ b/libphobos/src/std/ascii.d
@@ -0,0 +1,729 @@
+// Written in the D programming language.
+
+/++
+ Functions which operate on ASCII characters.
+
+ All of the functions in std._ascii accept Unicode characters but
+ effectively ignore them if they're not ASCII. All $(D isX) functions return
+ $(D false) for non-ASCII characters, and all $(D toX) functions do nothing
+ to non-ASCII characters.
+
+ For functions which operate on Unicode characters, see
+ $(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),
+ $(HTTP en.wikipedia.org/wiki/Ascii, Wikipedia)
+
+ 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;
+
+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;
+}
+
+
+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
+
+/++
+ Letter case specifier.
+ +/
+enum LetterCase : bool
+{
+ upper, /// Upper case letters
+ lower /// Lower case letters
+}
+
+///
+@safe unittest
+{
+ import std.conv : to;
+
+ assert(42.to!string(16, LetterCase.upper) == "2A");
+ assert(42.to!string(16, LetterCase.lower) == "2a");
+}
+
+///
+@system unittest
+{
+ import std.digest.hmac : hmac;
+ import std.digest.digest : toHexString;
+ import std.digest.sha : SHA1;
+ import std.string : representation;
+
+ const sha1HMAC = "A very long phrase".representation
+ .hmac!SHA1("secret".representation)
+ .toHexString!(LetterCase.lower);
+ assert(sha1HMAC == "49f2073c7bf58577e8c9ae59fe8cfd37c9ab94e5");
+}
+
+/// Newline sequence for this system.
+version (Windows)
+ immutable newline = "\r\n";
+else version (Posix)
+ immutable newline = "\n";
+else
+ static assert(0, "Unsupported OS");
+
+
+/++
+ Params: c = The character to test.
+ Returns: Whether $(D c) is a letter or a number (0 .. 9, a .. z, A .. Z).
+ +/
+bool isAlphaNum(dchar c) @safe pure nothrow @nogc
+{
+ return c <= 'z' && c >= '0' && (c <= '9' || c >= 'a' || (c >= 'A' && c <= 'Z'));
+}
+
+///
+@safe pure nothrow @nogc unittest
+{
+ assert( isAlphaNum('A'));
+ assert( isAlphaNum('1'));
+ assert(!isAlphaNum('#'));
+
+ // N.B.: does not return true for non-ASCII Unicode alphanumerics:
+ assert(!isAlphaNum('á'));
+}
+
+@safe unittest
+{
+ foreach (c; chain(digits, octalDigits, fullHexDigits, letters, lowercase, uppercase))
+ assert(isAlphaNum(c));
+
+ foreach (c; whitespace)
+ assert(!isAlphaNum(c));
+}
+
+
+/++
+ Params: c = The character to test.
+ Returns: Whether $(D c) is an ASCII letter (A .. Z, a .. z).
+ +/
+bool isAlpha(dchar c) @safe pure nothrow @nogc
+{
+ // Optimizer can turn this into a bitmask operation on 64 bit code
+ return (c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z');
+}
+
+///
+@safe pure nothrow @nogc unittest
+{
+ assert( isAlpha('A'));
+ assert(!isAlpha('1'));
+ assert(!isAlpha('#'));
+
+ // N.B.: does not return true for non-ASCII Unicode alphabetic characters:
+ assert(!isAlpha('á'));
+}
+
+@safe unittest
+{
+ foreach (c; chain(letters, lowercase, uppercase))
+ assert(isAlpha(c));
+
+ 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).
+ +/
+bool isLower(dchar c) @safe pure nothrow @nogc
+{
+ return c >= 'a' && c <= 'z';
+}
+
+///
+@safe pure nothrow @nogc unittest
+{
+ assert( isLower('a'));
+ assert(!isLower('A'));
+ assert(!isLower('#'));
+
+ // N.B.: does not return true for non-ASCII Unicode lowercase letters
+ assert(!isLower('á'));
+ assert(!isLower('Á'));
+}
+
+@safe unittest
+{
+ foreach (c; lowercase)
+ assert(isLower(c));
+
+ 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).
+ +/
+bool isUpper(dchar c) @safe pure nothrow @nogc
+{
+ return c <= 'Z' && 'A' <= c;
+}
+
+///
+@safe pure nothrow @nogc unittest
+{
+ assert( isUpper('A'));
+ assert(!isUpper('a'));
+ assert(!isUpper('#'));
+
+ // N.B.: does not return true for non-ASCII Unicode uppercase letters
+ assert(!isUpper('á'));
+ assert(!isUpper('Á'));
+}
+
+@safe unittest
+{
+ foreach (c; uppercase)
+ assert(isUpper(c));
+
+ foreach (c; chain(digits, lowercase, whitespace))
+ assert(!isUpper(c));
+}
+
+
+/++
+ Params: c = The character to test.
+ Returns: Whether $(D c) is a digit (0 .. 9).
+ +/
+bool isDigit(dchar c) @safe pure nothrow @nogc
+{
+ return '0' <= c && c <= '9';
+}
+
+///
+@safe pure nothrow @nogc unittest
+{
+ assert( isDigit('3'));
+ assert( isDigit('8'));
+ assert(!isDigit('B'));
+ assert(!isDigit('#'));
+
+ // N.B.: does not return true for non-ASCII Unicode numbers
+ assert(!isDigit('0')); // full-width digit zero (U+FF10)
+ assert(!isDigit('4')); // full-width digit four (U+FF14)
+}
+
+@safe unittest
+{
+ foreach (c; digits)
+ assert(isDigit(c));
+
+ 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).
+ +/
+bool isOctalDigit(dchar c) @safe pure nothrow @nogc
+{
+ return c >= '0' && c <= '7';
+}
+
+///
+@safe pure nothrow @nogc unittest
+{
+ assert( isOctalDigit('0'));
+ assert( isOctalDigit('7'));
+ assert(!isOctalDigit('8'));
+ assert(!isOctalDigit('A'));
+ assert(!isOctalDigit('#'));
+}
+
+@safe unittest
+{
+ foreach (c; octalDigits)
+ assert(isOctalDigit(c));
+
+ 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).
+ +/
+bool isHexDigit(dchar c) @safe pure nothrow @nogc
+{
+ return c <= 'f' && c >= '0' && (c <= '9' || c >= 'a' || (c >= 'A' && c <= 'F'));
+}
+
+///
+@safe pure nothrow @nogc unittest
+{
+ assert( isHexDigit('0'));
+ assert( isHexDigit('A'));
+ assert( isHexDigit('f')); // lowercase hex digits are accepted
+ assert(!isHexDigit('g'));
+ assert(!isHexDigit('G'));
+ assert(!isHexDigit('#'));
+}
+
+@safe unittest
+{
+ foreach (c; fullHexDigits)
+ assert(isHexDigit(c));
+
+ foreach (c; chain(lowercase[6 .. $], uppercase[6 .. $], whitespace))
+ assert(!isHexDigit(c));
+}
+
+
+/++
+ Params: c = The character to test.
+ Returns: Whether or not $(D c) is a whitespace character. That includes the
+ space, tab, vertical tab, form feed, carriage return, and linefeed
+ characters.
+ +/
+bool isWhite(dchar c) @safe pure nothrow @nogc
+{
+ return c == ' ' || (c >= 0x09 && c <= 0x0D);
+}
+
+///
+@safe pure nothrow @nogc unittest
+{
+ assert( isWhite(' '));
+ assert( isWhite('\t'));
+ assert( isWhite('\n'));
+ assert(!isWhite('1'));
+ assert(!isWhite('a'));
+ assert(!isWhite('#'));
+
+ // N.B.: Does not return true for non-ASCII Unicode whitespace characters.
+ static import std.uni;
+ assert(std.uni.isWhite('\u00A0'));
+ assert(!isWhite('\u00A0')); // std.ascii.isWhite
+}
+
+@safe unittest
+{
+ foreach (c; whitespace)
+ assert(isWhite(c));
+
+ foreach (c; chain(digits, letters))
+ assert(!isWhite(c));
+}
+
+
+/++
+ Params: c = The character to test.
+ Returns: Whether $(D c) is a control character.
+ +/
+bool isControl(dchar c) @safe pure nothrow @nogc
+{
+ return c < 0x20 || c == 0x7F;
+}
+
+///
+@safe pure nothrow @nogc unittest
+{
+ assert( isControl('\0'));
+ assert( isControl('\022'));
+ assert( isControl('\n')); // newline is both whitespace and control
+ assert(!isControl(' '));
+ assert(!isControl('1'));
+ assert(!isControl('a'));
+ assert(!isControl('#'));
+
+ // N.B.: non-ASCII Unicode control characters are not recognized:
+ assert(!isControl('\u0080'));
+ assert(!isControl('\u2028'));
+ assert(!isControl('\u2029'));
+}
+
+@safe unittest
+{
+ foreach (dchar c; 0 .. 32)
+ assert(isControl(c));
+ assert(isControl(127));
+
+ foreach (c; chain(digits, letters, [' ']))
+ assert(!isControl(c));
+}
+
+
+/++
+ Params: c = The character to test.
+ Returns: Whether or not $(D c) is a punctuation character. That includes
+ all ASCII characters which are not control characters, letters, digits, or
+ whitespace.
+ +/
+bool isPunctuation(dchar c) @safe pure nothrow @nogc
+{
+ return c <= '~' && c >= '!' && !isAlphaNum(c);
+}
+
+///
+@safe pure nothrow @nogc unittest
+{
+ assert( isPunctuation('.'));
+ assert( isPunctuation(','));
+ assert( isPunctuation(':'));
+ assert( isPunctuation('!'));
+ assert( isPunctuation('#'));
+ assert( isPunctuation('~'));
+ assert( isPunctuation('+'));
+ assert( isPunctuation('_'));
+
+ assert(!isPunctuation('1'));
+ assert(!isPunctuation('a'));
+ assert(!isPunctuation(' '));
+ assert(!isPunctuation('\n'));
+ assert(!isPunctuation('\0'));
+
+ // N.B.: Non-ASCII Unicode punctuation characters are not recognized.
+ assert(!isPunctuation('\u2012')); // (U+2012 = en-dash)
+}
+
+@safe unittest
+{
+ foreach (dchar c; 0 .. 128)
+ {
+ if (isControl(c) || isAlphaNum(c) || c == ' ')
+ assert(!isPunctuation(c));
+ else
+ assert(isPunctuation(c));
+ }
+}
+
+
+/++
+ Params: c = The character to test.
+ Returns: Whether or not $(D c) is a printable character other than the
+ space character.
+ +/
+bool isGraphical(dchar c) @safe pure nothrow @nogc
+{
+ return '!' <= c && c <= '~';
+}
+
+///
+@safe pure nothrow @nogc unittest
+{
+ assert( isGraphical('1'));
+ assert( isGraphical('a'));
+ assert( isGraphical('#'));
+ assert(!isGraphical(' ')); // whitespace is not graphical
+ assert(!isGraphical('\n'));
+ assert(!isGraphical('\0'));
+
+ // N.B.: Unicode graphical characters are not regarded as such.
+ assert(!isGraphical('á'));
+}
+
+@safe unittest
+{
+ foreach (dchar c; 0 .. 128)
+ {
+ if (isControl(c) || c == ' ')
+ assert(!isGraphical(c));
+ else
+ assert(isGraphical(c));
+ }
+}
+
+
+/++
+ Params: c = The character to test.
+ Returns: Whether or not $(D c) is a printable character - including the
+ space character.
+ +/
+bool isPrintable(dchar c) @safe pure nothrow @nogc
+{
+ return c >= ' ' && c <= '~';
+}
+
+///
+@safe pure nothrow @nogc unittest
+{
+ assert( isPrintable(' ')); // whitespace is printable
+ assert( isPrintable('1'));
+ assert( isPrintable('a'));
+ assert( isPrintable('#'));
+ assert(!isPrintable('\0')); // control characters are not printable
+
+ // N.B.: Printable non-ASCII Unicode characters are not recognized.
+ assert(!isPrintable('á'));
+}
+
+@safe unittest
+{
+ foreach (dchar c; 0 .. 128)
+ {
+ if (isControl(c))
+ assert(!isPrintable(c));
+ else
+ assert(isPrintable(c));
+ }
+}
+
+
+/++
+ 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.
+ +/
+pragma(inline, true)
+bool isASCII(dchar c) @safe pure nothrow @nogc
+{
+ return c <= 0x7F;
+}
+
+///
+@safe pure nothrow @nogc unittest
+{
+ assert( isASCII('a'));
+ assert(!isASCII('á'));
+}
+
+@safe unittest
+{
+ foreach (dchar c; 0 .. 128)
+ assert(isASCII(c));
+
+ assert(!isASCII(128));
+}
+
+
+/++
+ Converts an ASCII letter to lowercase.
+
+ Params: c = A character of any type that implicitly converts to $(D dchar).
+ In the case where it's a built-in type, or an enum of a built-in type,
+ $(D Unqual!(OriginalType!C)) is returned, whereas if it's a user-defined
+ type, $(D dchar) is returned.
+
+ Returns: The corresponding lowercase letter, if $(D c) is an uppercase
+ ASCII character, otherwise $(D c) itself.
+ +/
+auto toLower(C)(C c)
+if (is(C : dchar))
+{
+ import std.traits : isAggregateType, OriginalType, Unqual;
+
+ alias OC = OriginalType!C;
+ static if (isAggregateType!OC)
+ alias R = dchar;
+ else
+ alias R = Unqual!OC;
+
+ return isUpper(c) ? cast(R)(cast(R) c + 'a' - 'A') : cast(R) c;
+}
+
+///
+@safe pure nothrow @nogc unittest
+{
+ assert(toLower('a') == 'a');
+ assert(toLower('A') == 'a');
+ assert(toLower('#') == '#');
+
+ // N.B.: Non-ASCII Unicode uppercase letters are not converted.
+ assert(toLower('Á') == 'Á');
+}
+
+@safe pure nothrow unittest
+{
+
+ foreach (C; AliasSeq!(char, wchar, dchar, immutable char, ubyte))
+ {
+ foreach (i, c; uppercase)
+ assert(toLower(cast(C) c) == lowercase[i]);
+
+ foreach (C c; 0 .. 128)
+ {
+ if (c < 'A' || c > 'Z')
+ assert(toLower(c) == c);
+ else
+ assert(toLower(c) != c);
+ }
+
+ foreach (C c; 128 .. C.max)
+ assert(toLower(c) == c);
+
+ //CTFE
+ static assert(toLower(cast(C)'a') == 'a');
+ static assert(toLower(cast(C)'A') == 'a');
+ }
+}
+
+
+/++
+ Converts an ASCII letter to uppercase.
+
+ Params: c = Any type which implicitly converts to $(D dchar). In the case
+ where it's a built-in type, or an enum of a built-in type,
+ $(D Unqual!(OriginalType!C)) is returned, whereas if it's a user-defined
+ type, $(D dchar) is returned.
+
+ Returns: The corresponding uppercase letter, if $(D c) is a lowercase ASCII
+ character, otherwise $(D c) itself.
+ +/
+auto toUpper(C)(C c)
+if (is(C : dchar))
+{
+ import std.traits : isAggregateType, OriginalType, Unqual;
+
+ alias OC = OriginalType!C;
+ static if (isAggregateType!OC)
+ alias R = dchar;
+ else
+ alias R = Unqual!OC;
+
+ return isLower(c) ? cast(R)(cast(R) c - ('a' - 'A')) : cast(R) c;
+}
+
+///
+@safe pure nothrow @nogc unittest
+{
+ assert(toUpper('a') == 'A');
+ assert(toUpper('A') == 'A');
+ assert(toUpper('#') == '#');
+
+ // N.B.: Non-ASCII Unicode lowercase letters are not converted.
+ assert(toUpper('á') == 'á');
+}
+
+@safe pure nothrow unittest
+{
+ foreach (C; AliasSeq!(char, wchar, dchar, immutable char, ubyte))
+ {
+ foreach (i, c; lowercase)
+ assert(toUpper(cast(C) c) == uppercase[i]);
+
+ foreach (C c; 0 .. 128)
+ {
+ if (c < 'a' || c > 'z')
+ assert(toUpper(c) == c);
+ else
+ assert(toUpper(c) != c);
+ }
+
+ foreach (C c; 128 .. C.max)
+ assert(toUpper(c) == c);
+
+ //CTFE
+ static assert(toUpper(cast(C)'a') == 'A');
+ static assert(toUpper(cast(C)'A') == 'A');
+ }
+}
+
+
+@safe unittest //Test both toUpper and toLower with non-builtin
+{
+ //User Defined [Char|Wchar|Dchar]
+ static struct UDC { char c; alias c this; }
+ static struct UDW { wchar c; alias c this; }
+ static struct UDD { dchar c; alias c this; }
+ //[Char|Wchar|Dchar] Enum
+ enum CE : char {a = 'a', A = 'A'}
+ enum WE : wchar {a = 'a', A = 'A'}
+ enum DE : dchar {a = 'a', A = 'A'}
+ //User Defined [Char|Wchar|Dchar] Enum
+ enum UDCE : UDC {a = UDC('a'), A = UDC('A')}
+ enum UDWE : UDW {a = UDW('a'), A = UDW('A')}
+ enum UDDE : UDD {a = UDD('a'), A = UDD('A')}
+
+ //User defined types with implicit cast to dchar test.
+ foreach (Char; AliasSeq!(UDC, UDW, UDD))
+ {
+ assert(toLower(Char('a')) == 'a');
+ assert(toLower(Char('A')) == 'a');
+ static assert(toLower(Char('a')) == 'a');
+ static assert(toLower(Char('A')) == 'a');
+ static assert(toUpper(Char('a')) == 'A');
+ static assert(toUpper(Char('A')) == 'A');
+ }
+
+ //Various enum tests.
+ foreach (Enum; AliasSeq!(CE, WE, DE, UDCE, UDWE, UDDE))
+ {
+ assert(toLower(Enum.a) == 'a');
+ assert(toLower(Enum.A) == 'a');
+ assert(toUpper(Enum.a) == 'A');
+ assert(toUpper(Enum.A) == 'A');
+ static assert(toLower(Enum.a) == 'a');
+ static assert(toLower(Enum.A) == 'a');
+ static assert(toUpper(Enum.a) == 'A');
+ static assert(toUpper(Enum.A) == 'A');
+ }
+
+ //Return value type tests for enum of non-UDT. These should be the original type.
+ foreach (T; AliasSeq!(CE, WE, DE))
+ {
+ alias C = OriginalType!T;
+ static assert(is(typeof(toLower(T.init)) == C));
+ static assert(is(typeof(toUpper(T.init)) == C));
+ }
+
+ //Return value tests for UDT and enum of UDT. These should be dchar
+ 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/libphobos/src/std/base64.d b/libphobos/src/std/base64.d
new file mode 100644
index 0000000..6211d10
--- /dev/null
+++ b/libphobos/src/std/base64.d
@@ -0,0 +1,2099 @@
+// Written in the D programming language.
+
+/**
+ * Support for Base64 encoding and decoding.
+ *
+ * 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:
+ * -----
+ * 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 range API is supported for both encoding and decoding:
+ *
+ * Example:
+ * -----
+ * // Create MIME Base64 with CRLF, per line 76.
+ * File f = File("./text.txt", "r");
+ * scope(exit) f.close();
+ *
+ * Appender!string mime64 = appender!string;
+ *
+ * foreach (encoded; Base64.encoder(f.byChunk(57)))
+ * {
+ * mime64.put(encoded);
+ * mime64.put("\r\n");
+ * }
+ *
+ * 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: $(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=<a href="#$1">$(D $2)</a>
+ */
+module std.base64;
+
+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]);
+}
+
+/**
+ * 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);
+}
+
+
+/**
+ * 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);
+}
+
+/**
+ * Unpadded variation of Base64 encoding that is safe for use in URLs and
+ * filenames, as used in RFCs 4648 and 7515 (JWS/JWT/JWE).
+ *
+ * See $(LREF Base64Impl) for a description of available methods.
+ */
+alias Base64URLNoPadding = Base64Impl!('-', '_', Base64.NoPadding);
+
+///
+@safe unittest
+{
+ ubyte[] data = [0x83, 0xd7, 0x30, 0x7b, 0xef];
+ assert(Base64URLNoPadding.encode(data) == "g9cwe-8");
+ assert(Base64URLNoPadding.decode("g9cwe-8") == data);
+}
+
+/**
+ * Template for implementing Base64 encoding and decoding.
+ *
+ * For most purposes, direct usage of this template is not necessary; instead,
+ * this module provides default implementations: $(LREF Base64), implementing
+ * basic Base64 encoding, and $(LREF Base64URL) and $(LREF Base64URLNoPadding),
+ * that implement the Base64 variant for use in URLs and filenames, with
+ * and without padding, respectively.
+ *
+ * Customized Base64 encoding schemes can be implemented by instantiating this
+ * template with the appropriate arguments. For example:
+ *
+ * -----
+ * // Non-standard Base64 format for embedding in regular expressions.
+ * alias Base64Re = Base64Impl!('!', '=', Base64.NoPadding);
+ * -----
+ *
+ * NOTE:
+ * 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 = '=')
+{
+ enum NoPadding = '\0'; /// represents no-padding encoding
+
+
+ // Verify Base64 characters
+ static assert(Map62th < 'A' || Map62th > 'Z', "Character '" ~ Map62th ~ "' cannot be used twice");
+ static assert(Map63th < 'A' || Map63th > 'Z', "Character '" ~ Map63th ~ "' cannot be used twice");
+ static assert(Padding < 'A' || Padding > 'Z', "Character '" ~ Padding ~ "' cannot be used twice");
+ static assert(Map62th < 'a' || Map62th > 'z', "Character '" ~ Map62th ~ "' cannot be used twice");
+ static assert(Map63th < 'a' || Map63th > 'z', "Character '" ~ Map63th ~ "' cannot be used twice");
+ static assert(Padding < 'a' || Padding > 'z', "Character '" ~ Padding ~ "' cannot be used twice");
+ static assert(Map62th < '0' || Map62th > '9', "Character '" ~ Map62th ~ "' cannot be used twice");
+ static assert(Map63th < '0' || Map63th > '9', "Character '" ~ Map63th ~ "' cannot be used twice");
+ static assert(Padding < '0' || Padding > '9', "Character '" ~ Padding ~ "' cannot be used twice");
+ static assert(Map62th != Map63th, "Character '" ~ Map63th ~ "' cannot be used twice");
+ static assert(Map62th != Padding, "Character '" ~ Padding ~ "' cannot be used twice");
+ static assert(Map63th != Padding, "Character '" ~ Padding ~ "' cannot be used twice");
+ static assert(Map62th != NoPadding, "'\\0' is not a valid Base64character");
+ static assert(Map63th != NoPadding, "'\\0' is not a valid Base64character");
+
+
+ /* Encode functions */
+
+
+ private immutable EncodeMap = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789" ~ Map62th ~ Map63th;
+
+
+ /**
+ * Calculates the length needed to store the encoded string corresponding
+ * to an input of the given length.
+ *
+ * Params:
+ * sourceLength = Length of the source array.
+ *
+ * Returns:
+ * The length of a Base64 encoding of an array of the given length.
+ */
+ @safe
+ pure nothrow size_t encodeLength(in size_t sourceLength)
+ {
+ static if (Padding == NoPadding)
+ return (sourceLength / 3) * 4 + (sourceLength % 3 == 0 ? 0 : sourceLength % 3 == 1 ? 2 : 3);
+ else
+ 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[Base64.encodeLength(data.length)];
+
+ Base64.encode(data, buf);
+ assert(buf == "Gis8TV1u");
+ }
+
+
+ // ubyte[] to char[]
+
+
+ /**
+ * Encode $(D_PARAM source) into a $(D char[]) buffer using Base64
+ * encoding.
+ *
+ * Params:
+ * source = The $(LINK2 std_range_primitives.html#isInputRange, input
+ * range) to _encode.
+ * buffer = The $(D char[]) buffer to store the encoded result.
+ *
+ * Returns:
+ * 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) &&
+ is(R2 == char[]))
+ in
+ {
+ assert(buffer.length >= encodeLength(source.length), "Insufficient buffer for encoding");
+ }
+ out(result)
+ {
+ assert(result.length == encodeLength(source.length), "The length of result is different from Base64");
+ }
+ body
+ {
+ immutable srcLen = source.length;
+ if (srcLen == 0)
+ return [];
+
+ immutable blocks = srcLen / 3;
+ immutable remain = srcLen % 3;
+ auto bufptr = buffer.ptr;
+ auto srcptr = source.ptr;
+
+ foreach (Unused; 0 .. blocks)
+ {
+ immutable val = srcptr[0] << 16 | srcptr[1] << 8 | srcptr[2];
+ *bufptr++ = EncodeMap[val >> 18 ];
+ *bufptr++ = EncodeMap[val >> 12 & 0x3f];
+ *bufptr++ = EncodeMap[val >> 6 & 0x3f];
+ *bufptr++ = EncodeMap[val & 0x3f];
+ srcptr += 3;
+ }
+
+ 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)
+ {
+ case 2:
+ *bufptr++ = EncodeMap[val >> 6 & 0x3f];
+ static if (Padding != NoPadding)
+ *bufptr++ = Padding;
+ break;
+ case 1:
+ static if (Padding != NoPadding)
+ {
+ *bufptr++ = Padding;
+ *bufptr++ = Padding;
+ }
+ break;
+ }
+ }
+
+ // encode method can't assume buffer length. So, slice needed.
+ 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/");
+ }
+
+
+ // InputRange to char[]
+
+
+ /**
+ * ditto
+ */
+ char[] encode(R1, R2)(R1 source, R2 buffer) if (!isArray!R1 && isInputRange!R1 &&
+ is(ElementType!R1 : ubyte) && hasLength!R1 &&
+ is(R2 == char[]))
+ in
+ {
+ assert(buffer.length >= encodeLength(source.length), "Insufficient buffer for encoding");
+ }
+ out(result)
+ {
+ // @@@BUG@@@ D's DbC can't caputre an argument of function and store the result of precondition.
+ //assert(result.length == encodeLength(source.length), "The length of result is different from Base64");
+ }
+ body
+ {
+ immutable srcLen = source.length;
+ if (srcLen == 0)
+ return [];
+
+ immutable blocks = srcLen / 3;
+ immutable remain = srcLen % 3;
+ auto bufptr = buffer.ptr;
+
+ foreach (Unused; 0 .. blocks)
+ {
+ immutable v1 = source.front; source.popFront();
+ immutable v2 = source.front; source.popFront();
+ immutable v3 = source.front; source.popFront();
+ immutable val = v1 << 16 | v2 << 8 | v3;
+ *bufptr++ = EncodeMap[val >> 18 ];
+ *bufptr++ = EncodeMap[val >> 12 & 0x3f];
+ *bufptr++ = EncodeMap[val >> 6 & 0x3f];
+ *bufptr++ = EncodeMap[val & 0x3f];
+ }
+
+ if (remain)
+ {
+ size_t val = source.front << 16;
+ if (remain == 2)
+ {
+ source.popFront();
+ val |= source.front << 8;
+ }
+
+ *bufptr++ = EncodeMap[val >> 18 ];
+ *bufptr++ = EncodeMap[val >> 12 & 0x3f];
+
+ final switch (remain)
+ {
+ case 2:
+ *bufptr++ = EncodeMap[val >> 6 & 0x3f];
+ static if (Padding != NoPadding)
+ *bufptr++ = Padding;
+ break;
+ case 1:
+ static if (Padding != NoPadding)
+ {
+ *bufptr++ = Padding;
+ *bufptr++ = Padding;
+ }
+ break;
+ }
+ }
+
+ // @@@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"
+ );
+
+ // encode method can't assume buffer length. So, slice needed.
+ return buffer[0 .. bufptr - buffer.ptr];
+ }
+
+
+ // ubyte[] to OutputRange
+
+
+ /**
+ * Encodes $(D_PARAM source) into an
+ * $(LINK2 std_range_primitives.html#isOutputRange, output range) using
+ * Base64 encoding.
+ *
+ * Params:
+ * 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 times the output range's $(D put) method was invoked.
+ */
+ 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");
+ }
+ body
+ {
+ immutable srcLen = source.length;
+ if (srcLen == 0)
+ return 0;
+
+ immutable blocks = srcLen / 3;
+ immutable remain = srcLen % 3;
+ auto srcptr = source.ptr;
+ size_t pcount;
+
+ 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]);
+ put(range, EncodeMap[val >> 6 & 0x3f]);
+ put(range, EncodeMap[val & 0x3f]);
+ srcptr += 3;
+ pcount += 4;
+ }
+
+ 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)
+ {
+ case 2:
+ put(range, EncodeMap[val >> 6 & 0x3f]);
+ pcount++;
+
+ static if (Padding != NoPadding)
+ {
+ put(range, Padding);
+ pcount++;
+ }
+ break;
+ case 1:
+ static if (Padding != NoPadding)
+ {
+ put(range, Padding);
+ put(range, Padding);
+ pcount += 2;
+ }
+ break;
+ }
+ }
+
+ 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
+
+
+ /**
+ * ditto
+ */
+ 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.
+ //assert(result == encodeLength(source.length), "The number of put is different from the length of Base64");
+ }
+ body
+ {
+ immutable srcLen = source.length;
+ if (srcLen == 0)
+ return 0;
+
+ immutable blocks = srcLen / 3;
+ immutable remain = srcLen % 3;
+ size_t pcount;
+
+ foreach (Unused; 0 .. blocks)
+ {
+ immutable v1 = source.front; source.popFront();
+ immutable v2 = source.front; source.popFront();
+ immutable v3 = source.front; source.popFront();
+ immutable val = v1 << 16 | v2 << 8 | v3;
+ put(range, EncodeMap[val >> 18 ]);
+ put(range, EncodeMap[val >> 12 & 0x3f]);
+ put(range, EncodeMap[val >> 6 & 0x3f]);
+ put(range, EncodeMap[val & 0x3f]);
+ pcount += 4;
+ }
+
+ if (remain)
+ {
+ size_t val = source.front << 16;
+ if (remain == 2)
+ {
+ source.popFront();
+ val |= source.front << 8;
+ }
+
+ put(range, EncodeMap[val >> 18 ]);
+ put(range, EncodeMap[val >> 12 & 0x3f]);
+ pcount += 2;
+
+ final switch (remain)
+ {
+ case 2:
+ put(range, EncodeMap[val >> 6 & 0x3f]);
+ pcount++;
+
+ static if (Padding != NoPadding)
+ {
+ put(range, Padding);
+ pcount++;
+ }
+ break;
+ case 1:
+ static if (Padding != NoPadding)
+ {
+ put(range, Padding);
+ put(range, Padding);
+ pcount += 2;
+ }
+ break;
+ }
+ }
+
+ // @@@BUG@@@ Workaround for DbC problem.
+ version (unittest)
+ assert(
+ pcount == encodeLength(srcLen),
+ "The number of put is different from the length of Base64"
+ );
+
+ return pcount;
+ }
+
+
+ /**
+ * 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.
+ *
+ * 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))
+ {
+ return encode(source, new char[encodeLength(source.length)]);
+ }
+
+ ///
+ @safe unittest
+ {
+ ubyte[] data = [0x1a, 0x2b, 0x3c, 0x4d, 0x5d, 0x6e];
+ assert(Base64.encode(data) == "Gis8TV1u");
+ }
+
+
+ /**
+ * ditto
+ */
+ char[] encode(Range)(Range source) if (!isArray!Range && isInputRange!Range &&
+ is(ElementType!Range : ubyte) && hasLength!Range)
+ {
+ return encode(source, new char[encodeLength(source.length)]);
+ }
+
+
+ /**
+ * 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)[])))
+ {
+ private:
+ Range range_;
+ char[] buffer_, encoded_;
+
+
+ public:
+ this(Range range)
+ {
+ range_ = range;
+ doEncoding();
+ }
+
+
+ /**
+ * Returns:
+ * true if there is no more encoded data left.
+ */
+ @property @trusted
+ bool empty()
+ {
+ return range_.empty;
+ }
+
+
+ /**
+ * Returns: The current chunk of encoded data.
+ */
+ @property @safe
+ nothrow char[] front()
+ {
+ return encoded_;
+ }
+
+
+ /**
+ * Advance the range to the next chunk of encoded data.
+ *
+ * Throws:
+ * $(D Base64Exception) If invoked when
+ * $(LREF2 .Base64Impl.Encoder.empty, empty) returns $(D true).
+ */
+ void popFront()
+ {
+ enforce(!empty, new Base64Exception("Cannot call popFront on Encoder with no data remaining"));
+
+ range_.popFront();
+
+ /*
+ * This check is very ugly. I think this is a Range's flaw.
+ * I very strongly want the Range guideline for unified implementation.
+ *
+ * In this case, Encoder becomes a beautiful implementation if 'front' performs Base64 encoding.
+ */
+ if (!empty)
+ doEncoding();
+ }
+
+
+ static if (isForwardRange!Range)
+ {
+ /**
+ * 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).
+ */
+ @property
+ typeof(this) save()
+ {
+ typeof(return) encoder;
+
+ encoder.range_ = range_.save;
+ encoder.buffer_ = buffer_.dup;
+ encoder.encoded_ = encoder.buffer_[0 .. encoded_.length];
+
+ return encoder;
+ }
+ }
+
+
+ private:
+ void doEncoding()
+ {
+ auto data = cast(const(ubyte)[])range_.front;
+ auto size = encodeLength(data.length);
+ if (size > buffer_.length)
+ buffer_.length = size;
+
+ encoded_ = encode(data, buffer_);
+ }
+ }
+
+
+ /**
+ * 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))
+ {
+ private:
+ Range range_;
+ ubyte first;
+ int pos, padding;
+
+
+ public:
+ this(Range range)
+ {
+ range_ = range;
+ static if (isForwardRange!Range)
+ range_ = range_.save;
+
+ if (range_.empty)
+ pos = -1;
+ else
+ popFront();
+ }
+
+
+ /**
+ * Returns:
+ * true if there are no more encoded characters to be iterated.
+ */
+ @property @safe
+ nothrow bool empty() const
+ {
+ static if (Padding == NoPadding)
+ return pos < 0;
+ else
+ return pos < 0 && !padding;
+ }
+
+
+ /**
+ * Returns: The current encoded character.
+ */
+ @property @safe
+ nothrow ubyte front()
+ {
+ return first;
+ }
+
+
+ /**
+ * Advance to the next encoded character.
+ *
+ * Throws:
+ * $(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)
+ {
+ first = Padding;
+ pos = -1;
+ padding--;
+ return;
+ }
+
+ if (range_.empty)
+ {
+ pos = -1;
+ return;
+ }
+
+ final switch (pos)
+ {
+ case 0:
+ first = EncodeMap[range_.front >> 2];
+ break;
+ case 1:
+ immutable t = (range_.front & 0b11) << 4;
+ range_.popFront();
+
+ if (range_.empty)
+ {
+ first = EncodeMap[t];
+ padding = 3;
+ }
+ else
+ {
+ first = EncodeMap[t | (range_.front >> 4)];
+ }
+ break;
+ case 2:
+ immutable t = (range_.front & 0b1111) << 2;
+ range_.popFront();
+
+ if (range_.empty)
+ {
+ first = EncodeMap[t];
+ padding = 2;
+ }
+ else
+ {
+ first = EncodeMap[t | (range_.front >> 6)];
+ }
+ break;
+ case 3:
+ first = EncodeMap[range_.front & 0b111111];
+ range_.popFront();
+ break;
+ }
+
+ ++pos %= 4;
+ }
+
+
+ static if (isForwardRange!Range)
+ {
+ /**
+ * 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).
+ */
+ @property
+ typeof(this) save()
+ {
+ auto encoder = this;
+ encoder.range_ = encoder.range_.save;
+ return encoder;
+ }
+ }
+ }
+
+
+ /**
+ * Construct an $(D Encoder) that iterates over the Base64 encoding of the
+ * given $(LINK2 std_range_primitives.html#isInputRange, input range).
+ *
+ * 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();
+ *
+ * uint line = 0;
+ * foreach (encoded; Base64.encoder(f.byLine()))
+ * {
+ * writeln(++line, ". ", encoded);
+ * }
+ * -----
+ *
+ * Example:
+ * This example encodes the input data one byte at a time.
+ * -----
+ * ubyte[] data = cast(ubyte[]) "0123456789";
+ *
+ * // The ElementType of data is not aggregation type
+ * foreach (encoded; Base64.encoder(data))
+ * {
+ * writeln(encoded);
+ * }
+ * -----
+ */
+ Encoder!(Range) encoder(Range)(Range range) if (isInputRange!Range)
+ {
+ return typeof(return)(range);
+ }
+
+
+ /* Decode functions */
+
+
+ private immutable int[char.max + 1] DecodeMap = [
+ 'A':0b000000, 'B':0b000001, 'C':0b000010, 'D':0b000011, 'E':0b000100,
+ 'F':0b000101, 'G':0b000110, 'H':0b000111, 'I':0b001000, 'J':0b001001,
+ 'K':0b001010, 'L':0b001011, 'M':0b001100, 'N':0b001101, 'O':0b001110,
+ 'P':0b001111, 'Q':0b010000, 'R':0b010001, 'S':0b010010, 'T':0b010011,
+ 'U':0b010100, 'V':0b010101, 'W':0b010110, 'X':0b010111, 'Y':0b011000,
+ 'Z':0b011001, 'a':0b011010, 'b':0b011011, 'c':0b011100, 'd':0b011101,
+ 'e':0b011110, 'f':0b011111, 'g':0b100000, 'h':0b100001, 'i':0b100010,
+ 'j':0b100011, 'k':0b100100, 'l':0b100101, 'm':0b100110, 'n':0b100111,
+ 'o':0b101000, 'p':0b101001, 'q':0b101010, 'r':0b101011, 's':0b101100,
+ 't':0b101101, 'u':0b101110, 'v':0b101111, 'w':0b110000, 'x':0b110001,
+ 'y':0b110010, 'z':0b110011, '0':0b110100, '1':0b110101, '2':0b110110,
+ '3':0b110111, '4':0b111000, '5':0b111001, '6':0b111010, '7':0b111011,
+ '8':0b111100, '9':0b111101, Map62th:0b111110, Map63th:0b111111, Padding:-1
+ ];
+
+
+ /**
+ * Given a Base64 encoded string, calculates the length of the decoded
+ * string.
+ *
+ * Params:
+ * sourceLength = The length of the Base64 encoding.
+ *
+ * Returns:
+ * 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)
+ {
+ static if (Padding == NoPadding)
+ return (sourceLength / 4) * 3 + (sourceLength % 4 < 2 ? 0 : sourceLength % 4 == 2 ? 1 : 2);
+ else
+ 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.
+ @safe
+ pure nothrow private size_t realDecodeLength(R)(R source)
+ {
+ auto expect = decodeLength(source.length);
+ static if (Padding != NoPadding)
+ {
+ if (source.length % 4 == 0)
+ {
+ expect -= source.length == 0 ? 0 :
+ source[$ - 2] == Padding ? 2 :
+ source[$ - 1] == Padding ? 1 : 0;
+ }
+ }
+ return expect;
+ }
+
+
+ // char[] to ubyte[]
+
+
+ /**
+ * Decodes $(D_PARAM source) into the given buffer.
+ *
+ * Params:
+ * source = The $(LINK2 std_range_primitives.html#isInputRange, input
+ * range) to _decode.
+ * buffer = The buffer to store decoded result.
+ *
+ * Returns:
+ * The slice of $(D_PARAM buffer) containing the decoded result.
+ *
+ * Throws:
+ * $(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 >= realDecodeLength(source), "Insufficient buffer for decoding");
+ }
+ out(result)
+ {
+ immutable expect = realDecodeLength(source);
+ assert(result.length == expect, "The length of result is different from the expected length");
+ }
+ body
+ {
+ immutable srcLen = source.length;
+ if (srcLen == 0)
+ return [];
+ static if (Padding != NoPadding)
+ enforce(srcLen % 4 == 0, new Base64Exception("Invalid length of encoded data"));
+
+ immutable blocks = srcLen / 4;
+ auto srcptr = source.ptr;
+ auto bufptr = buffer.ptr;
+
+ foreach (Unused; 0 .. blocks)
+ {
+ immutable v1 = decodeChar(*srcptr++);
+ immutable v2 = decodeChar(*srcptr++);
+
+ *bufptr++ = cast(ubyte)(v1 << 2 | v2 >> 4);
+
+ immutable v3 = decodeChar(*srcptr++);
+ if (v3 == -1)
+ break;
+
+ *bufptr++ = cast(ubyte)((v2 << 4 | v3 >> 2) & 0xff);
+
+ immutable v4 = decodeChar(*srcptr++);
+ if (v4 == -1)
+ break;
+
+ *bufptr++ = cast(ubyte)((v3 << 6 | v4) & 0xff);
+ }
+
+ static if (Padding == NoPadding)
+ {
+ immutable remain = srcLen % 4;
+
+ if (remain)
+ {
+ immutable v1 = decodeChar(*srcptr++);
+ immutable v2 = decodeChar(*srcptr++);
+
+ *bufptr++ = cast(ubyte)(v1 << 2 | v2 >> 4);
+
+ if (remain == 3)
+ *bufptr++ = cast(ubyte)((v2 << 4 | decodeChar(*srcptr++) >> 2) & 0xff);
+ }
+ }
+
+ 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[]
+
+
+ /**
+ * ditto
+ */
+ ubyte[] decode(R1, R2)(R1 source, R2 buffer) if (!isArray!R1 && isInputRange!R1 &&
+ is(ElementType!R1 : dchar) && hasLength!R1 &&
+ is(R2 == ubyte[]) && isOutputRange!(R2, ubyte))
+ in
+ {
+ assert(buffer.length >= decodeLength(source.length), "Insufficient buffer for decoding");
+ }
+ out(result)
+ {
+ // @@@BUG@@@ Workaround for DbC problem.
+ //immutable expect = decodeLength(source.length) - 2;
+ //assert(result.length >= expect, "The length of result is smaller than expected length");
+ }
+ body
+ {
+ immutable srcLen = source.length;
+ if (srcLen == 0)
+ return [];
+ static if (Padding != NoPadding)
+ enforce(srcLen % 4 == 0, new Base64Exception("Invalid length of encoded data"));
+
+ immutable blocks = srcLen / 4;
+ auto bufptr = buffer.ptr;
+
+ foreach (Unused; 0 .. blocks)
+ {
+ immutable v1 = decodeChar(source.front); source.popFront();
+ immutable v2 = decodeChar(source.front); source.popFront();
+
+ *bufptr++ = cast(ubyte)(v1 << 2 | v2 >> 4);
+
+ immutable v3 = decodeChar(source.front);
+ if (v3 == -1)
+ break;
+
+ *bufptr++ = cast(ubyte)((v2 << 4 | v3 >> 2) & 0xff);
+ source.popFront();
+
+ immutable v4 = decodeChar(source.front);
+ if (v4 == -1)
+ break;
+
+ *bufptr++ = cast(ubyte)((v3 << 6 | v4) & 0xff);
+ source.popFront();
+ }
+
+ static if (Padding == NoPadding)
+ {
+ immutable remain = srcLen % 4;
+
+ if (remain)
+ {
+ immutable v1 = decodeChar(source.front); source.popFront();
+ immutable v2 = decodeChar(source.front);
+
+ *bufptr++ = cast(ubyte)(v1 << 2 | v2 >> 4);
+
+ if (remain == 3)
+ {
+ source.popFront();
+ *bufptr++ = cast(ubyte)((v2 << 4 | decodeChar(source.front) >> 2) & 0xff);
+ }
+ }
+ }
+
+ // @@@BUG@@@ Workaround for DbC problem.
+ version (unittest)
+ assert(
+ (bufptr - buffer.ptr) >= (decodeLength(srcLen) - 2),
+ "The length of result is smaller than expected length"
+ );
+
+ return buffer[0 .. bufptr - buffer.ptr];
+ }
+
+
+ // char[] to OutputRange
+
+
+ /**
+ * Decodes $(D_PARAM source) into a given
+ * $(LINK2 std_range_primitives.html#isOutputRange, output range).
+ *
+ * Params:
+ * 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 times the output range's $(D put) method was invoked.
+ *
+ * Throws:
+ * $(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, auto ref R2 range)
+ if (isArray!R1 && is(ElementType!R1 : dchar) &&
+ !is(R2 == ubyte[]) && isOutputRange!(R2, ubyte))
+ out(result)
+ {
+ immutable expect = realDecodeLength(source);
+ assert(result == expect, "The result of decode is different from the expected");
+ }
+ body
+ {
+ immutable srcLen = source.length;
+ if (srcLen == 0)
+ return 0;
+ static if (Padding != NoPadding)
+ enforce(srcLen % 4 == 0, new Base64Exception("Invalid length of encoded data"));
+
+ immutable blocks = srcLen / 4;
+ auto srcptr = source.ptr;
+ size_t pcount;
+
+ foreach (Unused; 0 .. blocks)
+ {
+ immutable v1 = decodeChar(*srcptr++);
+ immutable v2 = decodeChar(*srcptr++);
+
+ put(range, cast(ubyte)(v1 << 2 | v2 >> 4));
+ pcount++;
+
+ immutable v3 = decodeChar(*srcptr++);
+ if (v3 == -1)
+ break;
+
+ put(range, cast(ubyte)((v2 << 4 | v3 >> 2) & 0xff));
+ pcount++;
+
+ immutable v4 = decodeChar(*srcptr++);
+ if (v4 == -1)
+ break;
+
+ put(range, cast(ubyte)((v3 << 6 | v4) & 0xff));
+ pcount++;
+ }
+
+ static if (Padding == NoPadding)
+ {
+ immutable remain = srcLen % 4;
+
+ if (remain)
+ {
+ immutable v1 = decodeChar(*srcptr++);
+ immutable v2 = decodeChar(*srcptr++);
+
+ put(range, cast(ubyte)(v1 << 2 | v2 >> 4));
+ pcount++;
+
+ if (remain == 3)
+ {
+ put(range, cast(ubyte)((v2 << 4 | decodeChar(*srcptr++) >> 2) & 0xff));
+ pcount++;
+ }
+ }
+ }
+
+ 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
+
+
+ /**
+ * ditto
+ */
+ 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.
+ //immutable expect = decodeLength(source.length) - 2;
+ //assert(result >= expect, "The length of result is smaller than expected length");
+ }
+ body
+ {
+ immutable srcLen = source.length;
+ if (srcLen == 0)
+ return 0;
+ static if (Padding != NoPadding)
+ enforce(srcLen % 4 == 0, new Base64Exception("Invalid length of encoded data"));
+
+ immutable blocks = srcLen / 4;
+ size_t pcount;
+
+ foreach (Unused; 0 .. blocks)
+ {
+ immutable v1 = decodeChar(source.front); source.popFront();
+ immutable v2 = decodeChar(source.front); source.popFront();
+
+ put(range, cast(ubyte)(v1 << 2 | v2 >> 4));
+ pcount++;
+
+ immutable v3 = decodeChar(source.front);
+ if (v3 == -1)
+ break;
+
+ put(range, cast(ubyte)((v2 << 4 | v3 >> 2) & 0xff));
+ source.popFront();
+ pcount++;
+
+ immutable v4 = decodeChar(source.front);
+ if (v4 == -1)
+ break;
+
+ put(range, cast(ubyte)((v3 << 6 | v4) & 0xff));
+ source.popFront();
+ pcount++;
+ }
+
+ static if (Padding == NoPadding)
+ {
+ immutable remain = srcLen % 4;
+
+ 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)
+ {
+ source.popFront();
+ put(range, cast(ubyte)((v2 << 4 | decodeChar(source.front) >> 2) & 0xff));
+ pcount++;
+ }
+ }
+ }
+
+ // @@@BUG@@@ Workaround for DbC problem.
+ version (unittest)
+ assert(
+ pcount >= (decodeLength(srcLen) - 2),
+ "The length of result is smaller than expected length"
+ );
+
+ return pcount;
+ }
+
+
+ /**
+ * 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.
+ *
+ * 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))
+ {
+ return decode(source, new ubyte[decodeLength(source.length)]);
+ }
+
+ ///
+ @safe unittest
+ {
+ auto data = "Gis8TV1u";
+ assert(Base64.decode(data) == [0x1a, 0x2b, 0x3c, 0x4d, 0x5d, 0x6e]);
+ }
+
+
+ /**
+ * ditto
+ */
+ ubyte[] decode(Range)(Range source) if (!isArray!Range && isInputRange!Range &&
+ is(ElementType!Range : dchar) && hasLength!Range)
+ {
+ return decode(source, new ubyte[decodeLength(source.length)]);
+ }
+
+
+ /**
+ * 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)[])))
+ {
+ private:
+ Range range_;
+ ubyte[] buffer_, decoded_;
+
+
+ public:
+ this(Range range)
+ {
+ range_ = range;
+ doDecoding();
+ }
+
+
+ /**
+ * Returns:
+ * true if there are no more elements to be iterated.
+ */
+ @property @trusted
+ bool empty()
+ {
+ return range_.empty;
+ }
+
+
+ /**
+ * Returns: The decoding of the current element in the input.
+ */
+ @property @safe
+ nothrow ubyte[] front()
+ {
+ return decoded_;
+ }
+
+
+ /**
+ * Advance to the next element in the input to be decoded.
+ *
+ * Throws:
+ * $(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."));
+
+ range_.popFront();
+
+ /*
+ * I mentioned Encoder's popFront.
+ */
+ if (!empty)
+ doDecoding();
+ }
+
+
+ static if (isForwardRange!Range)
+ {
+ /**
+ * Saves the current iteration state.
+ *
+ * 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()
+ {
+ typeof(return) decoder;
+
+ decoder.range_ = range_.save;
+ decoder.buffer_ = buffer_.dup;
+ decoder.decoded_ = decoder.buffer_[0 .. decoded_.length];
+
+ return decoder;
+ }
+ }
+
+
+ private:
+ void doDecoding()
+ {
+ auto data = cast(const(char)[])range_.front;
+
+ static if (Padding == NoPadding)
+ {
+ while (data.length % 4 == 1)
+ {
+ range_.popFront();
+ data ~= cast(const(char)[])range_.front;
+ }
+ }
+ else
+ {
+ while (data.length % 4 != 0)
+ {
+ range_.popFront();
+ data ~= cast(const(char)[])range_.front;
+ }
+ }
+
+ auto size = decodeLength(data.length);
+ if (size > buffer_.length)
+ buffer_.length = size;
+
+ decoded_ = decode(data, buffer_);
+ }
+ }
+
+
+ /**
+ * 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))
+ {
+ private:
+ Range range_;
+ ubyte first;
+ int pos;
+
+
+ public:
+ this(Range range)
+ {
+ range_ = range;
+ static if (isForwardRange!Range)
+ range_ = range_.save;
+
+ static if (Padding != NoPadding && hasLength!Range)
+ enforce(range_.length % 4 == 0, new Base64Exception("Invalid length of encoded data"));
+
+ if (range_.empty)
+ pos = -1;
+ else
+ popFront();
+ }
+
+
+ /**
+ * Returns:
+ * true if there are no more elements to be iterated.
+ */
+ @property @safe
+ nothrow bool empty() const
+ {
+ return pos < 0;
+ }
+
+
+ /**
+ * Returns: The current decoded byte.
+ */
+ @property @safe
+ nothrow ubyte front()
+ {
+ return first;
+ }
+
+
+ /**
+ * Advance to the next decoded byte.
+ *
+ * Throws:
+ * $(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)
+ {
+ bool endCondition()
+ {
+ return range_.empty;
+ }
+ }
+ else
+ {
+ bool endCondition()
+ {
+ enforce(!range_.empty, new Base64Exception("Missing padding"));
+ return range_.front == Padding;
+ }
+ }
+
+ if (range_.empty || range_.front == Padding)
+ {
+ pos = -1;
+ return;
+ }
+
+ final switch (pos)
+ {
+ case 0:
+ enforce(!endCondition(), new Base64Exception("Premature end of data found"));
+
+ immutable t = DecodeMap[range_.front] << 2;
+ range_.popFront();
+
+ enforce(!endCondition(), new Base64Exception("Premature end of data found"));
+ first = cast(ubyte)(t | (DecodeMap[range_.front] >> 4));
+ break;
+ case 1:
+ immutable t = (DecodeMap[range_.front] & 0b1111) << 4;
+ range_.popFront();
+
+ if (endCondition())
+ {
+ pos = -1;
+ return;
+ }
+ else
+ {
+ first = cast(ubyte)(t | (DecodeMap[range_.front] >> 2));
+ }
+ break;
+ case 2:
+ immutable t = (DecodeMap[range_.front] & 0b11) << 6;
+ range_.popFront();
+
+ if (endCondition())
+ {
+ pos = -1;
+ return;
+ }
+ else
+ {
+ first = cast(ubyte)(t | DecodeMap[range_.front]);
+ }
+
+ range_.popFront();
+ break;
+ }
+
+ ++pos %= 3;
+ }
+
+
+ static if (isForwardRange!Range)
+ {
+ /**
+ * Saves the current iteration state.
+ *
+ * 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()
+ {
+ auto decoder = this;
+ decoder.range_ = decoder.range_.save;
+ return 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.
+ *
+ * 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()))
+ * {
+ * writeln(decoded);
+ * }
+ * -----
+ *
+ * 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)))
+ * {
+ * writeln(n);
+ * }
+ * -----
+ */
+ Decoder!(Range) decoder(Range)(Range range) if (isInputRange!Range)
+ {
+ return typeof(return)(range);
+ }
+
+
+ private:
+ @safe
+ pure int decodeChar()(char chr)
+ {
+ immutable val = DecodeMap[chr];
+
+ // enforce can't be a pure function, so I use trivial check.
+ if (val == 0 && chr != 'A')
+ throw new Base64Exception("Invalid character: " ~ chr);
+
+ return val;
+ }
+
+
+ @safe
+ pure int decodeChar()(dchar chr)
+ {
+ // See above comment.
+ if (chr > 0x7f)
+ throw new Base64Exception("Base64-encoded character must be a single byte");
+
+ return decodeChar(cast(char) chr);
+ }
+}
+
+///
+@safe unittest
+{
+ import std.string : representation;
+
+ // pre-defined: alias Base64 = Base64Impl!('+', '/');
+ ubyte[] emptyArr;
+ assert(Base64.encode(emptyArr) == "");
+ assert(Base64.encode("f".representation) == "Zg==");
+ assert(Base64.encode("foo".representation) == "Zm9v");
+
+ alias Base64Re = Base64Impl!('!', '=', Base64.NoPadding);
+ assert(Base64Re.encode("f".representation) == "Zg");
+ assert(Base64Re.encode("foo".representation) == "Zm9v");
+}
+
+/**
+ * Exception thrown upon encountering Base64 encoding or decoding errors.
+ */
+class Base64Exception : Exception
+{
+ @safe pure nothrow
+ this(string s, string fn = __FILE__, size_t ln = __LINE__)
+ {
+ super(s, fn, ln);
+ }
+}
+
+///
+@system unittest
+{
+ import std.exception : assertThrown;
+ assertThrown!Base64Exception(Base64.decode("ab|c"));
+}
+
+
+@system unittest
+{
+ import std.algorithm.comparison : equal;
+ import std.algorithm.sorting : sort;
+ import std.conv;
+ import std.file;
+ import std.stdio;
+
+ alias Base64Re = Base64Impl!('!', '=', Base64.NoPadding);
+
+ // Test vectors from RFC 4648
+ ubyte[][string] tv = [
+ "" :cast(ubyte[])"",
+ "f" :cast(ubyte[])"f",
+ "fo" :cast(ubyte[])"fo",
+ "foo" :cast(ubyte[])"foo",
+ "foob" :cast(ubyte[])"foob",
+ "fooba" :cast(ubyte[])"fooba",
+ "foobar":cast(ubyte[])"foobar"
+ ];
+
+ { // Base64
+ // encode
+ assert(Base64.encodeLength(tv[""].length) == 0);
+ assert(Base64.encodeLength(tv["f"].length) == 4);
+ assert(Base64.encodeLength(tv["fo"].length) == 4);
+ assert(Base64.encodeLength(tv["foo"].length) == 4);
+ assert(Base64.encodeLength(tv["foob"].length) == 8);
+ assert(Base64.encodeLength(tv["fooba"].length) == 8);
+ assert(Base64.encodeLength(tv["foobar"].length) == 8);
+
+ assert(Base64.encode(tv[""]) == "");
+ assert(Base64.encode(tv["f"]) == "Zg==");
+ assert(Base64.encode(tv["fo"]) == "Zm8=");
+ assert(Base64.encode(tv["foo"]) == "Zm9v");
+ assert(Base64.encode(tv["foob"]) == "Zm9vYg==");
+ assert(Base64.encode(tv["fooba"]) == "Zm9vYmE=");
+ assert(Base64.encode(tv["foobar"]) == "Zm9vYmFy");
+
+ // decode
+ assert(Base64.decodeLength(Base64.encode(tv[""]).length) == 0);
+ assert(Base64.decodeLength(Base64.encode(tv["f"]).length) == 3);
+ assert(Base64.decodeLength(Base64.encode(tv["fo"]).length) == 3);
+ assert(Base64.decodeLength(Base64.encode(tv["foo"]).length) == 3);
+ assert(Base64.decodeLength(Base64.encode(tv["foob"]).length) == 6);
+ assert(Base64.decodeLength(Base64.encode(tv["fooba"]).length) == 6);
+ assert(Base64.decodeLength(Base64.encode(tv["foobar"]).length) == 6);
+
+ assert(Base64.decode(Base64.encode(tv[""])) == tv[""]);
+ assert(Base64.decode(Base64.encode(tv["f"])) == tv["f"]);
+ assert(Base64.decode(Base64.encode(tv["fo"])) == tv["fo"]);
+ assert(Base64.decode(Base64.encode(tv["foo"])) == tv["foo"]);
+ assert(Base64.decode(Base64.encode(tv["foob"])) == tv["foob"]);
+ assert(Base64.decode(Base64.encode(tv["fooba"])) == tv["fooba"]);
+ assert(Base64.decode(Base64.encode(tv["foobar"])) == tv["foobar"]);
+
+ assertThrown!Base64Exception(Base64.decode("ab|c"));
+
+ // Test decoding incomplete strings. RFC does not specify the correct
+ // behavior, but the code should never throw Errors on invalid input.
+
+ // decodeLength is nothrow
+ assert(Base64.decodeLength(1) == 0);
+ assert(Base64.decodeLength(2) <= 1);
+ assert(Base64.decodeLength(3) <= 2);
+
+ // may throw Exceptions, may not throw Errors
+ assertThrown!Base64Exception(Base64.decode("Zg"));
+ assertThrown!Base64Exception(Base64.decode("Zg="));
+ assertThrown!Base64Exception(Base64.decode("Zm8"));
+ assertThrown!Base64Exception(Base64.decode("Zg==;"));
+ }
+
+ { // No padding
+ // encode
+ assert(Base64Re.encodeLength(tv[""].length) == 0);
+ assert(Base64Re.encodeLength(tv["f"].length) == 2);
+ assert(Base64Re.encodeLength(tv["fo"].length) == 3);
+ assert(Base64Re.encodeLength(tv["foo"].length) == 4);
+ assert(Base64Re.encodeLength(tv["foob"].length) == 6);
+ assert(Base64Re.encodeLength(tv["fooba"].length) == 7);
+ assert(Base64Re.encodeLength(tv["foobar"].length) == 8);
+
+ assert(Base64Re.encode(tv[""]) == "");
+ assert(Base64Re.encode(tv["f"]) == "Zg");
+ assert(Base64Re.encode(tv["fo"]) == "Zm8");
+ assert(Base64Re.encode(tv["foo"]) == "Zm9v");
+ assert(Base64Re.encode(tv["foob"]) == "Zm9vYg");
+ assert(Base64Re.encode(tv["fooba"]) == "Zm9vYmE");
+ assert(Base64Re.encode(tv["foobar"]) == "Zm9vYmFy");
+
+ // decode
+ assert(Base64Re.decodeLength(Base64Re.encode(tv[""]).length) == 0);
+ assert(Base64Re.decodeLength(Base64Re.encode(tv["f"]).length) == 1);
+ assert(Base64Re.decodeLength(Base64Re.encode(tv["fo"]).length) == 2);
+ assert(Base64Re.decodeLength(Base64Re.encode(tv["foo"]).length) == 3);
+ assert(Base64Re.decodeLength(Base64Re.encode(tv["foob"]).length) == 4);
+ assert(Base64Re.decodeLength(Base64Re.encode(tv["fooba"]).length) == 5);
+ assert(Base64Re.decodeLength(Base64Re.encode(tv["foobar"]).length) == 6);
+
+ assert(Base64Re.decode(Base64Re.encode(tv[""])) == tv[""]);
+ assert(Base64Re.decode(Base64Re.encode(tv["f"])) == tv["f"]);
+ assert(Base64Re.decode(Base64Re.encode(tv["fo"])) == tv["fo"]);
+ assert(Base64Re.decode(Base64Re.encode(tv["foo"])) == tv["foo"]);
+ assert(Base64Re.decode(Base64Re.encode(tv["foob"])) == tv["foob"]);
+ assert(Base64Re.decode(Base64Re.encode(tv["fooba"])) == tv["fooba"]);
+ assert(Base64Re.decode(Base64Re.encode(tv["foobar"])) == tv["foobar"]);
+
+ // decodeLength is nothrow
+ assert(Base64.decodeLength(1) == 0);
+ }
+
+ { // with OutputRange
+ import std.array;
+
+ auto a = Appender!(char[])([]);
+ auto b = Appender!(ubyte[])([]);
+
+ assert(Base64.encode(tv[""], a) == 0);
+ assert(Base64.decode(a.data, b) == 0);
+ assert(tv[""] == b.data); a.clear(); b.clear();
+
+ assert(Base64.encode(tv["f"], a) == 4);
+ assert(Base64.decode(a.data, b) == 1);
+ assert(tv["f"] == b.data); a.clear(); b.clear();
+
+ assert(Base64.encode(tv["fo"], a) == 4);
+ assert(Base64.decode(a.data, b) == 2);
+ assert(tv["fo"] == b.data); a.clear(); b.clear();
+
+ assert(Base64.encode(tv["foo"], a) == 4);
+ assert(Base64.decode(a.data, b) == 3);
+ assert(tv["foo"] == b.data); a.clear(); b.clear();
+
+ assert(Base64.encode(tv["foob"], a) == 8);
+ assert(Base64.decode(a.data, b) == 4);
+ assert(tv["foob"] == b.data); a.clear(); b.clear();
+
+ assert(Base64.encode(tv["fooba"], a) == 8);
+ assert(Base64.decode(a.data, b) == 5);
+ assert(tv["fooba"] == b.data); a.clear(); b.clear();
+
+ assert(Base64.encode(tv["foobar"], a) == 8);
+ assert(Base64.decode(a.data, b) == 6);
+ assert(tv["foobar"] == b.data); a.clear(); b.clear();
+ }
+
+ // @@@9543@@@ These tests were disabled because they actually relied on the input range having length.
+ // The implementation (currently) doesn't support encoding/decoding from a length-less source.
+ version (none)
+ { // with InputRange
+ // InputRange to ubyte[] or char[]
+ auto encoded = Base64.encode(map!(to!(ubyte))(["20", "251", "156", "3", "217", "126"]));
+ assert(encoded == "FPucA9l+");
+ assert(Base64.decode(map!q{a}(encoded)) == [0x14, 0xfb, 0x9c, 0x03, 0xd9, 0x7e]);
+
+ // InputRange to OutputRange
+ auto a = Appender!(char[])([]);
+ auto b = Appender!(ubyte[])([]);
+ assert(Base64.encode(map!(to!(ubyte))(["20", "251", "156", "3", "217", "126"]), a) == 8);
+ assert(a.data == "FPucA9l+");
+ assert(Base64.decode(map!q{a}(a.data), b) == 6);
+ assert(b.data == [0x14, 0xfb, 0x9c, 0x03, 0xd9, 0x7e]);
+ }
+
+ { // Encoder and Decoder
+ {
+ string encode_file = std.file.deleteme ~ "-testingEncoder";
+ std.file.write(encode_file, "\nf\nfo\nfoo\nfoob\nfooba\nfoobar");
+
+ auto witness = ["", "Zg==", "Zm8=", "Zm9v", "Zm9vYg==", "Zm9vYmE=", "Zm9vYmFy"];
+ auto f = File(encode_file);
+ scope(exit)
+ {
+ f.close();
+ assert(!f.isOpen);
+ std.file.remove(encode_file);
+ }
+
+ size_t i;
+ foreach (encoded; Base64.encoder(f.byLine()))
+ assert(encoded == witness[i++]);
+
+ assert(i == witness.length);
+ }
+
+ {
+ string decode_file = std.file.deleteme ~ "-testingDecoder";
+ std.file.write(decode_file, "\nZg==\nZm8=\nZm9v\nZm9vYg==\nZm9vYmE=\nZm9vYmFy");
+
+ auto witness = sort(tv.keys);
+ auto f = File(decode_file);
+ scope(exit)
+ {
+ f.close();
+ assert(!f.isOpen);
+ std.file.remove(decode_file);
+ }
+
+ size_t i;
+ foreach (decoded; Base64.decoder(f.byLine()))
+ assert(decoded == witness[i++]);
+
+ assert(i == witness.length);
+ }
+
+ { // ForwardRange
+ {
+ auto encoder = Base64.encoder(sort(tv.values));
+ auto witness = ["", "Zg==", "Zm8=", "Zm9v", "Zm9vYg==", "Zm9vYmE=", "Zm9vYmFy"];
+ size_t i;
+
+ assert(encoder.front == witness[i++]); encoder.popFront();
+ assert(encoder.front == witness[i++]); encoder.popFront();
+ assert(encoder.front == witness[i++]); encoder.popFront();
+
+ foreach (encoded; encoder.save)
+ assert(encoded == witness[i++]);
+ }
+
+ {
+ auto decoder = Base64.decoder(["", "Zg==", "Zm8=", "Zm9v", "Zm9vYg==", "Zm9vYmE=", "Zm9vYmFy"]);
+ auto witness = sort(tv.values);
+ size_t i;
+
+ assert(decoder.front == witness[i++]); decoder.popFront();
+ assert(decoder.front == witness[i++]); decoder.popFront();
+ assert(decoder.front == witness[i++]); decoder.popFront();
+
+ foreach (decoded; decoder.save)
+ assert(decoded == witness[i++]);
+ }
+ }
+ }
+
+ { // Encoder and Decoder for single character encoding and decoding
+ alias Base64NoPadding = Base64Impl!('+', '/', Base64.NoPadding);
+
+ auto tests = [
+ "" : ["", "", "", ""],
+ "f" : ["Zg==", "Zg==", "Zg", "Zg"],
+ "fo" : ["Zm8=", "Zm8=", "Zm8", "Zm8"],
+ "foo" : ["Zm9v", "Zm9v", "Zm9v", "Zm9v"],
+ "foob" : ["Zm9vYg==", "Zm9vYg==", "Zm9vYg", "Zm9vYg"],
+ "fooba" : ["Zm9vYmE=", "Zm9vYmE=", "Zm9vYmE", "Zm9vYmE"],
+ "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));
+
+ 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(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/libphobos/src/std/bigint.d b/libphobos/src/std/bigint.d
new file mode 100644
index 0000000..4c518f2
--- /dev/null
+++ b/libphobos/src/std/bigint.d
@@ -0,0 +1,1705 @@
+/** Arbitrary-precision ('bignum') arithmetic.
+ *
+ * Performance is optimized for numbers below ~1000 decimal digits.
+ * For X86 machines, highly optimised assembly routines are used.
+ *
+ * The following algorithms are currently implemented:
+ * $(UL
+ * $(LI Karatsuba multiplication)
+ * $(LI Squaring is optimized independently of multiplication)
+ * $(LI Divide-and-conquer division)
+ * $(LI Binary exponentiation)
+ * )
+ *
+ * For very large numbers, consider using the $(HTTP gmplib.org, GMP library) instead.
+ *
+ * License: $(HTTP www.boost.org/LICENSE_1_0.txt, Boost License 1.0).
+ * Authors: Don Clugston
+ * Source: $(PHOBOSSRC std/_bigint.d)
+ */
+/* Copyright Don Clugston 2008 - 2010.
+ * 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.bigint;
+
+import std.conv : ConvException;
+
+import std.format : FormatSpec, FormatException;
+import std.internal.math.biguintcore;
+import std.range.primitives;
+import std.traits;
+
+/** A struct representing an arbitrary precision integer.
+ *
+ * All arithmetic operations are supported, except unsigned shift right (>>>).
+ * Bitwise operations (|, &, ^, ~) are supported, and behave as if BigInt was
+ * an infinite length 2's complement number.
+ *
+ * BigInt implements value semantics using copy-on-write. This means that
+ * assignment is cheap, but operations such as x++ will cause heap
+ * allocation. (But note that for most bigint operations, heap allocation is
+ * inevitable anyway.)
+ */
+struct BigInt
+{
+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 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 &&
+ !isSomeString!Range)
+ {
+ import std.algorithm.iteration : filterBidirectional;
+ import std.algorithm.searching : startsWith;
+ import std.conv : ConvException;
+ import std.exception : enforce;
+ import std.utf : byChar;
+
+ enforce!ConvException(!s.empty, "Can't initialize BigInt with an empty range");
+
+ bool neg = false;
+ bool ok;
+
+ data = 0UL;
+
+ // check for signs and if the string is a hex value
+ if (s.front == '+')
+ {
+ s.popFront(); // skip '+'
+ }
+ else if (s.front == '-')
+ {
+ neg = true;
+ s.popFront();
+ }
+
+ if (s.save.startsWith("0x".byChar) ||
+ s.save.startsWith("0X".byChar))
+ {
+ s.popFront;
+ s.popFront;
+
+ if (!s.empty)
+ ok = data.fromHexString(s.filterBidirectional!(a => a != '_'));
+ else
+ ok = false;
+ }
+ else
+ {
+ ok = data.fromDecimalString(s.filterBidirectional!(a => a != '_'));
+ }
+
+ enforce!ConvException(ok, "Not a valid numerical string");
+
+ if (isZero())
+ neg = false;
+
+ sign = neg;
+ }
+
+ /// ditto
+ this(Range)(Range s) pure if (isSomeString!Range)
+ {
+ import std.utf : byCodeUnit;
+ this(s.byCodeUnit);
+ }
+
+ @system unittest
+ {
+ // system because of the dummy ranges eventually call std.array!string
+ import std.exception : assertThrown;
+ import std.internal.test.dummyrange;
+
+ 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)
+ {
+ data = data.init; // @@@: Workaround for compiler bug
+ opAssign(x);
+ }
+
+ ///
+ @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"));
+ }
+
+ /// Construct a BigInt from another BigInt.
+ this(T)(T x) pure nothrow if (is(Unqual!T == BigInt))
+ {
+ opAssign(x);
+ }
+
+ ///
+ @system unittest
+ {
+ const(BigInt) b1 = BigInt("1_234_567_890");
+ BigInt b2 = BigInt(b1);
+ assert(b2 == BigInt("1_234_567_890"));
+ }
+
+ /// Assignment from built-in integer types.
+ BigInt opAssign(T)(T x) pure nothrow if (isIntegral!T)
+ {
+ data = cast(ulong) absUnsign(x);
+ sign = (x < 0);
+ return this;
+ }
+
+ ///
+ @system unittest
+ {
+ auto b = BigInt("123");
+ b = 456;
+ assert(b == BigInt("456"));
+ }
+
+ /// Assignment from another BigInt.
+ BigInt opAssign(T:BigInt)(T x) pure @nogc
+ {
+ data = x.data;
+ sign = x.sign;
+ return this;
+ }
+
+ ///
+ @system unittest
+ {
+ auto b1 = BigInt("123");
+ auto b2 = BigInt("456");
+ b2 = b1;
+ assert(b2 == BigInt("123"));
+ }
+
+ /**
+ * Implements assignment operators from built-in integers of the form
+ * $(D BigInt op= integer).
+ */
+ BigInt opOpAssign(string op, T)(T y) pure nothrow
+ if ((op=="+" || op=="-" || op=="*" || op=="/" || op=="%"
+ || op==">>" || op=="<<" || op=="^^" || op=="|" || op=="&" || op=="^") && isIntegral!T)
+ {
+ ulong u = absUnsign(y);
+
+ static if (op=="+")
+ {
+ data = BigUint.addOrSubInt(data, u, sign != (y<0), sign);
+ }
+ else static if (op=="-")
+ {
+ data = BigUint.addOrSubInt(data, u, sign == (y<0), sign);
+ }
+ else static if (op=="*")
+ {
+ if (y == 0)
+ {
+ sign = false;
+ data = 0UL;
+ }
+ else
+ {
+ sign = ( sign != (y<0) );
+ data = BigUint.mulInt(data, u);
+ }
+ }
+ else static if (op=="/")
+ {
+ assert(y != 0, "Division by zero");
+ static if (T.sizeof <= uint.sizeof)
+ {
+ data = BigUint.divInt(data, cast(uint) u);
+ }
+ else
+ {
+ data = BigUint.divInt(data, u);
+ }
+ sign = data.isZero() ? false : sign ^ (y < 0);
+ }
+ else static if (op=="%")
+ {
+ 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);
+ if (data.isZero())
+ sign = false;
+ }
+ // x%y always has the same sign as x.
+ // This is not the same as mathematical mod.
+ }
+ else static if (op==">>" || op=="<<")
+ {
+ // Do a left shift if y>0 and <<, or
+ // if y<0 and >>; else do a right shift.
+ if (y == 0)
+ return this;
+ else if ((y > 0) == (op=="<<"))
+ {
+ // Sign never changes during left shift
+ data = data.opShl(u);
+ } else
+ {
+ data = data.opShr(u);
+ if (data.isZero())
+ sign = false;
+ }
+ }
+ else static if (op=="^^")
+ {
+ sign = (y & 1) ? sign : false;
+ data = BigUint.pow(data, u);
+ }
+ else static if (op=="|" || op=="&" || op=="^")
+ {
+ BigInt b = y;
+ opOpAssign!op(b);
+ }
+ else static assert(0, "BigInt " ~ op[0..$-1] ~ "= " ~ T.stringof ~ " is not supported");
+ return this;
+ }
+
+ ///
+ @system unittest
+ {
+ //@system because opOpAssign is @system
+ auto b = BigInt("1_000_000_000");
+
+ b += 12345;
+ assert(b == BigInt("1_000_012_345"));
+
+ b /= 5;
+ assert(b == BigInt("200_002_469"));
+ }
+
+ /**
+ * Implements assignment operators of the form $(D BigInt op= BigInt).
+ */
+ BigInt opOpAssign(string op, T)(T y) pure nothrow
+ if ((op=="+" || op== "-" || op=="*" || op=="|" || op=="&" || op=="^" || op=="/" || op=="%")
+ && is (T: BigInt))
+ {
+ static if (op == "+")
+ {
+ data = BigUint.addOrSub(data, y.data, sign != y.sign, &sign);
+ }
+ else static if (op == "-")
+ {
+ data = BigUint.addOrSub(data, y.data, sign == y.sign, &sign);
+ }
+ else static if (op == "*")
+ {
+ data = BigUint.mul(data, y.data);
+ sign = isZero() ? false : sign ^ y.sign;
+ }
+ else static if (op == "/")
+ {
+ y.checkDivByZero();
+ if (!isZero())
+ {
+ data = BigUint.div(data, y.data);
+ sign = isZero() ? false : sign ^ y.sign;
+ }
+ }
+ else static if (op == "%")
+ {
+ y.checkDivByZero();
+ if (!isZero())
+ {
+ data = BigUint.mod(data, y.data);
+ // x%y always has the same sign as x.
+ if (isZero())
+ sign = false;
+ }
+ }
+ else static if (op == "|" || op == "&" || op == "^")
+ {
+ data = BigUint.bitwiseOp!op(data, y.data, sign, y.sign, sign);
+ }
+ else static assert(0, "BigInt " ~ op[0..$-1] ~ "= " ~
+ T.stringof ~ " is not supported");
+ return this;
+ }
+
+ ///
+ @system unittest
+ {
+ // @system because opOpAssign is @system
+ auto x = BigInt("123");
+ auto y = BigInt("321");
+ x += y;
+ assert(x == BigInt("444"));
+ }
+
+ /**
+ * Implements binary operators between BigInts.
+ */
+ BigInt opBinary(string op, T)(T y) pure nothrow const
+ if ((op=="+" || op == "*" || op=="-" || op=="|" || op=="&" || op=="^" ||
+ op=="/" || op=="%")
+ && is (T: BigInt))
+ {
+ BigInt r = this;
+ return r.opOpAssign!(op)(y);
+ }
+
+ ///
+ @system unittest
+ {
+ auto x = BigInt("123");
+ auto y = BigInt("456");
+ BigInt z = x * y;
+ assert(z == BigInt("56088"));
+ }
+
+ /**
+ * Implements binary operators between BigInt's and built-in integers.
+ */
+ BigInt opBinary(string op, T)(T y) pure nothrow const
+ if ((op=="+" || op == "*" || op=="-" || op=="/" || op=="|" || op=="&" ||
+ op=="^"|| op==">>" || op=="<<" || op=="^^")
+ && isIntegral!T)
+ {
+ BigInt r = this;
+ return r.opOpAssign!(op)(y);
+ }
+
+ ///
+ @system unittest
+ {
+ auto x = BigInt("123");
+ x *= 300;
+ assert(x == BigInt("36900"));
+ }
+
+ /**
+ Implements a narrowing remainder operation with built-in integer types.
+
+ This binary operator returns a narrower, built-in integer type
+ where applicable, according to the following table.
+
+ $(TABLE ,
+ $(TR $(TD `BigInt`) $(TD $(CODE_PERCENT)) $(TD `long`) $(TD $(RARR)) $(TD `long`))
+ $(TR $(TD `BigInt`) $(TD $(CODE_PERCENT)) $(TD `ulong`) $(TD $(RARR)) $(TD `BigInt`))
+ $(TR $(TD `BigInt`) $(TD $(CODE_PERCENT)) $(TD other type) $(TD $(RARR)) $(TD `int`))
+ )
+ */
+ auto opBinary(string op, T)(T y) pure nothrow const
+ if (op == "%" && isIntegral!T)
+ {
+ assert(y != 0);
+
+ // BigInt % long => long
+ // BigInt % ulong => BigInt
+ // BigInt % other_type => int
+ static if (is(Unqual!T == long) || is(Unqual!T == ulong))
+ {
+ auto r = this % BigInt(y);
+
+ static if (is(Unqual!T == long))
+ {
+ return r.toLong();
+ }
+ else
+ {
+ // return as-is to avoid overflow
+ return r;
+ }
+ }
+ else
+ {
+ 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.
+ return sign ? -rem : rem;
+ }
+ }
+
+ ///
+ @system unittest
+ {
+ auto x = BigInt("1_000_000_500");
+ long l = 1_000_000L;
+ ulong ul = 2_000_000UL;
+ int i = 500_000;
+ short s = 30_000;
+
+ assert(is(typeof(x % l) == long) && x % l == 500L);
+ assert(is(typeof(x % ul) == BigInt) && x % ul == BigInt(500));
+ assert(is(typeof(x % i) == int) && x % i == 500);
+ assert(is(typeof(x % s) == int) && x % s == 10500);
+ }
+
+ /**
+ Implements operators with built-in integers on the left-hand side and
+ BigInt on the right-hand side.
+ */
+ BigInt opBinaryRight(string op, T)(T y) pure nothrow const
+ if ((op=="+" || op=="*" || op=="|" || op=="&" || op=="^") && isIntegral!T)
+ {
+ return opBinary!(op)(y);
+ }
+
+ ///
+ @system unittest
+ {
+ auto x = BigInt("100");
+ BigInt y = 123 + x;
+ assert(y == BigInt("223"));
+
+ BigInt z = 123 - x;
+ assert(z == BigInt("23"));
+
+ // Dividing a built-in integer type by BigInt always results in
+ // something that fits in a built-in type, so the built-in type is
+ // returned, not BigInt.
+ assert(is(typeof(1000 / x) == int));
+ assert(1000 / x == 10);
+ }
+
+ // BigInt = integer op BigInt
+ /// ditto
+ BigInt opBinaryRight(string op, T)(T y) pure nothrow const
+ if (op == "-" && isIntegral!T)
+ {
+ ulong u = absUnsign(y);
+ BigInt r;
+ static if (op == "-")
+ {
+ r.sign = sign;
+ r.data = BigUint.addOrSubInt(data, u, sign == (y<0), r.sign);
+ r.negate();
+ }
+ return r;
+ }
+
+ // integer = integer op BigInt
+ /// ditto
+ T opBinaryRight(string op, T)(T x) pure nothrow const
+ if ((op=="%" || op=="/") && isIntegral!T)
+ {
+ checkDivByZero();
+
+ static if (op == "%")
+ {
+ // x%y always has the same sign as x.
+ if (data.ulongLength > 1)
+ return x;
+ 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);
+ }
+ else static if (op == "/")
+ {
+ if (data.ulongLength > 1)
+ return 0;
+ return cast(T)(x / data.peekUlong(0));
+ }
+ }
+
+ // const unary operations
+ /**
+ Implements BigInt unary operators.
+ */
+ BigInt opUnary(string op)() pure nothrow const if (op=="+" || op=="-" || op=="~")
+ {
+ static if (op=="-")
+ {
+ BigInt r = this;
+ r.negate();
+ return r;
+ }
+ else static if (op=="~")
+ {
+ return -(this+1);
+ }
+ else static if (op=="+")
+ return this;
+ }
+
+ // non-const unary operations
+ /// ditto
+ BigInt opUnary(string op)() pure nothrow if (op=="++" || op=="--")
+ {
+ static if (op=="++")
+ {
+ data = BigUint.addOrSubInt(data, 1UL, sign, sign);
+ return this;
+ }
+ else static if (op=="--")
+ {
+ data = BigUint.addOrSubInt(data, 1UL, !sign, sign);
+ return this;
+ }
+ }
+
+ ///
+ @system unittest
+ {
+ auto x = BigInt("1234");
+ assert(-x == BigInt("-1234"));
+
+ ++x;
+ assert(x == BigInt("1235"));
+ }
+
+ /**
+ Implements BigInt equality test with other BigInt's and built-in
+ integer types.
+ */
+ bool opEquals()(auto ref const BigInt y) const pure @nogc
+ {
+ return sign == y.sign && y.data == data;
+ }
+
+ /// ditto
+ bool opEquals(T)(T y) const pure nothrow @nogc if (isIntegral!T)
+ {
+ if (sign != (y<0))
+ return 0;
+ return data.opEquals(cast(ulong) absUnsign(y));
+ }
+
+ ///
+ @system unittest
+ {
+ auto x = BigInt("12345");
+ auto y = BigInt("12340");
+ int z = 12345;
+ int w = 54321;
+
+ assert(x == x);
+ assert(x != y);
+ assert(x == y + 5);
+ assert(x == z);
+ assert(x != w);
+ }
+
+ /**
+ Implements casting to bool.
+ */
+ T opCast(T:bool)() pure nothrow @nogc const
+ {
+ return !isZero();
+ }
+
+ ///
+ @system unittest
+ {
+ // Non-zero values are regarded as true
+ auto x = BigInt("1");
+ auto y = BigInt("10");
+ assert(x);
+ assert(y);
+
+ // Zero value is regarded as false
+ auto z = BigInt("0");
+ 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))
+ {
+ return this;
+ }
+
+ ///
+ @system unittest
+ {
+ const(BigInt) x = BigInt("123");
+ BigInt y = cast() x; // cast away const
+ assert(y == x);
+ }
+
+ // Hack to make BigInt's typeinfo.compare work properly.
+ // Note that this must appear before the other opCmp overloads, otherwise
+ // DMD won't find it.
+ /**
+ Implements 3-way comparisons of BigInt with BigInt or BigInt with
+ built-in integers.
+ */
+ int opCmp(ref const BigInt y) pure nothrow @nogc const
+ {
+ // Simply redirect to the "real" opCmp implementation.
+ return this.opCmp!BigInt(y);
+ }
+
+ /// ditto
+ int opCmp(T)(T y) pure nothrow @nogc const if (isIntegral!T)
+ {
+ if (sign != (y<0) )
+ return sign ? -1 : 1;
+ 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)
+ return sign ? -1 : 1;
+ immutable cmp = data.opCmp(y.data);
+ return sign? -cmp: cmp;
+ }
+
+ ///
+ @system unittest
+ {
+ auto x = BigInt("100");
+ auto y = BigInt("10");
+ int z = 50;
+ const int w = 200;
+
+ assert(y < x);
+ assert(x > z);
+ assert(z > y);
+ assert(x < w);
+ }
+
+ /**
+ Returns: The value of this BigInt as a long, or +/- long.max if outside
+ the representable range.
+ */
+ long toLong() @safe pure nothrow const @nogc
+ {
+ return (sign ? -1 : 1) *
+ (data.ulongLength == 1 && (data.peekUlong(0) <= sign+cast(ulong)(long.max)) // 1+long.max = |long.min|
+ ? cast(long)(data.peekUlong(0))
+ : long.max);
+ }
+
+ ///
+ @system unittest
+ {
+ auto b = BigInt("12345");
+ long l = b.toLong();
+ assert(l == 12345);
+ }
+
+ /**
+ Returns: The value of this BigInt as an int, or +/- int.max if outside
+ the representable range.
+ */
+ int toInt() @safe pure nothrow @nogc const
+ {
+ return (sign ? -1 : 1) *
+ (data.uintLength == 1 && (data.peekUint(0) <= sign+cast(uint)(int.max)) // 1+int.max = |int.min|
+ ? cast(int)(data.peekUint(0))
+ : int.max);
+ }
+
+ ///
+ @system unittest
+ {
+ auto big = BigInt("5_000_000");
+ auto i = big.toInt();
+ assert(i == 5_000_000);
+
+ // Numbers that are too big to fit into an int will be clamped to int.max.
+ auto tooBig = BigInt("5_000_000_000");
+ i = tooBig.toInt();
+ assert(i == int.max);
+ }
+
+ /// Number of significant uints which are used in storing this number.
+ /// The absolute value of this BigInt is always &lt; 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 &lt; 2$(SUPERSCRIPT 64*ulongLength)
+ @property size_t ulongLength() @safe pure nothrow @nogc const
+ {
+ return data.ulongLength;
+ }
+
+ /** Convert the BigInt to string, passing it to the given sink.
+ *
+ * Params:
+ * sink = A delegate for accepting possibly piecewise segments of the
+ * formatted string.
+ * formatString = A format string specifying the output format.
+ *
+ * $(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") ))
+ * $(TR $(TD null) $(TD Default formatting (same as "d") ))
+ * )
+ */
+ void toString(scope void delegate(const (char)[]) sink, string formatString) const
+ {
+ auto f = FormatSpec!char(formatString);
+ f.writeUpToNextSpec(sink);
+ toString(sink, f);
+ }
+
+ /// ditto
+ void toString(scope void delegate(const(char)[]) sink, ref FormatSpec!char f) const
+ {
+ 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;
+ 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;
+ auto minw = buff.length + (signChar ? 1 : 0);
+
+ if (!hex && !signChar && (f.width == 0 || minw < f.width))
+ {
+ if (f.flPlus)
+ {
+ signChar = '+';
+ ++minw;
+ }
+ else if (f.flSpace)
+ {
+ signChar = ' ';
+ ++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]);
+
+ if (!f.flDash && f.flZero)
+ foreach (i; 0 .. difw)
+ sink("0");
+
+ sink(buff);
+
+ if (f.flDash)
+ foreach (i; 0 .. difw)
+ sink(" ");
+ }
+
+ /**
+ $(D toString) is rarely directly invoked; the usual way of using it is via
+ $(REF format, std, format):
+ */
+ @system unittest
+ {
+ import std.format : format;
+
+ auto x = BigInt("1_000_000");
+ 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.
+ /**
+ Returns: A unique hash of the BigInt's value suitable for use in a hash
+ table.
+ */
+ size_t toHash() const @safe nothrow
+ {
+ return data.toHash() + sign;
+ }
+
+ /**
+ $(D toHash) is rarely directly invoked; it is implicitly used when
+ BigInt is used as the key of an associative array.
+ */
+ @safe unittest
+ {
+ string[BigInt] aa;
+ aa[BigInt(123)] = "abc";
+ aa[BigInt(456)] = "def";
+
+ assert(aa[BigInt(123)] == "abc");
+ assert(aa[BigInt(456)] == "def");
+ }
+
+private:
+ void negate() @safe pure nothrow @nogc
+ {
+ if (!data.isZero())
+ sign = !sign;
+ }
+ bool isZero() pure const nothrow @nogc @safe
+ {
+ return data.isZero();
+ }
+ bool isNegative() pure const nothrow @nogc @safe
+ {
+ return sign;
+ }
+
+ // Generate a runtime error if division by zero occurs
+ void checkDivByZero() pure const nothrow @safe
+ {
+ if (isZero())
+ throw new Error("BigInt division by zero");
+ }
+}
+
+///
+@system unittest
+{
+ BigInt a = "9588669891916142";
+ BigInt b = "7452469135154800";
+ auto c = a * b;
+ assert(c == BigInt("71459266416693160362545788781600"));
+ auto d = b * a;
+ assert(d == BigInt("71459266416693160362545788781600"));
+ assert(d == c);
+ d = c * BigInt("794628672112");
+ assert(d == BigInt("56783581982794522489042432639320434378739200"));
+ auto e = c + d;
+ assert(e == BigInt("56783581982865981755459125799682980167520800"));
+ auto f = d + c;
+ assert(f == e);
+ auto g = f - c;
+ assert(g == d);
+ g = f - d;
+ assert(g == c);
+ e = 12345678;
+ g = c + e;
+ auto h = g / b;
+ auto i = g % b;
+ assert(h == a);
+ assert(i == e);
+ BigInt j = "-0x9A56_57f4_7B83_AB78";
+ j ^^= 11;
+}
+
+/**
+Params:
+ x = The $(D BigInt) to convert to a decimal $(D string).
+
+Returns:
+ A $(D string) that represents the $(D BigInt) as a decimal number.
+
+*/
+string toDecimalString(const(BigInt) x)
+{
+ string outbuff="";
+ void sink(const(char)[] s) { outbuff ~= s; }
+ x.toString(&sink, "%d");
+ return outbuff;
+}
+
+///
+@system unittest
+{
+ auto x = BigInt("123");
+ x *= 1000;
+ x += 456;
+
+ auto xstr = x.toDecimalString();
+ assert(xstr == "123456");
+}
+
+/**
+Params:
+ x = The $(D BigInt) to convert to a hexadecimal $(D string).
+
+Returns:
+ A $(D string) that represents the $(D BigInt) as a hexadecimal (base 16)
+ number in upper case.
+
+*/
+string toHex(const(BigInt) x)
+{
+ string outbuff="";
+ void sink(const(char)[] s) { outbuff ~= s; }
+ x.toString(&sink, "%X");
+ return outbuff;
+}
+
+///
+@system unittest
+{
+ auto x = BigInt("123");
+ x *= 1000;
+ x += 456;
+
+ auto xstr = x.toHex();
+ assert(xstr == "1E240");
+}
+
+/** Returns the absolute value of x converted to the corresponding unsigned
+type.
+
+Params:
+ x = The integral value to return the absolute value of.
+
+Returns:
+ The absolute value of x.
+
+*/
+Unsigned!T absUnsign(T)(T x)
+if (isIntegral!T)
+{
+ static if (isSigned!T)
+ {
+ 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.
+ */
+ return unsigned((x < 0) ? cast(T)(0-x) : x);
+ }
+ else
+ {
+ return x;
+ }
+}
+
+///
+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 @system
+unittest
+{
+ long a;
+ BigInt b;
+ auto c = a + b;
+ auto d = b + a;
+}
+
+nothrow pure @system
+unittest
+{
+ BigInt x = 1, y = 2;
+ assert(x < y);
+ assert(x <= y);
+ assert(y >= x);
+ assert(y > x);
+ assert(x != y);
+
+ long r1 = x.toLong;
+ assert(r1 == 1);
+
+ BigInt r2 = 10 % x;
+ assert(r2 == 0);
+
+ BigInt r3 = 10 / y;
+ assert(r3 == 5);
+
+ BigInt[] arr = [BigInt(1)];
+ auto incr = arr[0]++;
+ assert(arr == [BigInt(2)]);
+ assert(incr == BigInt(1));
+}
+
+@system unittest
+{
+ // Radix conversion
+ assert( toDecimalString(BigInt("-1_234_567_890_123_456_789"))
+ == "-1234567890123456789");
+ assert( toHex(BigInt("0x1234567890123456789")) == "123_45678901_23456789");
+ assert( toHex(BigInt("0x00000000000000000000000000000000000A234567890123456789"))
+ == "A23_45678901_23456789");
+ assert( toHex(BigInt("0x000_00_000000_000_000_000000000000_000000_")) == "0");
+
+ assert(BigInt(-0x12345678).toInt() == -0x12345678);
+ assert(BigInt(-0x12345678).toLong() == -0x12345678);
+ assert(BigInt(0x1234_5678_9ABC_5A5AL).ulongLength == 1);
+ assert(BigInt(0x1234_5678_9ABC_5A5AL).toLong() == 0x1234_5678_9ABC_5A5AL);
+ assert(BigInt(-0x1234_5678_9ABC_5A5AL).toLong() == -0x1234_5678_9ABC_5A5AL);
+ assert(BigInt(0xF234_5678_9ABC_5A5AL).toLong() == long.max);
+ assert(BigInt(-0x123456789ABCL).toInt() == -int.max);
+ char[] s1 = "123".dup; // bug 8164
+ assert(BigInt(s1) == 123);
+ char[] s2 = "0xABC".dup;
+ assert(BigInt(s2) == 2748);
+
+ assert((BigInt(-2) + BigInt(1)) == BigInt(-1));
+ BigInt a = ulong.max - 5;
+ auto b = -long.max % a;
+ assert( b == -long.max % (ulong.max - 5));
+ b = long.max / a;
+ assert( b == long.max /(ulong.max - 5));
+ assert(BigInt(1) - 1 == 0);
+ assert((-4) % BigInt(5) == -4); // bug 5928
+ 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");
+}
+
+@system unittest // Minimum signed value bug tests.
+{
+ assert(BigInt("-0x8000000000000000") == BigInt(long.min));
+ assert(BigInt("-0x8000000000000000")+1 > BigInt(long.min));
+ assert(BigInt("-0x80000000") == BigInt(int.min));
+ assert(BigInt("-0x80000000")+1 > BigInt(int.min));
+ assert(BigInt(long.min).toLong() == long.min); // lossy toLong bug for long.min
+ assert(BigInt(int.min).toInt() == int.min); // lossy toInt bug for int.min
+ assert(BigInt(long.min).ulongLength == 1);
+ assert(BigInt(int.min).uintLength == 1); // cast/sign extend bug in opAssign
+ BigInt a;
+ a += int.min;
+ assert(a == BigInt(int.min));
+ a = int.min - BigInt(int.min);
+ assert(a == 0);
+ a = int.min;
+ assert(a == BigInt(int.min));
+ assert(int.min % (BigInt(int.min)-1) == int.min);
+ assert((BigInt(int.min)-1)%int.min == -1);
+}
+
+@system unittest // Recursive division, bug 5568
+{
+ enum Z = 4843;
+ BigInt m = (BigInt(1) << (Z*8) ) - 1;
+ m -= (BigInt(1) << (Z*6)) - 1;
+ BigInt oldm = m;
+
+ BigInt a = (BigInt(1) << (Z*4) )-1;
+ BigInt b = m % a;
+ m /= a;
+ m *= a;
+ assert( m + b == oldm);
+
+ m = (BigInt(1) << (4846 + 4843) ) - 1;
+ a = (BigInt(1) << 4846 ) - 1;
+ b = (BigInt(1) << (4846*2 + 4843)) - 1;
+ BigInt c = (BigInt(1) << (4846*2 + 4843*2)) - 1;
+ BigInt w = c - b + a;
+ assert(w % m == 0);
+
+ // Bug 6819. ^^
+ BigInt z1 = BigInt(10)^^64;
+ BigInt w1 = BigInt(10)^^128;
+ assert(z1^^2 == w1);
+ BigInt z2 = BigInt(1)<<64;
+ BigInt w2 = BigInt(1)<<128;
+ assert(z2^^2 == w2);
+ // Bug 7993
+ BigInt n7793 = 10;
+ assert( n7793 / 1 == 10);
+ // Bug 7973
+ auto a7973 = 10_000_000_000_000_000;
+ const c7973 = 10_000_000_000_000_000;
+ immutable i7973 = 10_000_000_000_000_000;
+ BigInt v7973 = 2551700137;
+ v7973 %= a7973;
+ assert(v7973 == 2551700137);
+ v7973 %= c7973;
+ assert(v7973 == 2551700137);
+ v7973 %= i7973;
+ assert(v7973 == 2551700137);
+ // 8165
+ BigInt[2] a8165;
+ a8165[0] = a8165[1] = 1;
+}
+
+@system unittest
+{
+ import std.array;
+ import std.format;
+
+ immutable string[][] table = [
+ /* fmt, +10 -10 */
+ ["%d", "10", "-10"],
+ ["%+d", "+10", "-10"],
+ ["%-d", "10", "-10"],
+ ["%+-d", "+10", "-10"],
+
+ ["%4d", " 10", " -10"],
+ ["%+4d", " +10", " -10"],
+ ["%-4d", "10 ", "-10 "],
+ ["%+-4d", "+10 ", "-10 "],
+
+ ["%04d", "0010", "-010"],
+ ["%+04d", "+010", "-010"],
+ ["%-04d", "10 ", "-10 "],
+ ["%+-04d", "+10 ", "-10 "],
+
+ ["% 04d", " 010", "-010"],
+ ["%+ 04d", "+010", "-010"],
+ ["%- 04d", " 10 ", "-10 "],
+ ["%+- 04d", "+10 ", "-10 "],
+ ];
+
+ 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);
+ 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;
+
+ 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;
+
+ 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();
+ }
+}
+
+// 6448
+@system unittest
+{
+ import std.array;
+ import std.format;
+
+ auto w1 = appender!string();
+ auto w2 = appender!string();
+
+ int x = 100;
+ formattedWrite(w1, "%010d", x);
+ BigInt bx = x;
+ formattedWrite(w2, "%010d", bx);
+ assert(w1.data == w2.data);
+ //8011
+ BigInt y = -3;
+ ++y;
+ assert(y.toLong() == -2);
+ y = 1;
+ --y;
+ assert(y.toLong() == 0);
+ --y;
+ assert(y.toLong() == -1);
+ --y;
+ assert(y.toLong() == -2);
+}
+
+@safe unittest
+{
+ import std.math : abs;
+ auto r = abs(BigInt(-1000)); // 6486
+ assert(r == 1000);
+
+ auto r2 = abs(const(BigInt)(-500)); // 11188
+ assert(r2 == 500);
+ auto r3 = abs(immutable(BigInt)(-733)); // 11188
+ assert(r3 == 733);
+
+ // opCast!bool
+ BigInt one = 1, zero;
+ assert(one && !zero);
+}
+
+@system unittest // 6850
+{
+ pure long pureTest() {
+ BigInt a = 1;
+ BigInt b = 1336;
+ a += b;
+ return a.toLong();
+ }
+
+ assert(pureTest() == 1337);
+}
+
+@system unittest // 8435 & 10118
+{
+ auto i = BigInt(100);
+ auto j = BigInt(100);
+
+ // Two separate BigInt instances representing same value should have same
+ // hash.
+ assert(typeid(i).getHash(&i) == typeid(j).getHash(&j));
+ assert(typeid(i).compare(&i, &j) == 0);
+
+ // BigInt AA keys should behave consistently.
+ int[BigInt] aa;
+ aa[BigInt(123)] = 123;
+ assert(BigInt(123) in aa);
+
+ aa[BigInt(123)] = 321;
+ assert(aa[BigInt(123)] == 321);
+
+ auto keys = aa.byKey;
+ assert(keys.front == BigInt(123));
+ keys.popFront();
+ assert(keys.empty);
+}
+
+@system unittest // 11148
+{
+ void foo(BigInt) {}
+ const BigInt cbi = 3;
+ immutable BigInt ibi = 3;
+
+ assert(__traits(compiles, foo(cbi)));
+ assert(__traits(compiles, foo(ibi)));
+
+ import std.conv : to;
+ import std.meta : AliasSeq;
+
+ foreach (T1; AliasSeq!(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;
+
+ assert(t2 == t1);
+ assert(t2 == 2);
+
+ assert(t2_1 == t1);
+ assert(t2_1 == 2);
+
+ assert(t2_2 == t1);
+ assert(t2_2 == 2);
+ }
+ }
+
+ BigInt n = 2;
+ n *= 2;
+}
+
+@safe unittest // 8167
+{
+ BigInt a = BigInt(3);
+ BigInt b = BigInt(a);
+}
+
+@safe unittest // 9061
+{
+ long l1 = 0x12345678_90ABCDEF;
+ long l2 = 0xFEDCBA09_87654321;
+ long l3 = l1 | l2;
+ long l4 = l1 & l2;
+ long l5 = l1 ^ l2;
+
+ BigInt b1 = l1;
+ BigInt b2 = l2;
+ BigInt b3 = b1 | b2;
+ BigInt b4 = b1 & b2;
+ BigInt b5 = b1 ^ b2;
+
+ assert(l3 == b3);
+ assert(l4 == b4);
+ assert(l5 == b5);
+}
+
+@system unittest // 11600
+{
+ import std.conv;
+ import std.exception : assertThrown;
+
+ // Original bug report
+ assertThrown!ConvException(to!BigInt("avadakedavra"));
+
+ // Digit string lookalikes that are actually invalid
+ assertThrown!ConvException(to!BigInt("0123hellothere"));
+ assertThrown!ConvException(to!BigInt("-hihomarylowe"));
+ assertThrown!ConvException(to!BigInt("__reallynow__"));
+ assertThrown!ConvException(to!BigInt("-123four"));
+}
+
+@safe unittest // 11583
+{
+ BigInt x = 0;
+ assert((x > 0) == false);
+}
+
+@system unittest // 13391
+{
+ BigInt x1 = "123456789";
+ BigInt x2 = "123456789123456789";
+ BigInt x3 = "123456789123456789123456789";
+
+ 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);
+ assert((x3 * T.max) / T.max == x3);
+ }
+
+ assert(x1 / -123456789 == -1);
+ assert(x1 / 123456789U == 1);
+ assert(x1 / -123456789L == -1);
+ assert(x1 / 123456789UL == 1);
+ assert(x2 / -123456789123456789L == -1);
+ assert(x2 / 123456789123456789UL == 1);
+
+ assert(x1 / uint.max == 0);
+ assert(x1 / ulong.max == 0);
+ assert(x2 / ulong.max == 0);
+
+ x1 /= 123456789UL;
+ assert(x1 == 1);
+ x2 /= 123456789123456789UL;
+ assert(x2 == 1);
+}
+
+@system unittest // 13963
+{
+ BigInt x = 1;
+ import std.meta : AliasSeq;
+ foreach (Int; AliasSeq!(byte, ubyte, short, ushort, int, uint))
+ {
+ assert(is(typeof(x % Int(1)) == int));
+ }
+ assert(is(typeof(x % 1L) == long));
+ assert(is(typeof(x % 1UL) == BigInt));
+
+ auto x1 = BigInt(8);
+ auto x2 = -BigInt(long.min) + 1;
+
+ // long
+ assert(x1 % 2L == 0L);
+ assert(-x1 % 2L == 0L);
+
+ assert(x1 % 3L == 2L);
+ assert(x1 % -3L == 2L);
+ assert(-x1 % 3L == -2L);
+ assert(-x1 % -3L == -2L);
+
+ assert(x1 % 11L == 8L);
+ assert(x1 % -11L == 8L);
+ assert(-x1 % 11L == -8L);
+ assert(-x1 % -11L == -8L);
+
+ // ulong
+ assert(x1 % 2UL == BigInt(0));
+ assert(-x1 % 2UL == BigInt(0));
+
+ assert(x1 % 3UL == BigInt(2));
+ assert(-x1 % 3UL == -BigInt(2));
+
+ assert(x1 % 11UL == BigInt(8));
+ assert(-x1 % 11UL == -BigInt(8));
+
+ assert(x2 % ulong.max == x2);
+ assert(-x2 % ulong.max == -x2);
+}
+
+@system unittest // 14124
+{
+ auto x = BigInt(-3);
+ x %= 3;
+ assert(!x.isNegative());
+ assert(x.isZero());
+
+ x = BigInt(-3);
+ x %= cast(ushort) 3;
+ assert(!x.isNegative());
+ assert(x.isZero());
+
+ x = BigInt(-3);
+ x %= 3L;
+ assert(!x.isNegative());
+ assert(x.isZero());
+
+ x = BigInt(3);
+ x %= -3;
+ 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)
+ ]));
+}
+
+// Issue 17330
+@system unittest
+{
+ auto b = immutable BigInt("123");
+}
diff --git a/libphobos/src/std/bitmanip.d b/libphobos/src/std/bitmanip.d
new file mode 100644
index 0000000..7802dff
--- /dev/null
+++ b/libphobos/src/std/bitmanip.d
@@ -0,0 +1,4009 @@
+// Written in the D programming language.
+
+/**
+Bit-level manipulation facilities.
+
+$(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: $(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,
+ Amaury SECHET
+Source: $(PHOBOSSRC std/_bitmanip.d)
+*/
+/*
+ Copyright Digital Mars 2007 - 2012.
+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.bitmanip;
+
+//debug = bitarray; // uncomment to turn on debugging printf's
+
+import std.range.primitives;
+public import std.system : Endian;
+import std.traits;
+
+version (unittest)
+{
+ import std.stdio;
+}
+
+
+private string myToString(ulong n)
+{
+ 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(
+ string store, T, string name, size_t len, size_t offset)
+{
+ static if (!name.length)
+ {
+ // No need to create any accessor
+ enum result = "";
+ }
+ else static if (len == 0)
+ {
+ // Fields of length 0 are always zero
+ enum result = "enum "~T.stringof~" "~name~" = 0;\n";
+ }
+ else
+ {
+ enum ulong
+ maskAllElse = ((~0uL) >> (64 - len)) << offset,
+ signBitCheck = 1uL << (len - 1);
+
+ static if (T.min < 0)
+ {
+ enum long minVal = -(1uL << (len - 1));
+ enum ulong maxVal = (1uL << (len - 1)) - 1;
+ alias UT = Unsigned!(T);
+ enum UT extendSign = cast(UT)~((~0uL) >> (64 - len));
+ }
+ else
+ {
+ enum ulong minVal = 0;
+ enum ulong maxVal = (~0uL) >> (64 - len);
+ enum extendSign = 0;
+ }
+
+ static if (is(T == bool))
+ {
+ static assert(len == 1);
+ enum result =
+ // getter
+ "@property bool " ~ name ~ "() @safe pure nothrow @nogc const { return "
+ ~"("~store~" & "~myToString(maskAllElse)~") != 0;}\n"
+ // setter
+ ~"@property void " ~ name ~ "(bool v) @safe pure nothrow @nogc { "
+ ~"if (v) "~store~" |= "~myToString(maskAllElse)~";"
+ ~"else "~store~" &= cast(typeof("~store~"))(-1-cast(typeof("~store~"))"~myToString(maskAllElse)~");}\n";
+ }
+ else
+ {
+ // getter
+ enum result = "@property "~T.stringof~" "~name~"() @safe pure nothrow @nogc const { auto result = "
+ ~"("~store~" & "
+ ~ myToString(maskAllElse) ~ ") >>"
+ ~ myToString(offset) ~ ";"
+ ~ (T.min < 0
+ ? "if (result >= " ~ myToString(signBitCheck)
+ ~ ") result |= " ~ myToString(extendSign) ~ ";"
+ : "")
+ ~ " return cast("~T.stringof~") result;}\n"
+ // setter
+ ~"@property void "~name~"("~T.stringof~" v) @safe pure nothrow @nogc { "
+ ~"assert(v >= "~name~`_min, "Value is smaller than the minimum value of bitfield '`~name~`'"); `
+ ~"assert(v <= "~name~`_max, "Value is greater than the maximum value of bitfield '`~name~`'"); `
+ ~store~" = cast(typeof("~store~"))"
+ ~" (("~store~" & (-1-cast(typeof("~store~"))"~myToString(maskAllElse)~"))"
+ ~" | ((cast(typeof("~store~")) v << "~myToString(offset)~")"
+ ~" & "~myToString(maskAllElse)~"));}\n"
+ // constants
+ ~"enum "~T.stringof~" "~name~"_min = cast("~T.stringof~")"
+ ~myToString(minVal)~"; "
+ ~" enum "~T.stringof~" "~name~"_max = cast("~T.stringof~")"
+ ~myToString(maxVal)~"; ";
+ }
+ }
+}
+
+private template createStoreName(Ts...)
+{
+ static if (Ts.length < 2)
+ enum createStoreName = "";
+ else
+ enum createStoreName = "_" ~ Ts[1] ~ createStoreName!(Ts[3 .. $]);
+}
+
+private template createStorageAndFields(Ts...)
+{
+ 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)
+{
+ ulong bits = 0;
+ while ((a & 0x01) == 0)
+ {
+ bits++;
+ a >>= 1;
+ }
+
+ assert(a == 1, "alignment is not a power of 2");
+ return bits;
+}
+
+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 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~`'"); `
+ ~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)
+ enum sizeOfBitField = 0;
+ else
+ enum sizeOfBitField = T[2] + sizeOfBitField!(T[3 .. $]);
+}
+
+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."
+ );
+ enum StoreName = createStoreName!(T, name, 0, Ts);
+ enum result
+ = createReferenceAccessor!(StoreName, T, sizeOfBitField!Ts, name).result
+ ~ createFields!(StoreName, 0, Ts, size_t, "", T.sizeof * 8 - sizeOfBitField!Ts).result;
+}
+
+/**
+Allows creating bit fields inside $(D_PARAM struct)s and $(D_PARAM
+class)es.
+
+Example:
+
+----
+struct A
+{
+ int a;
+ mixin(bitfields!(
+ uint, "x", 2,
+ int, "y", 3,
+ uint, "z", 2,
+ bool, "flag", 1));
+}
+A obj;
+obj.x = 2;
+obj.z = obj.x;
+----
+
+The example above creates a bitfield pack of eight bits, which fit in
+one $(D_PARAM ubyte). The bitfields are allocated starting from the
+least significant bit, i.e. x occupies the two least significant bits
+of the bitfields storage.
+
+The sum of all bit lengths in one $(D_PARAM bitfield) instantiation
+must be exactly 8, 16, 32, or 64. If padding is needed, just allocate
+one bitfield with an empty name.
+
+Example:
+
+----
+struct A
+{
+ mixin(bitfields!(
+ bool, "flag1", 1,
+ bool, "flag2", 1,
+ uint, "", 6));
+}
+----
+
+The type of a bit field can be any integral type or enumerated
+type. The most efficient type to store in bitfields is $(D_PARAM
+bool), followed by unsigned types, followed by signed types.
+*/
+
+template bitfields(T...)
+{
+ enum { bitfields = createStorageAndFields!T.result }
+}
+
+/**
+This string mixin generator allows one to create tagged pointers inside $(D_PARAM struct)s and $(D_PARAM class)es.
+
+A tagged pointer uses the bits known to be zero in a normal pointer or class reference to store extra information.
+For example, a pointer to an integer must be 4-byte aligned, so there are 2 bits that are always known to be zero.
+One can store a 2-bit integer there.
+
+The example above creates a tagged pointer in the struct A. The pointer is of type
+$(D uint*) as specified by the first argument, and is named x, as specified by the second
+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 alignment.
+*/
+
+template taggedPointer(T : T*, string name, Ts...) {
+ enum taggedPointer = createTaggedReference!(T*, T.alignof, name, Ts).result;
+}
+
+///
+@safe unittest
+{
+ struct A
+ {
+ int a;
+ mixin(taggedPointer!(
+ uint*, "x",
+ bool, "b1", 1,
+ bool, "b2", 1));
+ }
+ A obj;
+ obj.x = new uint;
+ obj.b1 = true;
+ obj.b2 = false;
+}
+
+/**
+This string mixin generator allows one to create tagged class reference inside $(D_PARAM struct)s and $(D_PARAM class)es.
+
+A tagged class reference uses the bits known to be zero in a normal class reference to store extra information.
+For example, a pointer to an integer must be 4-byte aligned, so there are 2 bits that are always known to be zero.
+One can store a 2-bit integer there.
+
+The example above creates a tagged reference to an Object in the struct A. This expects the same parameters
+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!(T, 8, name, Ts).result;
+}
+
+///
+@safe unittest
+{
+ struct A
+ {
+ int a;
+ mixin(taggedClassRef!(
+ Object, "o",
+ uint, "i", 2));
+ }
+ A obj;
+ obj.o = new Object();
+ obj.i = 3;
+}
+
+@safe pure nothrow @nogc
+unittest
+{
+ // Degenerate bitfields (#8474 / #11160) tests mixed with range tests
+ struct Test1
+ {
+ mixin(bitfields!(uint, "a", 32,
+ uint, "b", 4,
+ uint, "c", 4,
+ uint, "d", 8,
+ uint, "e", 16,));
+
+ static assert(Test1.b_min == 0);
+ static assert(Test1.b_max == 15);
+ }
+
+ struct Test2
+ {
+ mixin(bitfields!(bool, "a", 0,
+ ulong, "b", 64));
+
+ static assert(Test2.b_min == ulong.min);
+ static assert(Test2.b_max == ulong.max);
+ }
+
+ struct Test1b
+ {
+ mixin(bitfields!(bool, "a", 0,
+ int, "b", 8));
+ }
+
+ struct Test2b
+ {
+ mixin(bitfields!(int, "a", 32,
+ int, "b", 4,
+ int, "c", 4,
+ int, "d", 8,
+ int, "e", 16,));
+
+ static assert(Test2b.b_min == -8);
+ static assert(Test2b.b_max == 7);
+ }
+
+ struct Test3b
+ {
+ mixin(bitfields!(bool, "a", 0,
+ long, "b", 64));
+
+ static assert(Test3b.b_min == long.min);
+ static assert(Test3b.b_max == long.max);
+ }
+
+ struct Test4b
+ {
+ mixin(bitfields!(long, "a", 32,
+ int, "b", 32));
+ }
+
+ // Sign extension tests
+ Test2b t2b;
+ Test4b t4b;
+ t2b.b = -5; assert(t2b.b == -5);
+ t2b.d = -5; assert(t2b.d == -5);
+ t2b.e = -5; assert(t2b.e == -5);
+ t4b.a = -5; assert(t4b.a == -5L);
+}
+
+@system unittest
+{
+ struct Test5
+ {
+ mixin(taggedPointer!(
+ int*, "a",
+ uint, "b", 2));
+ }
+
+ Test5 t5;
+ t5.a = null;
+ t5.b = 3;
+ assert(t5.a is null);
+ assert(t5.b == 3);
+
+ int myint = 42;
+ t5.a = &myint;
+ assert(t5.a is &myint);
+ assert(t5.b == 3);
+
+ struct Test6
+ {
+ mixin(taggedClassRef!(
+ Object, "o",
+ bool, "b", 1));
+ }
+
+ Test6 t6;
+ t6.o = null;
+ t6.b = false;
+ assert(t6.o is null);
+ assert(t6.b == false);
+
+ auto o = new Object();
+ t6.o = o;
+ t6.b = true;
+ assert(t6.o is o);
+ assert(t6.b == true);
+}
+
+@safe unittest
+{
+ static assert(!__traits(compiles,
+ taggedPointer!(
+ int*, "a",
+ uint, "b", 3)));
+
+ static assert(!__traits(compiles,
+ 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)));
+}
+
+@safe unittest
+{
+ // Bug #6686
+ union S {
+ ulong bits = ulong.max;
+ mixin (bitfields!(
+ ulong, "back", 31,
+ ulong, "front", 33)
+ );
+ }
+ S num;
+
+ num.bits = ulong.max;
+ num.back = 1;
+ assert(num.bits == 0xFFFF_FFFF_8000_0001uL);
+}
+
+@safe unittest
+{
+ // Bug #5942
+ struct S
+ {
+ mixin(bitfields!(
+ int, "a" , 32,
+ int, "b" , 32
+ ));
+ }
+
+ S data;
+ data.b = 42;
+ data.a = 1;
+ assert(data.b == 42);
+}
+
+@safe unittest
+{
+ struct Test
+ {
+ mixin(bitfields!(bool, "a", 1,
+ uint, "b", 3,
+ short, "c", 4));
+ }
+
+ @safe void test() pure nothrow
+ {
+ Test t;
+
+ t.a = true;
+ t.b = 5;
+ t.c = 2;
+
+ assert(t.a);
+ assert(t.b == 5);
+ assert(t.c == 2);
+ }
+
+ test();
+}
+
+@safe unittest
+{
+ {
+ static struct Integrals {
+ bool checkExpectations(bool eb, int ei, short es) { return b == eb && i == ei && s == es; }
+
+ mixin(bitfields!(
+ bool, "b", 1,
+ uint, "i", 3,
+ short, "s", 4));
+ }
+ Integrals i;
+ assert(i.checkExpectations(false, 0, 0));
+ i.b = true;
+ assert(i.checkExpectations(true, 0, 0));
+ i.i = 7;
+ assert(i.checkExpectations(true, 7, 0));
+ i.s = -8;
+ assert(i.checkExpectations(true, 7, -8));
+ i.s = 7;
+ assert(i.checkExpectations(true, 7, 7));
+ }
+
+ //Bug# 8876
+ {
+ struct MoreIntegrals {
+ bool checkExpectations(uint eu, ushort es, uint ei) { return u == eu && s == es && i == ei; }
+
+ mixin(bitfields!(
+ uint, "u", 24,
+ short, "s", 16,
+ int, "i", 24));
+ }
+
+ MoreIntegrals i;
+ assert(i.checkExpectations(0, 0, 0));
+ i.s = 20;
+ assert(i.checkExpectations(0, 20, 0));
+ i.i = 72;
+ assert(i.checkExpectations(0, 20, 72));
+ i.u = 8;
+ assert(i.checkExpectations(8, 20, 72));
+ i.s = 7;
+ assert(i.checkExpectations(8, 7, 72));
+ }
+
+ enum A { True, False }
+ enum B { One, Two, Three, Four }
+ static struct Enums {
+ bool checkExpectations(A ea, B eb) { return a == ea && b == eb; }
+
+ mixin(bitfields!(
+ A, "a", 1,
+ B, "b", 2,
+ uint, "", 5));
+ }
+ Enums e;
+ assert(e.checkExpectations(A.True, B.One));
+ e.a = A.False;
+ assert(e.checkExpectations(A.False, B.One));
+ e.b = B.Three;
+ assert(e.checkExpectations(A.False, B.Three));
+
+ static struct SingleMember {
+ bool checkExpectations(bool eb) { return b == eb; }
+
+ mixin(bitfields!(
+ bool, "b", 1,
+ uint, "", 7));
+ }
+ SingleMember f;
+ assert(f.checkExpectations(false));
+ f.b = true;
+ assert(f.checkExpectations(true));
+}
+
+// Issue 12477
+@system unittest
+{
+ import core.exception : AssertError;
+ import std.algorithm.searching : canFind;
+ import std.bitmanip : bitfields;
+
+ static struct S
+ {
+ mixin(bitfields!(
+ uint, "a", 6,
+ int, "b", 2));
+ }
+
+ S s;
+
+ try { s.a = uint.max; assert(0); }
+ catch (AssertError ae)
+ { 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.canFind("Value is smaller than the minimum value of bitfield 'b'"), ae.msg); }
+}
+
+/**
+ Allows manipulating the fraction, exponent, and sign parts of a
+ $(D_PARAM float) separately. The definition is:
+
+----
+struct FloatRep
+{
+ union
+ {
+ float value;
+ mixin(bitfields!(
+ uint, "fraction", 23,
+ ubyte, "exponent", 8,
+ bool, "sign", 1));
+ }
+ enum uint bias = 127, fractionBits = 23, exponentBits = 8, signBits = 1;
+}
+----
+*/
+
+struct FloatRep
+{
+ union
+ {
+ float value;
+ mixin(bitfields!(
+ uint, "fraction", 23,
+ ubyte, "exponent", 8,
+ bool, "sign", 1));
+ }
+ enum uint bias = 127, fractionBits = 23, exponentBits = 8, signBits = 1;
+}
+
+/**
+ Allows manipulating the fraction, exponent, and sign parts of a
+ $(D_PARAM double) separately. The definition is:
+
+----
+struct DoubleRep
+{
+ union
+ {
+ double value;
+ mixin(bitfields!(
+ ulong, "fraction", 52,
+ ushort, "exponent", 11,
+ bool, "sign", 1));
+ }
+ enum uint bias = 1023, signBits = 1, fractionBits = 52, exponentBits = 11;
+}
+----
+*/
+
+struct DoubleRep
+{
+ union
+ {
+ double value;
+ mixin(bitfields!(
+ ulong, "fraction", 52,
+ ushort, "exponent", 11,
+ bool, "sign", 1));
+ }
+ enum uint bias = 1023, signBits = 1, fractionBits = 52, exponentBits = 11;
+}
+
+@safe unittest
+{
+ // test reading
+ DoubleRep x;
+ x.value = 1.0;
+ assert(x.fraction == 0 && x.exponent == 1023 && !x.sign);
+ x.value = -0.5;
+ assert(x.fraction == 0 && x.exponent == 1022 && x.sign);
+ x.value = 0.5;
+ assert(x.fraction == 0 && x.exponent == 1022 && !x.sign);
+
+ // test writing
+ x.fraction = 1125899906842624;
+ x.exponent = 1025;
+ x.sign = true;
+ assert(x.value == -5.0);
+
+ // test enums
+ enum ABC { A, B, C }
+ struct EnumTest
+ {
+ mixin(bitfields!(
+ ABC, "x", 2,
+ bool, "y", 1,
+ ubyte, "z", 5));
+ }
+}
+
+@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 core.bitop : bts, btr, bsf, bt;
+ import std.format : FormatSpec;
+
+ size_t _len;
+ size_t* _ptr;
+ enum bitsPerSizeT = size_t.sizeof * 8;
+
+ @property size_t fullWords() const @nogc pure nothrow
+ {
+ return _len / bitsPerSizeT;
+ }
+ // Number of bits after the last full word
+ @property size_t endBits() const @nogc pure nothrow
+ {
+ 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 @safe
+ {
+ return (len + (bitsPerSizeT-1)) / bitsPerSizeT;
+ }
+
+public:
+ /**********************************************
+ * Gets the amount of native words backing this $(D BitArray).
+ */
+ @property size_t dim() const @nogc pure nothrow @safe
+ {
+ return lenToDim(_len);
+ }
+
+ /**********************************************
+ * Gets the amount of bits in the $(D BitArray).
+ */
+ @property size_t length() const @nogc pure nothrow @safe
+ {
+ return _len;
+ }
+
+ /**********************************************
+ * Sets the amount of bits in the $(D BitArray).
+ * $(RED Warning: increasing length may overwrite bits in
+ * 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 @system
+ {
+ if (newlen != _len)
+ {
+ size_t olddim = dim;
+ immutable newdim = lenToDim(newlen);
+
+ if (newdim != olddim)
+ {
+ // Create a fake array so we can use D's realloc machinery
+ auto b = _ptr[0 .. olddim];
+ b.length = newdim; // realloc
+ _ptr = b.ptr;
+ }
+
+ _len = newlen;
+ }
+ return _len;
+ }
+
+ /**********************************************
+ * Gets the $(D i)'th bit in the $(D BitArray).
+ */
+ bool opIndex(size_t i) const @nogc pure nothrow
+ in
+ {
+ assert(i < _len);
+ }
+ body
+ {
+ return cast(bool) bt(_ptr, i);
+ }
+
+ @system unittest
+ {
+ debug(bitarray) printf("BitArray.opIndex.unittest\n");
+
+ void Fun(const BitArray arr)
+ {
+ auto x = arr[0];
+ assert(x == 1);
+ }
+ BitArray a;
+ a.length = 3;
+ a[0] = 1;
+ Fun(a);
+ }
+
+ /**********************************************
+ * Sets the $(D i)'th bit in the $(D BitArray).
+ */
+ bool opIndexAssign(bool b, size_t i) @nogc pure nothrow
+ in
+ {
+ assert(i < _len);
+ }
+ body
+ {
+ if (b)
+ bts(_ptr, i);
+ else
+ btr(_ptr, i);
+ return b;
+ }
+
+ /**********************************************
+ * Duplicates the $(D BitArray) and its contents.
+ */
+ @property BitArray dup() const pure nothrow
+ {
+ BitArray ba;
+
+ auto b = _ptr[0 .. dim].dup;
+ ba._len = _len;
+ ba._ptr = b.ptr;
+ return ba;
+ }
+
+ @system unittest
+ {
+ BitArray a;
+ BitArray b;
+ int i;
+
+ debug(bitarray) printf("BitArray.dup.unittest\n");
+
+ a.length = 3;
+ a[0] = 1; a[1] = 0; a[2] = 1;
+ b = a.dup;
+ assert(b.length == 3);
+ for (i = 0; i < 3; i++)
+ { debug(bitarray) printf("b[%d] = %d\n", i, b[i]);
+ assert(b[i] == (((i ^ 1) & 1) ? true : false));
+ }
+ }
+
+ /**********************************************
+ * Support for $(D foreach) loops for $(D BitArray).
+ */
+ int opApply(scope int delegate(ref bool) dg)
+ {
+ int result;
+
+ foreach (i; 0 .. _len)
+ {
+ bool b = opIndex(i);
+ result = dg(b);
+ this[i] = b;
+ if (result)
+ break;
+ }
+ return result;
+ }
+
+ /** ditto */
+ int opApply(scope int delegate(bool) dg) const
+ {
+ int result;
+
+ foreach (i; 0 .. _len)
+ {
+ immutable b = opIndex(i);
+ result = dg(b);
+ if (result)
+ break;
+ }
+ return result;
+ }
+
+ /** ditto */
+ int opApply(scope int delegate(size_t, ref bool) dg)
+ {
+ int result;
+
+ foreach (i; 0 .. _len)
+ {
+ bool b = opIndex(i);
+ result = dg(i, b);
+ this[i] = b;
+ if (result)
+ break;
+ }
+ return result;
+ }
+
+ /** ditto */
+ int opApply(scope int delegate(size_t, bool) dg) const
+ {
+ int result;
+
+ foreach (i; 0 .. _len)
+ {
+ immutable b = opIndex(i);
+ result = dg(i, b);
+ if (result)
+ break;
+ }
+ return result;
+ }
+
+ @system unittest
+ {
+ debug(bitarray) printf("BitArray.opApply unittest\n");
+
+ static bool[] ba = [1,0,1];
+
+ auto a = BitArray(ba);
+
+ int i;
+ foreach (b;a)
+ {
+ switch (i)
+ {
+ case 0: assert(b == true); break;
+ case 1: assert(b == false); break;
+ case 2: assert(b == true); break;
+ default: assert(0);
+ }
+ i++;
+ }
+
+ foreach (j,b;a)
+ {
+ switch (j)
+ {
+ case 0: assert(b == true); break;
+ case 1: assert(b == false); break;
+ case 2: assert(b == true); break;
+ default: assert(0);
+ }
+ }
+ }
+
+
+ /**********************************************
+ * Reverses the bits of the $(D BitArray).
+ */
+ @property BitArray reverse() @nogc pure nothrow
+ out (result)
+ {
+ assert(result == this);
+ }
+ body
+ {
+ if (_len >= 2)
+ {
+ bool t;
+ size_t lo, hi;
+
+ lo = 0;
+ hi = _len - 1;
+ for (; lo < hi; lo++, hi--)
+ {
+ t = this[lo];
+ this[lo] = this[hi];
+ this[hi] = t;
+ }
+ }
+ return this;
+ }
+
+ @system unittest
+ {
+ debug(bitarray) printf("BitArray.reverse.unittest\n");
+
+ BitArray b;
+ static bool[5] data = [1,0,1,1,0];
+ int i;
+
+ b = BitArray(data);
+ b.reverse;
+ for (i = 0; i < data.length; i++)
+ {
+ assert(b[i] == data[4 - i]);
+ }
+ }
+
+
+ /**********************************************
+ * Sorts the $(D BitArray)'s elements.
+ */
+ @property BitArray sort() @nogc pure nothrow
+ out (result)
+ {
+ assert(result == this);
+ }
+ body
+ {
+ if (_len >= 2)
+ {
+ size_t lo, hi;
+
+ lo = 0;
+ hi = _len - 1;
+ while (1)
+ {
+ while (1)
+ {
+ if (lo >= hi)
+ goto Ldone;
+ if (this[lo] == true)
+ break;
+ lo++;
+ }
+
+ while (1)
+ {
+ if (lo >= hi)
+ goto Ldone;
+ if (this[hi] == false)
+ break;
+ hi--;
+ }
+
+ this[lo] = false;
+ this[hi] = true;
+
+ lo++;
+ hi--;
+ }
+ }
+ Ldone:
+ return this;
+ }
+
+ @system unittest
+ {
+ debug(bitarray) printf("BitArray.sort.unittest\n");
+
+ __gshared size_t x = 0b1100011000;
+ __gshared ba = BitArray(10, &x);
+ ba.sort;
+ for (size_t i = 0; i < 6; i++)
+ assert(ba[i] == false);
+ for (size_t i = 6; i < 10; i++)
+ assert(ba[i] == true);
+ }
+
+
+ /***************************************
+ * Support for operators == and != for $(D BitArray).
+ */
+ bool opEquals(const ref BitArray a2) const @nogc pure nothrow
+ {
+ if (this.length != a2.length)
+ return false;
+ auto p1 = this._ptr;
+ auto p2 = a2._ptr;
+
+ if (p1[0 .. fullWords] != p2[0 .. fullWords])
+ return false;
+
+ if (!endBits)
+ return true;
+
+ auto i = fullWords;
+ return (p1[i] & endMask) == (p2[i] & endMask);
+ }
+
+ @system unittest
+ {
+ debug(bitarray) printf("BitArray.opEquals unittest\n");
+
+ static bool[] ba = [1,0,1,0,1];
+ static bool[] bb = [1,0,1];
+ static bool[] bc = [1,0,1,0,1,0,1];
+ static bool[] bd = [1,0,1,1,1];
+ static bool[] be = [1,0,1,0,1];
+ static bool[] bf = [1,0,1,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0];
+ static bool[] bg = [1,0,1,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1];
+
+ auto a = BitArray(ba);
+ auto b = BitArray(bb);
+ auto c = BitArray(bc);
+ auto d = BitArray(bd);
+ auto e = BitArray(be);
+ auto f = BitArray(bf);
+ auto g = BitArray(bg);
+
+ assert(a != b);
+ assert(a != c);
+ assert(a != d);
+ assert(a == e);
+ assert(f != g);
+ }
+
+ /***************************************
+ * Supports comparison operators for $(D BitArray).
+ */
+ int opCmp(BitArray a2) const @nogc pure nothrow
+ {
+ 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)
+ {
+ if (p1[i] != p2[i])
+ {
+ return p1[i] & (size_t(1) << bsf(p1[i] ^ p2[i])) ? 1 : -1;
+ }
+ }
+
+ if (endBits)
+ {
+ immutable i = fullWords;
+ immutable diff = p1[i] ^ p2[i];
+ if (diff)
+ {
+ immutable index = bsf(diff);
+ if (index < endBits)
+ {
+ return p1[i] & (size_t(1) << index) ? 1 : -1;
+ }
+ }
+ }
+
+ // Standard:
+ // A bool value can be implicitly converted to any integral type,
+ // with false becoming 0 and true becoming 1
+ return (this.length > a2.length) - (this.length < a2.length);
+ }
+
+ @system unittest
+ {
+ debug(bitarray) printf("BitArray.opCmp unittest\n");
+
+ static bool[] ba = [1,0,1,0,1];
+ static bool[] bb = [1,0,1];
+ static bool[] bc = [1,0,1,0,1,0,1];
+ static bool[] bd = [1,0,1,1,1];
+ static bool[] be = [1,0,1,0,1];
+ static bool[] bf = [1,0,1,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1];
+ static bool[] bg = [1,0,1,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0];
+
+ auto a = BitArray(ba);
+ auto b = BitArray(bb);
+ auto c = BitArray(bc);
+ auto d = BitArray(bd);
+ auto e = BitArray(be);
+ auto f = BitArray(bf);
+ auto g = BitArray(bg);
+
+ assert(a > b);
+ assert(a >= b);
+ assert(a < c);
+ assert(a <= c);
+ assert(a < d);
+ assert(a <= d);
+ assert(a == e);
+ assert(a <= e);
+ assert(a >= e);
+ assert(f < g);
+ assert(g <= g);
+
+ bool[] v;
+ foreach (i; 1 .. 256)
+ {
+ v.length = i;
+ v[] = false;
+ auto x = BitArray(v);
+ v[i-1] = true;
+ auto y = BitArray(v);
+ assert(x < y);
+ assert(x <= y);
+ }
+
+ BitArray a1, a2;
+
+ for (size_t len = 4; len <= 256; len <<= 1)
+ {
+ a1.length = a2.length = len;
+ a1[len-2] = a2[len-1] = true;
+ assert(a1 > a2);
+ a1[len-2] = a2[len-1] = false;
+ }
+
+ foreach (j; 1 .. a1.length)
+ {
+ a1[j-1] = a2[j] = true;
+ assert(a1 > a2);
+ a1[j-1] = a2[j] = false;
+ }
+ }
+
+ /***************************************
+ * Support for hashing for $(D BitArray).
+ */
+ size_t toHash() const @nogc pure nothrow
+ {
+ size_t hash = 3557;
+ auto fullBytes = _len / 8;
+ foreach (i; 0 .. fullBytes)
+ {
+ hash *= 3559;
+ hash += (cast(byte*) this._ptr)[i];
+ }
+ foreach (i; 8*fullBytes .. _len)
+ {
+ hash *= 3571;
+ hash += this[i];
+ }
+ return hash;
+ }
+
+ /***************************************
+ * Set this $(D BitArray) to the contents of $(D ba).
+ */
+ this(bool[] ba) pure nothrow @system
+ {
+ length = ba.length;
+ foreach (i, b; ba)
+ {
+ this[i] = b;
+ }
+ }
+
+ // Deliberately undocumented: raw initialization of bit array.
+ this(size_t len, size_t* ptr)
+ {
+ _len = len;
+ _ptr = ptr;
+ }
+
+ /***************************************
+ * Map the $(D BitArray) onto $(D v), with $(D numbits) being the number of bits
+ * in the array. Does not copy the data. $(D v.length) must be a multiple of
+ * $(D size_t.sizeof). If there are unmapped bits in the final mapped word then
+ * these will be set to 0.
+ *
+ * This is the inverse of $(D opCast).
+ */
+ this(void[] v, size_t numbits) pure nothrow
+ in
+ {
+ assert(numbits <= v.length * 8);
+ assert(v.length % size_t.sizeof == 0);
+ }
+ body
+ {
+ _ptr = cast(size_t*) v.ptr;
+ _len = numbits;
+ if (endBits)
+ {
+ // Need to mask away extraneous bits from v.
+ _ptr[dim - 1] &= endMask;
+ }
+ }
+
+ @system unittest
+ {
+ debug(bitarray) printf("BitArray.init unittest\n");
+
+ static bool[] ba = [1,0,1,0,1];
+
+ auto a = BitArray(ba);
+ void[] v;
+
+ v = cast(void[]) a;
+ auto b = BitArray(v, a.length);
+
+ assert(b[0] == 1);
+ assert(b[1] == 0);
+ assert(b[2] == 1);
+ assert(b[3] == 0);
+ assert(b[4] == 1);
+
+ a[0] = 0;
+ assert(b[0] == 0);
+
+ assert(a == b);
+ }
+
+ /***************************************
+ * Convert to $(D void[]).
+ */
+ void[] opCast(T : void[])() @nogc pure nothrow
+ {
+ return cast(void[])_ptr[0 .. dim];
+ }
+
+ /***************************************
+ * Convert to $(D size_t[]).
+ */
+ size_t[] opCast(T : size_t[])() @nogc pure nothrow
+ {
+ return _ptr[0 .. dim];
+ }
+
+ @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;
+
+ assert(v.length == a.dim * size_t.sizeof);
+ }
+
+ /***************************************
+ * Support for unary operator ~ for $(D BitArray).
+ */
+ BitArray opCom() const pure nothrow
+ {
+ auto dim = this.dim;
+
+ BitArray result;
+ result.length = _len;
+
+ 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;
+
+ return result;
+ }
+
+ @system unittest
+ {
+ debug(bitarray) printf("BitArray.opCom unittest\n");
+
+ static bool[] ba = [1,0,1,0,1];
+
+ auto a = BitArray(ba);
+ BitArray b = ~a;
+
+ assert(b[0] == 0);
+ assert(b[1] == 1);
+ assert(b[2] == 0);
+ assert(b[3] == 1);
+ assert(b[4] == 0);
+ }
+
+
+ /***************************************
+ * Support for binary bitwise operators for $(D BitArray).
+ */
+ BitArray opBinary(string op)(const BitArray e2) const pure nothrow
+ if (op == "-" || op == "&" || op == "|" || op == "^")
+ in
+ {
+ assert(_len == e2.length);
+ }
+ body
+ {
+ auto dim = this.dim;
+
+ BitArray result;
+ result.length = _len;
+
+ static if (op == "-")
+ 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];");
+
+ // Avoid putting garbage in extra bits
+ // Remove once we zero on length extension
+ if (endBits)
+ result._ptr[dim - 1] &= endMask;
+
+ return result;
+ }
+
+ @system unittest
+ {
+ debug(bitarray) printf("BitArray.opAnd unittest\n");
+
+ static bool[] ba = [1,0,1,0,1];
+ static bool[] bb = [1,0,1,1,0];
+
+ auto a = BitArray(ba);
+ auto b = BitArray(bb);
+
+ BitArray c = a & b;
+
+ assert(c[0] == 1);
+ assert(c[1] == 0);
+ assert(c[2] == 1);
+ assert(c[3] == 0);
+ assert(c[4] == 0);
+ }
+
+ @system unittest
+ {
+ debug(bitarray) printf("BitArray.opOr unittest\n");
+
+ static bool[] ba = [1,0,1,0,1];
+ static bool[] bb = [1,0,1,1,0];
+
+ auto a = BitArray(ba);
+ auto b = BitArray(bb);
+
+ BitArray c = a | b;
+
+ assert(c[0] == 1);
+ assert(c[1] == 0);
+ assert(c[2] == 1);
+ assert(c[3] == 1);
+ assert(c[4] == 1);
+ }
+
+ @system unittest
+ {
+ debug(bitarray) printf("BitArray.opXor unittest\n");
+
+ static bool[] ba = [1,0,1,0,1];
+ static bool[] bb = [1,0,1,1,0];
+
+ auto a = BitArray(ba);
+ auto b = BitArray(bb);
+
+ BitArray c = a ^ b;
+
+ assert(c[0] == 0);
+ assert(c[1] == 0);
+ assert(c[2] == 0);
+ assert(c[3] == 1);
+ assert(c[4] == 1);
+ }
+
+ @system unittest
+ {
+ debug(bitarray) printf("BitArray.opSub unittest\n");
+
+ static bool[] ba = [1,0,1,0,1];
+ static bool[] bb = [1,0,1,1,0];
+
+ auto a = BitArray(ba);
+ auto b = BitArray(bb);
+
+ BitArray c = a - b;
+
+ assert(c[0] == 0);
+ assert(c[1] == 0);
+ assert(c[2] == 0);
+ assert(c[3] == 0);
+ assert(c[4] == 1);
+ }
+
+
+ /***************************************
+ * Support for operator op= for $(D BitArray).
+ */
+ BitArray opOpAssign(string op)(const BitArray e2) @nogc pure nothrow
+ if (op == "-" || op == "&" || op == "|" || op == "^")
+ in
+ {
+ assert(_len == e2.length);
+ }
+ body
+ {
+ foreach (i; 0 .. fullWords)
+ {
+ static if (op == "-")
+ _ptr[i] &= ~e2._ptr[i];
+ else
+ mixin("_ptr[i] "~op~"= e2._ptr[i];");
+ }
+ if (!endBits)
+ return this;
+
+ size_t i = fullWords;
+ size_t endWord = _ptr[i];
+ static if (op == "-")
+ endWord &= ~e2._ptr[i];
+ else
+ mixin("endWord "~op~"= e2._ptr[i];");
+ _ptr[i] = (_ptr[i] & ~endMask) | (endWord & endMask);
+
+ return this;
+ }
+
+ @system unittest
+ {
+ static bool[] ba = [1,0,1,0,1,1,0,1,0,1];
+ static bool[] bb = [1,0,1,1,0];
+ auto a = BitArray(ba);
+ auto b = BitArray(bb);
+ BitArray c = a;
+ c.length = 5;
+ c &= b;
+ assert(a[5] == 1);
+ assert(a[6] == 0);
+ assert(a[7] == 1);
+ assert(a[8] == 0);
+ assert(a[9] == 1);
+ }
+
+ @system unittest
+ {
+ debug(bitarray) printf("BitArray.opAndAssign unittest\n");
+
+ static bool[] ba = [1,0,1,0,1];
+ static bool[] bb = [1,0,1,1,0];
+
+ auto a = BitArray(ba);
+ auto b = BitArray(bb);
+
+ a &= b;
+ assert(a[0] == 1);
+ assert(a[1] == 0);
+ assert(a[2] == 1);
+ assert(a[3] == 0);
+ assert(a[4] == 0);
+ }
+
+ @system unittest
+ {
+ debug(bitarray) printf("BitArray.opOrAssign unittest\n");
+
+ static bool[] ba = [1,0,1,0,1];
+ static bool[] bb = [1,0,1,1,0];
+
+ auto a = BitArray(ba);
+ auto b = BitArray(bb);
+
+ a |= b;
+ assert(a[0] == 1);
+ assert(a[1] == 0);
+ assert(a[2] == 1);
+ assert(a[3] == 1);
+ assert(a[4] == 1);
+ }
+
+ @system unittest
+ {
+ debug(bitarray) printf("BitArray.opXorAssign unittest\n");
+
+ static bool[] ba = [1,0,1,0,1];
+ static bool[] bb = [1,0,1,1,0];
+
+ auto a = BitArray(ba);
+ auto b = BitArray(bb);
+
+ a ^= b;
+ assert(a[0] == 0);
+ assert(a[1] == 0);
+ assert(a[2] == 0);
+ assert(a[3] == 1);
+ assert(a[4] == 1);
+ }
+
+ @system unittest
+ {
+ debug(bitarray) printf("BitArray.opSubAssign unittest\n");
+
+ static bool[] ba = [1,0,1,0,1];
+ static bool[] bb = [1,0,1,1,0];
+
+ auto a = BitArray(ba);
+ auto b = BitArray(bb);
+
+ a -= b;
+ assert(a[0] == 0);
+ assert(a[1] == 0);
+ assert(a[2] == 0);
+ assert(a[3] == 0);
+ assert(a[4] == 1);
+ }
+
+ /***************************************
+ * Support for operator ~= for $(D BitArray).
+ * $(RED Warning: This will overwrite a bit in the final word
+ * of the current underlying data regardless of whether it is
+ * shared between BitArray objects. i.e. D dynamic array
+ * concatenation semantics are not followed)
+ */
+
+ BitArray opCatAssign(bool b) pure nothrow
+ {
+ length = _len + 1;
+ this[_len - 1] = b;
+ return this;
+ }
+
+ @system unittest
+ {
+ debug(bitarray) printf("BitArray.opCatAssign unittest\n");
+
+ static bool[] ba = [1,0,1,0,1];
+
+ auto a = BitArray(ba);
+ BitArray b;
+
+ b = (a ~= true);
+ assert(a[0] == 1);
+ assert(a[1] == 0);
+ assert(a[2] == 1);
+ assert(a[3] == 0);
+ assert(a[4] == 1);
+ assert(a[5] == 1);
+
+ assert(b == a);
+ }
+
+ /***************************************
+ * ditto
+ */
+
+ BitArray opCatAssign(BitArray b) pure nothrow
+ {
+ auto istart = _len;
+ length = _len + b.length;
+ for (auto i = istart; i < _len; i++)
+ this[i] = b[i - istart];
+ return this;
+ }
+
+ @system unittest
+ {
+ debug(bitarray) printf("BitArray.opCatAssign unittest\n");
+
+ static bool[] ba = [1,0];
+ static bool[] bb = [0,1,0];
+
+ auto a = BitArray(ba);
+ auto b = BitArray(bb);
+ BitArray c;
+
+ c = (a ~= b);
+ assert(a.length == 5);
+ assert(a[0] == 1);
+ assert(a[1] == 0);
+ assert(a[2] == 0);
+ assert(a[3] == 1);
+ assert(a[4] == 0);
+
+ assert(c == a);
+ }
+
+ /***************************************
+ * Support for binary operator ~ for $(D BitArray).
+ */
+ BitArray opCat(bool b) const pure nothrow
+ {
+ BitArray r;
+
+ r = this.dup;
+ r.length = _len + 1;
+ r[_len] = b;
+ return r;
+ }
+
+ /** ditto */
+ BitArray opCat_r(bool b) const pure nothrow
+ {
+ BitArray r;
+
+ r.length = _len + 1;
+ r[0] = b;
+ foreach (i; 0 .. _len)
+ r[1 + i] = this[i];
+ return r;
+ }
+
+ /** ditto */
+ BitArray opCat(BitArray b) const pure nothrow
+ {
+ BitArray r;
+
+ r = this.dup;
+ r ~= b;
+ return r;
+ }
+
+ @system unittest
+ {
+ debug(bitarray) printf("BitArray.opCat unittest\n");
+
+ static bool[] ba = [1,0];
+ static bool[] bb = [0,1,0];
+
+ auto a = BitArray(ba);
+ auto b = BitArray(bb);
+ BitArray c;
+
+ c = (a ~ b);
+ assert(c.length == 5);
+ assert(c[0] == 1);
+ assert(c[1] == 0);
+ assert(c[2] == 0);
+ assert(c[3] == 1);
+ assert(c[4] == 0);
+
+ c = (a ~ true);
+ assert(c.length == 3);
+ assert(c[0] == 1);
+ assert(c[1] == 0);
+ assert(c[2] == 1);
+
+ c = (false ~ a);
+ assert(c.length == 3);
+ assert(c[0] == 0);
+ assert(c[1] == 1);
+ assert(c[2] == 0);
+ }
+
+ // Rolls double word (upper, lower) to the right by n bits and returns the
+ // lower word of the result.
+ private static size_t rollRight()(size_t upper, size_t lower, size_t nbits)
+ pure @safe nothrow @nogc
+ in
+ {
+ assert(nbits < bitsPerSizeT);
+ }
+ body
+ {
+ return (upper << (bitsPerSizeT - nbits)) | (lower >> nbits);
+ }
+
+ @safe unittest
+ {
+ static if (size_t.sizeof == 8)
+ {
+ size_t x = 0x12345678_90ABCDEF;
+ size_t y = 0xFEDBCA09_87654321;
+
+ assert(rollRight(x, y, 32) == 0x90ABCDEF_FEDBCA09);
+ assert(rollRight(y, x, 4) == 0x11234567_890ABCDE);
+ }
+ else static if (size_t.sizeof == 4)
+ {
+ size_t x = 0x12345678;
+ size_t y = 0x90ABCDEF;
+
+ assert(rollRight(x, y, 16) == 0x567890AB);
+ assert(rollRight(y, x, 4) == 0xF1234567);
+ }
+ else
+ static assert(0, "Unsupported size_t width");
+ }
+
+ // Rolls double word (upper, lower) to the left by n bits and returns the
+ // upper word of the result.
+ private static size_t rollLeft()(size_t upper, size_t lower, size_t nbits)
+ pure @safe nothrow @nogc
+ in
+ {
+ assert(nbits < bitsPerSizeT);
+ }
+ body
+ {
+ return (upper << nbits) | (lower >> (bitsPerSizeT - nbits));
+ }
+
+ @safe unittest
+ {
+ static if (size_t.sizeof == 8)
+ {
+ size_t x = 0x12345678_90ABCDEF;
+ size_t y = 0xFEDBCA09_87654321;
+
+ assert(rollLeft(x, y, 32) == 0x90ABCDEF_FEDBCA09);
+ assert(rollLeft(y, x, 4) == 0xEDBCA098_76543211);
+ }
+ else static if (size_t.sizeof == 4)
+ {
+ size_t x = 0x12345678;
+ size_t y = 0x90ABCDEF;
+
+ assert(rollLeft(x, y, 16) == 0x567890AB);
+ assert(rollLeft(y, x, 4) == 0x0ABCDEF1);
+ }
+ }
+
+ /**
+ * Operator $(D <<=) support.
+ *
+ * Shifts all the bits in the array to the left by the given number of
+ * bits. The leftmost bits are dropped, and 0's are appended to the end
+ * to fill up the vacant bits.
+ *
+ * $(RED Warning: unused bits in the final word up to the next word
+ * boundary may be overwritten by this operation. It does not attempt to
+ * preserve bits past the end of the array.)
+ */
+ void opOpAssign(string op)(size_t nbits) @nogc pure nothrow
+ if (op == "<<")
+ {
+ size_t wordsToShift = nbits / bitsPerSizeT;
+ size_t bitsToShift = nbits % bitsPerSizeT;
+
+ if (wordsToShift < dim)
+ {
+ foreach_reverse (i; 1 .. dim - wordsToShift)
+ {
+ _ptr[i + wordsToShift] = rollLeft(_ptr[i], _ptr[i-1],
+ bitsToShift);
+ }
+ _ptr[wordsToShift] = rollLeft(_ptr[0], 0, bitsToShift);
+ }
+
+ import std.algorithm.comparison : min;
+ foreach (i; 0 .. min(wordsToShift, dim))
+ {
+ _ptr[i] = 0;
+ }
+ }
+
+ /**
+ * Operator $(D >>=) support.
+ *
+ * Shifts all the bits in the array to the right by the given number of
+ * bits. The rightmost bits are dropped, and 0's are inserted at the back
+ * to fill up the vacant bits.
+ *
+ * $(RED Warning: unused bits in the final word up to the next word
+ * boundary may be overwritten by this operation. It does not attempt to
+ * preserve bits past the end of the array.)
+ */
+ void opOpAssign(string op)(size_t nbits) @nogc pure nothrow
+ if (op == ">>")
+ {
+ size_t wordsToShift = nbits / bitsPerSizeT;
+ size_t bitsToShift = nbits % bitsPerSizeT;
+
+ if (wordsToShift + 1 < dim)
+ {
+ foreach (i; 0 .. dim - wordsToShift - 1)
+ {
+ _ptr[i] = rollRight(_ptr[i + wordsToShift + 1],
+ _ptr[i + wordsToShift], bitsToShift);
+ }
+ }
+
+ // The last word needs some care, as it must shift in 0's from past the
+ // end of the array.
+ if (wordsToShift < dim)
+ {
+ _ptr[dim - wordsToShift - 1] = rollRight(0, _ptr[dim - 1] & endMask,
+ bitsToShift);
+ }
+
+ import std.algorithm.comparison : min;
+ foreach (i; 0 .. min(wordsToShift, dim))
+ {
+ _ptr[dim - i - 1] = 0;
+ }
+ }
+
+ @system unittest
+ {
+ import std.format : format;
+
+ auto b = BitArray([1, 1, 0, 0, 1, 0, 1, 0, 1, 1, 0, 1, 1]);
+
+ b <<= 1;
+ assert(format("%b", b) == "01100_10101101");
+
+ b >>= 1;
+ assert(format("%b", b) == "11001_01011010");
+
+ b <<= 4;
+ assert(format("%b", b) == "00001_10010101");
+
+ b >>= 5;
+ assert(format("%b", b) == "10010_10100000");
+
+ b <<= 13;
+ assert(format("%b", b) == "00000_00000000");
+
+ b = BitArray([1, 0, 1, 1, 0, 1, 1, 1]);
+ b >>= 8;
+ assert(format("%b", b) == "00000000");
+
+ }
+
+ // Test multi-word case
+ @system unittest
+ {
+ import std.format : format;
+
+ // This has to be long enough to occupy more than one size_t. On 64-bit
+ // machines, this would be at least 64 bits.
+ auto b = BitArray([
+ 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0,
+ 1, 1, 1, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0,
+ 1, 1, 1, 1, 1, 0, 0, 0, 1, 1, 1, 1, 1, 1, 0, 0,
+ 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 0, 1, 0, 1, 0, 1, 0, 0, 1, 0, 1, 0, 1, 0, 1,
+ ]);
+ b <<= 8;
+ assert(format("%b", b) ==
+ "00000000_10000000_"~
+ "11000000_11100000_"~
+ "11110000_11111000_"~
+ "11111100_11111110_"~
+ "11111111_10101010");
+
+ // Test right shift of more than one size_t's worth of bits
+ b <<= 68;
+ assert(format("%b", b) ==
+ "00000000_00000000_"~
+ "00000000_00000000_"~
+ "00000000_00000000_"~
+ "00000000_00000000_"~
+ "00000000_00001000");
+
+ b = BitArray([
+ 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0,
+ 1, 1, 1, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0,
+ 1, 1, 1, 1, 1, 0, 0, 0, 1, 1, 1, 1, 1, 1, 0, 0,
+ 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 0, 1, 0, 1, 0, 1, 0, 0, 1, 0, 1, 0, 1, 0, 1,
+ ]);
+ b >>= 8;
+ assert(format("%b", b) ==
+ "11000000_11100000_"~
+ "11110000_11111000_"~
+ "11111100_11111110_"~
+ "11111111_10101010_"~
+ "01010101_00000000");
+
+ // Test left shift of more than 1 size_t's worth of bits
+ b >>= 68;
+ assert(format("%b", b) ==
+ "01010000_00000000_"~
+ "00000000_00000000_"~
+ "00000000_00000000_"~
+ "00000000_00000000_"~
+ "00000000_00000000");
+ }
+
+ /***************************************
+ * Return a string representation of this BitArray.
+ *
+ * Two format specifiers are supported:
+ * $(LI $(B %s) which prints the bits as an array, and)
+ * $(LI $(B %b) which prints the bits as 8-bit byte packets)
+ * separated with an underscore.
+ */
+ void toString(scope void delegate(const(char)[]) sink,
+ FormatSpec!char fmt) const
+ {
+ switch (fmt.spec)
+ {
+ case 'b':
+ return formatBitString(sink);
+ case 's':
+ return formatBitArray(sink);
+ default:
+ throw new Exception("Unknown format specifier: %" ~ fmt.spec);
+ }
+ }
+
+ ///
+ @system unittest
+ {
+ import std.format : format;
+
+ debug(bitarray) printf("BitArray.toString unittest\n");
+ auto b = BitArray([0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 1, 1, 1, 1]);
+
+ auto s1 = format("%s", b);
+ assert(s1 == "[0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 1, 1, 1, 1]");
+
+ auto s2 = format("%b", b);
+ assert(s2 == "00001111_00001111");
+ }
+
+ /***************************************
+ * Return a lazy range of the indices of set bits.
+ */
+ @property auto bitsSet() const nothrow
+ {
+ 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))().
+ joiner();
+ }
+
+ ///
+ @system unittest
+ {
+ 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]));
+
+ BitArray b2;
+ b2.length = 1000;
+ b2[333] = true;
+ b2[666] = true;
+ b2[999] = true;
+ assert(b2.bitsSet.equal([333, 666, 999]));
+ }
+
+ @system unittest
+ {
+ import std.algorithm.comparison : equal;
+ import std.range : iota;
+
+ debug(bitarray) printf("BitArray.bitsSet unittest\n");
+ BitArray b;
+ enum wordBits = size_t.sizeof * 8;
+ b = BitArray([size_t.max], 0);
+ assert(b.bitsSet.empty);
+ b = BitArray([size_t.max], 1);
+ assert(b.bitsSet.equal([0]));
+ b = BitArray([size_t.max], wordBits);
+ assert(b.bitsSet.equal(iota(wordBits)));
+ b = BitArray([size_t.max, size_t.max], wordBits);
+ assert(b.bitsSet.equal(iota(wordBits)));
+ b = BitArray([size_t.max, size_t.max], wordBits + 1);
+ assert(b.bitsSet.equal(iota(wordBits + 1)));
+ b = BitArray([size_t.max, size_t.max], wordBits * 2);
+ assert(b.bitsSet.equal(iota(wordBits * 2)));
+ }
+
+ private void formatBitString(scope void delegate(const(char)[]) sink) const
+ {
+ if (!length)
+ return;
+
+ auto leftover = _len % 8;
+ foreach (idx; 0 .. leftover)
+ {
+ char[1] res = cast(char)(this[idx] + '0');
+ sink.put(res[]);
+ }
+
+ if (leftover && _len > 8)
+ sink.put("_");
+
+ size_t count;
+ foreach (idx; leftover .. _len)
+ {
+ char[1] res = cast(char)(this[idx] + '0');
+ sink.put(res[]);
+ if (++count == 8 && idx != _len - 1)
+ {
+ sink.put("_");
+ count = 0;
+ }
+ }
+ }
+
+ private void formatBitArray(scope void delegate(const(char)[]) sink) const
+ {
+ sink("[");
+ foreach (idx; 0 .. _len)
+ {
+ char[1] res = cast(char)(this[idx] + '0');
+ sink(res[]);
+ if (idx+1 < _len)
+ sink(", ");
+ }
+ sink("]");
+ }
+}
+
+@system unittest
+{
+ import std.format : format;
+
+ BitArray b;
+
+ b = BitArray([]);
+ assert(format("%s", b) == "[]");
+ assert(format("%b", b) is null);
+
+ b = BitArray([1]);
+ assert(format("%s", b) == "[1]");
+ assert(format("%b", b) == "1");
+
+ b = BitArray([0, 0, 0, 0]);
+ assert(format("%b", b) == "0000");
+
+ b = BitArray([0, 0, 0, 0, 1, 1, 1, 1]);
+ assert(format("%s", b) == "[0, 0, 0, 0, 1, 1, 1, 1]");
+ assert(format("%b", b) == "00001111");
+
+ b = BitArray([0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 1, 1, 1, 1]);
+ assert(format("%s", b) == "[0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 1, 1, 1, 1]");
+ assert(format("%b", b) == "00001111_00001111");
+
+ b = BitArray([1, 0, 0, 0, 0, 1, 1, 1, 1]);
+ assert(format("%b", b) == "1_00001111");
+
+ b = BitArray([1, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 1, 1, 1, 1]);
+ assert(format("%b", b) == "1_00001111_00001111");
+}
+
+/++
+ 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)
+{
+ static if (val.sizeof == 1)
+ return val;
+ 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 assert(0, T.stringof ~ " unsupported by swapEndian.");
+}
+
+private ushort swapEndianImpl(ushort val) @safe pure nothrow @nogc
+{
+ return ((val & 0xff00U) >> 8) |
+ ((val & 0x00ffU) << 8);
+}
+
+private uint swapEndianImpl(uint val) @trusted pure nothrow @nogc
+{
+ 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);
+ return res << 32 | bswap(cast(uint)(val >> 32));
+}
+
+@safe unittest
+{
+ 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;
+ const T cval;
+ immutable T ival;
+
+ assert(swapEndian(swapEndian(val)) == val);
+ assert(swapEndian(swapEndian(cval)) == cval);
+ assert(swapEndian(swapEndian(ival)) == ival);
+ assert(swapEndian(swapEndian(T.min)) == T.min);
+ assert(swapEndian(swapEndian(T.max)) == T.max);
+
+ 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)
+ assert(swapEndian(swapEndian(minI)) == minI);
+ }
+
+ static if (isSigned!T)
+ assert(swapEndian(swapEndian(cast(T) 0)) == 0);
+
+ // used to trigger BUG6354
+ 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)
+ {
+ assert(swapEndian(left) == right);
+ assert(swapEndian(right) == left);
+ left >>= 8;
+ right <<= 8;
+ }
+ }
+ }
+}
+
+
+private union EndianSwapper(T)
+if (canSwapEndianness!T)
+{
+ Unqual!T value;
+ ubyte[T.sizeof] array;
+
+ static if (is(FloatingPointTypeOf!T == float))
+ uint intValue;
+ else static if (is(FloatingPointTypeOf!T == double))
+ ulong intValue;
+
+}
+
+
+/++
+ Converts the given value from the native endianness to big endian and
+ returns it as a $(D ubyte[n]) where $(D n) is the size of the given type.
+
+ Returning a $(D ubyte[n]) helps prevent accidentally using a swapped value
+ as a regular one (and in the case of floating point values, it's necessary,
+ because the FPU will mess up any swapped floating point values. So, you
+ can't actually have swapped floating point values as floating point values).
+
+ $(D real) is not supported, because its size is implementation-dependent
+ and therefore could vary from machine to machine (which could make it
+ unusable if you tried to transfer it to another machine).
+ +/
+auto nativeToBigEndian(T)(T val) @safe pure nothrow @nogc
+if (canSwapEndianness!T)
+{
+ return nativeToBigEndianImpl(val);
+}
+
+///
+@safe unittest
+{
+ int i = 12345;
+ ubyte[4] swappedI = nativeToBigEndian(i);
+ assert(i == bigEndianToNative!int(swappedI));
+
+ double d = 123.45;
+ ubyte[8] swappedD = nativeToBigEndian(d);
+ assert(d == bigEndianToNative!double(swappedD));
+}
+
+private auto nativeToBigEndianImpl(T)(T val) @safe pure nothrow @nogc
+if (isIntegral!T || isSomeChar!T || isBoolean!T)
+{
+ EndianSwapper!T es = void;
+
+ version (LittleEndian)
+ es.value = swapEndian(val);
+ else
+ es.value = val;
+
+ return es.array;
+}
+
+private auto nativeToBigEndianImpl(T)(T val) @safe pure nothrow @nogc
+if (isFloatOrDouble!T)
+{
+ version (LittleEndian)
+ return floatEndianImpl!(T, true)(val);
+ else
+ return floatEndianImpl!(T, false)(val);
+}
+
+@safe unittest
+{
+ 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.
+ * When a nan passes through the XMM, it does not convert signaling to quiet.
+ * float.init is a signaling nan.
+ * The binary API sometimes passes the data through the XMM, sometimes through
+ * the x87, meaning these will fail the 'is' bit compare under some circumstances.
+ * I cannot think of a fix for this that makes consistent sense.
+ */
+ /*,float, double*/))
+ {
+ scope(failure) writefln("Failed type: %s", T.stringof);
+ T val;
+ const T cval;
+ immutable T ival;
+
+ //is instead of == because of NaN for floating point values.
+ assert(bigEndianToNative!T(nativeToBigEndian(val)) is val);
+ assert(bigEndianToNative!T(nativeToBigEndian(cval)) is cval);
+ assert(bigEndianToNative!T(nativeToBigEndian(ival)) is ival);
+ 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 (!is(T == bool))
+ {
+ 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)
+ assert(nativeToBigEndian(maxI) != nativeToLittleEndian(maxI));
+ else
+ assert(nativeToBigEndian(maxI) == nativeToLittleEndian(maxI));
+
+ static if (isSigned!T)
+ {
+ assert(bigEndianToNative!T(nativeToBigEndian(minI)) == minI);
+
+ static if (T.sizeof > 1)
+ assert(nativeToBigEndian(minI) != nativeToLittleEndian(minI));
+ else
+ assert(nativeToBigEndian(minI) == nativeToLittleEndian(minI));
+ }
+ }
+ }
+
+ 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)
+ assert(nativeToBigEndian(T.min) == nativeToLittleEndian(T.min));
+ else
+ assert(nativeToBigEndian(T.min) != nativeToLittleEndian(T.min));
+ }
+}
+
+
+/++
+ Converts the given value from big endian to the native endianness and
+ returns it. The value is given as a $(D ubyte[n]) where $(D n) is the size
+ of the target type. You must give the target type as a template argument,
+ because there are multiple types with the same size and so the type of the
+ argument is not enough to determine the return type.
+
+ Taking a $(D ubyte[n]) helps prevent accidentally using a swapped value
+ as a regular one (and in the case of floating point values, it's necessary,
+ because the FPU will mess up any swapped floating point values. So, you
+ 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)
+{
+ return bigEndianToNativeImpl!(T, n)(val);
+}
+
+///
+@safe unittest
+{
+ ushort i = 12345;
+ ubyte[2] swappedI = nativeToBigEndian(i);
+ assert(i == bigEndianToNative!ushort(swappedI));
+
+ dchar c = 'D';
+ ubyte[4] swappedC = nativeToBigEndian(c);
+ assert(c == bigEndianToNative!dchar(swappedC));
+}
+
+private T bigEndianToNativeImpl(T, size_t n)(ubyte[n] val) @safe pure nothrow @nogc
+if ((isIntegral!T || isSomeChar!T || isBoolean!T) &&
+ n == T.sizeof)
+{
+ EndianSwapper!T es = void;
+ es.array = val;
+
+ version (LittleEndian)
+ immutable retval = swapEndian(es.value);
+ else
+ immutable retval = es.value;
+
+ return retval;
+}
+
+private T bigEndianToNativeImpl(T, size_t n)(ubyte[n] val) @safe pure nothrow @nogc
+if (isFloatOrDouble!T && n == T.sizeof)
+{
+ version (LittleEndian)
+ return cast(T) floatEndianImpl!(n, true)(val);
+ else
+ return cast(T) floatEndianImpl!(n, false)(val);
+}
+
+
+/++
+ Converts the given value from the native endianness to little endian and
+ returns it as a $(D ubyte[n]) where $(D n) is the size of the given type.
+
+ Returning a $(D ubyte[n]) helps prevent accidentally using a swapped value
+ as a regular one (and in the case of floating point values, it's necessary,
+ because the FPU will mess up any swapped floating point values. So, you
+ can't actually have swapped floating point values as floating point values).
+ +/
+auto nativeToLittleEndian(T)(T val) @safe pure nothrow @nogc
+if (canSwapEndianness!T)
+{
+ return nativeToLittleEndianImpl(val);
+}
+
+///
+@safe unittest
+{
+ int i = 12345;
+ ubyte[4] swappedI = nativeToLittleEndian(i);
+ assert(i == littleEndianToNative!int(swappedI));
+
+ double d = 123.45;
+ ubyte[8] swappedD = nativeToLittleEndian(d);
+ assert(d == littleEndianToNative!double(swappedD));
+}
+
+private auto nativeToLittleEndianImpl(T)(T val) @safe pure nothrow @nogc
+if (isIntegral!T || isSomeChar!T || isBoolean!T)
+{
+ EndianSwapper!T es = void;
+
+ version (BigEndian)
+ es.value = swapEndian(val);
+ else
+ es.value = val;
+
+ return es.array;
+}
+
+private auto nativeToLittleEndianImpl(T)(T val) @safe pure nothrow @nogc
+if (isFloatOrDouble!T)
+{
+ version (BigEndian)
+ return floatEndianImpl!(T, true)(val);
+ else
+ return floatEndianImpl!(T, false)(val);
+}
+
+@safe unittest
+{
+ 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;
+ const T cval;
+ immutable T ival;
+
+ //is instead of == because of NaN for floating point values.
+ assert(littleEndianToNative!T(nativeToLittleEndian(val)) is val);
+ assert(littleEndianToNative!T(nativeToLittleEndian(cval)) is cval);
+ assert(littleEndianToNative!T(nativeToLittleEndian(ival)) is ival);
+ 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 (!is(T == bool))
+ {
+ 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)
+ assert(littleEndianToNative!T(nativeToLittleEndian(minI)) == minI);
+ }
+ }
+ }
+}
+
+
+/++
+ Converts the given value from little endian to the native endianness and
+ returns it. The value is given as a $(D ubyte[n]) where $(D n) is the size
+ of the target type. You must give the target type as a template argument,
+ because there are multiple types with the same size and so the type of the
+ argument is not enough to determine the return type.
+
+ Taking a $(D ubyte[n]) helps prevent accidentally using a swapped value
+ as a regular one (and in the case of floating point values, it's necessary,
+ because the FPU will mess up any swapped floating point values. So, you
+ can't actually have swapped floating point values as floating point values).
+
+ $(D real) is not supported, because its size is implementation-dependent
+ and therefore could vary from machine to machine (which could make it
+ 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)
+{
+ return littleEndianToNativeImpl!T(val);
+}
+
+///
+@safe unittest
+{
+ ushort i = 12345;
+ ubyte[2] swappedI = nativeToLittleEndian(i);
+ assert(i == littleEndianToNative!ushort(swappedI));
+
+ dchar c = 'D';
+ ubyte[4] swappedC = nativeToLittleEndian(c);
+ assert(c == littleEndianToNative!dchar(swappedC));
+}
+
+private T littleEndianToNativeImpl(T, size_t n)(ubyte[n] val) @safe pure nothrow @nogc
+if ((isIntegral!T || isSomeChar!T || isBoolean!T) &&
+ n == T.sizeof)
+{
+ EndianSwapper!T es = void;
+ es.array = val;
+
+ version (BigEndian)
+ immutable retval = swapEndian(es.value);
+ else
+ immutable retval = es.value;
+
+ return retval;
+}
+
+private T littleEndianToNativeImpl(T, size_t n)(ubyte[n] val) @safe pure nothrow @nogc
+if (((isFloatOrDouble!T) &&
+ n == T.sizeof))
+{
+ version (BigEndian)
+ return floatEndianImpl!(n, true)(val);
+ else
+ return floatEndianImpl!(n, false)(val);
+}
+
+private auto floatEndianImpl(T, bool swap)(T val) @safe pure nothrow @nogc
+if (isFloatOrDouble!T)
+{
+ EndianSwapper!T es = void;
+ es.value = val;
+
+ 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)
+{
+ static if (n == 4) EndianSwapper!float es = void;
+ else static if (n == 8) EndianSwapper!double es = void;
+
+ es.array = val;
+
+ static if (swap)
+ es.intValue = swapEndian(es.intValue);
+
+ return es.value;
+}
+
+private template isFloatOrDouble(T)
+{
+ enum isFloatOrDouble = isFloatingPoint!T &&
+ !is(Unqual!(FloatingPointTypeOf!T) == real);
+}
+
+@safe unittest
+{
+ import std.meta;
+ foreach (T; AliasSeq!(float, double))
+ {
+ static assert(isFloatOrDouble!(T));
+ static assert(isFloatOrDouble!(const T));
+ static assert(isFloatOrDouble!(immutable T));
+ static assert(isFloatOrDouble!(shared T));
+ static assert(isFloatOrDouble!(shared(const T)));
+ static assert(isFloatOrDouble!(shared(immutable T)));
+ }
+
+ static assert(!isFloatOrDouble!(real));
+ static assert(!isFloatOrDouble!(const real));
+ static assert(!isFloatOrDouble!(immutable real));
+ static assert(!isFloatOrDouble!(shared real));
+ static assert(!isFloatOrDouble!(shared(const real)));
+ static assert(!isFloatOrDouble!(shared(immutable real)));
+}
+
+private template canSwapEndianness(T)
+{
+ enum canSwapEndianness = isIntegral!T ||
+ isSomeChar!T ||
+ isBoolean!T ||
+ isFloatOrDouble!T;
+}
+
+@safe unittest
+{
+ 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));
+ static assert(canSwapEndianness!(immutable T));
+ static assert(canSwapEndianness!(shared(T)));
+ static assert(canSwapEndianness!(shared(const T)));
+ static assert(canSwapEndianness!(shared(immutable T)));
+ }
+
+ //!
+ foreach (T; AliasSeq!(real, string, wstring, dstring))
+ {
+ static assert(!canSwapEndianness!(T));
+ static assert(!canSwapEndianness!(const T));
+ static assert(!canSwapEndianness!(immutable T));
+ static assert(!canSwapEndianness!(shared(T)));
+ static assert(!canSwapEndianness!(shared(const T)));
+ static assert(!canSwapEndianness!(shared(immutable T)));
+ }
+}
+
+/++
+ Takes a range of $(D ubyte)s and converts the first $(D T.sizeof) bytes to
+ $(D T). The value returned is converted from the given endianness to the
+ native endianness. The range is not consumed.
+
+ Params:
+ T = The integral type to convert the first $(D T.sizeof) bytes to.
+ endianness = The endianness that the bytes are assumed to be in.
+ range = The range to read from.
+ index = The index to start reading from (instead of starting at the
+ front). If index is a pointer, then it is updated to the index
+ after the bytes read. The overloads with index are only
+ available if $(D hasSlicing!R) is $(D true).
+ +/
+
+T peek(T, Endian endianness = Endian.bigEndian, R)(R range)
+if (canSwapEndianness!T &&
+ isForwardRange!R &&
+ is(ElementType!R : const ubyte))
+{
+ static if (hasSlicing!R)
+ const ubyte[T.sizeof] bytes = range[0 .. T.sizeof];
+ else
+ {
+ ubyte[T.sizeof] bytes;
+ //Make sure that range is not consumed, even if it's a class.
+ range = range.save;
+
+ foreach (ref e; bytes)
+ {
+ e = range.front;
+ range.popFront();
+ }
+ }
+
+ static if (endianness == Endian.bigEndian)
+ return bigEndianToNative!T(bytes);
+ else
+ return littleEndianToNative!T(bytes);
+}
+
+/++ 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))
+{
+ 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))
+{
+ assert(index);
+
+ immutable begin = *index;
+ immutable end = begin + T.sizeof;
+ const ubyte[T.sizeof] bytes = range[begin .. end];
+ *index = end;
+
+ static if (endianness == Endian.bigEndian)
+ return bigEndianToNative!T(bytes);
+ else
+ return littleEndianToNative!T(bytes);
+}
+
+///
+@system unittest
+{
+ ubyte[] buffer = [1, 5, 22, 9, 44, 255, 8];
+ assert(buffer.peek!uint() == 17110537);
+ assert(buffer.peek!ushort() == 261);
+ assert(buffer.peek!ubyte() == 1);
+
+ assert(buffer.peek!uint(2) == 369700095);
+ assert(buffer.peek!ushort(2) == 5641);
+ assert(buffer.peek!ubyte(2) == 22);
+
+ size_t index = 0;
+ assert(buffer.peek!ushort(&index) == 261);
+ assert(index == 2);
+
+ assert(buffer.peek!uint(&index) == 369700095);
+ assert(index == 6);
+
+ assert(buffer.peek!ubyte(&index) == 8);
+ assert(index == 7);
+}
+
+@system unittest
+{
+ {
+ //bool
+ ubyte[] buffer = [0, 1];
+ assert(buffer.peek!bool() == false);
+ assert(buffer.peek!bool(1) == true);
+
+ size_t index = 0;
+ assert(buffer.peek!bool(&index) == false);
+ assert(index == 1);
+
+ assert(buffer.peek!bool(&index) == true);
+ assert(index == 2);
+ }
+
+ {
+ //char (8bit)
+ ubyte[] buffer = [97, 98, 99, 100];
+ assert(buffer.peek!char() == 'a');
+ assert(buffer.peek!char(1) == 'b');
+
+ size_t index = 0;
+ assert(buffer.peek!char(&index) == 'a');
+ assert(index == 1);
+
+ assert(buffer.peek!char(&index) == 'b');
+ assert(index == 2);
+ }
+
+ {
+ //wchar (16bit - 2x ubyte)
+ ubyte[] buffer = [1, 5, 32, 29, 1, 7];
+ assert(buffer.peek!wchar() == 'ą');
+ assert(buffer.peek!wchar(2) == '”');
+ assert(buffer.peek!wchar(4) == 'ć');
+
+ size_t index = 0;
+ assert(buffer.peek!wchar(&index) == 'ą');
+ assert(index == 2);
+
+ assert(buffer.peek!wchar(&index) == '”');
+ assert(index == 4);
+
+ assert(buffer.peek!wchar(&index) == 'ć');
+ assert(index == 6);
+ }
+
+ {
+ //dchar (32bit - 4x ubyte)
+ ubyte[] buffer = [0, 0, 1, 5, 0, 0, 32, 29, 0, 0, 1, 7];
+ assert(buffer.peek!dchar() == 'ą');
+ assert(buffer.peek!dchar(4) == '”');
+ assert(buffer.peek!dchar(8) == 'ć');
+
+ size_t index = 0;
+ assert(buffer.peek!dchar(&index) == 'ą');
+ assert(index == 4);
+
+ assert(buffer.peek!dchar(&index) == '”');
+ assert(index == 8);
+
+ assert(buffer.peek!dchar(&index) == 'ć');
+ assert(index == 12);
+ }
+
+ {
+ //float (32bit - 4x ubyte)
+ ubyte[] buffer = [66, 0, 0, 0, 65, 200, 0, 0];
+ assert(buffer.peek!float()== 32.0);
+ assert(buffer.peek!float(4) == 25.0f);
+
+ size_t index = 0;
+ assert(buffer.peek!float(&index) == 32.0f);
+ assert(index == 4);
+
+ assert(buffer.peek!float(&index) == 25.0f);
+ assert(index == 8);
+ }
+
+ {
+ //double (64bit - 8x ubyte)
+ ubyte[] buffer = [64, 64, 0, 0, 0, 0, 0, 0, 64, 57, 0, 0, 0, 0, 0, 0];
+ assert(buffer.peek!double() == 32.0);
+ assert(buffer.peek!double(8) == 25.0);
+
+ size_t index = 0;
+ assert(buffer.peek!double(&index) == 32.0);
+ assert(index == 8);
+
+ assert(buffer.peek!double(&index) == 25.0);
+ assert(index == 16);
+ }
+
+ {
+ //enum
+ ubyte[] buffer = [0, 0, 0, 10, 0, 0, 0, 20, 0, 0, 0, 30];
+
+ enum Foo
+ {
+ one = 10,
+ two = 20,
+ three = 30
+ }
+
+ assert(buffer.peek!Foo() == Foo.one);
+ assert(buffer.peek!Foo(0) == Foo.one);
+ assert(buffer.peek!Foo(4) == Foo.two);
+ assert(buffer.peek!Foo(8) == Foo.three);
+
+ size_t index = 0;
+ assert(buffer.peek!Foo(&index) == Foo.one);
+ assert(index == 4);
+
+ assert(buffer.peek!Foo(&index) == Foo.two);
+ assert(index == 8);
+
+ assert(buffer.peek!Foo(&index) == Foo.three);
+ assert(index == 12);
+ }
+
+ {
+ //enum - bool
+ ubyte[] buffer = [0, 1];
+
+ enum Bool: bool
+ {
+ bfalse = false,
+ btrue = true,
+ }
+
+ assert(buffer.peek!Bool() == Bool.bfalse);
+ assert(buffer.peek!Bool(0) == Bool.bfalse);
+ assert(buffer.peek!Bool(1) == Bool.btrue);
+
+ size_t index = 0;
+ assert(buffer.peek!Bool(&index) == Bool.bfalse);
+ assert(index == 1);
+
+ assert(buffer.peek!Bool(&index) == Bool.btrue);
+ assert(index == 2);
+ }
+
+ {
+ //enum - float
+ ubyte[] buffer = [66, 0, 0, 0, 65, 200, 0, 0];
+
+ enum Float: float
+ {
+ one = 32.0f,
+ two = 25.0f
+ }
+
+ assert(buffer.peek!Float() == Float.one);
+ assert(buffer.peek!Float(0) == Float.one);
+ assert(buffer.peek!Float(4) == Float.two);
+
+ size_t index = 0;
+ assert(buffer.peek!Float(&index) == Float.one);
+ assert(index == 4);
+
+ assert(buffer.peek!Float(&index) == Float.two);
+ assert(index == 8);
+ }
+
+ {
+ //enum - double
+ ubyte[] buffer = [64, 64, 0, 0, 0, 0, 0, 0, 64, 57, 0, 0, 0, 0, 0, 0];
+
+ enum Double: double
+ {
+ one = 32.0,
+ two = 25.0
+ }
+
+ assert(buffer.peek!Double() == Double.one);
+ assert(buffer.peek!Double(0) == Double.one);
+ assert(buffer.peek!Double(8) == Double.two);
+
+ size_t index = 0;
+ assert(buffer.peek!Double(&index) == Double.one);
+ assert(index == 8);
+
+ assert(buffer.peek!Double(&index) == Double.two);
+ assert(index == 16);
+ }
+
+ {
+ //enum - real
+ ubyte[] buffer = [64, 64, 0, 0, 0, 0, 0, 0, 64, 57, 0, 0, 0, 0, 0, 0];
+
+ enum Real: real
+ {
+ one = 32.0,
+ two = 25.0
+ }
+
+ static assert(!__traits(compiles, buffer.peek!Real()));
+ }
+}
+
+@safe unittest
+{
+ import std.algorithm.iteration : filter;
+ ubyte[] buffer = [1, 5, 22, 9, 44, 255, 7];
+ auto range = filter!"true"(buffer);
+ assert(range.peek!uint() == 17110537);
+ assert(range.peek!ushort() == 261);
+ assert(range.peek!ubyte() == 1);
+}
+
+
+/++
+ Takes a range of $(D ubyte)s and converts the first $(D T.sizeof) bytes to
+ $(D T). The value returned is converted from the given endianness to the
+ native endianness. The $(D T.sizeof) bytes which are read are consumed from
+ the range.
+
+ Params:
+ T = The integral type to convert the first $(D T.sizeof) bytes to.
+ endianness = The endianness that the bytes are assumed to be in.
+ 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))
+{
+ 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);
+ }
+ else
+ {
+ ubyte[T.sizeof] bytes;
+
+ foreach (ref e; bytes)
+ {
+ e = range.front;
+ range.popFront();
+ }
+ }
+
+ static if (endianness == Endian.bigEndian)
+ return bigEndianToNative!T(bytes);
+ else
+ return littleEndianToNative!T(bytes);
+}
+
+///
+@safe unittest
+{
+ import std.range.primitives : empty;
+ ubyte[] buffer = [1, 5, 22, 9, 44, 255, 8];
+ assert(buffer.length == 7);
+
+ assert(buffer.read!ushort() == 261);
+ assert(buffer.length == 5);
+
+ assert(buffer.read!uint() == 369700095);
+ assert(buffer.length == 1);
+
+ assert(buffer.read!ubyte() == 8);
+ assert(buffer.empty);
+}
+
+@safe unittest
+{
+ {
+ //bool
+ ubyte[] buffer = [0, 1];
+ assert(buffer.length == 2);
+
+ assert(buffer.read!bool() == false);
+ assert(buffer.length == 1);
+
+ assert(buffer.read!bool() == true);
+ assert(buffer.empty);
+ }
+
+ {
+ //char (8bit)
+ ubyte[] buffer = [97, 98, 99];
+ assert(buffer.length == 3);
+
+ assert(buffer.read!char() == 'a');
+ assert(buffer.length == 2);
+
+ assert(buffer.read!char() == 'b');
+ assert(buffer.length == 1);
+
+ assert(buffer.read!char() == 'c');
+ assert(buffer.empty);
+ }
+
+ {
+ //wchar (16bit - 2x ubyte)
+ ubyte[] buffer = [1, 5, 32, 29, 1, 7];
+ assert(buffer.length == 6);
+
+ assert(buffer.read!wchar() == 'ą');
+ assert(buffer.length == 4);
+
+ assert(buffer.read!wchar() == '”');
+ assert(buffer.length == 2);
+
+ assert(buffer.read!wchar() == 'ć');
+ assert(buffer.empty);
+ }
+
+ {
+ //dchar (32bit - 4x ubyte)
+ ubyte[] buffer = [0, 0, 1, 5, 0, 0, 32, 29, 0, 0, 1, 7];
+ assert(buffer.length == 12);
+
+ assert(buffer.read!dchar() == 'ą');
+ assert(buffer.length == 8);
+
+ assert(buffer.read!dchar() == '”');
+ assert(buffer.length == 4);
+
+ assert(buffer.read!dchar() == 'ć');
+ assert(buffer.empty);
+ }
+
+ {
+ //float (32bit - 4x ubyte)
+ ubyte[] buffer = [66, 0, 0, 0, 65, 200, 0, 0];
+ assert(buffer.length == 8);
+
+ assert(buffer.read!float()== 32.0);
+ assert(buffer.length == 4);
+
+ assert(buffer.read!float() == 25.0f);
+ assert(buffer.empty);
+ }
+
+ {
+ //double (64bit - 8x ubyte)
+ ubyte[] buffer = [64, 64, 0, 0, 0, 0, 0, 0, 64, 57, 0, 0, 0, 0, 0, 0];
+ assert(buffer.length == 16);
+
+ assert(buffer.read!double() == 32.0);
+ assert(buffer.length == 8);
+
+ assert(buffer.read!double() == 25.0);
+ assert(buffer.empty);
+ }
+
+ {
+ //enum - uint
+ ubyte[] buffer = [0, 0, 0, 10, 0, 0, 0, 20, 0, 0, 0, 30];
+ assert(buffer.length == 12);
+
+ enum Foo
+ {
+ one = 10,
+ two = 20,
+ three = 30
+ }
+
+ assert(buffer.read!Foo() == Foo.one);
+ assert(buffer.length == 8);
+
+ assert(buffer.read!Foo() == Foo.two);
+ assert(buffer.length == 4);
+
+ assert(buffer.read!Foo() == Foo.three);
+ assert(buffer.empty);
+ }
+
+ {
+ //enum - bool
+ ubyte[] buffer = [0, 1];
+ assert(buffer.length == 2);
+
+ enum Bool: bool
+ {
+ bfalse = false,
+ btrue = true,
+ }
+
+ assert(buffer.read!Bool() == Bool.bfalse);
+ assert(buffer.length == 1);
+
+ assert(buffer.read!Bool() == Bool.btrue);
+ assert(buffer.empty);
+ }
+
+ {
+ //enum - float
+ ubyte[] buffer = [66, 0, 0, 0, 65, 200, 0, 0];
+ assert(buffer.length == 8);
+
+ enum Float: float
+ {
+ one = 32.0f,
+ two = 25.0f
+ }
+
+ assert(buffer.read!Float() == Float.one);
+ assert(buffer.length == 4);
+
+ assert(buffer.read!Float() == Float.two);
+ assert(buffer.empty);
+ }
+
+ {
+ //enum - double
+ ubyte[] buffer = [64, 64, 0, 0, 0, 0, 0, 0, 64, 57, 0, 0, 0, 0, 0, 0];
+ assert(buffer.length == 16);
+
+ enum Double: double
+ {
+ one = 32.0,
+ two = 25.0
+ }
+
+ assert(buffer.read!Double() == Double.one);
+ assert(buffer.length == 8);
+
+ assert(buffer.read!Double() == Double.two);
+ assert(buffer.empty);
+ }
+
+ {
+ //enum - real
+ ubyte[] buffer = [64, 64, 0, 0, 0, 0, 0, 0, 64, 57, 0, 0, 0, 0, 0, 0];
+
+ enum Real: real
+ {
+ one = 32.0,
+ two = 25.0
+ }
+
+ static assert(!__traits(compiles, buffer.read!Real()));
+ }
+}
+
+@safe unittest
+{
+ import std.algorithm.iteration : filter;
+ ubyte[] buffer = [1, 5, 22, 9, 44, 255, 8];
+ auto range = filter!"true"(buffer);
+ assert(walkLength(range) == 7);
+
+ assert(range.read!ushort() == 261);
+ assert(walkLength(range) == 5);
+
+ assert(range.read!uint() == 369700095);
+ assert(walkLength(range) == 1);
+
+ assert(range.read!ubyte() == 8);
+ 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
+ to the given range of $(D ubyte)s as a sequence of $(D T.sizeof) $(D ubyte)s
+ starting at index. $(D hasSlicing!R) must be $(D true).
+
+ 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.
+ 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))
+{
+ 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))
+{
+ assert(index);
+
+ static if (endianness == Endian.bigEndian)
+ immutable bytes = nativeToBigEndian!T(value);
+ else
+ immutable bytes = nativeToLittleEndian!T(value);
+
+ immutable begin = *index;
+ immutable end = begin + T.sizeof;
+ *index = end;
+ range[begin .. end] = bytes[0 .. T.sizeof];
+}
+
+///
+@system unittest
+{
+ {
+ ubyte[] buffer = [0, 0, 0, 0, 0, 0, 0, 0];
+ buffer.write!uint(29110231u, 0);
+ assert(buffer == [1, 188, 47, 215, 0, 0, 0, 0]);
+
+ buffer.write!ushort(927, 0);
+ assert(buffer == [3, 159, 47, 215, 0, 0, 0, 0]);
+
+ buffer.write!ubyte(42, 0);
+ assert(buffer == [42, 159, 47, 215, 0, 0, 0, 0]);
+ }
+
+ {
+ ubyte[] buffer = [0, 0, 0, 0, 0, 0, 0, 0, 0];
+ buffer.write!uint(142700095u, 2);
+ assert(buffer == [0, 0, 8, 129, 110, 63, 0, 0, 0]);
+
+ buffer.write!ushort(19839, 2);
+ assert(buffer == [0, 0, 77, 127, 110, 63, 0, 0, 0]);
+
+ buffer.write!ubyte(132, 2);
+ assert(buffer == [0, 0, 132, 127, 110, 63, 0, 0, 0]);
+ }
+
+ {
+ ubyte[] buffer = [0, 0, 0, 0, 0, 0, 0, 0];
+ size_t index = 0;
+ buffer.write!ushort(261, &index);
+ assert(buffer == [1, 5, 0, 0, 0, 0, 0, 0]);
+ assert(index == 2);
+
+ buffer.write!uint(369700095u, &index);
+ assert(buffer == [1, 5, 22, 9, 44, 255, 0, 0]);
+ assert(index == 6);
+
+ buffer.write!ubyte(8, &index);
+ assert(buffer == [1, 5, 22, 9, 44, 255, 8, 0]);
+ assert(index == 7);
+ }
+}
+
+@system unittest
+{
+ {
+ //bool
+ ubyte[] buffer = [0, 0];
+
+ buffer.write!bool(false, 0);
+ assert(buffer == [0, 0]);
+
+ buffer.write!bool(true, 0);
+ assert(buffer == [1, 0]);
+
+ buffer.write!bool(true, 1);
+ assert(buffer == [1, 1]);
+
+ buffer.write!bool(false, 1);
+ assert(buffer == [1, 0]);
+
+ size_t index = 0;
+ buffer.write!bool(false, &index);
+ assert(buffer == [0, 0]);
+ assert(index == 1);
+
+ buffer.write!bool(true, &index);
+ assert(buffer == [0, 1]);
+ assert(index == 2);
+ }
+
+ {
+ //char (8bit)
+ ubyte[] buffer = [0, 0, 0];
+
+ buffer.write!char('a', 0);
+ assert(buffer == [97, 0, 0]);
+
+ buffer.write!char('b', 1);
+ assert(buffer == [97, 98, 0]);
+
+ size_t index = 0;
+ buffer.write!char('a', &index);
+ assert(buffer == [97, 98, 0]);
+ assert(index == 1);
+
+ buffer.write!char('b', &index);
+ assert(buffer == [97, 98, 0]);
+ assert(index == 2);
+
+ buffer.write!char('c', &index);
+ assert(buffer == [97, 98, 99]);
+ assert(index == 3);
+ }
+
+ {
+ //wchar (16bit - 2x ubyte)
+ ubyte[] buffer = [0, 0, 0, 0];
+
+ buffer.write!wchar('ą', 0);
+ assert(buffer == [1, 5, 0, 0]);
+
+ buffer.write!wchar('”', 2);
+ assert(buffer == [1, 5, 32, 29]);
+
+ size_t index = 0;
+ buffer.write!wchar('ć', &index);
+ assert(buffer == [1, 7, 32, 29]);
+ assert(index == 2);
+
+ buffer.write!wchar('ą', &index);
+ assert(buffer == [1, 7, 1, 5]);
+ assert(index == 4);
+ }
+
+ {
+ //dchar (32bit - 4x ubyte)
+ ubyte[] buffer = [0, 0, 0, 0, 0, 0, 0, 0];
+
+ buffer.write!dchar('ą', 0);
+ assert(buffer == [0, 0, 1, 5, 0, 0, 0, 0]);
+
+ buffer.write!dchar('”', 4);
+ assert(buffer == [0, 0, 1, 5, 0, 0, 32, 29]);
+
+ size_t index = 0;
+ buffer.write!dchar('ć', &index);
+ assert(buffer == [0, 0, 1, 7, 0, 0, 32, 29]);
+ assert(index == 4);
+
+ buffer.write!dchar('ą', &index);
+ assert(buffer == [0, 0, 1, 7, 0, 0, 1, 5]);
+ assert(index == 8);
+ }
+
+ {
+ //float (32bit - 4x ubyte)
+ ubyte[] buffer = [0, 0, 0, 0, 0, 0, 0, 0];
+
+ buffer.write!float(32.0f, 0);
+ assert(buffer == [66, 0, 0, 0, 0, 0, 0, 0]);
+
+ buffer.write!float(25.0f, 4);
+ assert(buffer == [66, 0, 0, 0, 65, 200, 0, 0]);
+
+ size_t index = 0;
+ buffer.write!float(25.0f, &index);
+ assert(buffer == [65, 200, 0, 0, 65, 200, 0, 0]);
+ assert(index == 4);
+
+ buffer.write!float(32.0f, &index);
+ assert(buffer == [65, 200, 0, 0, 66, 0, 0, 0]);
+ assert(index == 8);
+ }
+
+ {
+ //double (64bit - 8x ubyte)
+ ubyte[] buffer = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0];
+
+ buffer.write!double(32.0, 0);
+ assert(buffer == [64, 64, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]);
+
+ buffer.write!double(25.0, 8);
+ assert(buffer == [64, 64, 0, 0, 0, 0, 0, 0, 64, 57, 0, 0, 0, 0, 0, 0]);
+
+ size_t index = 0;
+ buffer.write!double(25.0, &index);
+ assert(buffer == [64, 57, 0, 0, 0, 0, 0, 0, 64, 57, 0, 0, 0, 0, 0, 0]);
+ assert(index == 8);
+
+ buffer.write!double(32.0, &index);
+ assert(buffer == [64, 57, 0, 0, 0, 0, 0, 0, 64, 64, 0, 0, 0, 0, 0, 0]);
+ assert(index == 16);
+ }
+
+ {
+ //enum
+ ubyte[] buffer = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0];
+
+ enum Foo
+ {
+ one = 10,
+ two = 20,
+ three = 30
+ }
+
+ buffer.write!Foo(Foo.one, 0);
+ assert(buffer == [0, 0, 0, 10, 0, 0, 0, 0, 0, 0, 0, 0]);
+
+ buffer.write!Foo(Foo.two, 4);
+ assert(buffer == [0, 0, 0, 10, 0, 0, 0, 20, 0, 0, 0, 0]);
+
+ buffer.write!Foo(Foo.three, 8);
+ assert(buffer == [0, 0, 0, 10, 0, 0, 0, 20, 0, 0, 0, 30]);
+
+ size_t index = 0;
+ buffer.write!Foo(Foo.three, &index);
+ assert(buffer == [0, 0, 0, 30, 0, 0, 0, 20, 0, 0, 0, 30]);
+ assert(index == 4);
+
+ buffer.write!Foo(Foo.one, &index);
+ assert(buffer == [0, 0, 0, 30, 0, 0, 0, 10, 0, 0, 0, 30]);
+ assert(index == 8);
+
+ buffer.write!Foo(Foo.two, &index);
+ assert(buffer == [0, 0, 0, 30, 0, 0, 0, 10, 0, 0, 0, 20]);
+ assert(index == 12);
+ }
+
+ {
+ //enum - bool
+ ubyte[] buffer = [0, 0];
+
+ enum Bool: bool
+ {
+ bfalse = false,
+ btrue = true,
+ }
+
+ buffer.write!Bool(Bool.btrue, 0);
+ assert(buffer == [1, 0]);
+
+ buffer.write!Bool(Bool.btrue, 1);
+ assert(buffer == [1, 1]);
+
+ size_t index = 0;
+ buffer.write!Bool(Bool.bfalse, &index);
+ assert(buffer == [0, 1]);
+ assert(index == 1);
+
+ buffer.write!Bool(Bool.bfalse, &index);
+ assert(buffer == [0, 0]);
+ assert(index == 2);
+ }
+
+ {
+ //enum - float
+ ubyte[] buffer = [0, 0, 0, 0, 0, 0, 0, 0];
+
+ enum Float: float
+ {
+ one = 32.0f,
+ two = 25.0f
+ }
+
+ buffer.write!Float(Float.one, 0);
+ assert(buffer == [66, 0, 0, 0, 0, 0, 0, 0]);
+
+ buffer.write!Float(Float.two, 4);
+ assert(buffer == [66, 0, 0, 0, 65, 200, 0, 0]);
+
+ size_t index = 0;
+ buffer.write!Float(Float.two, &index);
+ assert(buffer == [65, 200, 0, 0, 65, 200, 0, 0]);
+ assert(index == 4);
+
+ buffer.write!Float(Float.one, &index);
+ assert(buffer == [65, 200, 0, 0, 66, 0, 0, 0]);
+ assert(index == 8);
+ }
+
+ {
+ //enum - double
+ ubyte[] buffer = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0];
+
+ enum Double: double
+ {
+ one = 32.0,
+ two = 25.0
+ }
+
+ buffer.write!Double(Double.one, 0);
+ assert(buffer == [64, 64, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]);
+
+ buffer.write!Double(Double.two, 8);
+ assert(buffer == [64, 64, 0, 0, 0, 0, 0, 0, 64, 57, 0, 0, 0, 0, 0, 0]);
+
+ size_t index = 0;
+ buffer.write!Double(Double.two, &index);
+ assert(buffer == [64, 57, 0, 0, 0, 0, 0, 0, 64, 57, 0, 0, 0, 0, 0, 0]);
+ assert(index == 8);
+
+ buffer.write!Double(Double.one, &index);
+ assert(buffer == [64, 57, 0, 0, 0, 0, 0, 0, 64, 64, 0, 0, 0, 0, 0, 0]);
+ assert(index == 16);
+ }
+
+ {
+ //enum - real
+ ubyte[] buffer = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0];
+
+ enum Real: real
+ {
+ one = 32.0,
+ two = 25.0
+ }
+
+ static assert(!__traits(compiles, buffer.write!Real(Real.one)));
+ }
+}
+
+
+/++
+ Takes an integral value, converts it to the given endianness, and appends
+ it to the given range of $(D ubyte)s (using $(D put)) as a sequence of
+ $(D T.sizeof) $(D ubyte)s starting at index. $(D hasSlicing!R) must be
+ $(D true).
+
+ 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.
+ value = The value to _append.
+ +/
+void append(T, Endian endianness = Endian.bigEndian, R)(R range, T value)
+if (canSwapEndianness!T && isOutputRange!(R, ubyte))
+{
+ static if (endianness == Endian.bigEndian)
+ immutable bytes = nativeToBigEndian!T(value);
+ else
+ immutable bytes = nativeToLittleEndian!T(value);
+
+ put(range, bytes[]);
+}
+
+///
+@safe unittest
+{
+ import std.array;
+ auto buffer = appender!(const ubyte[])();
+ buffer.append!ushort(261);
+ assert(buffer.data == [1, 5]);
+
+ buffer.append!uint(369700095u);
+ assert(buffer.data == [1, 5, 22, 9, 44, 255]);
+
+ buffer.append!ubyte(8);
+ assert(buffer.data == [1, 5, 22, 9, 44, 255, 8]);
+}
+
+@safe unittest
+{
+ import std.array;
+ {
+ //bool
+ auto buffer = appender!(const ubyte[])();
+
+ buffer.append!bool(true);
+ assert(buffer.data == [1]);
+
+ buffer.append!bool(false);
+ assert(buffer.data == [1, 0]);
+ }
+
+ {
+ //char wchar dchar
+ auto buffer = appender!(const ubyte[])();
+
+ buffer.append!char('a');
+ assert(buffer.data == [97]);
+
+ buffer.append!char('b');
+ assert(buffer.data == [97, 98]);
+
+ buffer.append!wchar('ą');
+ assert(buffer.data == [97, 98, 1, 5]);
+
+ buffer.append!dchar('ą');
+ assert(buffer.data == [97, 98, 1, 5, 0, 0, 1, 5]);
+ }
+
+ {
+ //float double
+ auto buffer = appender!(const ubyte[])();
+
+ buffer.append!float(32.0f);
+ assert(buffer.data == [66, 0, 0, 0]);
+
+ buffer.append!double(32.0);
+ assert(buffer.data == [66, 0, 0, 0, 64, 64, 0, 0, 0, 0, 0, 0]);
+ }
+
+ {
+ //enum
+ auto buffer = appender!(const ubyte[])();
+
+ enum Foo
+ {
+ one = 10,
+ two = 20,
+ three = 30
+ }
+
+ buffer.append!Foo(Foo.one);
+ assert(buffer.data == [0, 0, 0, 10]);
+
+ buffer.append!Foo(Foo.two);
+ assert(buffer.data == [0, 0, 0, 10, 0, 0, 0, 20]);
+
+ buffer.append!Foo(Foo.three);
+ assert(buffer.data == [0, 0, 0, 10, 0, 0, 0, 20, 0, 0, 0, 30]);
+ }
+
+ {
+ //enum - bool
+ auto buffer = appender!(const ubyte[])();
+
+ enum Bool: bool
+ {
+ bfalse = false,
+ btrue = true,
+ }
+
+ buffer.append!Bool(Bool.btrue);
+ assert(buffer.data == [1]);
+
+ buffer.append!Bool(Bool.bfalse);
+ assert(buffer.data == [1, 0]);
+
+ buffer.append!Bool(Bool.btrue);
+ assert(buffer.data == [1, 0, 1]);
+ }
+
+ {
+ //enum - float
+ auto buffer = appender!(const ubyte[])();
+
+ enum Float: float
+ {
+ one = 32.0f,
+ two = 25.0f
+ }
+
+ buffer.append!Float(Float.one);
+ assert(buffer.data == [66, 0, 0, 0]);
+
+ buffer.append!Float(Float.two);
+ assert(buffer.data == [66, 0, 0, 0, 65, 200, 0, 0]);
+ }
+
+ {
+ //enum - double
+ auto buffer = appender!(const ubyte[])();
+
+ enum Double: double
+ {
+ one = 32.0,
+ two = 25.0
+ }
+
+ buffer.append!Double(Double.one);
+ assert(buffer.data == [64, 64, 0, 0, 0, 0, 0, 0]);
+
+ buffer.append!Double(Double.two);
+ assert(buffer.data == [64, 64, 0, 0, 0, 0, 0, 0, 64, 57, 0, 0, 0, 0, 0, 0]);
+ }
+
+ {
+ //enum - real
+ auto buffer = appender!(const ubyte[])();
+
+ enum Real: real
+ {
+ one = 32.0,
+ two = 25.0
+ }
+
+ static assert(!__traits(compiles, buffer.append!Real(Real.one)));
+ }
+}
+
+@system unittest
+{
+ import std.array;
+ import std.format : format;
+ import std.meta;
+ foreach (endianness; AliasSeq!(Endian.bigEndian, Endian.littleEndian))
+ {
+ auto toWrite = appender!(ubyte[])();
+ 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)
+ {
+ toWrite.append!(T, endianness)(cast(T) values[index++]);
+ length += T.sizeof;
+ }
+
+ auto toRead = toWrite.data;
+ assert(toRead.length == length);
+
+ index = 0;
+ 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));
+ assert(toRead.length == length,
+ format("Failed Index [%s], Actual Length: %s", index, toRead.length));
+ assert(toRead.read!(T, endianness)() == values[index], format("Failed Index: %s", index));
+ length -= T.sizeof;
+ assert(toRead.length == length,
+ format("Failed Index [%s], Actual Length: %s", index, toRead.length));
+ ++index;
+ }
+ assert(toRead.empty);
+ }
+}
+
+/**
+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)
+{
+ // http://graphics.stanford.edu/~seander/bithacks.html#CountBitsSetParallel
+ static if (T.sizeof == 8)
+ {
+ T c = value - ((value >> 1) & 0x55555555_55555555);
+ c = ((c >> 2) & 0x33333333_33333333) + (c & 0x33333333_33333333);
+ c = ((c >> 4) + c) & 0x0F0F0F0F_0F0F0F0F;
+ c = ((c >> 8) + c) & 0x00FF00FF_00FF00FF;
+ c = ((c >> 16) + c) & 0x0000FFFF_0000FFFF;
+ c = ((c >> 32) + c) & 0x00000000_FFFFFFFF;
+ }
+ else static if (T.sizeof == 4)
+ {
+ T c = value - ((value >> 1) & 0x55555555);
+ c = ((c >> 2) & 0x33333333) + (c & 0x33333333);
+ c = ((c >> 4) + c) & 0x0F0F0F0F;
+ c = ((c >> 8) + c) & 0x00FF00FF;
+ c = ((c >> 16) + c) & 0x0000FFFF;
+ }
+ else static if (T.sizeof == 2)
+ {
+ uint c = value - ((value >> 1) & 0x5555);
+ c = ((c >> 2) & 0x3333) + (c & 0X3333);
+ c = ((c >> 4) + c) & 0x0F0F;
+ c = ((c >> 8) + c) & 0x00FF;
+ }
+ else static if (T.sizeof == 1)
+ {
+ uint c = value - ((value >> 1) & 0x55);
+ c = ((c >> 2) & 0x33) + (c & 0X33);
+ c = ((c >> 4) + c) & 0x0F;
+ }
+ else
+ {
+ static assert(false, "countBitsSet only supports 1, 2, 4, or 8 byte sized integers.");
+ }
+ return cast(uint) c;
+}
+
+@safe unittest
+{
+ assert(countBitsSet(1) == 1);
+ assert(countBitsSet(0) == 0);
+ assert(countBitsSet(int.min) == 1);
+ assert(countBitsSet(uint.max) == 32);
+}
+
+@safe unittest
+{
+ 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);
+ static if (isSigned!T)
+ {
+ assert(countBitsSet(cast(T)-1) == 8 * T.sizeof);
+ assert(countBitsSet(T.min) == 1);
+ }
+ else
+ {
+ assert(countBitsSet(T.max) == 8 * T.sizeof);
+ }
+ }
+ assert(countBitsSet(1_000_000) == 7);
+ foreach (i; 0 .. 63)
+ assert(countBitsSet(1UL << i) == 1);
+}
+
+private struct BitsSet(T)
+{
+ static assert(T.sizeof <= 8, "bitsSet assumes T is no more than 64-bit.");
+
+@nogc pure nothrow:
+
+ this(T value, size_t startIndex = 0)
+ {
+ _value = value;
+ // 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()
+ {
+ return _index;
+ }
+
+ @property bool empty() const
+ {
+ return !_value;
+ }
+
+ void popFront()
+ {
+ assert(_value, "Cannot call popFront on empty range.");
+
+ _value >>>= 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()
+ {
+ return this;
+ }
+
+ @property size_t length()
+ {
+ return countBitsSet(_value);
+ }
+
+ private T _value;
+ private size_t _index;
+}
+
+/**
+Range that iterates the indices of the set bits in $(D value).
+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)
+{
+ return BitsSet!T(value);
+}
+
+///
+@safe unittest
+{
+ import std.algorithm.comparison : equal;
+ import std.range : iota;
+
+ assert(bitsSet(1).equal([0]));
+ assert(bitsSet(5).equal([0, 2]));
+ assert(bitsSet(-1).equal(iota(32)));
+ assert(bitsSet(int.min).equal([31]));
+}
+
+@safe unittest
+{
+ import std.algorithm.comparison : equal;
+ import std.range : iota;
+
+ 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)));
+ static if (isSigned!T)
+ {
+ assert(bitsSet(cast(T)-1).equal(iota(8 * T.sizeof)));
+ assert(bitsSet(T.min).equal([8 * T.sizeof - 1]));
+ }
+ else
+ {
+ assert(bitsSet(T.max).equal(iota(8 * T.sizeof)));
+ }
+ }
+ assert(bitsSet(1_000_000).equal([6, 9, 14, 16, 17, 18, 19]));
+ foreach (i; 0 .. 63)
+ assert(bitsSet(1UL << i).equal([i]));
+}
diff --git a/libphobos/src/std/compiler.d b/libphobos/src/std/compiler.d
new file mode 100644
index 0000000..cb038f9
--- /dev/null
+++ b/libphobos/src/std/compiler.d
@@ -0,0 +1,58 @@
+// Written in the D programming language.
+
+/**
+ * Identify the compiler used and its various features.
+ *
+ * Copyright: Copyright Digital Mars 2000 - 2011.
+ * 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.
+ * 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.compiler;
+
+immutable
+{
+ /// Vendor specific string naming the compiler, for example: "Digital Mars D".
+ string name = __VENDOR__;
+
+ /// Master list of D compiler vendors.
+ enum Vendor
+ {
+ unknown = 0, /// Compiler vendor could not be detected
+ digitalMars = 1, /// Digital Mars D (DMD)
+ gnu = 2, /// GNU D Compiler (GDC)
+ llvm = 3, /// LLVM D Compiler (LDC)
+ dotNET = 4, /// D.NET
+ sdc = 5, /// Stupid D Compiler (SDC)
+ }
+
+ /// Which vendor produced this compiler.
+ version (StdDdoc) Vendor vendor;
+ else version (DigitalMars) Vendor vendor = Vendor.digitalMars;
+ else version (GNU) Vendor vendor = Vendor.gnu;
+ else version (LDC) Vendor vendor = Vendor.llvm;
+ else version (D_NET) Vendor vendor = Vendor.dotNET;
+ else version (SDC) Vendor vendor = Vendor.sdc;
+ else Vendor vendor = Vendor.unknown;
+
+
+ /**
+ * The vendor specific version number, as in
+ * version_major.version_minor
+ */
+ uint version_major = __VERSION__ / 1000;
+ uint version_minor = __VERSION__ % 1000; /// ditto
+
+
+ /**
+ * The version of the D Programming Language Specification
+ * supported by the compiler.
+ */
+ uint D_major = 2;
+ uint D_minor = 0;
+}
diff --git a/libphobos/src/std/complex.d b/libphobos/src/std/complex.d
new file mode 100644
index 0000000..7763822
--- /dev/null
+++ b/libphobos/src/std/complex.d
@@ -0,0 +1,994 @@
+// Written in the D programming language.
+
+/** 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
+ $(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: $(HTTP boost.org/LICENSE_1_0.txt, Boost License 1.0)
+ Source: $(PHOBOSSRC std/_complex.d)
+*/
+module std.complex;
+
+import std.traits;
+
+/** Helper function that returns a _complex number with the specified
+ real and imaginary parts.
+
+ Params:
+ R = (template parameter) type of real part of complex number
+ I = (template parameter) type of imaginary part of complex number
+
+ re = real part of complex number to be constructed
+ im = (optional) imaginary part of complex number, 0 if omitted.
+
+ Returns:
+ $(D Complex) instance with real and imaginary parts set
+ to the values provided as input. If neither $(D re) nor
+ $(D im) are floating-point numbers, the return type will
+ 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))
+{
+ static if (isFloatingPoint!R)
+ return Complex!R(re, 0);
+ else
+ return Complex!double(re, 0);
+}
+
+/// ditto
+auto complex(R, I)(R re, I im) @safe pure nothrow @nogc
+if (is(R : double) && is(I : double))
+{
+ static if (isFloatingPoint!R || isFloatingPoint!I)
+ return Complex!(CommonType!(R, I))(re, im);
+ else
+ return Complex!double(re, im);
+}
+
+///
+@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);
+
+ auto b = complex(2.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);
+
+ auto d = complex(3.0, 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);
+
+ auto f = complex(1L, 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);
+}
+
+
+/** 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)
+{
+ import std.format : FormatSpec;
+ import std.range.primitives : isOutputRange;
+
+ /** The real part of the number. */
+ T re;
+
+ /** The imaginary part of the number. */
+ T im;
+
+ /** Converts the complex number to a string representation.
+
+ The second form of this function is usually not called directly;
+ 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 $(MREF std, format) and $(REF format, std,string)
+ documentation for more information.
+ */
+ 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);
+ static trustedAssumeUnique(T)(T t) @trusted { return assumeUnique(t); }
+ return trustedAssumeUnique(buf);
+ }
+
+ static if (is(T == double))
+ ///
+ @safe unittest
+ {
+ auto c = complex(1.2, 3.4);
+
+ // Vanilla toString formatting:
+ assert(c.toString() == "1.2+3.4i");
+
+ // Formatting with std.string.format specs: the precision and width
+ // specifiers apply to both the real and imaginary parts of the
+ // complex number.
+ import std.format : format;
+ assert(format("%.2f", c) == "1.20+3.40i");
+ assert(format("%4.1f", c) == " 1.2+ 3.4i");
+ }
+
+ /// ditto
+ void toString(Writer, Char)(scope Writer w,
+ FormatSpec!Char formatSpec) const
+ if (isOutputRange!(Writer, const(Char)[]))
+ {
+ import std.format : formatValue;
+ import std.math : signbit;
+ 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:
+
+ /** Construct a complex number with the specified real and
+ imaginary parts. In the case where a single argument is passed
+ that is not complex, the imaginary part of the result will be
+ zero.
+ */
+ this(R : T)(Complex!R z)
+ {
+ re = z.re;
+ im = z.im;
+ }
+
+ /// ditto
+ this(Rx : T, Ry : T)(Rx x, Ry y)
+ {
+ re = x;
+ im = y;
+ }
+
+ /// ditto
+ this(R : T)(R r)
+ {
+ re = r;
+ im = 0;
+ }
+
+ // ASSIGNMENT OPERATORS
+
+ // this = complex
+ ref Complex opAssign(R : T)(Complex!R z)
+ {
+ re = z.re;
+ im = z.im;
+ return this;
+ }
+
+ // this = numeric
+ ref Complex opAssign(R : T)(R r)
+ {
+ re = r;
+ im = 0;
+ return this;
+ }
+
+ // COMPARISON OPERATORS
+
+ // this == complex
+ bool opEquals(R : T)(Complex!R z) const
+ {
+ return re == z.re && im == z.im;
+ }
+
+ // this == numeric
+ bool opEquals(R : T)(R r) const
+ {
+ return re == r && im == 0;
+ }
+
+ // UNARY OPERATORS
+
+ // +complex
+ Complex opUnary(string op)() const
+ if (op == "+")
+ {
+ return this;
+ }
+
+ // -complex
+ Complex opUnary(string op)() const
+ if (op == "-")
+ {
+ return Complex(-re, -im);
+ }
+
+ // BINARY OPERATORS
+
+ // complex op complex
+ Complex!(CommonType!(T,R)) opBinary(string op, R)(Complex!R z) const
+ {
+ alias C = typeof(return);
+ auto w = C(this.re, this.im);
+ return w.opOpAssign!(op)(z);
+ }
+
+ // complex op numeric
+ Complex!(CommonType!(T,R)) opBinary(string op, R)(R r) const
+ if (isNumeric!R)
+ {
+ alias C = typeof(return);
+ auto w = C(this.re, this.im);
+ return w.opOpAssign!(op)(r);
+ }
+
+ // numeric + complex, numeric * complex
+ Complex!(CommonType!(T, R)) opBinaryRight(string op, R)(R r) const
+ if ((op == "+" || op == "*") && (isNumeric!R))
+ {
+ return opBinary!(op)(r);
+ }
+
+ // numeric - complex
+ Complex!(CommonType!(T, R)) opBinaryRight(string op, R)(R r) const
+ if (op == "-" && isNumeric!R)
+ {
+ return Complex(r - re, -im);
+ }
+
+ // numeric / complex
+ Complex!(CommonType!(T, R)) opBinaryRight(string op, R)(R r) const
+ if (op == "/" && isNumeric!R)
+ {
+ import std.math : fabs;
+ typeof(return) w = void;
+ if (fabs(re) < fabs(im))
+ {
+ immutable ratio = re/im;
+ immutable rdivd = r/(re*ratio + im);
+
+ w.re = rdivd*ratio;
+ w.im = -rdivd;
+ }
+ else
+ {
+ immutable ratio = im/re;
+ immutable rdivd = r/(re + im*ratio);
+
+ w.re = rdivd;
+ w.im = -rdivd*ratio;
+ }
+
+ return w;
+ }
+
+ // numeric ^^ complex
+ Complex!(CommonType!(T, R)) opBinaryRight(string op, R)(R lhs) const
+ if (op == "^^" && isNumeric!R)
+ {
+ import std.math : cos, exp, log, sin, PI;
+ Unqual!(CommonType!(T, R)) ab = void, ar = void;
+
+ if (lhs >= 0)
+ {
+ // r = lhs
+ // theta = 0
+ ab = lhs ^^ this.re;
+ ar = log(lhs) * this.im;
+ }
+ else
+ {
+ // r = -lhs
+ // theta = PI
+ ab = (-lhs) ^^ this.re * exp(-PI * this.im);
+ ar = PI * this.re + log(-lhs) * this.im;
+ }
+
+ return typeof(return)(ab * cos(ar), ab * sin(ar));
+ }
+
+ // OP-ASSIGN OPERATORS
+
+ // complex += complex, complex -= complex
+ ref Complex opOpAssign(string op, C)(C z)
+ if ((op == "+" || op == "-") && is(C R == Complex!R))
+ {
+ mixin ("re "~op~"= z.re;");
+ mixin ("im "~op~"= z.im;");
+ return this;
+ }
+
+ // complex *= complex
+ ref Complex opOpAssign(string op, C)(C z)
+ if (op == "*" && is(C R == Complex!R))
+ {
+ auto temp = re*z.re - im*z.im;
+ im = im*z.re + re*z.im;
+ re = temp;
+ return this;
+ }
+
+ // complex /= complex
+ ref Complex opOpAssign(string op, C)(C z)
+ if (op == "/" && is(C R == Complex!R))
+ {
+ import std.math : fabs;
+ if (fabs(z.re) < fabs(z.im))
+ {
+ immutable ratio = z.re/z.im;
+ immutable denom = z.re*ratio + z.im;
+
+ immutable temp = (re*ratio + im)/denom;
+ im = (im*ratio - re)/denom;
+ re = temp;
+ }
+ else
+ {
+ immutable ratio = z.im/z.re;
+ immutable denom = z.re + z.im*ratio;
+
+ immutable temp = (re + im*ratio)/denom;
+ im = (im - re*ratio)/denom;
+ re = temp;
+ }
+ return this;
+ }
+
+ // complex ^^= complex
+ ref Complex opOpAssign(string op, C)(C z)
+ if (op == "^^" && is(C R == Complex!R))
+ {
+ 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*cos(ar);
+ im = ab*sin(ar);
+ return this;
+ }
+
+ // complex += numeric, complex -= numeric
+ ref Complex opOpAssign(string op, U : T)(U a)
+ if (op == "+" || op == "-")
+ {
+ mixin ("re "~op~"= a;");
+ return this;
+ }
+
+ // complex *= numeric, complex /= numeric
+ ref Complex opOpAssign(string op, U : T)(U a)
+ if (op == "*" || op == "/")
+ {
+ mixin ("re "~op~"= a;");
+ mixin ("im "~op~"= a;");
+ return this;
+ }
+
+ // complex ^^= real
+ 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*cos(ar);
+ im = ab*sin(ar);
+ return this;
+ }
+
+ // complex ^^= int
+ ref Complex opOpAssign(string op, U)(U i)
+ if (op == "^^" && isIntegral!U)
+ {
+ switch (i)
+ {
+ case 0:
+ re = 1.0;
+ im = 0.0;
+ break;
+ case 1:
+ // identity; do nothing
+ break;
+ case 2:
+ this *= this;
+ break;
+ case 3:
+ auto z = this;
+ this *= z;
+ this *= z;
+ break;
+ default:
+ this ^^= cast(real) i;
+ }
+ return this;
+ }
+}
+
+@safe pure nothrow unittest
+{
+ import std.complex;
+ import std.math;
+
+ enum EPS = double.epsilon;
+ auto c1 = complex(1.0, 1.0);
+
+ // Check unary operations.
+ auto c2 = Complex!double(0.5, 2.0);
+
+ 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);
+
+ auto cmc = c1 - c2;
+ 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));
+
+ auto cdc = c1 / c2;
+ 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));
+
+ // Check complex-real operations.
+ double a = 123.456;
+
+ auto cpr = c1 + a;
+ 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);
+
+ auto ctr = c1 * 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));
+
+ auto cer = c1^^3.0;
+ assert(approxEqual(abs(cer), abs(c1)^^3, EPS));
+ assert(approxEqual(arg(cer), arg(c1)*3, EPS));
+
+ auto rpc = a + c1;
+ assert(rpc == cpr);
+
+ auto rmc = a - c1;
+ assert(rmc.re == a-c1.re);
+ assert(rmc.im == -c1.im);
+
+ auto rtc = a * c1;
+ assert(rtc == ctr);
+
+ auto rdc = a / c1;
+ 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);
+ assert(rec1a.im == 0.0);
+
+ auto rec2a = 1.0 ^^ c2;
+ assert(rec2a.re == 1.0);
+ assert(rec2a.im == 0.0);
+
+ auto rec1b = (-1.0) ^^ c1;
+ assert(approxEqual(abs(rec1b), std.math.exp(-PI * c1.im), EPS));
+ auto arg1b = arg(rec1b);
+ /* The argument _should_ be PI, but floating-point rounding error
+ * means that in fact the imaginary part is very slightly negative.
+ */
+ assert(approxEqual(arg1b, PI, EPS) || approxEqual(arg1b, -PI, EPS));
+
+ auto rec2b = (-1.0) ^^ c2;
+ assert(approxEqual(abs(rec2b), std.math.exp(-2 * PI), EPS));
+ assert(approxEqual(arg(rec2b), PI_2, EPS));
+
+ auto rec3a = 0.79 ^^ complex(6.8, 5.7);
+ auto rec3b = complex(0.79, 0.0) ^^ complex(6.8, 5.7);
+ assert(approxEqual(rec3a.re, rec3b.re, EPS));
+ assert(approxEqual(rec3a.im, rec3b.im, EPS));
+
+ auto rec4a = (-0.79) ^^ complex(6.8, 5.7);
+ auto rec4b = complex(-0.79, 0.0) ^^ complex(6.8, 5.7);
+ assert(approxEqual(rec4a.re, rec4b.re, EPS));
+ assert(approxEqual(rec4a.im, rec4b.im, EPS));
+
+ auto rer = a ^^ complex(2.0, 0.0);
+ auto rcheck = a ^^ 2.0;
+ static assert(is(typeof(rcheck) == double));
+ assert(feqrel(rer.re, rcheck) == double.mant_dig);
+ assert(isIdentical(rer.re, rcheck));
+ assert(rer.im == 0.0);
+
+ auto rer2 = (-a) ^^ complex(2.0, 0.0);
+ rcheck = (-a) ^^ 2.0;
+ assert(feqrel(rer2.re, rcheck) == double.mant_dig);
+ assert(isIdentical(rer2.re, rcheck));
+ assert(approxEqual(rer2.im, 0.0, EPS));
+
+ auto rer3 = (-a) ^^ complex(-2.0, 0.0);
+ rcheck = (-a) ^^ (-2.0);
+ assert(feqrel(rer3.re, rcheck) == double.mant_dig);
+ assert(isIdentical(rer3.re, rcheck));
+ assert(approxEqual(rer3.im, 0.0, EPS));
+
+ auto rer4 = a ^^ complex(-2.0, 0.0);
+ rcheck = a ^^ (-2.0);
+ assert(feqrel(rer4.re, rcheck) == double.mant_dig);
+ assert(isIdentical(rer4.re, rcheck));
+ assert(rer4.im == 0.0);
+
+ // Check Complex-int operations.
+ foreach (i; 0 .. 6)
+ {
+ auto cei = c1^^i;
+ 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));
+ }
+
+ // Check operations between different complex types.
+ auto cf = Complex!float(1.0, 1.0);
+ 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);
+
+ 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));
+}
+
+@safe pure nothrow unittest
+{
+ // Initialization
+ Complex!double a = 1;
+ assert(a.re == 1 && a.im == 0);
+ Complex!double b = 1.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);
+}
+
+@safe pure nothrow unittest
+{
+ // Assignments and comparisons
+ Complex!double z;
+
+ z = 1;
+ 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);
+
+ z = 1.0L;
+ 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);
+
+ auto c = Complex!float(2.0, 2.0);
+ z = c;
+ assert(z == c);
+ assert(z.re == 2.0 && z.im == 2.0);
+}
+
+
+/* Makes Complex!(Complex!T) fold to Complex!T.
+
+ The rationale for this is that just like the real line is a
+ subspace of the complex plane, the complex plane is a subspace
+ of itself. Example of usage:
+ ---
+ Complex!T addI(T)(T x)
+ {
+ return x + Complex!T(0.0, 1.0);
+ }
+ ---
+ The above will work if T is both real and complex.
+*/
+template Complex(T)
+if (is(T R == Complex!R))
+{
+ alias Complex = T;
+}
+
+@safe pure nothrow unittest
+{
+ static assert(is(Complex!(Complex!real) == Complex!real));
+
+ Complex!T addI(T)(T x)
+ {
+ return x + Complex!T(0.0, 1.0);
+ }
+
+ auto z1 = addI(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);
+}
+
+
+/**
+ 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);
+}
+
+///
+@safe pure nothrow unittest
+{
+ 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));
+}
+
+
+/++
+ 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;
+}
+
+///
+@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));
+}
+
+/// ditto
+T sqAbs(T)(T x) @safe pure nothrow @nogc
+if (isFloatingPoint!T)
+{
+ return x*x;
+}
+
+@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));
+}
+
+
+/**
+ 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);
+}
+
+///
+@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);
+}
+
+
+/**
+ 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);
+}
+
+///
+@safe pure nothrow unittest
+{
+ 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.
+ 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*cos(argument), modulus*sin(argument));
+}
+
+///
+@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));
+}
+
+
+/**
+ 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;
+ auto cs = expi(z.re);
+ auto csh = coshisinh(z.im);
+ return typeof(return)(cs.im * csh.re, cs.re * csh.im);
+}
+
+///
+@safe pure nothrow unittest
+{
+ static import std.math;
+ assert(sin(complex(0.0)) == 0.0);
+ assert(sin(complex(2.0L, 0)) == std.math.sin(2.0L));
+}
+
+
+/// ditto
+Complex!T cos(T)(Complex!T z) @safe pure nothrow @nogc
+{
+ import std.math : expi, coshisinh;
+ auto cs = expi(z.re);
+ auto csh = coshisinh(z.im);
+ return typeof(return)(cs.re * csh.re, - cs.im * csh.im);
+}
+
+///
+@safe pure nothrow unittest
+{
+ import std.complex;
+ import std.math;
+ assert(cos(complex(0.0)) == 1.0);
+ assert(cos(complex(1.3L)) == std.math.cos(1.3L));
+ assert(cos(complex(0, 5.2L)) == cosh(5.2L));
+}
+
+
+/**
+ 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 $(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.
+*/
+Complex!real expi(real y) @trusted pure nothrow @nogc
+{
+ import std.math : cos, sin;
+ return Complex!real(cos(y), sin(y));
+}
+
+///
+@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);
+ auto z2 = std.math.expi(1.234);
+ assert(z1.re == z2.re && z1.im == z2.im);
+}
+
+
+/**
+ Params: z = A complex number.
+ Returns: The square root of `z`.
+*/
+Complex!T sqrt(T)(Complex!T z) @safe pure nothrow @nogc
+{
+ static import std.math;
+ typeof(return) c;
+ real x,y,w,r;
+
+ if (z == 0)
+ {
+ c = typeof(return)(0, 0);
+ }
+ else
+ {
+ real z_re = z.re;
+ real z_im = z.im;
+
+ x = std.math.fabs(z_re);
+ y = std.math.fabs(z_im);
+ if (x >= y)
+ {
+ r = y / x;
+ w = std.math.sqrt(x)
+ * std.math.sqrt(0.5 * (1 + std.math.sqrt(1 + r * r)));
+ }
+ else
+ {
+ r = x / y;
+ w = std.math.sqrt(y)
+ * std.math.sqrt(0.5 * (r + std.math.sqrt(1 + r * r)));
+ }
+
+ if (z_re >= 0)
+ {
+ c = typeof(return)(w, z_im / (w + w));
+ }
+ else
+ {
+ if (z_im < 0)
+ w = -w;
+ c = typeof(return)(z_im / (w + w), w);
+ }
+ }
+ return c;
+}
+
+///
+@safe pure nothrow unittest
+{
+ 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
+@safe unittest
+{
+ import std.format : format;
+
+ auto x = complex(1.2, 3.4);
+ assert(format("%.2f", x) == "1.20+3.40i");
+
+ auto y = complex(1.2, -3.4);
+ assert(format("%.2f", y) == "1.20-3.40i");
+}
+
+@safe unittest
+{
+ // Test wide string formatting
+ import std.format;
+ wstring wformat(T)(string format, Complex!T c)
+ {
+ import std.array : appender;
+ auto w = appender!wstring();
+ auto n = formattedWrite(w, format, c);
+ return w.data;
+ }
+
+ auto x = complex(1.2, 3.4);
+ assert(wformat("%.2f", x) == "1.20+3.40i"w);
+}
+
+@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/libphobos/src/std/concurrency.d b/libphobos/src/std/concurrency.d
new file mode 100644
index 0000000..cf77911
--- /dev/null
+++ b/libphobos/src/std/concurrency.d
@@ -0,0 +1,2531 @@
+/**
+ * This is a low-level messaging API upon which more structured or restrictive
+ * APIs may be built. The general idea is that every messageable entity is
+ * represented by a common handle type called a Tid, which allows messages to
+ * be sent to logical threads that are executing in both the current process
+ * and in external processes using the same interface. This is an important
+ * aspect of scalability because it allows the components of a program to be
+ * spread across available resources with few to no changes to the actual
+ * implementation.
+ *
+ * A logical thread is an execution context that has its own stack and which
+ * runs asynchronously to other logical threads. These may be preemptively
+ * scheduled kernel threads, fibers (cooperative user-space threads), or some
+ * other concept with similar behavior.
+ *
+ * The type of concurrency used when logical threads are created is determined
+ * by the Scheduler selected at initialization time. The default behavior is
+ * currently to create a new kernel thread per call to spawn, but other
+ * schedulers are available that multiplex fibers across the main thread or
+ * use some combination of the two approaches.
+ *
+ * Copyright: Copyright Sean Kelly 2009 - 2014.
+ * License: <a href="http://www.boost.org/LICENSE_1_0.txt">Boost License 1.0</a>.
+ * Authors: Sean Kelly, Alex Rønne Petersen, Martin Nowak
+ * Source: $(PHOBOSSRC std/_concurrency.d)
+ */
+/* Copyright Sean Kelly 2009 - 2014.
+ * 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.concurrency;
+
+public import std.variant;
+
+import core.atomic;
+import core.sync.condition;
+import core.sync.mutex;
+import core.thread;
+import std.range.primitives;
+import std.range.interfaces : InputRange;
+import std.traits;
+
+///
+@system unittest
+{
+ __gshared string received;
+ static void spawnedFunc(Tid ownerTid)
+ {
+ import std.conv : text;
+ // Receive a message from the owner thread.
+ receive((int i){
+ received = text("Received the number ", i);
+
+ // Send a message back to the owner thread
+ // indicating success.
+ send(ownerTid, true);
+ });
+ }
+
+ // Start spawnedFunc in a new thread.
+ auto childTid = spawn(&spawnedFunc, thisTid);
+
+ // Send the number 42 to this new thread.
+ send(childTid, 42);
+
+ // Receive the result code.
+ auto wasSuccessful = receiveOnly!(bool);
+ assert(wasSuccessful);
+ assert(received == "Received the number 42");
+}
+
+private
+{
+ template hasLocalAliasing(T...)
+ {
+ static if (!T.length)
+ enum hasLocalAliasing = false;
+ else
+ enum hasLocalAliasing = (std.traits.hasUnsharedAliasing!(T[0]) && !is(T[0] == Tid)) ||
+ std.concurrency.hasLocalAliasing!(T[1 .. $]);
+ }
+
+ enum MsgType
+ {
+ standard,
+ priority,
+ linkDead,
+ }
+
+ struct Message
+ {
+ MsgType type;
+ Variant data;
+
+ this(T...)(MsgType t, T vals) if (T.length > 0)
+ {
+ static if (T.length == 1)
+ {
+ type = t;
+ data = vals[0];
+ }
+ else
+ {
+ import std.typecons : Tuple;
+
+ type = t;
+ data = Tuple!(T)(vals);
+ }
+ }
+
+ @property auto 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 (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)
+ {
+ alias Args = Parameters!(Op);
+
+ static if (Args.length == 1)
+ {
+ static if (is(Args[0] == Variant))
+ return op(data);
+ else
+ return op(data.get!(Args));
+ }
+ else
+ {
+ import std.typecons : Tuple;
+ return op(data.get!(Tuple!(Args)).expand);
+ }
+ }
+ }
+
+ void checkops(T...)(T ops)
+ {
+ foreach (i, t1; T)
+ {
+ static assert(isFunctionPointer!t1 || isDelegate!t1);
+ alias a1 = Parameters!(t1);
+ alias r1 = ReturnType!(t1);
+
+ 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");
+
+ foreach (t2; T[i + 1 .. $])
+ {
+ static assert(isFunctionPointer!t2 || isDelegate!t2);
+ alias a2 = Parameters!(t2);
+
+ static assert(!is(a1 == a2),
+ "function with arguments " ~ a1.stringof ~ " occludes successive function");
+ }
+ }
+ }
+ }
+
+ @property ref ThreadInfo thisInfo() nothrow
+ {
+ 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
+ * the receiving thread expected is sent.
+ */
+class MessageMismatch : Exception
+{
+ ///
+ this(string msg = "Unexpected message type") @safe pure nothrow @nogc
+ {
+ 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") @safe pure nothrow @nogc
+ {
+ super(msg);
+ tid = t;
+ }
+
+ Tid tid;
+}
+
+/**
+ * Thrown if a linked thread has terminated.
+ */
+class LinkTerminated : Exception
+{
+ ///
+ this(Tid t, string msg = "Link terminated") @safe pure nothrow @nogc
+ {
+ super(msg);
+ tid = t;
+ }
+
+ Tid tid;
+}
+
+/**
+ * Thrown if a message was sent to a thread via
+ * $(REF prioritySend, std,concurrency) and the receiver does not have a handler
+ * for a message of this type.
+ */
+class PriorityMessageException : Exception
+{
+ ///
+ this(Variant vals)
+ {
+ super("Priority message");
+ message = vals;
+ }
+
+ /**
+ * The message that was sent.
+ */
+ 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") @safe pure nothrow @nogc
+ {
+ 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
+{
+ import std.exception : basicExceptionCtors;
+ ///
+ mixin basicExceptionCtors;
+}
+
+
+// Thread ID
+
+
+/**
+ * An opaque type used to represent a logical thread.
+ */
+struct Tid
+{
+private:
+ this(MessageBox m) @safe pure nothrow @nogc
+ {
+ mbox = m;
+ }
+
+ MessageBox mbox;
+
+public:
+
+ /**
+ * Generate a convenient string for identifying this Tid. This is only
+ * useful to see if Tid's that are currently executing are the same or
+ * different, e.g. for logging and debugging. It is potentially possible
+ * that a Tid executed in the future will have the same toString() output
+ * as another Tid that has already terminated.
+ */
+ void toString(scope void delegate(const(char)[]) sink)
+ {
+ 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 $(LREF Tid) of the caller's thread.
+ */
+@property Tid thisTid() @safe
+{
+ // TODO: remove when concurrency is safe
+ static auto trus() @trusted
+ {
+ if (thisInfo.ident != Tid.init)
+ return thisInfo.ident;
+ thisInfo.ident = Tid(new MessageBox);
+ return thisInfo.ident;
+ }
+
+ return trus();
+}
+
+/**
+ * Return the Tid of the thread which spawned the caller's thread.
+ *
+ * Throws: A $(D TidMissingException) exception if
+ * there is no owner thread.
+ */
+@property Tid ownerTid()
+{
+ import std.exception : enforce;
+
+ enforce!TidMissingException(thisInfo.owner.mbox !is null, "Error: Thread has no owner thread.");
+ return thisInfo.owner;
+}
+
+@system unittest
+{
+ import std.exception : assertThrown;
+
+ static void fun()
+ {
+ string res = receiveOnly!string();
+ assert(res == "Main calling");
+ ownerTid.send("Child responding");
+ }
+
+ assertThrown!TidMissingException(ownerTid);
+ auto child = spawn(&fun);
+ child.send("Main calling");
+ string res = receiveOnly!string();
+ assert(res == "Child responding");
+}
+
+// Thread Creation
+
+private template isSpawnable(F, T...)
+{
+ template isParamsImplicitlyConvertible(F1, F2, int i = 0)
+ {
+ 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);
+ else
+ enum isParamsImplicitlyConvertible = false;
+ }
+
+ enum isSpawnable = isCallable!F && is(ReturnType!F == void)
+ && isParamsImplicitlyConvertible!(F, void function(T))
+ && (isFunctionPointer!F || !hasUnsharedAliasing!F);
+}
+
+/**
+ * Starts fn(args) in a new logical thread.
+ *
+ * Executes the supplied function in a new logical thread represented by
+ * $(D Tid). The calling thread is designated as the owner of the new thread.
+ * When the owner thread terminates an $(D OwnerTerminated) message will be
+ * sent to the new thread, causing an $(D OwnerTerminated) exception to be
+ * thrown on $(D receive()).
+ *
+ * Params:
+ * fn = The function to execute.
+ * args = Arguments to the function.
+ *
+ * Returns:
+ * A Tid representing the new logical thread.
+ *
+ * Notes:
+ * $(D args) must not have unshared aliasing. In other words, all arguments
+ * to $(D fn) must either be $(D shared) or $(D immutable) or have no
+ * pointer indirection. This is necessary for enforcing isolation among
+ * threads.
+ *
+ * Example:
+ * ---
+ * import std.stdio, std.concurrency;
+ *
+ * void f1(string str)
+ * {
+ * writeln(str);
+ * }
+ *
+ * void f2(char[] str)
+ * {
+ * writeln(str);
+ * }
+ *
+ * void main()
+ * {
+ * auto str = "Hello, world";
+ *
+ * // Works: string is immutable.
+ * auto tid1 = spawn(&f1, str);
+ *
+ * // 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))
+{
+ 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.
+ *
+ * Executes the supplied function in a new logical thread represented by
+ * Tid. This new thread is linked to the calling thread so that if either
+ * it or the calling thread terminates a LinkTerminated message will be sent
+ * to the other, causing a LinkTerminated exception to be thrown on receive().
+ * The owner relationship from spawn() is preserved as well, so if the link
+ * between threads is broken, owner termination will still result in an
+ * OwnerTerminated exception to be thrown on receive().
+ *
+ * Params:
+ * fn = The function to execute.
+ * args = Arguments to the function.
+ *
+ * Returns:
+ * A Tid representing the new thread.
+ */
+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);
+}
+
+/*
+ *
+ */
+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 ownerTid = thisTid;
+
+ void exec()
+ {
+ thisInfo.ident = spawnTid;
+ thisInfo.owner = ownerTid;
+ fn(args);
+ }
+
+ // TODO: MessageList and &exec should be shared.
+ if (scheduler !is null)
+ scheduler.spawn(&exec);
+ else
+ {
+ auto t = new Thread(&exec);
+ t.start();
+ }
+ thisInfo.links[spawnTid] = linked;
+ return spawnTid;
+}
+
+@system unittest
+{
+ 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)));
+ 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 callable3 = 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 {} };
+ static assert(!__traits(compiles, spawn(callable1, 1)));
+ static assert( __traits(compiles, spawn(callable2, 2)));
+ static assert(!__traits(compiles, spawn(callable3, 3)));
+ static assert( __traits(compiles, spawn(callable4, 4)));
+ static assert(!__traits(compiles, spawn(callable5, 5)));
+ static assert(!__traits(compiles, spawn(callable6, 6)));
+ static assert(!__traits(compiles, spawn(callable7, 7)));
+ static assert( __traits(compiles, spawn(callable8, 8)));
+ static assert(!__traits(compiles, spawn(callable9, 9)));
+ static assert( __traits(compiles, spawn(callable10, 10)));
+ static assert( __traits(compiles, spawn(callable11, 11)));
+}
+
+/**
+ * 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
+ * $(REF spawn, std,concurrency), $(D T) must not have unshared aliasing.
+ */
+void send(T...)(Tid tid, T 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.
+ *
+ * Send a message to $(D tid) but place it at the front of $(D tid)'s message
+ * 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)
+{
+ 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)
+{
+ _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)
+{
+ auto msg = Message(type, vals);
+ tid.mbox.put(msg);
+}
+
+/**
+ * Receives a message from another thread.
+ *
+ * Receive a message from another thread, or block if no messages of the
+ * 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 $(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 $(REF Tuple, std,typecons) of all values
+ * sent.
+ *
+ * Example:
+ * ---
+ * import std.stdio;
+ * import std.variant;
+ * import std.concurrency;
+ *
+ * void spawnedFunction()
+ * {
+ * receive(
+ * (int i) { writeln("Received an int."); },
+ * (float f) { writeln("Received a float."); },
+ * (Variant v) { writeln("Received some other type."); }
+ * );
+ * }
+ *
+ * void main()
+ * {
+ * auto tid = spawn(&spawnedFunction);
+ * send(tid, 42);
+ * }
+ * ---
+ */
+void receive(T...)( 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.");
+}
+body
+{
+ checkops( ops );
+
+ thisInfo.ident.mbox.get( ops );
+}
+
+
+@safe unittest
+{
+ static assert( __traits( compiles,
+ {
+ receive( (Variant x) {} );
+ receive( (int x) {}, (Variant x) {} );
+ } ) );
+
+ static assert( !__traits( compiles,
+ {
+ receive( (Variant x) {}, (int x) {} );
+ } ) );
+
+ static assert( !__traits( compiles,
+ {
+ receive( (int x) {}, (int x) {} );
+ } ) );
+}
+
+// Make sure receive() works with free functions as well.
+version (unittest)
+{
+ private void receiveFunction(int x) {}
+}
+@safe unittest
+{
+ static assert( __traits( compiles,
+ {
+ receive( &receiveFunction );
+ receive( &receiveFunction, (Variant x) {} );
+ } ) );
+}
+
+
+private template receiveOnlyRet(T...)
+{
+ static if ( T.length == 1 )
+ {
+ alias receiveOnlyRet = T[0];
+ }
+ else
+ {
+ import std.typecons : Tuple;
+ alias receiveOnlyRet = Tuple!(T);
+ }
+}
+
+/**
+ * Receives only messages with arguments of types $(D T).
+ *
+ * Throws: $(D MessageMismatch) if a message of types other than $(D T)
+ * is received.
+ *
+ * Returns: The received message. If $(D T.length) is greater than one,
+ * the message will be packed into a $(REF Tuple, std,typecons).
+ *
+ * Example:
+ * ---
+ * import std.concurrency;
+ *
+ * void spawnedFunc()
+ * {
+ * auto msg = receiveOnly!(int, string)();
+ * assert(msg[0] == 42);
+ * assert(msg[1] == "42");
+ * }
+ *
+ * void main()
+ * {
+ * auto tid = spawn(&spawnedFunc);
+ * send(tid, 42, "42");
+ * }
+ * ---
+ */
+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.");
+}
+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;
+
+ 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;
+}
+
+@system unittest
+{
+ static void t1(Tid mainTid)
+ {
+ try
+ {
+ receiveOnly!string();
+ mainTid.send("");
+ }
+ catch (Throwable th)
+ {
+ mainTid.send(th.msg);
+ }
+ }
+
+ auto tid = spawn(&t1, thisTid);
+ tid.send(1);
+ string result = receiveOnly!string();
+ assert(result == "Unexpected message type: expected 'string', got 'int'");
+}
+
+/**
+ * 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
+ * $(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)
+in
+{
+ assert(thisInfo.ident.mbox !is null,
+ "Cannot receive a message until a thread was spawned or thisTid was passed to a running thread.");
+}
+body
+{
+ checkops(ops);
+
+ return thisInfo.ident.mbox.get(duration, ops);
+}
+
+@safe unittest
+{
+ 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.
+ throwException, /// Throw a MailboxFull exception.
+ ignore /// Abort the send and return.
+}
+
+private
+{
+ bool onCrowdingBlock(Tid tid) @safe pure nothrow @nogc
+ {
+ return true;
+ }
+
+ bool onCrowdingThrow(Tid tid) @safe pure
+ {
+ throw new MailboxFull(tid);
+ }
+
+ bool onCrowdingIgnore(Tid tid) @safe pure nothrow @nogc
+ {
+ return false;
+ }
+}
+
+/**
+ * Sets a maximum mailbox size.
+ *
+ * Sets a limit on the maximum number of user messages allowed in the mailbox.
+ * If this limit is reached, the caller attempting to add a new message will
+ * execute the behavior specified by doThis. If messages is zero, the mailbox
+ * is unbounded.
+ *
+ * Params:
+ * tid = The Tid of the thread for which this limit should be set.
+ * messages = The maximum number of messages or zero if no limit.
+ * doThis = The behavior executed when a message is sent to a full
+ * mailbox.
+ */
+void setMaxMailboxSize(Tid tid, size_t messages, OnCrowding doThis) @safe pure
+{
+ final switch (doThis)
+ {
+ case OnCrowding.block:
+ return tid.mbox.setMaxMsgs(messages, &onCrowdingBlock);
+ case OnCrowding.throwException:
+ return tid.mbox.setMaxMsgs(messages, &onCrowdingThrow);
+ case OnCrowding.ignore:
+ return tid.mbox.setMaxMsgs(messages, &onCrowdingIgnore);
+ }
+}
+
+/**
+ * Sets a maximum mailbox size.
+ *
+ * Sets a limit on the maximum number of user messages allowed in the mailbox.
+ * If this limit is reached, the caller attempting to add a new message will
+ * execute onCrowdingDoThis. If messages is zero, the mailbox is unbounded.
+ *
+ * Params:
+ * tid = The Tid of the thread for which this limit should be set.
+ * messages = The maximum number of messages or zero if no limit.
+ * onCrowdingDoThis = The routine called when a message is sent to a full
+ * mailbox.
+ */
+void setMaxMailboxSize(Tid tid, size_t messages, bool function(Tid) onCrowdingDoThis)
+{
+ tid.mbox.setMaxMsgs(messages, onCrowdingDoThis);
+}
+
+private
+{
+ __gshared Tid[string] tidByName;
+ __gshared string[][Tid] namesByTid;
+}
+
+private @property Mutex registryLock()
+{
+ __gshared Mutex impl;
+ initOnce!impl(new Mutex);
+ return impl;
+}
+
+private void unregisterMe()
+{
+ auto me = thisInfo.ident;
+ if (thisInfo.ident != Tid.init)
+ {
+ synchronized (registryLock)
+ {
+ if (auto allNames = me in namesByTid)
+ {
+ foreach (name; *allNames)
+ tidByName.remove(name);
+ namesByTid.remove(me);
+ }
+ }
+ }
+}
+
+/**
+ * Associates name with tid.
+ *
+ * Associates name with tid in a process-local map. When the thread
+ * represented by tid terminates, any names associated with it will be
+ * automatically unregistered.
+ *
+ * Params:
+ * name = The name to associate with tid.
+ * tid = The tid register by name.
+ *
+ * Returns:
+ * true if the name is available and tid is not known to represent a
+ * defunct thread.
+ */
+bool register(string name, Tid tid)
+{
+ synchronized (registryLock)
+ {
+ if (name in tidByName)
+ return false;
+ if (tid.mbox.isClosed)
+ return false;
+ namesByTid[tid] ~= name;
+ tidByName[name] = tid;
+ return true;
+ }
+}
+
+/**
+ * Removes the registered name associated with a tid.
+ *
+ * Params:
+ * name = The name to unregister.
+ *
+ * Returns:
+ * true if the name is registered, false if not.
+ */
+bool unregister(string name)
+{
+ import std.algorithm.mutation : remove, SwapStrategy;
+ import std.algorithm.searching : countUntil;
+
+ synchronized (registryLock)
+ {
+ if (auto tid = name in tidByName)
+ {
+ auto allNames = *tid in namesByTid;
+ auto pos = countUntil(*allNames, name);
+ remove!(SwapStrategy.unstable)(*allNames, pos);
+ tidByName.remove(name);
+ return true;
+ }
+ return false;
+ }
+}
+
+/**
+ * Gets the Tid associated with name.
+ *
+ * Params:
+ * name = The name to locate within the registry.
+ *
+ * Returns:
+ * The associated Tid or Tid.init if name is not registered.
+ */
+Tid locate(string name)
+{
+ synchronized (registryLock)
+ {
+ if (auto tid = name in tidByName)
+ return *tid;
+ return Tid.init;
+ }
+}
+
+/**
+ * Encapsulates all implementation-level data needed for scheduling.
+ *
+ * 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;
+ bool[Tid] links;
+ Tid owner;
+
+ /**
+ * Gets a thread-local instance of ThreadInfo.
+ *
+ * Gets a thread-local instance of ThreadInfo, which should be used as the
+ * default instance when info is requested for a thread not created by the
+ * Scheduler.
+ */
+ static @property ref thisInfo() nothrow
+ {
+ static ThreadInfo val;
+ return val;
+ }
+
+ /**
+ * Cleans up this ThreadInfo.
+ *
+ * This must be called when a scheduled thread terminates. It tears down
+ * the messaging system for the thread and notifies interested parties of
+ * the thread's termination.
+ */
+ void cleanup()
+ {
+ 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);
+ unregisterMe(); // clean up registry entries
+ }
+}
+
+/**
+ * A Scheduler controls how threading is performed by spawn.
+ *
+ * Implementing a Scheduler allows the concurrency mechanism used by this
+ * module to be customized according to different needs. By default, a call
+ * to spawn will create a new kernel thread that executes the supplied routine
+ * and terminates when finished. But it is possible to create Schedulers that
+ * reuse threads, that multiplex Fibers (coroutines) across a single thread,
+ * or any number of other approaches. By making the choice of Scheduler a
+ * user-level option, std.concurrency may be used for far more types of
+ * application than if this behavior were predefined.
+ *
+ * Example:
+ * ---
+ * import std.concurrency;
+ * import std.stdio;
+ *
+ * void main()
+ * {
+ * scheduler = new FiberScheduler;
+ * scheduler.start(
+ * {
+ * writeln("the rest of main goes here");
+ * });
+ * }
+ * ---
+ *
+ * Some schedulers have a dispatching loop that must run if they are to work
+ * properly, so for the sake of consistency, when using a scheduler, start()
+ * must be called within main(). This yields control to the scheduler and
+ * will ensure that any spawned threads are executed in an expected manner.
+ */
+interface Scheduler
+{
+ /**
+ * Spawns the supplied op and starts the Scheduler.
+ *
+ * This is intended to be called at the start of the program to yield all
+ * scheduling to the active Scheduler instance. This is necessary for
+ * schedulers that explicitly dispatch threads rather than simply relying
+ * on the operating system to do so, and so start should always be called
+ * within main() to begin normal program execution.
+ *
+ * Params:
+ * op = A wrapper for whatever the main thread would have done in the
+ * absence of a custom scheduler. It will be automatically executed
+ * via a call to spawn by the Scheduler.
+ */
+ void start(void delegate() op);
+
+ /**
+ * Assigns a logical thread to execute the supplied op.
+ *
+ * This routine is called by spawn. It is expected to instantiate a new
+ * logical thread and run the supplied operation. This thread must call
+ * thisInfo.cleanup() when the thread terminates if the scheduled thread
+ * is not a kernel thread--all kernel threads will have their ThreadInfo
+ * cleaned up automatically by a thread-local destructor.
+ *
+ * Params:
+ * 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);
+
+ /**
+ * Yields execution to another logical thread.
+ *
+ * This routine is called at various points within concurrency-aware APIs
+ * to provide a scheduler a chance to yield execution when using some sort
+ * of cooperative multithreading model. If this is not appropriate, such
+ * as when each logical thread is backed by a dedicated kernel thread,
+ * this routine may be a no-op.
+ */
+ void yield() nothrow;
+
+ /**
+ * Returns an appropriate ThreadInfo instance.
+ *
+ * Returns an instance of ThreadInfo specific to the logical thread that
+ * is calling this routine or, if the calling thread was not create by
+ * this scheduler, returns ThreadInfo.thisInfo instead.
+ */
+ @property ref ThreadInfo thisInfo() nothrow;
+
+ /**
+ * 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
+ * yield, some schedulers may need to define custom behavior so that calls
+ * to Condition.wait() yield to another thread when no new messages are
+ * available instead of blocking.
+ *
+ * Params:
+ * m = The Mutex that will be associated with this condition. It will be
+ * locked prior to any operation on the condition, and so in some
+ * 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;
+}
+
+/**
+ * An example Scheduler using kernel threads.
+ *
+ * This is an example Scheduler that mirrors the default scheduling behavior
+ * of creating one kernel thread per call to spawn. It is fully functional
+ * and may be instantiated and used, but is not a necessary part of the
+ * default functioning of this module.
+ */
+class ThreadScheduler : Scheduler
+{
+ /**
+ * This simply runs op directly, since no real scheduling is needed by
+ * this approach.
+ */
+ void start(void delegate() op)
+ {
+ op();
+ }
+
+ /**
+ * Creates a new kernel thread and assigns it to run the supplied op.
+ */
+ void spawn(void delegate() op)
+ {
+ auto t = new Thread(op);
+ t.start();
+ }
+
+ /**
+ * This scheduler does no explicit multiplexing, so this is a no-op.
+ */
+ void yield() nothrow
+ {
+ // no explicit yield needed
+ }
+
+ /**
+ * Returns ThreadInfo.thisInfo, since it is a thread-local instance of
+ * ThreadInfo, which is the correct behavior for this scheduler.
+ */
+ @property ref ThreadInfo thisInfo() nothrow
+ {
+ return ThreadInfo.thisInfo;
+ }
+
+ /**
+ * Creates a new Condition variable. No custom behavior is needed here.
+ */
+ Condition newCondition(Mutex m) nothrow
+ {
+ 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
+{
+ /**
+ * This creates a new Fiber for the supplied op and then starts the
+ * dispatcher.
+ */
+ void start(void delegate() 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
+ {
+ create(op);
+ yield();
+ }
+
+ /**
+ * If the caller is a scheduled Fiber, this yields execution to another
+ * scheduled Fiber.
+ */
+ void yield() nothrow
+ {
+ // 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())
+ Fiber.yield();
+ }
+
+ /**
+ * Returns an appropriate ThreadInfo instance.
+ *
+ * Returns a ThreadInfo instance specific to the calling Fiber if the
+ * Fiber was created by this dispatcher, otherwise it returns
+ * ThreadInfo.thisInfo.
+ */
+ @property ref ThreadInfo thisInfo() nothrow
+ {
+ auto f = cast(InfoFiber) Fiber.getThis();
+
+ 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
+ {
+ return new FiberCondition(m);
+ }
+
+private:
+ static class InfoFiber : Fiber
+ {
+ ThreadInfo info;
+
+ this(void delegate() op) nothrow
+ {
+ super(op);
+ }
+ }
+
+ class FiberCondition : Condition
+ {
+ this(Mutex m) nothrow
+ {
+ super(m);
+ notified = false;
+ }
+
+ override void wait() nothrow
+ {
+ scope (exit) notified = false;
+
+ while (!notified)
+ switchContext();
+ }
+
+ override bool wait(Duration period) nothrow
+ {
+ import core.time : MonoTime;
+
+ scope (exit) notified = false;
+
+ for (auto limit = MonoTime.currTime + period;
+ !notified && !period.isNegative;
+ period = limit - MonoTime.currTime)
+ {
+ yield();
+ }
+ return notified;
+ }
+
+ override void notify() nothrow
+ {
+ notified = true;
+ switchContext();
+ }
+
+ override void notifyAll() nothrow
+ {
+ notified = true;
+ switchContext();
+ }
+
+ private:
+ void switchContext() nothrow
+ {
+ mutex_nothrow.unlock_nothrow();
+ scope (exit) mutex_nothrow.lock_nothrow();
+ yield();
+ }
+
+ private bool notified;
+ }
+
+private:
+ void dispatch()
+ {
+ import std.algorithm.mutation : remove;
+
+ while (m_fibers.length > 0)
+ {
+ 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_pos >= (m_fibers = remove(m_fibers, m_pos)).length)
+ m_pos = 0;
+ }
+ else if (m_pos++ >= m_fibers.length - 1)
+ {
+ m_pos = 0;
+ }
+ }
+ }
+
+ void create(void delegate() op) nothrow
+ {
+ void wrap()
+ {
+ scope (exit)
+ {
+ thisInfo.cleanup();
+ }
+ op();
+ }
+
+ m_fibers ~= new InfoFiber(&wrap);
+ }
+
+private:
+ Fiber[] m_fibers;
+ size_t m_pos;
+}
+
+@system unittest
+{
+ static void receive(Condition cond, ref size_t received)
+ {
+ while (true)
+ {
+ synchronized (cond.mutex)
+ {
+ cond.wait();
+ ++received;
+ }
+ }
+ }
+
+ static void send(Condition cond, ref size_t sent)
+ {
+ while (true)
+ {
+ synchronized (cond.mutex)
+ {
+ ++sent;
+ cond.notify();
+ }
+ }
+ }
+
+ auto fs = new FiberScheduler;
+ auto mtx = new Mutex;
+ auto cond = fs.newCondition(mtx);
+
+ size_t received, sent;
+ auto waiter = new Fiber({ receive(cond, received); }), notifier = new Fiber({ send(cond, sent); });
+ waiter.call();
+ assert(received == 0);
+ notifier.call();
+ assert(sent == 1);
+ assert(received == 0);
+ waiter.call();
+ assert(received == 1);
+ waiter.call();
+ assert(received == 1);
+}
+
+/**
+ * Sets the Scheduler behavior within the program.
+ *
+ * This variable sets the Scheduler behavior within this program. Typically,
+ * when setting a Scheduler, scheduler.start() should be called in main. This
+ * routine will not return until program execution is complete.
+ */
+__gshared Scheduler scheduler;
+
+// Generator
+
+/**
+ * If the caller is a Fiber and is not a Generator, this function will call
+ * scheduler.yield() or Fiber.yield(), as appropriate.
+ */
+void yield() nothrow
+{
+ auto fiber = Fiber.getThis();
+ if (!(cast(IsGenerator) fiber))
+ {
+ if (scheduler is null)
+ {
+ if (fiber)
+ return Fiber.yield();
+ }
+ else
+ scheduler.yield();
+ }
+}
+
+/// Used to determine whether a Generator is running.
+private interface IsGenerator {}
+
+
+/**
+ * A Generator is a Fiber that periodically returns values of type T to the
+ * caller via yield. This is represented as an InputRange.
+ *
+ * Example:
+ * ---
+ * import std.concurrency;
+ * import std.stdio;
+ *
+ *
+ * void main()
+ * {
+ * auto tid = spawn(
+ * {
+ * while (true)
+ * {
+ * writeln(receiveOnly!int());
+ * }
+ * });
+ *
+ * auto r = new Generator!int(
+ * {
+ * foreach (i; 1 .. 10)
+ * yield(i);
+ * });
+ *
+ * foreach (e; r)
+ * {
+ * tid.send(e);
+ * }
+ * }
+ * ---
+ */
+class Generator(T) :
+ Fiber, IsGenerator, InputRange!T
+{
+ /**
+ * Initializes a generator object which is associated with a static
+ * D function. The function will be called once to prepare the range
+ * for iteration.
+ *
+ * Params:
+ * fn = The fiber function.
+ *
+ * In:
+ * fn must not be null.
+ */
+ this(void function() fn)
+ {
+ super(fn);
+ call();
+ }
+
+ /**
+ * Initializes a generator object which is associated with a static
+ * D function. The function will be called once to prepare the range
+ * for iteration.
+ *
+ * Params:
+ * fn = The fiber function.
+ * sz = The stack size for this fiber.
+ *
+ * In:
+ * fn must not be null.
+ */
+ this(void function() fn, size_t sz)
+ {
+ super(fn, sz);
+ call();
+ }
+
+ /**
+ * Initializes a generator object which is associated with a dynamic
+ * D function. The function will be called once to prepare the range
+ * for iteration.
+ *
+ * Params:
+ * dg = The fiber function.
+ *
+ * In:
+ * dg must not be null.
+ */
+ this(void delegate() dg)
+ {
+ super(dg);
+ call();
+ }
+
+ /**
+ * Initializes a generator object which is associated with a dynamic
+ * D function. The function will be called once to prepare the range
+ * for iteration.
+ *
+ * Params:
+ * dg = The fiber function.
+ * sz = The stack size for this fiber.
+ *
+ * In:
+ * dg must not be null.
+ */
+ this(void delegate() dg, size_t sz)
+ {
+ super(dg, sz);
+ call();
+ }
+
+ /**
+ * Returns true if the generator is empty.
+ */
+ final bool empty() @property
+ {
+ return m_value is null || state == State.TERM;
+ }
+
+ /**
+ * Obtains the next value from the underlying function.
+ */
+ final void popFront()
+ {
+ call();
+ }
+
+ /**
+ * Returns the most recently generated value by shallow copy.
+ */
+ final T front() @property
+ {
+ return *m_value;
+ }
+
+ /**
+ * Returns the most recently generated value without executing a
+ * copy contructor. Will not compile for element types defining a
+ * postblit, because Generator does not return by reference.
+ */
+ final T moveFront()
+ {
+ static if (!hasElaborateCopyConstructor!T)
+ {
+ return front;
+ }
+ else
+ {
+ static assert(0,
+ "Fiber front is always rvalue and thus cannot be moved since it defines a postblit.");
+ }
+ }
+
+ final int opApply(scope int delegate(T) loopBody)
+ {
+ int broken;
+ for (; !empty; popFront())
+ {
+ broken = loopBody(front);
+ if (broken) break;
+ }
+ return broken;
+ }
+
+ final int opApply(scope int delegate(size_t, T) loopBody)
+ {
+ int broken;
+ for (size_t i; !empty; ++i, popFront())
+ {
+ broken = loopBody(i, front);
+ if (broken) break;
+ }
+ return broken;
+ }
+private:
+ T* m_value;
+}
+
+/**
+ * Yields a value of type T to the caller of the currently executing
+ * generator.
+ *
+ * Params:
+ * value = The value to yield.
+ */
+void yield(T)(ref T value)
+{
+ Generator!T cur = cast(Generator!T) Fiber.getThis();
+ if (cur !is null && cur.state == Fiber.State.EXEC)
+ {
+ cur.m_value = &value;
+ return Fiber.yield();
+ }
+ throw new Exception("yield(T) called with no active generator for the supplied type");
+}
+
+/// ditto
+void yield(T)(T value)
+{
+ yield(value);
+}
+
+@system unittest
+{
+ import core.exception;
+ import std.exception;
+
+ static void testScheduler(Scheduler s)
+ {
+ scheduler = s;
+ scheduler.start({
+ auto tid = spawn({
+ int i;
+
+ try
+ {
+ for (i = 1; i < 10; i++)
+ {
+ assertNotThrown!AssertError(assert(receiveOnly!int() == i));
+ }
+ }
+ catch (OwnerTerminated e)
+ {
+
+ }
+
+ // i will advance 1 past the last value expected
+ assert(i == 4);
+ });
+
+ auto r = new Generator!int({
+ assertThrown!Exception(yield(2.0));
+ yield(); // ensure this is a no-op
+ yield(1);
+ yield(); // also once something has been yielded
+ yield(2);
+ yield(3);
+ });
+
+ foreach (e; r)
+ {
+ tid.send(e);
+ }
+ });
+ scheduler = null;
+ }
+
+ testScheduler(new ThreadScheduler);
+ testScheduler(new FiberScheduler);
+}
+///
+@system unittest
+{
+ import std.range;
+
+ InputRange!int myIota = iota(10).inputRangeObject;
+
+ myIota.popFront();
+ myIota.popFront();
+ assert(myIota.moveFront == 2);
+ assert(myIota.front == 2);
+ myIota.popFront();
+ assert(myIota.front == 3);
+
+ //can be assigned to std.range.interfaces.InputRange directly
+ myIota = new Generator!int(
+ {
+ foreach (i; 0 .. 10) yield(i);
+ });
+
+ myIota.popFront();
+ myIota.popFront();
+ assert(myIota.moveFront == 2);
+ assert(myIota.front == 2);
+ myIota.popFront();
+ assert(myIota.front == 3);
+
+ size_t[2] counter = [0, 0];
+ foreach (i, unused; myIota) counter[] += [1, i];
+
+ assert(myIota.empty);
+ assert(counter == [7, 21]);
+}
+
+private
+{
+ /*
+ * A MessageBox is a message queue for one thread. Other threads may send
+ * messages to this owner by calling put(), and the owner receives them by
+ * calling get(). The put() call is therefore effectively shared and the
+ * get() call is effectively local. setMaxMsgs may be used by any thread
+ * to limit the size of the message queue.
+ */
+ class MessageBox
+ {
+ this() @trusted nothrow /* TODO: make @safe after relevant druntime PR gets merged */
+ {
+ m_lock = new Mutex;
+ m_closed = false;
+
+ if (scheduler is null)
+ {
+ 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);
+ }
+ }
+
+ ///
+ final @property bool isClosed() @safe @nogc pure
+ {
+ synchronized (m_lock)
+ {
+ return m_closed;
+ }
+ }
+
+ /*
+ * Sets a limit on the maximum number of user messages allowed in the
+ * mailbox. If this limit is reached, the caller attempting to add
+ * a new message will execute call. If num is zero, there is no limit
+ * on the message queue.
+ *
+ * Params:
+ * num = The maximum size of the queue or zero if the queue is
+ * unbounded.
+ * call = The routine to call when the queue is full.
+ */
+ final void setMaxMsgs(size_t num, bool function(Tid) call) @safe @nogc pure
+ {
+ synchronized (m_lock)
+ {
+ 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
+ * accepted if it is a control message, otherwise onCrowdingDoThis is
+ * called. If the routine returns true, this call will block until
+ * the owner has made space available in the queue. If it returns
+ * false, this call will abort.
+ *
+ * Params:
+ * msg = The message to put in the queue.
+ *
+ * Throws:
+ * An exception if the queue is full and onCrowdingDoThis throws.
+ */
+ final void put(ref Message msg)
+ {
+ 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)
+ {
+ while (true)
+ {
+ if (isPriorityMsg(msg))
+ {
+ m_sharedPty.put(msg);
+ m_putMsg.notify();
+ return;
+ }
+ if (!mboxFull() || isControlMsg(msg))
+ {
+ m_sharedBox.put(msg);
+ m_putMsg.notify();
+ return;
+ }
+ if (m_onMaxMsgs !is null && !m_onMaxMsgs(thisTid))
+ {
+ return;
+ }
+ m_putQueue++;
+ m_notFull.wait();
+ m_putQueue--;
+ }
+ }
+ }
+ }
+
+ /*
+ * Matches ops against each message in turn until a match is found.
+ *
+ * Params:
+ * ops = The operations to match. Each may return a bool to indicate
+ * whether a message with a matching type is truly a match.
+ *
+ * Returns:
+ * true if a message was retrieved and false if not (such as if a
+ * timeout occurred).
+ *
+ * Throws:
+ * LinkTerminated if a linked thread terminated, or OwnerTerminated
+ * if the owner thread terminates and no existing messages match the
+ * supplied ops.
+ */
+ bool get(T...)(scope T vals)
+ {
+ import std.meta : AliasSeq;
+
+ static assert(T.length);
+
+ static if (isImplicitlyConvertible!(T[0], Duration))
+ {
+ alias Ops = AliasSeq!(T[1 .. $]);
+ alias ops = vals[1 .. $];
+ enum timedWait = true;
+ Duration period = vals[0];
+ }
+ else
+ {
+ alias Ops = AliasSeq!(T);
+ alias ops = vals[0 .. $];
+ enum timedWait = false;
+ }
+
+ bool onStandardMsg(ref Message msg)
+ {
+ foreach (i, t; Ops)
+ {
+ alias Args = Parameters!(t);
+ auto op = ops[i];
+
+ if (msg.convertsTo!(Args))
+ {
+ static if (is(ReturnType!(t) == bool))
+ {
+ return msg.map(op);
+ }
+ else
+ {
+ msg.map(op);
+ return true;
+ }
+ }
+ }
+ return false;
+ }
+
+ bool onLinkDeadMsg(ref Message msg)
+ {
+ assert(msg.convertsTo!(Tid));
+ auto tid = msg.get!(Tid);
+
+ if (bool* pDepends = tid in thisInfo.links)
+ {
+ auto depends = *pDepends;
+ thisInfo.links.remove(tid);
+ // Give the owner relationship precedence.
+ if (depends && tid != thisInfo.owner)
+ {
+ auto e = new LinkTerminated(tid);
+ auto m = Message(MsgType.standard, e);
+ if (onStandardMsg(m))
+ return true;
+ throw e;
+ }
+ }
+ if (tid == thisInfo.owner)
+ {
+ thisInfo.owner = Tid.init;
+ 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)
+ {
+ switch (msg.type)
+ {
+ case MsgType.linkDead:
+ return onLinkDeadMsg(msg);
+ default:
+ return false;
+ }
+ }
+
+ bool scan(ref ListT list)
+ {
+ 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);
+
+ if (isControlMsg(range.front))
+ {
+ if (onControlMsg(range.front))
+ {
+ // Although the linkDead message is a control message,
+ // it can be handled by the user. Since the linkDead
+ // message throws if not handled, if we get here then
+ // 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))
+ {
+ list.removeAt(range);
+ continue;
+ }
+ list.removeAt(range);
+ return true;
+ }
+ range.popFront();
+ continue;
+ }
+ else
+ {
+ if (onStandardMsg(range.front))
+ {
+ list.removeAt(range);
+ return true;
+ }
+ range.popFront();
+ continue;
+ }
+ }
+ return false;
+ }
+
+ bool pty(ref ListT list)
+ {
+ if (!list.empty)
+ {
+ auto range = list[];
+
+ if (onStandardMsg(range.front))
+ {
+ list.removeAt(range);
+ return true;
+ }
+ if (range.front.convertsTo!(Throwable))
+ throw range.front.get!(Throwable);
+ else if (range.front.convertsTo!(shared(Throwable)))
+ throw range.front.get!(shared(Throwable));
+ else
+ throw new PriorityMessageException(range.front.data);
+ }
+ return false;
+ }
+
+ static if (timedWait)
+ {
+ import core.time : MonoTime;
+ auto limit = MonoTime.currTime + period;
+ }
+
+ while (true)
+ {
+ ListT arrived;
+
+ if (pty(m_localPty) || scan(m_localBox))
+ {
+ return true;
+ }
+ yield();
+ synchronized (m_lock)
+ {
+ updateMsgCount();
+ 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
+ // changed and we don't want to block sender threads
+ // 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())
+ m_notFull.notifyAll();
+ static if (timedWait)
+ {
+ if (period <= Duration.zero || !m_putMsg.wait(period))
+ return false;
+ }
+ else
+ {
+ m_putMsg.wait();
+ }
+ }
+ m_localPty.put(m_sharedPty);
+ arrived.put(m_sharedBox);
+ }
+ if (m_localPty.empty)
+ {
+ scope (exit) m_localBox.put(arrived);
+ if (scan(arrived))
+ {
+ return true;
+ }
+ else
+ {
+ static if (timedWait)
+ {
+ period = limit - MonoTime.currTime;
+ }
+ continue;
+ }
+ }
+ 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
+ * reject any future messages.
+ */
+ final void close()
+ {
+ static void onLinkDeadMsg(ref Message msg)
+ {
+ assert(msg.convertsTo!(Tid));
+ auto tid = msg.get!(Tid);
+
+ thisInfo.links.remove(tid);
+ if (tid == thisInfo.owner)
+ thisInfo.owner = Tid.init;
+ }
+
+ static void sweep(ref ListT list)
+ {
+ for (auto range = list[]; !range.empty; range.popFront())
+ {
+ if (range.front.type == MsgType.linkDead)
+ onLinkDeadMsg(range.front);
+ }
+ }
+
+ ListT arrived;
+
+ sweep(m_localBox);
+ synchronized (m_lock)
+ {
+ arrived.put(m_sharedBox);
+ m_closed = true;
+ }
+ m_localBox.clear();
+ sweep(arrived);
+ }
+
+ private:
+ // Routines involving local data only, no lock needed.
+
+ bool mboxFull() @safe @nogc pure nothrow
+ {
+ return m_maxMsgs && m_maxMsgs <= m_localMsgs + m_sharedBox.length;
+ }
+
+ void updateMsgCount() @safe @nogc pure nothrow
+ {
+ m_localMsgs = m_localBox.length;
+ }
+
+ bool isControlMsg(ref Message msg) @safe @nogc pure nothrow
+ {
+ return msg.type != MsgType.standard && msg.type != MsgType.priority;
+ }
+
+ bool isPriorityMsg(ref Message msg) @safe @nogc pure nothrow
+ {
+ return msg.type == MsgType.priority;
+ }
+
+ bool isLinkDeadMsg(ref Message msg) @safe @nogc pure nothrow
+ {
+ return msg.type == MsgType.linkDead;
+ }
+
+ alias OnMaxFn = bool function(Tid);
+ alias ListT = List!(Message);
+
+ 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;
+ }
+
+ /*
+ *
+ */
+ struct List(T)
+ {
+ struct Range
+ {
+ import std.exception : enforce;
+
+ @property bool empty() const
+ {
+ return !m_prev.next;
+ }
+
+ @property ref T front()
+ {
+ enforce(m_prev.next, "invalid list node");
+ return m_prev.next.val;
+ }
+
+ @property void front(T val)
+ {
+ enforce(m_prev.next, "invalid list node");
+ m_prev.next.val = val;
+ }
+
+ void popFront()
+ {
+ enforce(m_prev.next, "invalid list node");
+ m_prev = m_prev.next;
+ }
+
+ private this(Node* p)
+ {
+ m_prev = p;
+ }
+
+ private Node* m_prev;
+ }
+
+ void put(T val)
+ {
+ put(newNode(val));
+ }
+
+ void put(ref List!(T) rhs)
+ {
+ if (!rhs.empty)
+ {
+ 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_count = 0;
+ }
+ }
+
+ Range opSlice()
+ {
+ return Range(cast(Node*)&m_first);
+ }
+
+ void removeAt(Range r)
+ {
+ import std.exception : enforce;
+
+ assert(m_count);
+ Node* n = r.m_prev;
+ enforce(n && n.next, "attempting to remove invalid list node");
+
+ if (m_last is m_first)
+ m_last = null;
+ else if (m_last is n.next)
+ m_last = n; // nocoverage
+ Node* to_free = n.next;
+ n.next = n.next.next;
+ 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;
+
+ this(T v)
+ {
+ val = v;
+ }
+ }
+
+ static shared struct SpinLock
+ {
+ void lock() { while (!cas(&locked, false, true)) { Thread.yield(); } }
+ 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;
+ {
+ sm_lock.lock();
+ scope (exit) sm_lock.unlock();
+
+ if (sm_head)
+ {
+ n = cast(Node*) sm_head;
+ sm_head = sm_head.next;
+ }
+ }
+ if (n)
+ {
+ import std.conv : emplace;
+ emplace!Node(n, v);
+ }
+ else
+ {
+ n = new Node(v);
+ }
+ return n;
+ }
+
+ void freeNode(Node* n)
+ {
+ // destroy val to free any owned GC memory
+ destroy(n.val);
+
+ sm_lock.lock();
+ scope (exit) sm_lock.unlock();
+
+ auto sn = cast(shared(Node)*) n;
+ sn.next = sm_head;
+ sm_head = sn;
+ }
+
+ void put(Node* n)
+ {
+ m_count++;
+ if (!empty)
+ {
+ m_last.next = n;
+ m_last = n;
+ return;
+ }
+ m_first = n;
+ m_last = n;
+ }
+
+ Node* m_first;
+ Node* m_last;
+ size_t m_count;
+ }
+}
+
+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 simpleTest()
+ {
+ 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);
+ }
+
+ @system unittest
+ {
+ simpleTest();
+ }
+
+ @system unittest
+ {
+ scheduler = new ThreadScheduler;
+ simpleTest();
+ scheduler = null;
+ }
+}
+
+private @property Mutex initOnceLock()
+{
+ __gshared Mutex lock;
+ if (auto mtx = cast() 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 cast() atomicLoad!(MemoryOrder.acq)(*cast(shared)&lock);
+}
+
+/**
+ * Initializes $(D_PARAM var) with the lazy $(D_PARAM init) value in a
+ * thread-safe manner.
+ *
+ * The implementation guarantees that all threads simultaneously calling
+ * initOnce with the same $(D_PARAM var) argument block until $(D_PARAM var) is
+ * fully initialized. All side-effects of $(D_PARAM init) are globally visible
+ * afterwards.
+ *
+ * Params:
+ * var = The variable to initialize
+ * init = The lazy initializer value
+ *
+ * Returns:
+ * A reference to the initialized variable
+ */
+auto ref initOnce(alias var)(lazy typeof(var) init)
+{
+ return initOnce!var(init, initOnceLock);
+}
+
+/// A typical use-case is to perform lazy but thread-safe initialization.
+@system unittest
+{
+ static class MySingleton
+ {
+ static MySingleton instance()
+ {
+ static __gshared MySingleton inst;
+ return initOnce!inst(new MySingleton);
+ }
+ }
+
+ assert(MySingleton.instance !is null);
+}
+
+@system unittest
+{
+ static class MySingleton
+ {
+ static MySingleton instance()
+ {
+ static __gshared MySingleton inst;
+ return initOnce!inst(new MySingleton);
+ }
+
+ private:
+ this() { val = ++cnt; }
+ size_t val;
+ static __gshared size_t cnt;
+ }
+
+ foreach (_; 0 .. 10)
+ spawn({ ownerTid.send(MySingleton.instance.val); });
+ foreach (_; 0 .. 10)
+ assert(receiveOnly!size_t == MySingleton.instance.val);
+ assert(MySingleton.cnt == 1);
+}
+
+/**
+ * Same as above, but takes a separate mutex instead of sharing one among
+ * all initOnce instances.
+ *
+ * This should be used to avoid dead-locks when the $(D_PARAM init)
+ * expression waits for the result of another thread that might also
+ * call initOnce. Use with care.
+ *
+ * Params:
+ * var = The variable to initialize
+ * init = The lazy initializer value
+ * mutex = A mutex to prevent race conditions
+ *
+ * Returns:
+ * A reference to the initialized variable
+ */
+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 : atomicLoad, MemoryOrder, atomicStore;
+
+ static shared bool flag;
+ if (!atomicLoad!(MemoryOrder.acq)(flag))
+ {
+ synchronized (mutex)
+ {
+ if (!atomicLoad!(MemoryOrder.acq)(flag))
+ {
+ var = init;
+ atomicStore!(MemoryOrder.rel)(flag, true);
+ }
+ }
+ }
+ return var;
+}
+
+/// Use a separate mutex when init blocks on another thread that might also call initOnce.
+@system unittest
+{
+ import core.sync.mutex : Mutex;
+
+ static shared bool varA, varB;
+ __gshared Mutex m;
+ m = new Mutex;
+
+ spawn({
+ // use a different mutex for varB to avoid a dead-lock
+ initOnce!varB(true, m);
+ ownerTid.send(true);
+ });
+ // init depends on the result of the spawned thread
+ initOnce!varA(receiveOnly!bool);
+ assert(varA == true);
+ assert(varB == true);
+}
+
+@system unittest
+{
+ static shared bool a;
+ __gshared bool b;
+ static bool c;
+ bool d;
+ initOnce!a(true);
+ initOnce!b(true);
+ static assert(!__traits(compiles, initOnce!c(true))); // TLS
+ static assert(!__traits(compiles, initOnce!d(true))); // local variable
+}
diff --git a/libphobos/src/std/container/array.d b/libphobos/src/std/container/array.d
new file mode 100644
index 0000000..eee8901
--- /dev/null
+++ b/libphobos/src/std/container/array.d
@@ -0,0 +1,2419 @@
+/**
+ * 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 core.exception : RangeError;
+import std.range.primitives;
+import std.traits;
+
+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/post/vbmwhzvawhnkoxrhbnyb@forum.dlang.org
+ */
+ private A[1] _outer_;
+ private @property ref inout(A) _outer() inout { return _outer_[0]; }
+
+ private size_t _a, _b;
+
+ /* E is different from T when A is more restrictively qualified than T:
+ immutable(Array!int) => T == int, E = immutable(int) */
+ alias E = typeof(_outer_[0]._data._payload[0]);
+
+ private this(ref A data, size_t a, size_t b)
+ {
+ _outer_ = data;
+ _a = a;
+ _b = b;
+ }
+
+ @property RangeT save()
+ {
+ return this;
+ }
+
+ @property bool empty() @safe pure nothrow const
+ {
+ return _a >= _b;
+ }
+
+ @property size_t length() @safe pure nothrow const
+ {
+ return _b - _a;
+ }
+ alias opDollar = length;
+
+ @property ref inout(E) front() inout
+ {
+ assert(!empty, "Attempting to access the front of an empty Array");
+ return _outer[_a];
+ }
+ @property ref inout(E) back() inout
+ {
+ assert(!empty, "Attempting to access the back of an empty Array");
+ return _outer[_b - 1];
+ }
+
+ void popFront() @safe @nogc pure nothrow
+ {
+ assert(!empty, "Attempting to popFront an empty Array");
+ ++_a;
+ }
+
+ void popBack() @safe @nogc pure nothrow
+ {
+ assert(!empty, "Attempting to popBack an empty Array");
+ --_b;
+ }
+
+ static if (isMutable!A)
+ {
+ import std.algorithm.mutation : move;
+
+ E moveFront()
+ {
+ assert(!empty && _a < _outer.length);
+ return move(_outer._data._payload[_a]);
+ }
+
+ E moveBack()
+ {
+ assert(!empty && _b <= _outer.length);
+ return move(_outer._data._payload[_b - 1]);
+ }
+
+ E moveAt(size_t i)
+ {
+ assert(_a + i < _b && _a + i < _outer.length);
+ return move(_outer._data._payload[_a + i]);
+ }
+ }
+
+ ref inout(E) opIndex(size_t i) inout
+ {
+ assert(_a + i < _b);
+ return _outer[_a + i];
+ }
+
+ RangeT opSlice()
+ {
+ return typeof(return)(_outer, _a, _b);
+ }
+
+ RangeT opSlice(size_t i, size_t j)
+ {
+ assert(i <= j && _a + j <= _b);
+ return typeof(return)(_outer, _a + i, _a + j);
+ }
+
+ RangeT!(const(A)) opSlice() const
+ {
+ return typeof(return)(_outer, _a, _b);
+ }
+
+ RangeT!(const(A)) opSlice(size_t i, size_t j) const
+ {
+ assert(i <= j && _a + j <= _b);
+ return typeof(return)(_outer, _a + i, _a + j);
+ }
+
+ static if (isMutable!A)
+ {
+ void opSliceAssign(E value)
+ {
+ assert(_b <= _outer.length);
+ _outer[_a .. _b] = value;
+ }
+
+ void opSliceAssign(E value, size_t i, size_t j)
+ {
+ assert(_a + j <= _b);
+ _outer[_a + i .. _a + j] = value;
+ }
+
+ void opSliceUnary(string op)()
+ if (op == "++" || op == "--")
+ {
+ assert(_b <= _outer.length);
+ mixin(op~"_outer[_a .. _b];");
+ }
+
+ void opSliceUnary(string op)(size_t i, size_t j)
+ if (op == "++" || op == "--")
+ {
+ assert(_a + j <= _b);
+ mixin(op~"_outer[_a + i .. _a + j];");
+ }
+
+ void opSliceOpAssign(string op)(E value)
+ {
+ assert(_b <= _outer.length);
+ mixin("_outer[_a .. _b] "~op~"= value;");
+ }
+
+ void opSliceOpAssign(string op)(E value, size_t i, size_t j)
+ {
+ 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. `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 : malloc, realloc, free;
+ import core.stdc.string : memcpy, memmove, memset;
+
+ import core.memory : GC;
+
+ import std.exception : enforce;
+ import std.typecons : RefCounted, RefCountedAutoInitialize;
+
+ // This structure is not copyable.
+ private struct Payload
+ {
+ size_t _capacity;
+ T[] _payload;
+
+ this(T[] p) { _capacity = p.length; _payload = p; }
+
+ // Destructor releases array memory
+ ~this()
+ {
+ // Warning: destroy would destroy also class instances.
+ // The hasElaborateDestructor protects us here.
+ static if (hasElaborateDestructor!T)
+ foreach (ref e; _payload)
+ .destroy(e);
+
+ static if (hasIndirections!T)
+ GC.removeRange(_payload.ptr);
+
+ free(_payload.ptr);
+ }
+
+ this(this) @disable;
+
+ void opAssign(Payload rhs) @disable;
+
+ @property size_t length() const
+ {
+ return _payload.length;
+ }
+
+ @property void length(size_t newLength)
+ {
+ import std.algorithm.mutation : initializeAll;
+
+ if (length >= newLength)
+ {
+ // shorten
+ static if (hasElaborateDestructor!T)
+ foreach (ref e; _payload.ptr[newLength .. _payload.length])
+ .destroy(e);
+
+ _payload = _payload.ptr[0 .. newLength];
+ return;
+ }
+ 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]);
+ }
+
+ @property size_t capacity() const
+ {
+ return _capacity;
+ }
+
+ void reserve(size_t elements)
+ {
+ if (elements <= capacity) return;
+ 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
+ * threading bugs by using malloc/copy/free rather
+ * than realloc.
+ */
+ immutable oldLength = length;
+
+ 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
+ memset(newPayload.ptr + oldLength,
+ 0,
+ (elements - oldLength) * T.sizeof);
+ GC.addRange(newPayload.ptr, sz);
+ GC.removeRange(_payload.ptr);
+ free(_payload.ptr);
+ _payload = newPayload;
+ }
+ else
+ {
+ // 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(Elem)(Elem elem)
+ if (isImplicitlyConvertible!(Elem, T))
+ {
+ import std.conv : emplace;
+ if (_capacity == length)
+ {
+ reserve(1 + capacity * 3 / 2);
+ }
+ assert(capacity > length && _payload.ptr);
+ emplace(_payload.ptr + _payload.length, elem);
+ _payload = _payload.ptr[0 .. _payload.length + 1];
+ return 1;
+ }
+
+ // Insert a range of items
+ size_t insertBack(Range)(Range r)
+ if (isInputRange!Range && isImplicitlyConvertible!(ElementType!Range, T))
+ {
+ static if (hasLength!Range)
+ {
+ immutable oldLength = length;
+ reserve(oldLength + r.length);
+ }
+ size_t result;
+ foreach (item; r)
+ {
+ insertBack(item);
+ ++result;
+ }
+ static if (hasLength!Range)
+ {
+ assert(length == oldLength + r.length);
+ }
+ return result;
+ }
+ }
+ private alias Data = RefCounted!(Payload, RefCountedAutoInitialize.no);
+ private Data _data;
+
+ /**
+ * Constructor taking a number of items.
+ */
+ this(U)(U[] values...)
+ if (isImplicitlyConvertible!(U, T))
+ {
+ import core.checkedint : mulu;
+ import std.conv : emplace;
+ 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)
+ GC.addRange(p, T.sizeof * values.length);
+ }
+
+ foreach (i, e; values)
+ {
+ emplace(p + i, e);
+ }
+ _data = Data(p[0 .. values.length]);
+ }
+
+ /**
+ * Constructor taking an input range
+ */
+ this(Range)(Range r)
+ if (isInputRange!Range && isImplicitlyConvertible!(ElementType!Range, T) && !is(Range == T[]))
+ {
+ insertBack(r);
+ }
+
+ /**
+ * Comparison for equality.
+ */
+ bool opEquals(const Array rhs) const
+ {
+ return opEquals(rhs);
+ }
+
+ /// ditto
+ bool opEquals(ref const Array rhs) const
+ {
+ if (empty) return rhs.empty;
+ if (rhs.empty) return false;
+ return _data._payload == rhs._data._payload;
+ }
+
+ /**
+ * 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;
+
+ /// ditto
+ alias ConstRange = RangeT!(const Array);
+
+ /// ditto
+ alias ImmutableRange = RangeT!(immutable Array);
+
+ /**
+ * Duplicates the array. The elements themselves are not transitively
+ * duplicated.
+ *
+ * Complexity: $(BIGOH length).
+ */
+ @property Array dup()
+ {
+ if (!_data.refCountedStore.isInitialized) return this;
+ return Array(_data._payload);
+ }
+
+ /**
+ * 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 array.
+ *
+ * Complexity: $(BIGOH 1).
+ */
+ @property size_t length() const
+ {
+ return _data.refCountedStore.isInitialized ? _data._payload.length : 0;
+ }
+
+ /// ditto
+ size_t opDollar() const
+ {
+ return length;
+ }
+
+ /**
+ * 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 `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;
+ 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);
+ }
+ _data = Data(cast(T[]) p[0 .. 0]);
+ _data._capacity = elements;
+ }
+ else
+ {
+ _data.reserve(elements);
+ }
+ }
+
+ /**
+ * 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 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)
+ {
+ assert(i <= j && j <= length);
+ return typeof(return)(this, i, j);
+ }
+
+ ConstRange opSlice(size_t i, size_t j) const
+ {
+ assert(i <= j && j <= length);
+ return typeof(return)(this, i, j);
+ }
+
+ ImmutableRange opSlice(size_t i, size_t j) immutable
+ {
+ assert(i <= j && j <= length);
+ return typeof(return)(this, i, j);
+ }
+
+ /**
+ * Returns: The first element of the array.
+ *
+ * Precondition: `empty == false`
+ *
+ * Complexity: $(BIGOH 1)
+ */
+ @property ref inout(T) front() inout
+ {
+ assert(_data.refCountedStore.isInitialized);
+ return _data._payload[0];
+ }
+
+ /**
+ * Returns: The last element of the array.
+ *
+ * Precondition: `empty == false`
+ *
+ * Complexity: $(BIGOH 1)
+ */
+ @property ref inout(T) back() inout
+ {
+ assert(_data.refCountedStore.isInitialized);
+ return _data._payload[$ - 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
+ {
+ assert(_data.refCountedStore.isInitialized);
+ return _data._payload[i];
+ }
+
+ /**
+ * Slicing operators executing the specified operation on the entire slice.
+ *
+ * Precondition: `i < j && j < length`
+ *
+ * Complexity: $(BIGOH slice.length)
+ */
+ void opSliceAssign(T value)
+ {
+ if (!_data.refCountedStore.isInitialized) return;
+ _data._payload[] = value;
+ }
+
+ /// ditto
+ void opSliceAssign(T value, size_t i, size_t j)
+ {
+ auto slice = _data.refCountedStore.isInitialized ?
+ _data._payload :
+ T[].init;
+ slice[i .. j] = value;
+ }
+
+ /// ditto
+ void opSliceUnary(string op)()
+ if (op == "++" || op == "--")
+ {
+ if (!_data.refCountedStore.isInitialized) return;
+ mixin(op~"_data._payload[];");
+ }
+
+ /// ditto
+ void opSliceUnary(string op)(size_t i, size_t j)
+ if (op == "++" || op == "--")
+ {
+ auto slice = _data.refCountedStore.isInitialized ? _data._payload : T[].init;
+ mixin(op~"slice[i .. j];");
+ }
+
+ /// ditto
+ void opSliceOpAssign(string op)(T value)
+ {
+ if (!_data.refCountedStore.isInitialized) return;
+ mixin("_data._payload[] "~op~"= value;");
+ }
+
+ /// ditto
+ void opSliceOpAssign(string op)(T value, size_t i, size_t j)
+ {
+ auto slice = _data.refCountedStore.isInitialized ? _data._payload : T[].init;
+ mixin("slice[i .. j] "~op~"= value;");
+ }
+
+ private enum hasSliceWithLength(T) = is(typeof({ T t = T.init; t[].length; }));
+
+ /**
+ * 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 == "~")
+ {
+ Array result;
+
+ 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 `insertBack`.
+ */
+ void opOpAssign(string op, Stuff)(auto ref Stuff stuff)
+ if (op == "~")
+ {
+ static if (is(typeof(stuff[])) && isImplicitlyConvertible!(typeof(stuff[0]), T))
+ {
+ insertBack(stuff[]);
+ }
+ else
+ {
+ insertBack(stuff);
+ }
+ }
+
+ /**
+ * 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 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)
+ {
+ _data.refCountedStore.ensureInitialized();
+ _data.length = newLength;
+ }
+
+ /**
+ * 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()
+ {
+ auto result = back;
+ removeBack();
+ return result;
+ }
+
+ /// ditto
+ alias stableRemoveAny = removeAny;
+
+ /**
+ * 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) ||
+ isInputRange!Stuff && isImplicitlyConvertible!(ElementType!Stuff, T))
+ {
+ _data.refCountedStore.ensureInitialized();
+ return _data.insertBack(stuff);
+ }
+
+ /// ditto
+ alias insert = insertBack;
+
+ /**
+ * 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()
+ {
+ enforce(!empty);
+ static if (hasElaborateDestructor!T)
+ .destroy(_data._payload[$ - 1]);
+
+ _data._payload = _data._payload[0 .. $ - 1];
+ }
+
+ /// ditto
+ alias stableRemoveBack = removeBack;
+
+ /**
+ * 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)
+ {
+ if (howMany > length) howMany = length;
+ static if (hasElaborateDestructor!T)
+ foreach (ref e; _data._payload[$ - howMany .. $])
+ .destroy(e);
+
+ _data._payload = _data._payload[0 .. $ - howMany];
+ return howMany;
+ }
+
+ /// ditto
+ alias stableRemoveBack = removeBack;
+
+ /**
+ * 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))
+ {
+ import std.conv : emplace;
+ enforce(r._outer._data is _data && r._a <= length);
+ reserve(length + 1);
+ assert(_data.refCountedStore.isInitialized);
+ // Move elements over by one slot
+ memmove(_data._payload.ptr + r._a + 1,
+ _data._payload.ptr + r._a,
+ T.sizeof * (length - r._a));
+ emplace(_data._payload.ptr + r._a, stuff);
+ _data._payload = _data._payload.ptr[0 .. _data._payload.length + 1];
+ return 1;
+ }
+
+ /// ditto
+ size_t insertBefore(Stuff)(Range r, Stuff stuff)
+ if (isInputRange!Stuff && isImplicitlyConvertible!(ElementType!Stuff, T))
+ {
+ import std.conv : emplace;
+ enforce(r._outer._data is _data && r._a <= length);
+ static if (isForwardRange!Stuff)
+ {
+ // Can find the length in advance
+ auto extra = walkLength(stuff);
+ if (!extra) return 0;
+ reserve(length + extra);
+ assert(_data.refCountedStore.isInitialized);
+ // Move elements over by extra slots
+ memmove(_data._payload.ptr + r._a + extra,
+ _data._payload.ptr + r._a,
+ T.sizeof * (length - r._a));
+ foreach (p; _data._payload.ptr + r._a ..
+ _data._payload.ptr + r._a + extra)
+ {
+ emplace(p, stuff.front);
+ stuff.popFront();
+ }
+ _data._payload =
+ _data._payload.ptr[0 .. _data._payload.length + extra];
+ return extra;
+ }
+ else
+ {
+ import std.algorithm.mutation : bringToFront;
+ enforce(_data);
+ immutable offset = r._a;
+ enforce(offset <= length);
+ auto result = insertBack(stuff);
+ bringToFront(this[offset .. length - result],
+ this[length - result .. length]);
+ return result;
+ }
+ }
+
+ /// ditto
+ alias stableInsertBefore = insertBefore;
+
+ /// ditto
+ size_t insertAfter(Stuff)(Range r, Stuff stuff)
+ {
+ import std.algorithm.mutation : bringToFront;
+ enforce(r._outer._data is _data);
+ // TODO: optimize
+ immutable offset = r._b;
+ enforce(offset <= length);
+ auto result = insertBack(stuff);
+ bringToFront(this[offset .. length - result],
+ this[length - result .. length]);
+ return result;
+ }
+
+ /// ditto
+ size_t replace(Stuff)(Range r, Stuff stuff)
+ if (isInputRange!Stuff && isImplicitlyConvertible!(ElementType!Stuff, T))
+ {
+ enforce(r._outer._data is _data);
+ size_t result;
+ for (; !stuff.empty; stuff.popFront())
+ {
+ if (r.empty)
+ {
+ // insert the rest
+ return result + insertBefore(r, stuff);
+ }
+ r.front = stuff.front;
+ r.popFront();
+ ++result;
+ }
+ // Remove remaining stuff in r
+ linearRemove(r);
+ return result;
+ }
+
+ /// ditto
+ size_t replace(Stuff)(Range r, Stuff stuff)
+ if (isImplicitlyConvertible!(Stuff, T))
+ {
+ enforce(r._outer._data is _data);
+ if (r.empty)
+ {
+ insertBefore(r, stuff);
+ }
+ else
+ {
+ r.front = stuff;
+ r.popFront();
+ linearRemove(r);
+ }
+ return 1;
+ }
+
+ /**
+ * 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);
+ immutable offset1 = r._a;
+ immutable offset2 = r._b;
+ immutable tailLength = length - offset2;
+ // Use copy here, not a[] = b[] because the ranges may overlap
+ copy(this[offset2 .. length], this[offset1 .. offset1 + tailLength]);
+ length = offset1 + tailLength;
+ return this[length - tailLength .. length];
+ }
+}
+
+@system unittest
+{
+ Array!int a;
+ assert(a.empty);
+}
+
+@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;
+ auto b = a.dup;
+ assert(b == Array!int(1, 2, 3));
+ b.front = 42;
+ assert(b == Array!int(42, 2, 3));
+ assert(a == Array!int(1, 2, 3));
+}
+
+@system unittest
+{
+ auto a = Array!int(1, 2, 3);
+ assert(a.length == 3);
+}
+
+@system unittest
+{
+ const Array!int a = [1, 2];
+
+ assert(a[0] == 1);
+ assert(a.front == 1);
+ assert(a.back == 2);
+
+ static assert(!__traits(compiles, { a[0] = 1; }));
+ static assert(!__traits(compiles, { a.front = 1; }));
+ static assert(!__traits(compiles, { a.back = 1; }));
+
+ auto r = a[];
+ size_t i;
+ foreach (e; r)
+ {
+ assert(e == i + 1);
+ i++;
+ }
+}
+
+@safe unittest
+{
+ // REG https://issues.dlang.org/show_bug.cgi?id=13621
+ import std.container : Array, BinaryHeap;
+ alias Heap = BinaryHeap!(Array!int);
+}
+
+@system unittest
+{
+ Array!int a;
+ a.reserve(1000);
+ assert(a.length == 0);
+ assert(a.empty);
+ assert(a.capacity >= 1000);
+ auto p = a._data._payload.ptr;
+ foreach (i; 0 .. 1000)
+ {
+ a.insertBack(i);
+ }
+ assert(p == a._data._payload.ptr);
+}
+
+@system unittest
+{
+ auto a = Array!int(1, 2, 3);
+ a[1] *= 42;
+ assert(a[1] == 84);
+}
+
+@system unittest
+{
+ auto a = Array!int(1, 2, 3);
+ auto b = Array!int(11, 12, 13);
+ auto c = a ~ b;
+ assert(c == 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));
+}
+
+@system unittest
+{
+ auto a = Array!int(1, 2, 3);
+ auto b = Array!int(11, 12, 13);
+ a ~= b;
+ assert(a == Array!int(1, 2, 3, 11, 12, 13));
+}
+
+@system unittest
+{
+ auto a = Array!int(1, 2, 3, 4);
+ assert(a.removeAny() == 4);
+ assert(a == Array!int(1, 2, 3));
+}
+
+@system unittest
+{
+ auto a = Array!int(1, 2, 3, 4, 5);
+ auto r = a[2 .. a.length];
+ assert(a.insertBefore(r, 42) == 1);
+ assert(a == Array!int(1, 2, 42, 3, 4, 5));
+ r = a[2 .. 2];
+ assert(a.insertBefore(r, [8, 9]) == 2);
+ assert(a == Array!int(1, 2, 8, 9, 42, 3, 4, 5));
+}
+
+@system unittest
+{
+ auto a = Array!int(0, 1, 2, 3, 4, 5, 6, 7, 8);
+ a.linearRemove(a[4 .. 6]);
+ assert(a == Array!int(0, 1, 2, 3, 6, 7, 8));
+}
+
+// Give the Range object some testing.
+@system unittest
+{
+ 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)[];
+ alias A = typeof(a);
+
+ static assert(isRandomAccessRange!A);
+ static assert(hasSlicing!A);
+ static assert(hasAssignableElements!A);
+ static assert(hasMobileElements!A);
+
+ assert(equal(retro(b), a));
+ assert(a.length == 7);
+ assert(equal(a[1 .. 4], [1, 2, 3]));
+}
+// Test issue 5920
+@system unittest
+{
+ struct structBug5920
+ {
+ int order;
+ uint* pDestructionMask;
+ ~this()
+ {
+ if (pDestructionMask)
+ *pDestructionMask += 1 << order;
+ }
+ }
+
+ alias S = structBug5920;
+ uint dMask;
+
+ auto arr = Array!S(cast(S[])[]);
+ foreach (i; 0 .. 8)
+ arr.insertBack(S(i, &dMask));
+ // don't check dMask now as S may be copied multiple times (it's ok?)
+ {
+ assert(arr.length == 8);
+ dMask = 0;
+ arr.length = 6;
+ assert(arr.length == 6); // make sure shrinking calls the d'tor
+ assert(dMask == 0b1100_0000);
+ arr.removeBack();
+ assert(arr.length == 5); // make sure removeBack() calls the d'tor
+ assert(dMask == 0b1110_0000);
+ arr.removeBack(3);
+ assert(arr.length == 2); // ditto
+ assert(dMask == 0b1111_1100);
+ arr.clear();
+ assert(arr.length == 0); // make sure clear() calls the d'tor
+ assert(dMask == 0b1111_1111);
+ }
+ 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)
+@system unittest
+{
+ auto a = Array!(int[])([[1,2],[3,4]]);
+ a.reserve(4);
+ assert(a.capacity >= 4);
+ assert(a.length == 2);
+ assert(a[0] == [1,2]);
+ assert(a[1] == [3,4]);
+ a.reserve(16);
+ assert(a.capacity >= 16);
+ assert(a.length == 2);
+ assert(a[0] == [1,2]);
+ assert(a[1] == [3,4]);
+}
+
+// test replace!Stuff with range Stuff
+@system unittest
+{
+ 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
+@system unittest
+{
+ import std.algorithm.comparison : equal;
+ auto a = Array!int();
+ a.insertBefore(a[], 1);
+ assert(equal(a[], [1]));
+}
+@system unittest
+{
+ import std.algorithm.comparison : equal;
+ auto a = Array!int();
+ a.insertBefore(a[], [1, 2]);
+ assert(equal(a[], [1, 2]));
+}
+@system unittest
+{
+ import std.algorithm.comparison : equal;
+ auto a = Array!int();
+ a.replace(a[], [1, 2]);
+ assert(equal(a[], [1, 2]));
+}
+@system unittest
+{
+ 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
+@system unittest
+{
+ import std.exception : assertThrown;
+
+ Array!int a = [1, 2, 3];
+ auto r = a.dup[];
+ assertThrown(a.insertBefore(r, 42));
+ assertThrown(a.insertBefore(r, [42]));
+ assertThrown(a.insertAfter(r, 42));
+ assertThrown(a.replace(r, 42));
+ assertThrown(a.replace(r, [42]));
+ assertThrown(a.linearRemove(r));
+}
+@system unittest
+{
+ auto a = Array!int([1, 1]);
+ a[1] = 0; //Check Array.opIndexAssign
+ assert(a[1] == 0);
+ a[1] += 1; //Check Array.opIndexOpAssign
+ assert(a[1] == 1);
+
+ //Check Array.opIndexUnary
+ ++a[0];
+ //a[0]++ //op++ doesn't return, so this shouldn't work, even with 5044 fixed
+ assert(a[0] == 2);
+ assert(+a[0] == +2);
+ assert(-a[0] == -2);
+ assert(~a[0] == ~2);
+
+ auto r = a[];
+ r[1] = 0; //Check Array.Range.opIndexAssign
+ assert(r[1] == 0);
+ r[1] += 1; //Check Array.Range.opIndexOpAssign
+ assert(r[1] == 1);
+
+ //Check Array.Range.opIndexUnary
+ ++r[0];
+ //r[0]++ //op++ doesn't return, so this shouldn't work, even with 5044 fixed
+ assert(r[0] == 3);
+ assert(+r[0] == +3);
+ assert(-r[0] == -3);
+ assert(~r[0] == ~3);
+}
+
+@system unittest
+{
+ import std.algorithm.comparison : equal;
+
+ //Test "array-wide" operations
+ auto a = Array!int([0, 1, 2]); //Array
+ a[] += 5;
+ assert(a[].equal([5, 6, 7]));
+ ++a[];
+ assert(a[].equal([6, 7, 8]));
+ a[1 .. 3] *= 5;
+ assert(a[].equal([6, 35, 40]));
+ a[0 .. 2] = 0;
+ assert(a[].equal([0, 0, 40]));
+
+ //Test empty array
+ auto a2 = Array!int.init;
+ ++a2[];
+ ++a2[0 .. 0];
+ a2[] = 0;
+ a2[0 .. 0] = 0;
+ a2[] += 0;
+ a2[0 .. 0] += 0;
+
+ //Test "range-wide" operations
+ auto r = Array!int([0, 1, 2])[]; //Array.Range
+ r[] += 5;
+ assert(r.equal([5, 6, 7]));
+ ++r[];
+ assert(r.equal([6, 7, 8]));
+ r[1 .. 3] *= 5;
+ assert(r.equal([6, 35, 40]));
+ r[0 .. 2] = 0;
+ assert(r.equal([0, 0, 40]));
+
+ //Test empty Range
+ auto r2 = Array!int.init[];
+ ++r2[];
+ ++r2[0 .. 0];
+ r2[] = 0;
+ r2[0 .. 0] = 0;
+ r2[] += 0;
+ r2[0 .. 0] += 0;
+}
+
+// Test issue 11194
+@system unittest
+{
+ static struct S {
+ int i = 1337;
+ void* p;
+ this(this) { assert(i == 1337); }
+ ~this() { assert(i == 1337); }
+ }
+ Array!S arr;
+ S s;
+ arr ~= s;
+ arr ~= s;
+}
+
+@safe unittest //11459
+{
+ static struct S
+ {
+ bool b;
+ alias b this;
+ }
+ alias A = Array!S;
+ alias B = Array!(shared bool);
+}
+
+@system unittest //11884
+{
+ import std.algorithm.iteration : filter;
+ auto a = Array!int([1, 2, 2].filter!"true"());
+}
+
+@safe unittest //8282
+{
+ auto arr = new Array!int;
+}
+
+@system unittest //6998
+{
+ static int i = 0;
+ class C
+ {
+ int dummy = 1;
+ this(){++i;}
+ ~this(){--i;}
+ }
+
+ assert(i == 0);
+ auto c = new C();
+ assert(i == 1);
+
+ //scope
+ {
+ auto arr = Array!C(c);
+ assert(i == 1);
+ }
+ //Array should not have destroyed the class instance
+ assert(i == 1);
+
+ //Just to make sure the GC doesn't collect before the above test.
+ assert(c.dummy == 1);
+}
+@system unittest //6998-2
+{
+ static class C {int i;}
+ auto c = new C;
+ c.i = 42;
+ Array!C a;
+ a ~= c;
+ a.clear;
+ assert(c.i == 42); //fails
+}
+
+@safe unittest
+{
+ static assert(is(Array!int.Range));
+ static assert(is(Array!int.ConstRange));
+}
+
+@system unittest // const/immutable Array and Ranges
+{
+ static void test(A, R, E, S)()
+ {
+ A a;
+ R r = a[];
+ assert(r.empty);
+ assert(r.length == 0);
+ static assert(is(typeof(r.front) == E));
+ static assert(is(typeof(r.back) == E));
+ static assert(is(typeof(r[0]) == E));
+ static assert(is(typeof(r[]) == S));
+ static assert(is(typeof(r[0 .. 0]) == S));
+ }
+
+ alias A = Array!int;
+
+ test!(A, A.Range, int, A.Range);
+ test!(A, const A.Range, const int, A.ConstRange);
+
+ test!(const A, A.ConstRange, const int, A.ConstRange);
+ test!(const A, const A.ConstRange, const int, A.ConstRange);
+
+ test!(immutable A, A.ImmutableRange, immutable int, A.ImmutableRange);
+ test!(immutable A, const A.ImmutableRange, immutable int, A.ImmutableRange);
+ test!(immutable A, immutable A.ImmutableRange, immutable int,
+ 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 `bool`. Packs together values efficiently by
+ * allocating one bit per element.
+ */
+struct Array(T)
+if (is(Unqual!T == bool))
+{
+ import std.exception : enforce;
+ import std.typecons : RefCounted, RefCountedAutoInitialize;
+
+ static immutable uint bitsPerWord = size_t.sizeof * 8;
+ private static struct Data
+ {
+ Array!size_t.Payload _backend;
+ size_t _length;
+ }
+ private RefCounted!(Data, RefCountedAutoInitialize.no) _store;
+
+ private @property ref size_t[] data()
+ {
+ assert(_store.refCountedStore.isInitialized);
+ return _store._backend._payload;
+ }
+
+ /**
+ * Defines the array's primary range.
+ */
+ struct Range
+ {
+ private Array _outer;
+ private size_t _a, _b;
+ /// Range primitives
+ @property Range save()
+ {
+ version (bug4437)
+ {
+ return this;
+ }
+ else
+ {
+ auto copy = this;
+ return copy;
+ }
+ }
+ /// Ditto
+ @property bool empty()
+ {
+ return _a >= _b || _outer.length < _b;
+ }
+ /// Ditto
+ @property T front()
+ {
+ enforce(!empty, "Attempting to access the front of an empty Array");
+ return _outer[_a];
+ }
+ /// Ditto
+ @property void front(bool value)
+ {
+ enforce(!empty, "Attempting to set the front of an empty Array");
+ _outer[_a] = value;
+ }
+ /// Ditto
+ T moveFront()
+ {
+ enforce(!empty, "Attempting to move the front of an empty Array");
+ return _outer.moveAt(_a);
+ }
+ /// Ditto
+ void popFront()
+ {
+ enforce(!empty, "Attempting to popFront an empty Array");
+ ++_a;
+ }
+ /// Ditto
+ @property T back()
+ {
+ enforce(!empty, "Attempting to access the back of an empty Array");
+ return _outer[_b - 1];
+ }
+ /// Ditto
+ @property void back(bool value)
+ {
+ enforce(!empty, "Attempting to set the back of an empty Array");
+ _outer[_b - 1] = value;
+ }
+ /// Ditto
+ T moveBack()
+ {
+ enforce(!empty, "Attempting to move the back of an empty Array");
+ return _outer.moveAt(_b - 1);
+ }
+ /// Ditto
+ void popBack()
+ {
+ enforce(!empty, "Attempting to popBack an empty Array");
+ --_b;
+ }
+ /// Ditto
+ T opIndex(size_t i)
+ {
+ return _outer[_a + i];
+ }
+ /// Ditto
+ void opIndexAssign(T value, size_t i)
+ {
+ _outer[_a + i] = value;
+ }
+ /// Ditto
+ T moveAt(size_t i)
+ {
+ return _outer.moveAt(_a + i);
+ }
+ /// Ditto
+ @property size_t length() const
+ {
+ assert(_a <= _b);
+ return _b - _a;
+ }
+ alias opDollar = length;
+ /// ditto
+ Range opSlice(size_t low, size_t high)
+ {
+ assert(
+ _a <= low && low <= high && high <= _b,
+ "Using out of bounds indexes on an Array"
+ );
+ return Range(_outer, _a + low, _a + high);
+ }
+ }
+
+ /**
+ * Property returning `true` if and only if the array has
+ * no elements.
+ *
+ * Complexity: $(BIGOH 1)
+ */
+ @property bool empty()
+ {
+ return !length;
+ }
+
+ /**
+ * Returns: A duplicate of the array.
+ *
+ * Complexity: $(BIGOH length).
+ */
+ @property Array dup()
+ {
+ Array result;
+ result.insertBack(this[]);
+ return result;
+ }
+
+ /**
+ * Returns the number of elements in the array.
+ *
+ * Complexity: $(BIGOH 1).
+ */
+ @property size_t length() const
+ {
+ return _store.refCountedStore.isInitialized ? _store._length : 0;
+ }
+ size_t opDollar() const
+ {
+ return length;
+ }
+
+ /**
+ * 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 _store.refCountedStore.isInitialized
+ ? cast(size_t) bitsPerWord * _store._backend.capacity
+ : 0;
+ }
+
+ /**
+ * 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)
+ {
+ import std.conv : to;
+ _store.refCountedStore.ensureInitialized();
+ _store._backend.reserve(to!size_t((e + bitsPerWord - 1) / bitsPerWord));
+ }
+
+ /**
+ * Returns: A range that iterates over all elements of the array in forward order.
+ *
+ * Complexity: $(BIGOH 1)
+ */
+ Range opSlice()
+ {
+ return Range(this, 0, length);
+ }
+
+
+ /**
+ * Returns: A range that iterates the array between two specified positions.
+ *
+ * Complexity: $(BIGOH 1)
+ */
+ Range opSlice(size_t a, size_t b)
+ {
+ enforce(a <= b && b <= length);
+ return Range(this, a, b);
+ }
+
+ /**
+ * Returns: The first element of the array.
+ *
+ * Precondition: `empty == false`
+ *
+ * Complexity: $(BIGOH 1)
+ *
+ * Throws: `Exception` if the array is empty.
+ */
+ @property bool front()
+ {
+ enforce(!empty);
+ return data.ptr[0] & 1;
+ }
+
+ /// Ditto
+ @property void front(bool value)
+ {
+ enforce(!empty);
+ if (value) data.ptr[0] |= 1;
+ else data.ptr[0] &= ~cast(size_t) 1;
+ }
+
+ /**
+ * 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)));
+ }
+
+ /// Ditto
+ @property void back(bool value)
+ {
+ enforce(!empty);
+ if (value)
+ {
+ data.back |= (cast(size_t) 1 << ((_store._length - 1) % bitsPerWord));
+ }
+ else
+ {
+ data.back &=
+ ~(cast(size_t) 1 << ((_store._length - 1) % bitsPerWord));
+ }
+ }
+
+ /**
+ * 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));
+ }
+
+ /// 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);
+ }
+
+ /// 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));
+ // 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);
+ }
+ }
+
+ /// Ditto
+ T moveAt(size_t i)
+ {
+ return this[i];
+ }
+
+ /**
+ * 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 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;
+ }
+
+ /**
+ * Forwards to `insertBack`.
+ */
+ Array!bool opOpAssign(string op, Stuff)(Stuff stuff)
+ if (op == "~")
+ {
+ static if (is(typeof(stuff[]))) insertBack(stuff[]);
+ else insertBack(stuff);
+ return this;
+ }
+
+ /**
+ * Removes all the elements from the array and releases allocated memory.
+ *
+ * Postcondition: `empty == true && capacity == 0`
+ *
+ * Complexity: $(BIGOH length)
+ */
+ void clear()
+ {
+ this = Array();
+ }
+
+ /**
+ * 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)
+ {
+ import std.conv : to;
+ _store.refCountedStore.ensureInitialized();
+ auto newDataLength =
+ to!size_t((newLength + bitsPerWord - 1) / bitsPerWord);
+ _store._backend.length = newDataLength;
+ _store._length = newLength;
+ }
+
+ /**
+ * 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()
+ {
+ auto result = back;
+ removeBack();
+ return result;
+ }
+
+ /// ditto
+ alias stableRemoveAny = removeAny;
+
+ /**
+ * 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))
+ {
+ _store.refCountedStore.ensureInitialized();
+ auto rem = _store._length % bitsPerWord;
+ if (rem)
+ {
+ // Fits within the current array
+ if (stuff)
+ {
+ data[$ - 1] |= (cast(size_t) 1 << rem);
+ }
+ else
+ {
+ data[$ - 1] &= ~(cast(size_t) 1 << rem);
+ }
+ }
+ else
+ {
+ // Need to add more data
+ _store._backend.insertBack(stuff);
+ }
+ ++_store._length;
+ return 1;
+ }
+
+ /// ditto
+ size_t insertBack(Stuff)(Stuff stuff)
+ if (isInputRange!Stuff && is(ElementType!Stuff : bool))
+ {
+ static if (!hasLength!Stuff) size_t result;
+ for (; !stuff.empty; stuff.popFront())
+ {
+ insertBack(stuff.front);
+ static if (!hasLength!Stuff) ++result;
+ }
+ static if (!hasLength!Stuff) return result;
+ else return stuff.length;
+ }
+
+ /// ditto
+ alias stableInsertBack = insertBack;
+
+ /// ditto
+ alias insert = insertBack;
+
+ /// ditto
+ alias stableInsert = insertBack;
+
+ /// ditto
+ alias linearInsert = insertBack;
+
+ /// ditto
+ alias stableLinearInsert = insertBack;
+
+ /**
+ * 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()
+ {
+ enforce(_store._length);
+ if (_store._length % bitsPerWord)
+ {
+ // Cool, just decrease the length
+ --_store._length;
+ }
+ else
+ {
+ // Reduce the allocated space
+ --_store._length;
+ _store._backend.length = _store._backend.length - 1;
+ }
+ }
+
+ /// ditto
+ alias stableRemoveBack = removeBack;
+
+ /**
+ * 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)
+ {
+ if (howMany >= length)
+ {
+ howMany = length;
+ clear();
+ }
+ else
+ {
+ length = length - howMany;
+ }
+ return howMany;
+ }
+
+ /// ditto
+ alias stableRemoveBack = removeBack;
+
+ /**
+ * 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.mutation : bringToFront;
+ // TODO: make this faster, it moves one bit at a time
+ immutable inserted = stableInsertBack(stuff);
+ immutable tailLength = length - inserted;
+ bringToFront(
+ this[r._a .. tailLength],
+ this[tailLength .. length]);
+ return inserted;
+ }
+
+ /// ditto
+ alias stableInsertBefore = insertBefore;
+
+ /// ditto
+ size_t insertAfter(Stuff)(Range r, Stuff stuff)
+ {
+ import std.algorithm.mutation : bringToFront;
+ // TODO: make this faster, it moves one bit at a time
+ immutable inserted = stableInsertBack(stuff);
+ immutable tailLength = length - inserted;
+ bringToFront(
+ this[r._b .. tailLength],
+ this[tailLength .. length]);
+ return inserted;
+ }
+
+ /// ditto
+ alias stableInsertAfter = insertAfter;
+
+ /// ditto
+ size_t replace(Stuff)(Range r, Stuff stuff)
+ if (is(Stuff : bool))
+ {
+ if (!r.empty)
+ {
+ // There is room
+ r.front = stuff;
+ r.popFront();
+ linearRemove(r);
+ }
+ else
+ {
+ // No room, must insert
+ insertBefore(r, stuff);
+ }
+ return 1;
+ }
+
+ /// ditto
+ alias stableReplace = replace;
+
+ /**
+ * 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.mutation : copy;
+ copy(this[r._b .. length], this[r._a .. length]);
+ length = length - r.length;
+ return this[r._a .. length];
+ }
+}
+
+@system unittest
+{
+ Array!bool a;
+ assert(a.empty);
+}
+
+@system unittest
+{
+ Array!bool arr;
+ arr.insert([false, false, false, false]);
+ assert(arr.front == false);
+ assert(arr.back == false);
+ assert(arr[1] == false);
+ auto slice = arr[];
+ slice = arr[0 .. $];
+ slice = slice[1 .. $];
+ slice.front = true;
+ slice.back = true;
+ slice[1] = true;
+ assert(slice.front == true);
+ assert(slice.back == true);
+ assert(slice[1] == true);
+ assert(slice.moveFront == true);
+ 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);
+}
+
+@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');
+}
+
+@system unittest
+{
+ Array!bool a;
+ assert(a.empty);
+ a.insertBack(false);
+ assert(!a.empty);
+}
+
+@system unittest
+{
+ Array!bool a;
+ assert(a.empty);
+ auto b = a.dup;
+ assert(b.empty);
+ a.insertBack(true);
+ assert(b.empty);
+}
+
+@system unittest
+{
+ import std.conv : to;
+ Array!bool a;
+ assert(a.length == 0);
+ a.insert(true);
+ assert(a.length == 1, to!string(a.length));
+}
+
+@system unittest
+{
+ import std.conv : to;
+ Array!bool a;
+ assert(a.capacity == 0);
+ foreach (i; 0 .. 100)
+ {
+ a.insert(true);
+ assert(a.capacity >= a.length, to!string(a.capacity));
+ }
+}
+
+@system unittest
+{
+ Array!bool a;
+ assert(a.capacity == 0);
+ a.reserve(15657);
+ assert(a.capacity >= 15657);
+ a.reserve(100);
+ assert(a.capacity >= 15657);
+}
+
+@system unittest
+{
+ Array!bool a;
+ a.insertBack([true, false, true, true]);
+ assert(a[0 .. 2].length == 2);
+}
+
+@system unittest
+{
+ Array!bool a;
+ a.insertBack([true, false, true, true]);
+ assert(a[].length == 4);
+}
+
+@system unittest
+{
+ Array!bool a;
+ a.insertBack([true, false, true, true]);
+ assert(a.front);
+ a.front = false;
+ assert(!a.front);
+}
+
+@system unittest
+{
+ Array!bool a;
+ a.insertBack([true, false, true, true]);
+ assert(a[].length == 4);
+}
+
+@system unittest
+{
+ Array!bool a;
+ a.insertBack([true, false, true, true]);
+ assert(a.back);
+ a.back = false;
+ assert(!a.back);
+}
+
+@system unittest
+{
+ Array!bool a;
+ a.insertBack([true, false, true, true]);
+ assert(a[0] && !a[1]);
+ a[0] &= a[1];
+ assert(!a[0]);
+}
+
+@system unittest
+{
+ 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]));
+}
+@system unittest
+{
+ import std.algorithm.comparison : equal;
+ Array!bool a;
+ a.insertBack([true, false, true, true]);
+ Array!bool b;
+ a.insertBack([false, true, false, true, true]);
+ a ~= b;
+ assert(equal(
+ a[],
+ [true, false, true, true, false, true, false, true, true]));
+}
+
+@system unittest
+{
+ Array!bool a;
+ a.insertBack([true, false, true, true]);
+ a.clear();
+ assert(a.capacity == 0);
+}
+
+@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);
+}
+
+@system unittest
+{
+ Array!bool a;
+ a.length = 1057;
+ assert(!a.removeAny());
+ assert(a.length == 1056);
+ foreach (e; a)
+ {
+ assert(!e);
+ }
+}
+
+@system unittest
+{
+ Array!bool a;
+ for (int i = 0; i < 100; ++i)
+ a.insertBack(true);
+ foreach (e; a)
+ assert(e);
+}
+
+@system unittest
+{
+ Array!bool a;
+ a.length = 1057;
+ assert(a.removeBack(1000) == 1000);
+ assert(a.length == 57);
+ foreach (e; a)
+ {
+ assert(!e);
+ }
+}
+
+@system unittest
+{
+ import std.conv : to;
+ Array!bool a;
+ version (bugxxxx)
+ {
+ a._store.refCountedDebug = true;
+ }
+ a.insertBefore(a[], true);
+ 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]));
+}
+
+@system unittest
+{
+ import std.conv : to;
+ Array!bool a;
+ a.length = 10;
+ a.insertAfter(a[0 .. 5], true);
+ assert(a.length == 11, to!string(a.length));
+ assert(a[5]);
+}
+@system unittest
+{
+ alias V3 = int[3];
+ V3 v = [1, 2, 3];
+ Array!V3 arr;
+ arr ~= v;
+ assert(arr[0] == [1, 2, 3]);
+}
+@system unittest
+{
+ alias V3 = int[3];
+ V3[2] v = [[1, 2, 3], [4, 5, 6]];
+ Array!V3 arr;
+ arr ~= v;
+ assert(arr[0] == [1, 2, 3]);
+ assert(arr[1] == [4, 5, 6]);
+}
diff --git a/libphobos/src/std/container/binaryheap.d b/libphobos/src/std/container/binaryheap.d
new file mode 100644
index 0000000..4adf604
--- /dev/null
+++ b/libphobos/src/std/container/binaryheap.d
@@ -0,0 +1,595 @@
+/**
+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 $(MREF std, container).
+
+Source: $(PHOBOSSRC std/container/_binaryheap.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)
+*/
+module std.container.binaryheap;
+
+import std.range.primitives;
+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 $(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
+container as the $(I store) of the heap.
+
+The binary heap induces structure over the underlying store such that
+accessing the largest element (by using the $(D front) property) is a
+$(BIGOH 1) operation and extracting it (by using the $(D
+removeFront()) method) is done fast in $(BIGOH log n) time.
+
+If $(D less) is the less-than operator, which is the default option,
+then $(D BinaryHeap) defines a so-called max-heap that optimizes
+extraction of the $(I largest) elements. To define a min-heap,
+instantiate BinaryHeap with $(D "a > b") as its predicate.
+
+Simply extracting elements from a $(D BinaryHeap) container is
+tantamount to lazily fetching elements of $(D Store) in descending
+order. Extracting elements from the $(D BinaryHeap) to completion
+leaves the underlying store sorted in ascending order but, again,
+yields elements in descending order.
+
+If $(D Store) is a range, the $(D BinaryHeap) cannot grow beyond the
+size of that range. If $(D Store) is a container that supports $(D
+insertBack), the $(D BinaryHeap) may grow by adding elements to the
+container.
+ */
+struct BinaryHeap(Store, alias less = "a < b")
+if (isRandomAccessRange!(Store) || isRandomAccessRange!(typeof(Store.init[])))
+{
+ import std.algorithm.comparison : min;
+ import std.algorithm.mutation : move, swapAt;
+ import std.algorithm.sorting : HeapOps;
+ import std.exception : enforce;
+ import std.functional : binaryFun;
+ 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:
+
+ // The payload includes the support store and the effective length
+ private static struct Data
+ {
+ Store _store;
+ size_t _length;
+ }
+ private RefCounted!(Data, RefCountedAutoInitialize.no) _payload;
+ // Comparison predicate
+ private alias comp = binaryFun!(less);
+ // Convenience accessors
+ private @property ref Store _store()
+ {
+ assert(_payload.refCountedStore.isInitialized);
+ return _payload._store;
+ }
+ private @property ref size_t _length()
+ {
+ assert(_payload.refCountedStore.isInitialized);
+ return _payload._length;
+ }
+
+ // Asserts that the heap property is respected.
+ private void assertValid()
+ {
+ debug
+ {
+ import std.conv : to;
+ if (!_payload.refCountedStore.isInitialized) return;
+ if (_length < 2) return;
+ for (size_t n = _length - 1; n >= 1; --n)
+ {
+ auto parentIdx = (n - 1) / 2;
+ assert(!comp(_store[parentIdx], _store[n]), to!string(n));
+ }
+ }
+ }
+
+ // @@@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 = store[].moveFront();
+ auto t2 = store[].moveBack();
+ store.front = move(t2);
+ store.back = move(t1);
+ percolate(store[], 0, store.length - 1);
+ }
+
+public:
+
+ /**
+ Converts the store $(D s) into a heap. If $(D initialSize) is
+ specified, only the first $(D initialSize) elements in $(D s)
+ are transformed into a heap, after which the heap can grow up
+ to $(D r.length) (if $(D Store) is a range) or indefinitely (if
+ $(D Store) is a container with $(D insertBack)). Performs
+ $(BIGOH min(r.length, initialSize)) evaluations of $(D less).
+ */
+ this(Store s, size_t initialSize = size_t.max)
+ {
+ acquire(s, initialSize);
+ }
+
+/**
+Takes ownership of a store. After this, manipulating $(D s) may make
+the heap work incorrectly.
+ */
+ void acquire(Store s, size_t initialSize = size_t.max)
+ {
+ _payload.refCountedStore.ensureInitialized();
+ _store = move(s);
+ _length = min(_store.length, initialSize);
+ if (_length < 2) return;
+ buildHeap(_store[]);
+ assertValid();
+ }
+
+/**
+Takes ownership of a store assuming it already was organized as a
+heap.
+ */
+ void assume(Store s, size_t initialSize = size_t.max)
+ {
+ _payload.refCountedStore.ensureInitialized();
+ _store = s;
+ _length = min(_store.length, initialSize);
+ assertValid();
+ }
+
+/**
+Clears the heap. Returns the portion of the store from $(D 0) up to
+$(D length), which satisfies the $(LINK2 https://en.wikipedia.org/wiki/Heap_(data_structure),
+heap property).
+ */
+ auto release()
+ {
+ if (!_payload.refCountedStore.isInitialized)
+ {
+ return typeof(_store[0 .. _length]).init;
+ }
+ assertValid();
+ auto result = _store[0 .. _length];
+ _payload = _payload.init;
+ return result;
+ }
+
+/**
+Returns $(D true) if the heap is _empty, $(D false) otherwise.
+ */
+ @property bool empty()
+ {
+ return !length;
+ }
+
+/**
+Returns a duplicate of the heap. The $(D dup) method is available only if the
+underlying store supports it.
+ */
+ static if (is(typeof((Store s) { return s.dup; }(Store.init)) == Store))
+ {
+ @property BinaryHeap dup()
+ {
+ BinaryHeap result;
+ if (!_payload.refCountedStore.isInitialized) return result;
+ result.assume(_store.dup, length);
+ return result;
+ }
+ }
+
+/**
+Returns the _length of the heap.
+ */
+ @property size_t length()
+ {
+ return _payload.refCountedStore.isInitialized ? _length : 0;
+ }
+
+/**
+Returns the _capacity of the heap, which is the length of the
+underlying store (if the store is a range) or the _capacity of the
+underlying store (if the store is a container).
+ */
+ @property size_t capacity()
+ {
+ if (!_payload.refCountedStore.isInitialized) return 0;
+ static if (is(typeof(_store.capacity) : size_t))
+ {
+ return _store.capacity;
+ }
+ else
+ {
+ return _store.length;
+ }
+ }
+
+/**
+Returns a copy of the _front of the heap, which is the largest element
+according to $(D less).
+ */
+ @property ElementType!Store front()
+ {
+ enforce(!empty, "Cannot call front on an empty heap.");
+ return _store.front;
+ }
+
+/**
+Clears the heap by detaching it from the underlying store.
+ */
+ void clear()
+ {
+ _payload = _payload.init;
+ }
+
+/**
+Inserts $(D value) into the store. If the underlying store is a range
+and $(D length == capacity), throws an exception.
+ */
+ size_t insert(ElementType!Store value)
+ {
+ static if (is(typeof(_store.insertBack(value))))
+ {
+ _payload.refCountedStore.ensureInitialized();
+ if (length == _store.length)
+ {
+ // reallocate
+ _store.insertBack(value);
+ }
+ else
+ {
+ // no reallocation
+ _store[_length] = value;
+ }
+ }
+ else
+ {
+ 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
+ for (size_t n = _length; n; )
+ {
+ auto parentIdx = (n - 1) / 2;
+ if (!comp(_store[parentIdx], _store[n])) break; // done!
+ // must swap and continue
+ _store.swapAt(parentIdx, n);
+ n = parentIdx;
+ }
+ ++_length;
+ debug(BinaryHeap) assertValid();
+ return 1;
+ }
+
+/**
+Removes the largest element from the heap.
+ */
+ void removeFront()
+ {
+ enforce(!empty, "Cannot call removeFront on an empty heap.");
+ if (_length > 1)
+ {
+ auto t1 = _store[].moveFront();
+ auto t2 = _store[].moveAt(_length - 1);
+ _store.front = move(t2);
+ _store[_length - 1] = move(t1);
+ }
+ --_length;
+ percolate(_store[], 0, _length);
+ }
+
+ /// ditto
+ alias popFront = removeFront;
+
+/**
+Removes the largest element from the heap and returns a copy of
+it. The element still resides in the heap's store. For performance
+reasons you may want to use $(D removeFront) with heaps of objects
+that are expensive to copy.
+ */
+ ElementType!Store removeAny()
+ {
+ removeFront();
+ return _store[_length];
+ }
+
+/**
+Replaces the largest element in the store with $(D value).
+ */
+ void replaceFront(ElementType!Store value)
+ {
+ // must replace the top
+ assert(!empty, "Cannot call replaceFront on an empty heap.");
+ _store.front = value;
+ percolate(_store[], 0, _length);
+ debug(BinaryHeap) assertValid();
+ }
+
+/**
+If the heap has room to grow, inserts $(D value) into the store and
+returns $(D true). Otherwise, if $(D less(value, front)), calls $(D
+replaceFront(value)) and returns again $(D true). Otherwise, leaves
+the heap unaffected and returns $(D false). This method is useful in
+scenarios where the smallest $(D k) elements of a set of candidates
+must be collected.
+ */
+ bool conditionalInsert(ElementType!Store value)
+ {
+ _payload.refCountedStore.ensureInitialized();
+ if (_length < _store.length)
+ {
+ insert(value);
+ return true;
+ }
+
+ assert(!_store.empty, "Cannot replace front of an empty heap.");
+ if (!comp(value, _store.front)) return false; // value >= largest
+ _store.front = value;
+
+ 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
+@system unittest
+{
+ import std.algorithm.comparison : equal;
+ int[] a = [ 4, 1, 3, 2, 16, 9, 10, 14, 8, 7 ];
+ auto h = heapify(a);
+ // largest element
+ assert(h.front == 16);
+ // a has the heap property
+ assert(equal(a, [ 16, 14, 10, 8, 7, 9, 3, 2, 4, 1 ]));
+}
+
+/// $(D BinaryHeap) implements the standard input range interface, allowing
+/// lazy iteration of the underlying range in descending order.
+@system unittest
+{
+ 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);
+ assert(top5.equal([16, 14, 10, 9, 8]));
+}
+
+/**
+Convenience function that returns a $(D BinaryHeap!Store) object
+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);
+}
+
+///
+@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 ];
+ auto h = heapify(a);
+ h = heapify!"a < b"(a);
+ assert(h.front == 16);
+ assert(a == [ 16, 14, 10, 8, 7, 9, 3, 2, 4, 1 ]);
+ auto witness = [ 16, 14, 10, 9, 8, 7, 4, 3, 2, 1 ];
+ for (; !h.empty; h.removeFront(), witness.popFront())
+ {
+ assert(!witness.empty);
+ assert(witness.front == h.front);
+ }
+ assert(witness.empty);
+ }
+ {
+ int[] a = [ 4, 1, 3, 2, 16, 9, 10, 14, 8, 7 ];
+ int[] b = new int[a.length];
+ BinaryHeap!(int[]) h = BinaryHeap!(int[])(b, 0);
+ foreach (e; a)
+ {
+ h.insert(e);
+ }
+ assert(b == [ 16, 14, 10, 8, 7, 3, 9, 1, 4, 2 ], to!string(b));
+ }
+}
+
+@system unittest
+{
+ // Test range interface.
+ 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.algorithm.comparison : equal;
+ import std.internal.test.dummyrange;
+
+ 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/libphobos/src/std/container/dlist.d b/libphobos/src/std/container/dlist.d
new file mode 100644
index 0000000..633371f
--- /dev/null
+++ b/libphobos/src/std/container/dlist.d
@@ -0,0 +1,1039 @@
+/**
+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 $(MREF std, container).
+
+Source: $(PHOBOSSRC std/container/_dlist.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.dlist;
+
+///
+@safe unittest
+{
+ import std.algorithm.comparison : equal;
+ import std.container : DList;
+
+ 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);
+
+ // DList.Range can be used to remove elements from the list it spans
+ auto nl = DList!int([1, 2, 3, 4, 5]);
+ for (auto rn = nl[]; !rn.empty;)
+ if (rn.front % 2 == 0)
+ nl.popFirstOf(rn);
+ else
+ rn.popFront();
+ assert(equal(nl[], [1, 3, 5]));
+ auto rs = nl[];
+ rs.popFront();
+ nl.remove(rs);
+ assert(equal(nl[], [1]));
+}
+
+import std.range.primitives;
+import std.traits;
+
+public import std.container.util;
+
+/+
+A DList Node without payload. Used to handle the sentinel node (henceforth "sentinode").
+
+Also used for parts of the code that don't depend on the payload type.
+ +/
+private struct BaseNode
+{
+ private BaseNode* _prev = null;
+ private BaseNode* _next = null;
+
+ /+
+ Gets the payload associated with this node.
+ This is trusted because all nodes are associated with a payload, even
+ the sentinel node.
+ It is also not possible to mix Nodes in DLists of different types.
+ This function is implemented as a member function here, as UFCS does not
+ work with pointers.
+ +/
+ ref inout(T) getPayload(T)() inout @trusted
+ {
+ return (cast(inout(DList!T.PayNode)*)&this)._payload;
+ }
+
+ // Helper: Given nodes p and n, connects them.
+ static void connect(BaseNode* p, BaseNode* n) @safe nothrow pure
+ {
+ p._next = n;
+ n._prev = p;
+ }
+}
+
+/+
+The base DList Range. Contains Range primitives that don't depend on payload type.
+ +/
+private struct DRange
+{
+ @safe unittest
+ {
+ static assert(isBidirectionalRange!DRange);
+ static assert(is(ElementType!DRange == BaseNode*));
+ }
+
+nothrow @safe pure:
+ private BaseNode* _first;
+ private BaseNode* _last;
+
+ private this(BaseNode* first, BaseNode* last)
+ {
+ assert((first is null) == (last is null), "Dlist.Range.this: Invalid arguments");
+ _first = first;
+ _last = last;
+ }
+ private this(BaseNode* n)
+ {
+ this(n, n);
+ }
+
+ @property
+ bool empty() const
+ {
+ assert((_first is null) == (_last is null), "DList.Range: Invalidated state");
+ return !_first;
+ }
+
+ @property BaseNode* front()
+ {
+ assert(!empty, "DList.Range.front: Range is empty");
+ return _first;
+ }
+
+ void popFront()
+ {
+ assert(!empty, "DList.Range.popFront: Range is empty");
+ if (_first is _last)
+ {
+ _first = _last = null;
+ }
+ else
+ {
+ assert(_first._next && _first is _first._next._prev, "DList.Range: Invalidated state");
+ _first = _first._next;
+ }
+ }
+
+ @property BaseNode* back()
+ {
+ assert(!empty, "DList.Range.front: Range is empty");
+ return _last;
+ }
+
+ void popBack()
+ {
+ assert(!empty, "DList.Range.popBack: Range is empty");
+ if (_first is _last)
+ {
+ _first = _last = null;
+ }
+ else
+ {
+ assert(_last._prev && _last is _last._prev._next, "DList.Range: Invalidated state");
+ _last = _last._prev;
+ }
+ }
+
+ /// Forward range primitive.
+ @property DRange save() { return this; }
+}
+
+/**
+Implements a doubly-linked list.
+
+$(D DList) uses reference semantics.
+ */
+struct DList(T)
+{
+ import std.range : Take;
+
+ /*
+ A Node with a Payload. A PayNode.
+ */
+ struct PayNode
+ {
+ BaseNode _base;
+ alias _base this;
+
+ T _payload = T.init;
+
+ inout(BaseNode)* asBaseNode() inout @trusted
+ {
+ return &_base;
+ }
+ }
+
+ //The sentinel node
+ private BaseNode* _root;
+
+ private
+ {
+ //Construct as new PayNode, and returns it as a BaseNode.
+ static BaseNode* createNode(Stuff)(auto ref Stuff arg, BaseNode* prev = null, BaseNode* next = null)
+ {
+ return (new PayNode(BaseNode(prev, next), arg)).asBaseNode();
+ }
+
+ void initialize() nothrow @safe pure
+ {
+ if (_root) return;
+ //Note: We allocate a PayNode for safety reasons.
+ _root = (new PayNode()).asBaseNode();
+ _root._next = _root._prev = _root;
+ }
+ ref inout(BaseNode*) _first() @property @safe nothrow pure inout
+ {
+ assert(_root);
+ return _root._next;
+ }
+ ref inout(BaseNode*) _last() @property @safe nothrow pure inout
+ {
+ assert(_root);
+ return _root._prev;
+ }
+ } //end private
+
+/**
+Constructor taking a number of nodes
+ */
+ this(U)(U[] values...) if (isImplicitlyConvertible!(U, T))
+ {
+ insertBack(values);
+ }
+
+/**
+Constructor taking an input range
+ */
+ this(Stuff)(Stuff stuff)
+ if (isInputRange!Stuff && isImplicitlyConvertible!(ElementType!Stuff, T))
+ {
+ insertBack(stuff);
+ }
+
+/**
+Comparison for equality.
+
+Complexity: $(BIGOH min(n, n1)) where $(D n1) is the number of
+elements in $(D rhs).
+ */
+ bool opEquals()(ref const DList rhs) const
+ if (is(typeof(front == front)))
+ {
+ const lhs = this;
+ const lroot = lhs._root;
+ const rroot = rhs._root;
+
+ if (lroot is rroot) return true;
+ if (lroot is null) return rroot is rroot._next;
+ if (rroot is null) return lroot is lroot._next;
+
+ const(BaseNode)* pl = lhs._first;
+ const(BaseNode)* pr = rhs._first;
+ while (true)
+ {
+ if (pl is lroot) return pr is rroot;
+ if (pr is rroot) return false;
+
+ // !== because of NaN
+ if (!(pl.getPayload!T() == pr.getPayload!T())) return false;
+
+ pl = pl._next;
+ pr = pr._next;
+ }
+ }
+
+ /**
+ Defines the container's primary range, which embodies a bidirectional range.
+ */
+ struct Range
+ {
+ static assert(isBidirectionalRange!Range);
+
+ DRange _base;
+ alias _base this;
+
+ private this(BaseNode* first, BaseNode* last)
+ {
+ _base = DRange(first, last);
+ }
+ private this(BaseNode* n)
+ {
+ this(n, n);
+ }
+
+ @property ref T front()
+ {
+ return _base.front.getPayload!T();
+ }
+
+ @property ref T back()
+ {
+ return _base.back.getPayload!T();
+ }
+
+ //Note: shadows base DRange.save.
+ //Necessary for static covariance.
+ @property Range save() { return this; }
+ }
+
+/**
+Property returning $(D true) if and only if the container has no
+elements.
+
+Complexity: $(BIGOH 1)
+ */
+ bool empty() @property const nothrow
+ {
+ return _root is null || _root is _first;
+ }
+
+/**
+Removes all contents from the $(D DList).
+
+Postcondition: $(D empty)
+
+Complexity: $(BIGOH 1)
+ */
+ void clear()
+ {
+ //remove actual elements.
+ remove(this[]);
+ }
+
+/**
+Duplicates the container. The elements themselves are not transitively
+duplicated.
+
+Complexity: $(BIGOH n).
+ */
+ @property DList dup()
+ {
+ return DList(this[]);
+ }
+
+/**
+Returns a range that iterates over all elements of the container, in
+forward order.
+
+Complexity: $(BIGOH 1)
+ */
+ Range opSlice()
+ {
+ if (empty)
+ return Range(null, null);
+ else
+ return Range(_first, _last);
+ }
+
+/**
+Forward to $(D opSlice().front).
+
+Complexity: $(BIGOH 1)
+ */
+ @property ref inout(T) front() inout
+ {
+ assert(!empty, "DList.front: List is empty");
+ return _first.getPayload!T();
+ }
+
+/**
+Forward to $(D opSlice().back).
+
+Complexity: $(BIGOH 1)
+ */
+ @property ref inout(T) back() inout
+ {
+ assert(!empty, "DList.back: List is empty");
+ return _last.getPayload!T();
+ }
+
+/+ ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ +/
+/+ BEGIN CONCAT FUNCTIONS HERE +/
+/+ ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ +/
+
+/**
+Returns a new $(D DList) that's the concatenation of $(D this) and its
+argument $(D rhs).
+ */
+ DList opBinary(string op, Stuff)(Stuff rhs)
+ if (op == "~" && is(typeof(insertBack(rhs))))
+ {
+ auto ret = this.dup;
+ ret.insertBack(rhs);
+ return ret;
+ }
+
+/**
+Returns a new $(D DList) that's the concatenation of the argument $(D lhs)
+and $(D this).
+ */
+ DList opBinaryRight(string op, Stuff)(Stuff lhs)
+ if (op == "~" && is(typeof(insertFront(lhs))))
+ {
+ auto ret = this.dup;
+ ret.insertFront(lhs);
+ return ret;
+ }
+
+/**
+Appends the contents of the argument $(D rhs) into $(D this).
+ */
+ DList opOpAssign(string op, Stuff)(Stuff rhs)
+ if (op == "~" && is(typeof(insertBack(rhs))))
+ {
+ insertBack(rhs);
+ return this;
+ }
+
+/+ ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ +/
+/+ BEGIN INSERT FUNCTIONS HERE +/
+/+ ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ +/
+
+/**
+Inserts $(D stuff) to the front/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 log(n))
+ */
+ size_t insertFront(Stuff)(Stuff stuff)
+ {
+ initialize();
+ return insertAfterNode(_root, stuff);
+ }
+
+ /// ditto
+ size_t insertBack(Stuff)(Stuff stuff)
+ {
+ initialize();
+ return insertBeforeNode(_root, stuff);
+ }
+
+ /// ditto
+ alias insert = insertBack;
+
+ /// ditto
+ alias stableInsert = insert;
+
+ /// ditto
+ alias stableInsertFront = insertFront;
+
+ /// ditto
+ alias stableInsertBack = insertBack;
+
+/**
+Inserts $(D stuff) after range $(D r), which must be a non-empty 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 k + m), where $(D k) is the number of elements in
+$(D r) and $(D m) is the length of $(D stuff).
+ */
+ size_t insertBefore(Stuff)(Range r, Stuff stuff)
+ {
+ if (r._first)
+ return insertBeforeNode(r._first, stuff);
+ else
+ {
+ initialize();
+ return insertAfterNode(_root, stuff);
+ }
+ }
+
+ /// ditto
+ alias stableInsertBefore = insertBefore;
+
+ /// ditto
+ size_t insertAfter(Stuff)(Range r, Stuff stuff)
+ {
+ if (r._last)
+ return insertAfterNode(r._last, stuff);
+ else
+ {
+ initialize();
+ return insertBeforeNode(_root, stuff);
+ }
+ }
+
+ /// ditto
+ alias stableInsertAfter = insertAfter;
+
+/+ ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ +/
+/+ BEGIN REMOVE FUNCTIONS HERE +/
+/+ ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ +/
+
+/**
+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 1).
+ */
+ T removeAny()
+ {
+ import std.algorithm.mutation : move;
+
+ assert(!empty, "DList.removeAny: List is empty");
+ auto result = move(back);
+ removeBack();
+ return result;
+ }
+ /// ditto
+ alias stableRemoveAny = removeAny;
+
+/**
+Removes the value at the front/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 1).
+ */
+ void removeFront()
+ {
+ assert(!empty, "DList.removeFront: List is empty");
+ assert(_root is _first._prev, "DList: Inconsistent state");
+ BaseNode.connect(_root, _first._next);
+ }
+
+ /// ditto
+ alias stableRemoveFront = removeFront;
+
+ /// ditto
+ void removeBack()
+ {
+ assert(!empty, "DList.removeBack: List is empty");
+ assert(_last._next is _root, "DList: Inconsistent state");
+ BaseNode.connect(_last._prev, _root);
+ }
+
+ /// 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).
+ */
+ size_t removeFront(size_t howMany)
+ {
+ if (!_root) return 0;
+ size_t result;
+ auto p = _first;
+ while (p !is _root && result < howMany)
+ {
+ p = p._next;
+ ++result;
+ }
+ BaseNode.connect(_root, p);
+ return result;
+ }
+
+ /// ditto
+ alias stableRemoveFront = removeFront;
+
+ /// ditto
+ size_t removeBack(size_t howMany)
+ {
+ if (!_root) return 0;
+ size_t result;
+ auto p = _last;
+ while (p !is _root && result < howMany)
+ {
+ p = p._prev;
+ ++result;
+ }
+ BaseNode.connect(p, _root);
+ return result;
+ }
+
+ /// ditto
+ alias stableRemoveBack = removeBack;
+
+/**
+Removes all elements belonging to $(D r), which must be a range
+obtained originally from this container.
+
+Returns: A range spanning the remaining elements in the container that
+initially were right after $(D r).
+
+Complexity: $(BIGOH 1)
+ */
+ Range remove(Range r)
+ {
+ if (r.empty)
+ return r;
+
+ assert(_root !is null, "Cannot remove from an un-initialized List");
+ assert(r._first, "Remove: Range is empty");
+
+ BaseNode.connect(r._first._prev, r._last._next);
+ auto after = r._last._next;
+ if (after is _root)
+ return Range(null, null);
+ else
+ return Range(after, _last);
+ }
+
+ /// ditto
+ Range linearRemove(Range r)
+ {
+ return remove(r);
+ }
+
+/**
+Removes first element of $(D r), wich must be a range obtained originally
+from this container, from both DList instance and range $(D r).
+
+Compexity: $(BIGOH 1)
+ */
+ void popFirstOf(ref Range r)
+ {
+ assert(_root !is null, "Cannot remove from an un-initialized List");
+ assert(r._first, "popFirstOf: Range is empty");
+ auto prev = r._first._prev;
+ auto next = r._first._next;
+ r.popFront();
+ BaseNode.connect(prev, next);
+ }
+
+/**
+Removes last element of $(D r), wich must be a range obtained originally
+from this container, from both DList instance and range $(D r).
+
+Compexity: $(BIGOH 1)
+ */
+ void popLastOf(ref Range r)
+ {
+ assert(_root !is null, "Cannot remove from an un-initialized List");
+ assert(r._first, "popLastOf: Range is empty");
+ auto prev = r._last._prev;
+ auto next = r._last._next;
+ r.popBack();
+ BaseNode.connect(prev, next);
+ }
+
+/**
+$(D linearRemove) functions as $(D remove), but also accepts ranges that are
+result the of a $(D take) operation. This is a convenient way to remove a
+fixed amount of elements from the range.
+
+Complexity: $(BIGOH r.walkLength)
+ */
+ Range linearRemove(Take!Range r)
+ {
+ assert(_root !is null, "Cannot remove from an un-initialized List");
+ assert(r.source._first, "Remove: Range is empty");
+
+ BaseNode* first = r.source._first;
+ BaseNode* last = null;
+ do
+ {
+ last = r.source._first;
+ r.popFront();
+ } while ( !r.empty );
+
+ return remove(Range(first, last));
+ }
+
+ /// ditto
+ alias stableRemove = remove;
+ /// ditto
+ alias stableLinearRemove = linearRemove;
+
+private:
+
+ // Helper: Inserts stuff before the node n.
+ size_t insertBeforeNode(Stuff)(BaseNode* n, ref Stuff stuff)
+ if (isImplicitlyConvertible!(Stuff, T))
+ {
+ auto p = createNode(stuff, n._prev, n);
+ n._prev._next = p;
+ n._prev = p;
+ return 1;
+ }
+ // ditto
+ size_t insertBeforeNode(Stuff)(BaseNode* n, ref Stuff stuff)
+ if (isInputRange!Stuff && isImplicitlyConvertible!(ElementType!Stuff, T))
+ {
+ if (stuff.empty) return 0;
+ size_t result;
+ Range r = createRange(stuff, result);
+ BaseNode.connect(n._prev, r._first);
+ BaseNode.connect(r._last, n);
+ return result;
+ }
+
+ // Helper: Inserts stuff after the node n.
+ size_t insertAfterNode(Stuff)(BaseNode* n, ref Stuff stuff)
+ if (isImplicitlyConvertible!(Stuff, T))
+ {
+ auto p = createNode(stuff, n, n._next);
+ n._next._prev = p;
+ n._next = p;
+ return 1;
+ }
+ // ditto
+ size_t insertAfterNode(Stuff)(BaseNode* n, ref Stuff stuff)
+ if (isInputRange!Stuff && isImplicitlyConvertible!(ElementType!Stuff, T))
+ {
+ if (stuff.empty) return 0;
+ size_t result;
+ Range r = createRange(stuff, result);
+ BaseNode.connect(r._last, n._next);
+ BaseNode.connect(n, r._first);
+ return result;
+ }
+
+ // Helper: Creates a chain of nodes from the range stuff.
+ Range createRange(Stuff)(ref Stuff stuff, ref size_t result)
+ {
+ BaseNode* first = createNode(stuff.front);
+ BaseNode* last = first;
+ ++result;
+ for ( stuff.popFront() ; !stuff.empty ; stuff.popFront() )
+ {
+ auto p = createNode(stuff.front, last);
+ last = last._next = p;
+ ++result;
+ }
+ return Range(first, last);
+ }
+}
+
+@safe unittest
+{
+ import std.algorithm.comparison : equal;
+
+ //Tests construction signatures
+ alias IntList = DList!int;
+ auto a0 = IntList();
+ auto a1 = IntList(0);
+ auto a2 = IntList(0, 1);
+ auto a3 = IntList([0]);
+ auto a4 = IntList([0, 1]);
+
+ assert(a0[].empty);
+ assert(equal(a1[], [0]));
+ assert(equal(a2[], [0, 1]));
+ assert(equal(a3[], [0]));
+ assert(equal(a4[], [0, 1]));
+}
+
+@safe unittest
+{
+ import std.algorithm.comparison : equal;
+
+ alias IntList = DList!int;
+ IntList list = IntList([0,1,2,3]);
+ assert(equal(list[],[0,1,2,3]));
+ list.insertBack([4,5,6,7]);
+ assert(equal(list[],[0,1,2,3,4,5,6,7]));
+
+ list = IntList();
+ list.insertFront([0,1,2,3]);
+ assert(equal(list[],[0,1,2,3]));
+ list.insertFront([4,5,6,7]);
+ assert(equal(list[],[4,5,6,7,0,1,2,3]));
+}
+
+@safe unittest
+{
+ 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())
+ {
+ int item = range.front;
+ if (item == 2)
+ {
+ list.stableLinearRemove(take(range, 1));
+ break;
+ }
+ }
+ assert(equal(list[],[0,1,3]));
+
+ list = IntList([0,1,2,3]);
+ range = list[];
+ for ( ; !range.empty; range.popFront())
+ {
+ int item = range.front;
+ if (item == 2)
+ {
+ list.stableLinearRemove(take(range,2));
+ break;
+ }
+ }
+ assert(equal(list[],[0,1]));
+
+ list = IntList([0,1,2,3]);
+ range = list[];
+ for ( ; !range.empty; range.popFront())
+ {
+ int item = range.front;
+ if (item == 0)
+ {
+ list.stableLinearRemove(take(range,2));
+ break;
+ }
+ }
+ assert(equal(list[],[2,3]));
+
+ list = IntList([0,1,2,3]);
+ range = list[];
+ for ( ; !range.empty; range.popFront())
+ {
+ int item = range.front;
+ if (item == 1)
+ {
+ list.stableLinearRemove(take(range,2));
+ break;
+ }
+ }
+ assert(equal(list[],[0,3]));
+}
+
+@safe unittest
+{
+ import std.algorithm.comparison : equal;
+
+ auto dl = DList!int([1, 2, 3, 4, 5]);
+ auto r = dl[];
+ r.popFront();
+ dl.popFirstOf(r);
+ assert(equal(dl[], [1, 3, 4, 5]));
+ assert(equal(r, [3, 4, 5]));
+ r.popBack();
+ dl.popLastOf(r);
+ assert(equal(dl[], [1, 3, 5]));
+ assert(equal(r, [3]));
+ dl = DList!int([0]);
+ r = dl[];
+ dl.popFirstOf(r);
+ assert(dl.empty);
+ dl = DList!int([0]);
+ r = dl[];
+ dl.popLastOf(r);
+ assert(dl.empty);
+}
+
+@safe unittest
+{
+ import std.algorithm.comparison : equal;
+
+ auto dl = DList!string(["a", "b", "d"]);
+ dl.insertAfter(dl[], "e"); // insert at the end
+ assert(equal(dl[], ["a", "b", "d", "e"]));
+ auto dlr = dl[];
+ dlr.popBack(); dlr.popBack();
+ dl.insertAfter(dlr, "c"); // insert after "b"
+ assert(equal(dl[], ["a", "b", "c", "d", "e"]));
+}
+
+@safe unittest
+{
+ import std.algorithm.comparison : equal;
+
+ auto dl = DList!string(["a", "b", "d"]);
+ dl.insertBefore(dl[], "e"); // insert at the front
+ assert(equal(dl[], ["e", "a", "b", "d"]));
+ auto dlr = dl[];
+ dlr.popFront(); dlr.popFront();
+ dl.insertBefore(dlr, "c"); // insert before "b"
+ assert(equal(dl[], ["e", "a", "c", "b", "d"]));
+}
+
+@safe unittest
+{
+ auto d = DList!int([1, 2, 3]);
+ d.front = 5; //test frontAssign
+ assert(d.front == 5);
+ auto r = d[];
+ r.back = 1;
+ assert(r.back == 1);
+}
+
+// Issue 8895
+@safe unittest
+{
+ auto a = make!(DList!int)(1,2,3,4);
+ auto b = make!(DList!int)(1,2,3,4);
+ auto c = make!(DList!int)(1,2,3,5);
+ auto d = make!(DList!int)(1,2,3,4,5);
+ assert(a == b); // this better terminate!
+ assert(!(a == c));
+ assert(!(a == d));
+}
+
+@safe unittest
+{
+ auto d = DList!int([1, 2, 3]);
+ d.front = 5; //test frontAssign
+ assert(d.front == 5);
+ auto r = d[];
+ r.back = 1;
+ assert(r.back == 1);
+}
+
+@safe unittest
+{
+ auto a = DList!int();
+ assert(a.removeFront(10) == 0);
+ a.insert([1, 2, 3]);
+ assert(a.removeFront(10) == 3);
+ assert(a[].empty);
+}
+
+@safe unittest
+{
+ import std.algorithm.comparison : equal;
+
+ //Verify all flavors of ~
+ auto a = DList!int();
+ auto b = DList!int();
+ auto c = DList!int([1, 2, 3]);
+ auto d = DList!int([4, 5, 6]);
+
+ assert((a ~ b[])[].empty);
+ assert((c ~ d[])[].equal([1, 2, 3, 4, 5, 6]));
+ assert(c[].equal([1, 2, 3]));
+ assert(d[].equal([4, 5, 6]));
+
+ assert((c[] ~ d)[].equal([1, 2, 3, 4, 5, 6]));
+ assert(c[].equal([1, 2, 3]));
+ assert(d[].equal([4, 5, 6]));
+
+ a~=c[];
+ assert(a[].equal([1, 2, 3]));
+ assert(c[].equal([1, 2, 3]));
+
+ a~=d[];
+ assert(a[].equal([1, 2, 3, 4, 5, 6]));
+ assert(d[].equal([4, 5, 6]));
+
+ a~=[7, 8, 9];
+ assert(a[].equal([1, 2, 3, 4, 5, 6, 7, 8, 9]));
+
+ //trick test:
+ auto r = c[];
+ c.removeFront();
+ c.removeBack();
+}
+
+@safe unittest
+{
+ import std.algorithm.comparison : equal;
+
+ //8905
+ auto a = DList!int([1, 2, 3, 4]);
+ auto r = a[];
+ a.stableRemoveBack();
+ a.stableInsertBack(7);
+ assert(a[].equal([1, 2, 3, 7]));
+}
+
+@safe unittest //12566
+{
+ auto dl2 = DList!int([2,7]);
+ dl2.removeFront();
+ assert(dl2[].walkLength == 1);
+ dl2.removeBack();
+ assert(dl2.empty, "not empty?!");
+}
+
+@safe unittest //13076
+{
+ DList!int list;
+ assert(list.empty);
+ list.clear();
+}
+
+@safe unittest //13425
+{
+ import std.range : drop, take;
+ auto list = DList!int([1,2,3,4,5]);
+ auto r = list[].drop(4); // r is a view of the last element of list
+ assert(r.front == 5 && r.walkLength == 1);
+ r = list.linearRemove(r.take(1));
+ assert(r.empty); // fails
+}
+
+@safe unittest //14300
+{
+ interface ITest {}
+ static class Test : ITest {}
+
+ 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/libphobos/src/std/container/package.d b/libphobos/src/std/container/package.d
new file mode 100644
index 0000000..fa00505
--- /dev/null
+++ b/libphobos/src/std/container/package.d
@@ -0,0 +1,1156 @@
+// Written in the D programming language.
+
+/**
+This module defines generic containers.
+
+Construction:
+
+To implement the different containers both struct and class based
+approaches have been used. $(REF make, std,_container,util) allows for
+uniform construction with either approach.
+
+---
+import std.container;
+// Construct a red-black tree and an array both containing the values 1, 2, 3.
+// RedBlackTree should typically be allocated using `new`
+RedBlackTree!int rbTree = new RedBlackTree!int(1, 2, 3);
+// But `new` should not be used with Array
+Array!int array = Array!int(1, 2, 3);
+// `make` hides the differences
+RedBlackTree!int rbTree2 = make!(RedBlackTree!int)(1, 2, 3);
+Array!int array2 = make!(Array!int)(1, 2, 3);
+---
+
+Note that $(D make) can infer the element type from the given arguments.
+
+---
+import std.container;
+auto rbTree = make!RedBlackTree(1, 2, 3); // RedBlackTree!int
+auto array = make!Array("1", "2", "3"); // Array!string
+---
+
+Reference_semantics:
+
+All containers have reference semantics, which means that after
+assignment both variables refer to the same underlying data.
+
+To make a copy of a _container, use the $(D c._dup) _container primitive.
+---
+import std.container, std.range;
+Array!int originalArray = make!(Array!int)(1, 2, 3);
+Array!int secondArray = originalArray;
+assert(equal(originalArray[], secondArray[]));
+
+// changing one instance changes the other one as well!
+originalArray[0] = 12;
+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 affected
+assert(originalArray[0] == 12);
+---
+
+$(B Attention:) If the _container is implemented as a class, using an
+uninitialized instance can cause a null pointer dereference.
+
+---
+import std.container;
+
+RedBlackTree!int rbTree;
+rbTree.insert(5); // null pointer dereference
+---
+
+Using an uninitialized struct-based _container will work, because the struct
+intializes itself upon use; however, up to this point the _container will not
+have an identity and assignment does not create two references to the same
+data.
+
+---
+import std.container;
+
+// create an uninitialized array
+Array!int array1;
+// array2 does _not_ refer to array1
+Array!int array2 = array1;
+array2.insertBack(42);
+// thus array1 will not be affected
+assert(array1.empty);
+
+// after initialization reference semantics work as expected
+array1 = array2;
+// now affects array2 as well
+array1.removeBack();
+assert(array2.empty);
+---
+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.container, std.range;
+
+auto arrOfArrs = make!Array(generate!(() => make!(Array!int)).take(10));
+---
+
+Submodules:
+
+This module consists of the following submodules:
+
+$(UL
+ $(LI
+ 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 $(MREF std, _container, binaryheap) module
+ provides a binary heap implementation that can be applied to any
+ user-provided random-access range.
+ )
+ $(LI
+ The $(MREF std, _container, dlist) module provides
+ a doubly-linked list implementation.
+ )
+ $(LI
+ The $(MREF std, _container, rbtree) module
+ implements red-black trees.
+ )
+ $(LI
+ The $(MREF std, _container, slist) module
+ implements singly-linked lists.
+ )
+ $(LI
+ The $(MREF std, _container, util) module contains
+ some generic tools commonly used by _container implementations.
+ )
+)
+
+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 $(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 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
+these parameters $(B must) be obtained from the same _container instance
+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.comparison : equal;
+import std.algorithm.iteration : find;
+import std.container;
+import std.range : take;
+
+auto array = make!Array(1, 2, 3);
+
+// `find` returns an Array!int.Range advanced to the element "2"
+array.linearRemove(array[].find(2));
+
+assert(array[].equal([1]));
+
+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).take(1));
+
+assert(array[].equal([1, 3]));
+---
+
+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.comparison : equal;
+import std.container;
+import std.range : iota;
+
+auto array = make!Array(1, 2);
+
+// the range type returned by `iota` is completely unrelated to Array,
+// which is fine for Array.insertBack:
+array.insertBack(iota(3, 10));
+
+assert(array[].equal([1, 2, 3, 4, 5, 6, 7, 8, 9]));
+---
+
+Container_primitives:
+
+Containers do not form a class hierarchy, instead they implement a
+common set of primitives (see table below). These primitives each guarantee
+a specific worst case complexity and thus allow generic code to be written
+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 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 $(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
+$(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
+primitive is implemented, it must support the syntax described in the $(B
+syntax) column with the semantics described in the $(B description) column, and
+it must not have a worst-case complexity worse than denoted in big-O notation in
+the $(BIGOH &middot;) column. Below, $(D C) means a _container type, $(D c) is
+a value of _container type, $(D n$(SUBSCRIPT x)) represents the effective length of
+value $(D x), which could be a single element (in which case $(D n$(SUBSCRIPT x)) is
+$(D 1)), a _container, or a range.
+
+$(BOOKTABLE Container primitives,
+$(TR
+ $(TH Syntax)
+ $(TH $(BIGOH &middot;))
+ $(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)
+
+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 $(HTTP
+boost.org/LICENSE_1_0.txt)).
+
+Authors: Steven Schveighoffer, $(HTTP erdani.com, Andrei Alexandrescu)
+ */
+
+module std.container;
+
+public import std.container.array;
+public import std.container.binaryheap;
+public import std.container.dlist;
+public import std.container.rbtree;
+public import std.container.slist;
+
+import std.meta;
+
+
+/* The following documentation and type $(D TotalContainer) are
+intended for developers only.
+
+$(D TotalContainer) is an unimplemented container that illustrates a
+host of primitives that a container may define. It is to some extent
+the bottom of the conceptual container hierarchy. A given container
+most often will choose to only implement a subset of these primitives,
+and define its own additional ones. Adhering to the standard primitive
+names below allows generic code to work independently of containers.
+
+Things to remember: any container must be a reference type, whether
+implemented as a $(D class) or $(D struct). No primitive below
+requires the container to escape addresses of elements, which means
+that compliant containers can be defined to use reference counting or
+other deterministic memory management techniques.
+
+A container may choose to define additional specific operations. The
+only requirement is that those operations bear different names than
+the ones below, lest user code gets confused.
+
+Complexity of operations should be interpreted as "at least as good
+as". If an operation is required to have $(BIGOH n) complexity, it
+could have anything lower than that, e.g. $(BIGOH log(n)). Unless
+specified otherwise, $(D n) inside a $(BIGOH) expression stands for
+the number of elements in the container.
+ */
+struct TotalContainer(T)
+{
+/**
+If the container has a notion of key-value mapping, $(D KeyType)
+defines the type of the key of the container.
+ */
+ alias KeyType = T;
+
+/**
+If the container has a notion of multikey-value mapping, $(D
+KeyTypes[k]), where $(D k) is a zero-based unsigned number, defines
+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 = AliasSeq!T;
+
+/**
+If the container has a notion of key-value mapping, $(D ValueType)
+defines the type of the value of the container. Typically, a map-style
+container mapping values of type $(D K) to values of type $(D V)
+defines $(D KeyType) to be $(D K) and $(D ValueType) to be $(D V).
+ */
+ alias ValueType = T;
+
+/**
+Defines the container's primary range, which embodies one of the
+ranges defined in $(MREF std,range).
+
+Generally a container may define several types of ranges.
+ */
+ struct Range
+ {
+ /++
+ Range primitives.
+ +/
+ @property bool empty()
+ {
+ assert(0);
+ }
+ /// Ditto
+ @property ref T front() //ref return optional
+ {
+ assert(0);
+ }
+ /// Ditto
+ @property void front(T value) //Only when front does not return by ref
+ {
+ assert(0);
+ }
+ /// Ditto
+ T moveFront()
+ {
+ assert(0);
+ }
+ /// Ditto
+ void popFront()
+ {
+ assert(0);
+ }
+ /// Ditto
+ @property ref T back() //ref return optional
+ {
+ assert(0);
+ }
+ /// Ditto
+ @property void back(T value) //Only when front does not return by ref
+ {
+ assert(0);
+ }
+ /// Ditto
+ T moveBack()
+ {
+ assert(0);
+ }
+ /// Ditto
+ void popBack()
+ {
+ assert(0);
+ }
+ /// Ditto
+ T opIndex(size_t i) //ref return optional
+ {
+ assert(0);
+ }
+ /// Ditto
+ void opIndexAssign(size_t i, T value) //Only when front does not return by ref
+ {
+ assert(0);
+ }
+ /// Ditto
+ T opIndexUnary(string op)(size_t i) //Only when front does not return by ref
+ {
+ assert(0);
+ }
+ /// Ditto
+ void opIndexOpAssign(string op)(size_t i, T value) //Only when front does not return by ref
+ {
+ assert(0);
+ }
+ /// Ditto
+ T moveAt(size_t i)
+ {
+ assert(0);
+ }
+ /// Ditto
+ @property size_t length()
+ {
+ assert(0);
+ }
+ }
+
+/**
+Property returning $(D true) if and only if the container has no
+elements.
+
+Complexity: $(BIGOH 1)
+ */
+ @property bool empty()
+ {
+ assert(0);
+ }
+
+/**
+Returns a duplicate of the container. The elements themselves are not
+transitively duplicated.
+
+Complexity: $(BIGOH n).
+ */
+ @property TotalContainer dup()
+ {
+ assert(0);
+ }
+
+/**
+Returns the number of elements in the container.
+
+Complexity: $(BIGOH log(n)).
+*/
+ @property size_t length()
+ {
+ assert(0);
+ }
+
+/**
+Returns the maximum number of elements the container can store without
+(a) allocating memory, (b) invalidating iterators upon insertion.
+
+Complexity: $(BIGOH log(n)).
+ */
+ @property size_t capacity()
+ {
+ assert(0);
+ }
+
+/**
+Ensures sufficient capacity to accommodate $(D n) elements.
+
+Postcondition: $(D capacity >= n)
+
+Complexity: $(BIGOH log(e - capacity)) if $(D e > capacity), otherwise
+$(BIGOH 1).
+ */
+ void reserve(size_t e)
+ {
+ assert(0);
+ }
+
+/**
+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))
+ */
+ Range opSlice()
+ {
+ assert(0);
+ }
+
+ /**
+ Returns a range that iterates the container between two
+ specified positions.
+
+ Complexity: $(BIGOH log(n))
+ */
+ Range opSlice(size_t a, size_t b)
+ {
+ assert(0);
+ }
+
+/**
+Forward to $(D opSlice().front) and $(D opSlice().back), respectively.
+
+Complexity: $(BIGOH log(n))
+ */
+ @property ref T front() //ref return optional
+ {
+ assert(0);
+ }
+ /// Ditto
+ @property void front(T value) //Only when front does not return by ref
+ {
+ assert(0);
+ }
+ /// Ditto
+ T moveFront()
+ {
+ assert(0);
+ }
+ /// Ditto
+ @property ref T back() //ref return optional
+ {
+ assert(0);
+ }
+ /// Ditto
+ @property void back(T value) //Only when front does not return by ref
+ {
+ assert(0);
+ }
+ /// Ditto
+ T moveBack()
+ {
+ assert(0);
+ }
+
+/**
+Indexing operators yield or modify the value at a specified index.
+ */
+ ref T opIndex(KeyType) //ref return optional
+ {
+ assert(0);
+ }
+ /// ditto
+ void opIndexAssign(KeyType i, T value) //Only when front does not return by ref
+ {
+ assert(0);
+ }
+ /// ditto
+ T opIndexUnary(string op)(KeyType i) //Only when front does not return by ref
+ {
+ assert(0);
+ }
+ /// ditto
+ void opIndexOpAssign(string op)(KeyType i, T value) //Only when front does not return by ref
+ {
+ assert(0);
+ }
+ /// ditto
+ T moveAt(KeyType i)
+ {
+ assert(0);
+ }
+
+/**
+$(D k in container) returns true if the given key is in the container.
+ */
+ bool opBinaryRight(string op)(KeyType k) if (op == "in")
+ {
+ assert(0);
+ }
+
+/**
+Returns a range of all elements containing $(D k) (could be empty or a
+singleton range).
+ */
+ Range equalRange(KeyType k)
+ {
+ assert(0);
+ }
+
+/**
+Returns a range of all elements with keys less than $(D k) (could be
+empty or a singleton range). Only defined by containers that store
+data sorted at all times.
+ */
+ Range lowerBound(KeyType k)
+ {
+ assert(0);
+ }
+
+/**
+Returns a range of all elements with keys larger than $(D k) (could be
+empty or a singleton range). Only defined by containers that store
+data sorted at all times.
+ */
+ Range upperBound(KeyType k)
+ {
+ assert(0);
+ }
+
+/**
+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).
+
+Complexity: $(BIGOH n + m), where m is the number of elements in $(D
+stuff)
+ */
+ TotalContainer opBinary(string op)(Stuff rhs) if (op == "~")
+ {
+ assert(0);
+ }
+
+ /// ditto
+ TotalContainer opBinaryRight(string op)(Stuff lhs) if (op == "~")
+ {
+ assert(0);
+ }
+
+/**
+Forwards to $(D insertAfter(this[], stuff)).
+ */
+ void opOpAssign(string op)(Stuff stuff) if (op == "~")
+ {
+ assert(0);
+ }
+
+/**
+Removes all contents from the container. The container decides how $(D
+capacity) is affected.
+
+Postcondition: $(D empty)
+
+Complexity: $(BIGOH n)
+ */
+ void clear()
+ {
+ assert(0);
+ }
+
+/**
+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
+.init).
+
+Complexity: $(BIGOH abs(n - newLength))
+
+Postcondition: $(D _length == newLength)
+ */
+ @property void length(size_t newLength)
+ {
+ assert(0);
+ }
+
+/**
+Inserts $(D stuff) in an unspecified position in the
+container. Implementations should choose whichever insertion means is
+the most advantageous for the container, but document the exact
+behavior. $(D stuff) can be a value convertible to the element type of
+the container, or a range of values convertible to it.
+
+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). Such code would
+not compile against containers that don't support it.
+
+Returns: The number of elements added.
+
+Complexity: $(BIGOH m * log(n)), where $(D m) is the number of
+elements in $(D stuff)
+ */
+ size_t insert(Stuff)(Stuff stuff)
+ {
+ assert(0);
+ }
+ ///ditto
+ size_t stableInsert(Stuff)(Stuff stuff)
+ {
+ assert(0);
+ }
+
+/**
+Same as $(D insert(stuff)) and $(D stableInsert(stuff)) respectively,
+but relax the complexity constraint to linear.
+ */
+ size_t linearInsert(Stuff)(Stuff stuff)
+ {
+ assert(0);
+ }
+ ///ditto
+ size_t stableLinearInsert(Stuff)(Stuff stuff)
+ {
+ assert(0);
+ }
+
+/**
+Picks one value in an unspecified position in the container, removes
+it from the container, and returns it. Implementations should pick the
+value that's the most advantageous for the container. 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)).
+ */
+ T removeAny()
+ {
+ assert(0);
+ }
+ /// ditto
+ T stableRemoveAny()
+ {
+ assert(0);
+ }
+
+/**
+Inserts $(D value) to the front or back of the container. $(D stuff)
+can be a value convertible to the container's element type or a range
+of values convertible to it. 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)).
+ */
+ size_t insertFront(Stuff)(Stuff stuff)
+ {
+ assert(0);
+ }
+ /// ditto
+ size_t stableInsertFront(Stuff)(Stuff stuff)
+ {
+ assert(0);
+ }
+ /// ditto
+ size_t insertBack(Stuff)(Stuff stuff)
+ {
+ assert(0);
+ }
+ /// ditto
+ size_t stableInsertBack(T value)
+ {
+ assert(0);
+ }
+
+/**
+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.
+
+Precondition: $(D !empty)
+
+Complexity: $(BIGOH log(n)).
+ */
+ void removeFront()
+ {
+ assert(0);
+ }
+ /// ditto
+ void stableRemoveFront()
+ {
+ assert(0);
+ }
+ /// ditto
+ void removeBack()
+ {
+ assert(0);
+ }
+ /// ditto
+ void stableRemoveBack()
+ {
+ assert(0);
+ }
+
+/**
+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)).
+ */
+ size_t removeFront(size_t howMany)
+ {
+ assert(0);
+ }
+ /// ditto
+ size_t stableRemoveFront(size_t howMany)
+ {
+ assert(0);
+ }
+ /// ditto
+ size_t removeBack(size_t howMany)
+ {
+ assert(0);
+ }
+ /// ditto
+ size_t stableRemoveBack(size_t howMany)
+ {
+ assert(0);
+ }
+
+/**
+Removes all values corresponding to key $(D k).
+
+Complexity: $(BIGOH m * log(n)), where $(D m) is the number of
+elements with the same key.
+
+Returns: The number of elements removed.
+ */
+ size_t removeKey(KeyType k)
+ {
+ assert(0);
+ }
+
+/**
+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 the container's element type or a range
+of objects convertible to it. 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)
+ */
+ size_t insertBefore(Stuff)(Range r, Stuff stuff)
+ {
+ assert(0);
+ }
+ /// ditto
+ size_t stableInsertBefore(Stuff)(Range r, Stuff stuff)
+ {
+ assert(0);
+ }
+ /// ditto
+ size_t insertAfter(Stuff)(Range r, Stuff stuff)
+ {
+ assert(0);
+ }
+ /// ditto
+ size_t stableInsertAfter(Stuff)(Range r, Stuff stuff)
+ {
+ assert(0);
+ }
+ /// ditto
+ size_t replace(Stuff)(Range r, Stuff stuff)
+ {
+ assert(0);
+ }
+ /// ditto
+ size_t stableReplace(Stuff)(Range r, Stuff stuff)
+ {
+ assert(0);
+ }
+
+/**
+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 m * log(n)), where $(D m) is the number of
+elements in $(D r)
+ */
+ Range remove(Range r)
+ {
+ assert(0);
+ }
+ /// ditto
+ Range stableRemove(Range r)
+ {
+ assert(0);
+ }
+
+/**
+Same as $(D remove) above, but has complexity relaxed to linear.
+
+Returns: A range spanning the remaining elements in the container that
+initially were right after $(D r).
+
+Complexity: $(BIGOH n)
+ */
+ Range linearRemove(Range r)
+ {
+ assert(0);
+ }
+ /// ditto
+ Range stableLinearRemove(Range r)
+ {
+ assert(0);
+ }
+}
+
+@safe unittest
+{
+ TotalContainer!int test;
+}
diff --git a/libphobos/src/std/container/rbtree.d b/libphobos/src/std/container/rbtree.d
new file mode 100644
index 0000000..861da5e
--- /dev/null
+++ b/libphobos/src/std/container/rbtree.d
@@ -0,0 +1,2065 @@
+/**
+This module implements a red-black tree container.
+
+This module is a submodule of $(MREF std, container).
+
+Source: $(PHOBOSSRC std/container/_rbtree.d)
+
+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 $(HTTP
+boost.org/LICENSE_1_0.txt)).
+
+Authors: Steven Schveighoffer, $(HTTP erdani.com, Andrei Alexandrescu)
+*/
+module std.container.rbtree;
+
+///
+@safe pure unittest
+{
+ import std.algorithm.comparison : equal;
+ import std.container.rbtree;
+
+ 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.format;
+import std.functional : binaryFun;
+
+public import std.container.util;
+
+version (unittest) debug = RBDoChecks;
+
+//debug = RBDoChecks;
+
+/*
+ * Implementation for a Red Black node for use in a Red Black Tree (see below)
+ *
+ * this implementation assumes we have a marker Node that is the parent of the
+ * root Node. This marker Node is not a valid Node, but marks the end of the
+ * collection. The root is the left child of the marker Node, so it is always
+ * last in the collection. The marker Node is passed in to the setColor
+ * function, and the Node which has this Node as its parent is assumed to be
+ * the root Node.
+ *
+ * A Red Black tree should have O(lg(n)) insertion, removal, and search time.
+ */
+struct RBNode(V)
+{
+ /*
+ * Convenience alias
+ */
+ alias Node = RBNode*;
+
+ private Node _left;
+ private Node _right;
+ private Node _parent;
+
+ /**
+ * The value held by this node
+ */
+ V value;
+
+ /**
+ * Enumeration determining what color the node is. Null nodes are assumed
+ * to be black.
+ */
+ enum Color : byte
+ {
+ Red,
+ Black
+ }
+
+ /**
+ * The color of the node.
+ */
+ Color color;
+
+ /**
+ * Get the left child
+ */
+ @property inout(RBNode)* left() inout
+ {
+ return _left;
+ }
+
+ /**
+ * Get the right child
+ */
+ @property inout(RBNode)* right() inout
+ {
+ return _right;
+ }
+
+ /**
+ * Get the parent
+ */
+ @property inout(RBNode)* parent() inout
+ {
+ return _parent;
+ }
+
+ /**
+ * Set the left child. Also updates the new child's parent node. This
+ * does not update the previous child.
+ *
+ * Returns newNode
+ */
+ @property Node left(Node newNode)
+ {
+ _left = newNode;
+ if (newNode !is null)
+ newNode._parent = &this;
+ return newNode;
+ }
+
+ /**
+ * Set the right child. Also updates the new child's parent node. This
+ * does not update the previous child.
+ *
+ * Returns newNode
+ */
+ @property Node right(Node newNode)
+ {
+ _right = newNode;
+ if (newNode !is null)
+ newNode._parent = &this;
+ return newNode;
+ }
+
+ // assume _left is not null
+ //
+ // performs rotate-right operation, where this is T, _right is R, _left is
+ // L, _parent is P:
+ //
+ // P P
+ // | -> |
+ // T L
+ // / \ / \
+ // L R a T
+ // / \ / \
+ // a b b R
+ //
+ /**
+ * Rotate right. This performs the following operations:
+ * - The left child becomes the parent of this node.
+ * - This node becomes the new parent's right child.
+ * - The old right child of the new parent becomes the left child of this
+ * node.
+ */
+ Node rotateR()
+ in
+ {
+ assert(_left !is null);
+ }
+ body
+ {
+ // sets _left._parent also
+ if (isLeftNode)
+ parent.left = _left;
+ else
+ parent.right = _left;
+ Node tmp = _left._right;
+
+ // sets _parent also
+ _left.right = &this;
+
+ // sets tmp._parent also
+ left = tmp;
+
+ return &this;
+ }
+
+ // assumes _right is non null
+ //
+ // performs rotate-left operation, where this is T, _right is R, _left is
+ // L, _parent is P:
+ //
+ // P P
+ // | -> |
+ // T R
+ // / \ / \
+ // L R T b
+ // / \ / \
+ // a b L a
+ //
+ /**
+ * Rotate left. This performs the following operations:
+ * - The right child becomes the parent of this node.
+ * - This node becomes the new parent's left child.
+ * - The old left child of the new parent becomes the right child of this
+ * node.
+ */
+ Node rotateL()
+ in
+ {
+ assert(_right !is null);
+ }
+ body
+ {
+ // sets _right._parent also
+ if (isLeftNode)
+ parent.left = _right;
+ else
+ parent.right = _right;
+ Node tmp = _right._left;
+
+ // sets _parent also
+ _right.left = &this;
+
+ // sets tmp._parent also
+ right = tmp;
+ return &this;
+ }
+
+
+ /**
+ * Returns true if this node is a left child.
+ *
+ * Note that this should always return a value because the root has a
+ * parent which is the marker node.
+ */
+ @property bool isLeftNode() const
+ in
+ {
+ assert(_parent !is null);
+ }
+ body
+ {
+ return _parent._left is &this;
+ }
+
+ /**
+ * Set the color of the node after it is inserted. This performs an
+ * update to the whole tree, possibly rotating nodes to keep the Red-Black
+ * properties correct. This is an O(lg(n)) operation, where n is the
+ * number of nodes in the tree.
+ *
+ * end is the marker node, which is the parent of the topmost valid node.
+ */
+ void setColor(Node end)
+ {
+ // test against the marker node
+ if (_parent !is end)
+ {
+ if (_parent.color == Color.Red)
+ {
+ Node cur = &this;
+ while (true)
+ {
+ // because root is always black, _parent._parent always exists
+ 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)
+ {
+ cur._parent.color = Color.Black;
+ y.color = Color.Black;
+ cur = cur._parent._parent;
+ if (cur._parent is end)
+ {
+ // root node
+ cur.color = Color.Black;
+ break;
+ }
+ else
+ {
+ // not root node
+ cur.color = Color.Red;
+ if (cur._parent.color == Color.Black)
+ // satisfied, exit the loop
+ break;
+ }
+ }
+ else
+ {
+ if (!cur.isLeftNode)
+ cur = cur._parent.rotateL();
+ cur._parent.color = Color.Black;
+ cur = cur._parent._parent.rotateR();
+ cur.color = Color.Red;
+ // tree should be satisfied now
+ break;
+ }
+ }
+ else
+ {
+ // parent is right node, y is 'uncle'
+ Node y = cur._parent._parent._left;
+ 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)
+ {
+ // root node
+ cur.color = Color.Black;
+ break;
+ }
+ else
+ {
+ // not root node
+ cur.color = Color.Red;
+ if (cur._parent.color == Color.Black)
+ // satisfied, exit the loop
+ break;
+ }
+ }
+ else
+ {
+ if (cur.isLeftNode)
+ cur = cur._parent.rotateR();
+ cur._parent.color = Color.Black;
+ cur = cur._parent._parent.rotateL();
+ cur.color = Color.Red;
+ // tree should be satisfied now
+ break;
+ }
+ }
+ }
+
+ }
+ }
+ else
+ {
+ //
+ // this is the root node, color it black
+ //
+ color = Color.Black;
+ }
+ }
+
+ /**
+ * Remove this node from the tree. The 'end' node is used as the marker
+ * which is root's parent. Note that this cannot be null!
+ *
+ * Returns the next highest valued node in the tree after this one, or end
+ * if this was the highest-valued node.
+ */
+ Node remove(Node end)
+ {
+ //
+ // remove this node from the tree, fixing the color if necessary.
+ //
+ Node x;
+ Node ret = next;
+
+ // if this node has 2 children
+ if (_left !is null && _right !is null)
+ {
+ //
+ // normally, we can just swap this node's and y's value, but
+ // because an iterator could be pointing to y and we don't want to
+ // disturb it, we swap this node and y's structure instead. This
+ // can also be a benefit if the value of the tree is a large
+ // struct, which takes a long time to copy.
+ //
+ Node yp, yl, yr;
+ Node y = ret; // y = next
+ yp = y._parent;
+ yl = y._left;
+ yr = y._right;
+ auto yc = y.color;
+ auto isyleft = y.isLeftNode;
+
+ //
+ // replace y's structure with structure of this node.
+ //
+ if (isLeftNode)
+ _parent.left = y;
+ else
+ _parent.right = y;
+ //
+ // need special case so y doesn't point back to itself
+ //
+ y.left = _left;
+ if (_right is y)
+ y.right = &this;
+ else
+ y.right = _right;
+ y.color = color;
+
+ //
+ // replace this node's structure with structure of y.
+ //
+ left = yl;
+ right = yr;
+ if (_parent !is y)
+ {
+ if (isyleft)
+ yp.left = &this;
+ else
+ yp.right = &this;
+ }
+ color = yc;
+ }
+
+ // if this has less than 2 children, remove it
+ if (_left !is null)
+ x = _left;
+ else
+ x = _right;
+
+ bool deferedUnlink = false;
+ if (x is null)
+ {
+ // pretend this is a null node, defer unlinking the node
+ x = &this;
+ deferedUnlink = true;
+ }
+ 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)
+ {
+ // need to recolor the tree.
+ while (x._parent !is end && x.color == Node.Color.Black)
+ {
+ if (x.isLeftNode)
+ {
+ // left node
+ Node w = x._parent._right;
+ if (w.color == Node.Color.Red)
+ {
+ w.color = Node.Color.Black;
+ x._parent.color = Node.Color.Red;
+ x._parent.rotateL();
+ w = x._parent._right;
+ }
+ Node wl = w.left;
+ Node wr = w.right;
+ if ((wl is null || wl.color == Node.Color.Black) &&
+ (wr is null || wr.color == Node.Color.Black))
+ {
+ w.color = Node.Color.Red;
+ x = x._parent;
+ }
+ else
+ {
+ if (wr is null || wr.color == Node.Color.Black)
+ {
+ // wl cannot be null here
+ wl.color = Node.Color.Black;
+ w.color = Node.Color.Red;
+ w.rotateR();
+ w = x._parent._right;
+ }
+
+ w.color = x._parent.color;
+ x._parent.color = Node.Color.Black;
+ w._right.color = Node.Color.Black;
+ x._parent.rotateL();
+ x = end.left; // x = root
+ }
+ }
+ else
+ {
+ // right node
+ Node w = x._parent._left;
+ if (w.color == Node.Color.Red)
+ {
+ w.color = Node.Color.Black;
+ x._parent.color = Node.Color.Red;
+ x._parent.rotateR();
+ w = x._parent._left;
+ }
+ Node wl = w.left;
+ Node wr = w.right;
+ if ((wl is null || wl.color == Node.Color.Black) &&
+ (wr is null || wr.color == Node.Color.Black))
+ {
+ w.color = Node.Color.Red;
+ x = x._parent;
+ }
+ else
+ {
+ if (wl is null || wl.color == Node.Color.Black)
+ {
+ // wr cannot be null here
+ wr.color = Node.Color.Black;
+ w.color = Node.Color.Red;
+ w.rotateL();
+ w = x._parent._left;
+ }
+
+ w.color = x._parent.color;
+ x._parent.color = Node.Color.Black;
+ w._left.color = Node.Color.Black;
+ x._parent.rotateR();
+ x = end.left; // x = root
+ }
+ }
+ }
+ x.color = Node.Color.Black;
+ }
+
+ if (deferedUnlink)
+ {
+ //
+ // unlink this node from the tree
+ //
+ if (isLeftNode)
+ _parent.left = null;
+ else
+ _parent.right = null;
+ }
+
+ // clean references to help GC - Bugzilla 12915
+ _left = _right = _parent = null;
+
+ return ret;
+ }
+
+ /**
+ * Return the leftmost descendant of this node.
+ */
+ @property inout(RBNode)* leftmost() inout
+ {
+ inout(RBNode)* result = &this;
+ while (result._left !is null)
+ result = result._left;
+ return result;
+ }
+
+ /**
+ * Return the rightmost descendant of this node
+ */
+ @property inout(RBNode)* rightmost() inout
+ {
+ inout(RBNode)* result = &this;
+ while (result._right !is null)
+ result = result._right;
+ return result;
+ }
+
+ /**
+ * Returns the next valued node in the tree.
+ *
+ * You should never call this on the marker node, as it is assumed that
+ * there is a valid next node.
+ */
+ @property inout(RBNode)* next() inout
+ {
+ inout(RBNode)* n = &this;
+ if (n.right is null)
+ {
+ while (!n.isLeftNode)
+ n = n._parent;
+ return n._parent;
+ }
+ else
+ return n.right.leftmost;
+ }
+
+ /**
+ * Returns the previous valued node in the tree.
+ *
+ * You should never call this on the leftmost node of the tree as it is
+ * assumed that there is a valid previous node.
+ */
+ @property inout(RBNode)* prev() inout
+ {
+ inout(RBNode)* n = &this;
+ if (n.left is null)
+ {
+ while (n.isLeftNode)
+ n = n._parent;
+ return n._parent;
+ }
+ else
+ return n.left.rightmost;
+ }
+
+ Node dup(scope Node delegate(V v) alloc)
+ {
+ //
+ // duplicate this and all child nodes
+ //
+ // The recursion should be lg(n), so we shouldn't have to worry about
+ // stack size.
+ //
+ Node copy = alloc(value);
+ copy.color = color;
+ if (_left !is null)
+ copy.left = _left.dup(alloc);
+ if (_right !is null)
+ copy.right = _right.dup(alloc);
+ return copy;
+ }
+
+ Node dup()
+ {
+ Node copy = new RBNode!V(null, null, null, value);
+ copy.color = color;
+ if (_left !is null)
+ copy.left = _left.dup();
+ 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 $(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 $(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.
+ *
+ * Note that less should produce a strict ordering. That is, for two unequal
+ * elements $(D a) and $(D b), $(D less(a, b) == !less(b, a)). $(D less(a, a)) should
+ * always equal $(D false).
+ *
+ * If $(D allowDuplicates) is set to $(D true), then inserting the same element more than
+ * once continues to add more elements. If it is $(D false), duplicate elements are
+ * ignored on insertion. If duplicates are allowed, then new elements are
+ * 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))))
+{
+ import std.meta : allSatisfy;
+ import std.range : Take;
+ import std.range.primitives : isInputRange, walkLength;
+ import std.traits : isIntegral, isDynamicArray, isImplicitlyConvertible;
+
+ alias _less = binaryFun!less;
+
+ version (unittest)
+ {
+ static if (is(typeof(less) == string))
+ {
+ private enum doUnittest = isIntegral!T && (less == "a < b" || less == "a > b");
+ }
+ else
+ enum doUnittest = false;
+
+ // note, this must be final so it does not affect the vtable layout
+ bool arrayEqual(T[] arr)
+ {
+ if (walkLength(this[]) == arr.length)
+ {
+ foreach (v; arr)
+ {
+ if (!(v in this))
+ return false;
+ }
+ return true;
+ }
+ return false;
+ }
+ }
+ else
+ {
+ private enum doUnittest = false;
+ }
+
+ /**
+ * Element type for the tree
+ */
+ alias Elem = T;
+
+ // used for convenience
+ private alias RBNode = .RBNode!Elem;
+ private alias Node = RBNode.Node;
+
+ private Node _end;
+ private Node _begin;
+ private size_t _length;
+
+ private void _setup()
+ {
+ assert(!_end); //Make sure that _setup isn't run more than once.
+ _begin = _end = allocate();
+ }
+
+ static private Node allocate()
+ {
+ return new RBNode;
+ }
+
+ static private Node allocate(Elem v)
+ {
+ return new RBNode(null, null, null, v);
+ }
+
+ /**
+ * The range types for $(D RedBlackTree)
+ */
+ alias Range = RBRange!(RBNode*);
+ alias ConstRange = RBRange!(const(RBNode)*); /// Ditto
+ alias ImmutableRange = RBRange!(immutable(RBNode)*); /// Ditto
+
+ static if (doUnittest) @safe pure unittest
+ {
+ 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")
+ auto vals = [1, 2, 3, 4, 5];
+ else
+ auto vals = [5, 4, 3, 2, 1];
+ assert(equal(r, vals));
+
+ assert(r.front == vals.front);
+ assert(r.back != r.front);
+ auto oldfront = r.front;
+ auto oldback = r.back;
+ r.popFront();
+ r.popBack();
+ assert(r.front != r.back);
+ assert(r.front != oldfront);
+ assert(r.back != oldback);
+ assert(ts.length == 5);
+ }
+
+ // find a node based on an element value
+ private inout(RBNode)* _find(Elem e) inout
+ {
+ static if (allowDuplicates)
+ {
+ inout(RBNode)* cur = _end.left;
+ inout(RBNode)* result = null;
+ while (cur)
+ {
+ if (_less(cur.value, e))
+ cur = cur.right;
+ else if (_less(e, cur.value))
+ cur = cur.left;
+ else
+ {
+ // want to find the left-most element
+ result = cur;
+ cur = cur.left;
+ }
+ }
+ return result;
+ }
+ else
+ {
+ inout(RBNode)* cur = _end.left;
+ while (cur)
+ {
+ if (_less(cur.value, e))
+ cur = cur.right;
+ else if (_less(e, cur.value))
+ cur = cur.left;
+ else
+ return cur;
+ }
+ return null;
+ }
+ }
+
+ // 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.
+ */
+ @property bool empty()
+ {
+ return _end.left is null;
+ }
+
+ /++
+ Returns the number of elements in the container.
+
+ Complexity: $(BIGOH 1).
+ +/
+ @property size_t length() const
+ {
+ return _length;
+ }
+
+ /**
+ * Duplicate this container. The resulting container contains a shallow
+ * copy of the elements.
+ *
+ * Complexity: $(BIGOH n)
+ */
+ @property RedBlackTree dup()
+ {
+ return new RedBlackTree(_end.dup(), _length);
+ }
+
+ static if (doUnittest) @safe pure unittest
+ {
+ 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);
+ assert(!equal(ts[], ts2[]));
+ assert(ts.length == 5 && ts2.length == 6);
+ }
+
+ /**
+ * Fetch a range that spans all the elements in the container.
+ *
+ * Complexity: $(BIGOH 1)
+ */
+ Range opSlice()
+ {
+ 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
+ *
+ * Complexity: $(BIGOH 1)
+ */
+ Elem front()
+ {
+ return _begin.value;
+ }
+
+ /**
+ * The last element in the container
+ *
+ * Complexity: $(BIGOH log(n))
+ */
+ Elem back()
+ {
+ return _end.prev.value;
+ }
+
+ /++
+ $(D in) operator. Check to see if the given element exists in the
+ container.
+
+ Complexity: $(BIGOH log(n))
+ +/
+ bool opBinaryRight(string op)(Elem e) const if (op == "in")
+ {
+ return _find(e) !is null;
+ }
+
+ 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);
+ }
+
+ /**
+ * Compares two trees for equality.
+ *
+ * Complexity: $(BIGOH n)
+ */
+ override bool opEquals(Object 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;
+
+ auto thisRange = this[];
+ auto thatRange = that[];
+ return equal!(function(Elem a, Elem b) => !_less(a,b) && !_less(b,a))
+ (thisRange, thatRange);
+ }
+
+ static if (doUnittest) @system unittest
+ {
+ auto t1 = new RedBlackTree(1,2,3,4);
+ auto t2 = new RedBlackTree(1,2,3,4);
+ auto t3 = new RedBlackTree(1,2,3,5);
+ auto t4 = new RedBlackTree(1,2,3,4,5);
+ auto o = new Object();
+
+ assert(t1 == t1);
+ assert(t1 == t2);
+ assert(t1 != t3);
+ assert(t1 != t4);
+ assert(t1 != o); // pathological case, must not crash
+ }
+
+ /**
+ * Removes all elements from the container.
+ *
+ * Complexity: $(BIGOH 1)
+ */
+ void clear()
+ {
+ _end.left = null;
+ _begin = _end;
+ _length = 0;
+ }
+
+ static if (doUnittest) @safe pure unittest
+ {
+ auto ts = new RedBlackTree(1,2,3,4,5);
+ assert(ts.length == 5);
+ ts.clear();
+ assert(ts.empty && ts.length == 0);
+ }
+
+ /**
+ * 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)
+ {
+ _add(stuff);
+ return 1;
+ }
+ else
+ {
+ return(_add(stuff).added ? 1 : 0);
+ }
+ }
+
+ /**
+ * 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 result = 0;
+ static if (allowDuplicates)
+ {
+ foreach (e; stuff)
+ {
+ ++result;
+ _add(e);
+ }
+ }
+ else
+ {
+ foreach (e; stuff)
+ {
+ if (_add(e).added)
+ ++result;
+ }
+ }
+ return result;
+ }
+
+ /// ditto
+ alias insert = stableInsert;
+
+ static if (doUnittest) @safe pure unittest
+ {
+ auto ts = new RedBlackTree(2,1,3,4,5,2,5);
+ 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);
+
+ 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]));
+ }
+ else
+ {
+ assert(ts.length == 5);
+ Elem[] elems = [7, 8, 6, 9, 10, 8];
+ assert(ts.stableInsert(elems) == 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);
+
+ 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]));
+ }
+ }
+
+ /**
+ * Remove an element from the container and return its value.
+ *
+ * Complexity: $(BIGOH log(n))
+ */
+ Elem removeAny()
+ {
+ scope(success)
+ --_length;
+ auto n = _begin;
+ auto result = n.value;
+ _begin = n.remove(_end);
+ debug(RBDoChecks)
+ check();
+ return result;
+ }
+
+ 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;
+ assert(ts.arrayEqual(arr));
+ }
+
+ /**
+ * Remove the front element from the container.
+ *
+ * Complexity: $(BIGOH log(n))
+ */
+ void removeFront()
+ {
+ scope(success)
+ --_length;
+ _begin = _begin.remove(_end);
+ debug(RBDoChecks)
+ check();
+ }
+
+ /**
+ * Remove the back element from the container.
+ *
+ * Complexity: $(BIGOH log(n))
+ */
+ void removeBack()
+ {
+ scope(success)
+ --_length;
+ auto lastnode = _end.prev;
+ if (lastnode is _begin)
+ _begin = _begin.remove(_end);
+ else
+ lastnode.remove(_end);
+ debug(RBDoChecks)
+ check();
+ }
+
+ 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")
+ assert(ts.arrayEqual([1,2,3,4]));
+ else
+ assert(ts.arrayEqual([2,3,4,5]));
+
+ ts.removeFront();
+ assert(ts.arrayEqual([2,3,4]) && ts.length == 3);
+ }
+
+ /++
+ Removes the given range from the container.
+
+ Returns: A range containing all of the elements that were after the
+ given range.
+
+ Complexity: $(BIGOH m * log(n)) (where m is the number of elements in
+ the range)
+ +/
+ Range remove(Range r)
+ {
+ auto b = r._begin;
+ auto e = r._end;
+ if (_begin is b)
+ _begin = e;
+ while (b !is e)
+ {
+ b = b.remove(_end);
+ --_length;
+ }
+ debug(RBDoChecks)
+ check();
+ return Range(e, _end);
+ }
+
+ static if (doUnittest) @safe pure unittest
+ {
+ import std.algorithm.comparison : equal;
+ auto ts = new RedBlackTree(1,2,3,4,5);
+ assert(ts.length == 5);
+ auto r = ts[];
+ r.popFront();
+ r.popBack();
+ assert(ts.length == 5);
+ auto r2 = ts.remove(r);
+ assert(ts.length == 2);
+ assert(ts.arrayEqual([1,5]));
+
+ static if (less == "a < b")
+ assert(equal(r2, [5]));
+ else
+ assert(equal(r2, [1]));
+ }
+
+ /++
+ Removes the given $(D Take!Range) from the container
+
+ Returns: A range containing all of the elements that were after the
+ given range.
+
+ Complexity: $(BIGOH m * log(n)) (where m is the number of elements in
+ the range)
+ +/
+ Range remove(Take!Range r)
+ {
+ immutable isBegin = (r.source._begin is _begin);
+ auto b = r.source._begin;
+
+ while (!r.empty)
+ {
+ r.popFront();
+ b = b.remove(_end);
+ --_length;
+ }
+
+ if (isBegin)
+ _begin = b;
+
+ return Range(b, _end);
+ }
+
+ static if (doUnittest) @safe pure unittest
+ {
+ import std.algorithm.comparison : equal;
+ import std.range : take;
+ auto ts = new RedBlackTree(1,2,3,4,5);
+ auto r = ts[];
+ r.popFront();
+ assert(ts.length == 5);
+ auto r2 = ts.remove(take(r, 0));
+
+ static if (less == "a < b")
+ {
+ assert(equal(r2, [2,3,4,5]));
+ auto r3 = ts.remove(take(r, 2));
+ assert(ts.arrayEqual([1,4,5]) && ts.length == 3);
+ assert(equal(r3, [4,5]));
+ }
+ else
+ {
+ assert(equal(r2, [4,3,2,1]));
+ auto r3 = ts.remove(take(r, 2));
+ assert(ts.arrayEqual([5,2,1]) && ts.length == 3);
+ assert(equal(r3, [2,1]));
+ }
+ }
+
+ /++
+ Removes elements from the container that are equal to the given values
+ according to the less comparator. One element is removed for each value
+ given which is in the container. If $(D allowDuplicates) is true,
+ duplicates are removed only if duplicate values are given.
+
+ Returns: The number of elements removed.
+
+ Complexity: $(BIGOH m log(n)) (where m is the number of elements to remove)
+
+ Example:
+--------------------
+auto rbt = redBlackTree!true(0, 1, 1, 1, 4, 5, 7);
+rbt.removeKey(1, 4, 7);
+assert(equal(rbt[], [0, 1, 1, 5]));
+rbt.removeKey(1, 1, 0);
+assert(equal(rbt[], [5]));
+--------------------
+ +/
+ size_t removeKey(U...)(U elems)
+ if (allSatisfy!(isImplicitlyConvertibleToElem, U))
+ {
+ Elem[U.length] toRemove = [elems];
+ return removeKey(toRemove[]);
+ }
+
+ /++ Ditto +/
+ size_t removeKey(U)(U[] elems)
+ if (isImplicitlyConvertible!(U, Elem))
+ {
+ immutable lenBefore = length;
+
+ foreach (e; elems)
+ {
+ auto beg = _firstGreaterEqual(e);
+ if (beg is _end || _less(e, beg.value))
+ // no values are equal
+ continue;
+ immutable isBegin = (beg is _begin);
+ beg = beg.remove(_end);
+ if (isBegin)
+ _begin = beg;
+ --_length;
+ }
+
+ return lenBefore - length;
+ }
+
+ /++ Ditto +/
+ size_t removeKey(Stuff)(Stuff stuff)
+ if (isInputRange!Stuff &&
+ isImplicitlyConvertible!(ElementType!Stuff, Elem) &&
+ !isDynamicArray!Stuff)
+ {
+ import std.array : array;
+ //We use array in case stuff is a Range from this RedBlackTree - either
+ //directly or indirectly.
+ return removeKey(array(stuff));
+ }
+
+ //Helper for removeKey.
+ private template isImplicitlyConvertibleToElem(U)
+ {
+ enum isImplicitlyConvertibleToElem = isImplicitlyConvertible!(U, Elem);
+ }
+
+ static if (doUnittest) @safe pure unittest
+ {
+ 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)
+ {
+ assert(rbt.length == 11);
+ 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.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")
+ assert(equal(rbt[], [7,7,19,45]));
+ else
+ assert(equal(rbt[], [7,5,3,2]));
+ }
+ else
+ {
+ assert(rbt.length == 9);
+ 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.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")
+ assert(equal(rbt[], [19,45]));
+ else
+ assert(equal(rbt[], [5,3]));
+ }
+ }
+
+ // find the first node where the value is > e
+ private inout(RBNode)* _firstGreater(Elem e) inout
+ {
+ // can't use _find, because we cannot return null
+ auto cur = _end.left;
+ inout(RBNode)* result = _end;
+ while (cur)
+ {
+ if (_less(e, cur.value))
+ {
+ result = cur;
+ cur = cur.left;
+ }
+ else
+ cur = cur.right;
+ }
+ return result;
+ }
+
+ // find the first node where the value is >= e
+ private inout(RBNode)* _firstGreaterEqual(Elem e) inout
+ {
+ // can't use _find, because we cannot return null.
+ auto cur = _end.left;
+ inout(RBNode)* result = _end;
+ while (cur)
+ {
+ if (_less(cur.value, e))
+ cur = cur.right;
+ else
+ {
+ result = cur;
+ cur = cur.left;
+ }
+
+ }
+ return result;
+ }
+
+ /**
+ * Get a range from the container with all elements that are > e according
+ * to the less comparator
+ *
+ * Complexity: $(BIGOH log(n))
+ */
+ Range upperBound(Elem e)
+ {
+ 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
+ *
+ * Complexity: $(BIGOH log(n))
+ */
+ Range lowerBound(Elem e)
+ {
+ 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))
+ */
+ auto equalRange(this This)(Elem e)
+ {
+ auto beg = _firstGreaterEqual(e);
+ alias RangeType = RBRange!(typeof(beg));
+ if (beg is _end || _less(e, beg.value))
+ // no values are equal
+ return RangeType(beg, beg);
+ static if (allowDuplicates)
+ {
+ 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 RangeType(beg, beg.next);
+ }
+ }
+
+ static if (doUnittest) @safe pure unittest
+ {
+ 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")
+ {
+ assert(equal(rl, [1,2]));
+ assert(equal(ru, [4,5]));
+ }
+ else
+ {
+ assert(equal(rl, [5,4]));
+ assert(equal(ru, [2,1]));
+ }
+
+ assert(equal(re, [3]));
+ }
+
+ debug(RBDoChecks)
+ {
+ /*
+ * Print the tree. This prints a sideways view of the tree in ASCII form,
+ * with the number of indentations representing the level of the nodes.
+ * It does not print values, only the tree structure and color of nodes.
+ */
+ void printTree(Node n, int indent = 0)
+ {
+ import std.stdio : write, writeln;
+ if (n !is null)
+ {
+ printTree(n.right, indent + 2);
+ 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++)
+ write(".");
+ writeln("N");
+ }
+ if (indent is 0)
+ writeln();
+ }
+
+ /*
+ * Check the tree for validity. This is called after every add or remove.
+ * This should only be enabled to debug the implementation of the RB Tree.
+ */
+ void check()
+ {
+ //
+ // check implementation of the tree
+ //
+ int recurse(Node n, string path)
+ {
+ import std.stdio : writeln;
+ if (n is null)
+ return 1;
+ 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)
+ {
+ 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))
+ throw new Exception("ordering invalid at path " ~ path);
+ }
+ if (n.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)
+ {
+ 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"
+ );
+ }
+ return l + (n.color == n.color.Black ? 1 : 0);
+ }
+
+ try
+ {
+ recurse(_end.left, "");
+ }
+ catch (Exception e)
+ {
+ debug printTree(_end.left, 0);
+ throw e;
+ }
+ }
+ }
+
+ /**
+ 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.
+ */
+ this(Elem[] elems...)
+ {
+ _setup();
+ stableInsert(elems);
+ }
+
+ /**
+ * Constructor. Pass in a range of elements to initialize the tree with.
+ */
+ this(Stuff)(Stuff stuff) if (isInputRange!Stuff && isImplicitlyConvertible!(ElementType!Stuff, Elem))
+ {
+ _setup();
+ stableInsert(stuff);
+ }
+
+ ///
+ this()
+ {
+ _setup();
+ }
+
+ private this(Node end, size_t length)
+ {
+ _end = end;
+ _begin = end.leftmost;
+ _length = length;
+ }
+}
+
+//Verify Example for removeKey.
+@safe pure unittest
+{
+ 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]));
+ rbt.removeKey(1, 1, 0);
+ assert(equal(rbt[], [5]));
+}
+
+//Tests for removeKey
+@safe pure unittest
+{
+ import std.algorithm.comparison : equal;
+ {
+ auto rbt = redBlackTree(["hello", "world", "foo", "bar"]);
+ assert(equal(rbt[], ["bar", "foo", "hello", "world"]));
+ assert(rbt.removeKey("hello") == 1);
+ assert(equal(rbt[], ["bar", "foo", "world"]));
+ assert(rbt.removeKey("hello") == 0);
+ assert(equal(rbt[], ["bar", "foo", "world"]));
+ assert(rbt.removeKey("hello", "foo", "bar") == 2);
+ assert(equal(rbt[], ["world"]));
+ assert(rbt.removeKey(["", "world", "hello"]) == 1);
+ assert(rbt.empty);
+ }
+
+ {
+ auto rbt = redBlackTree([1, 2, 12, 27, 4, 500]);
+ 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(equal(rbt[], [2, 4, 12, 27, 500]));
+ 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(equal(rbt[], [2, 4]));
+ }
+}
+
+@safe pure unittest
+{
+ void test(T)()
+ {
+ auto rt1 = new RedBlackTree!(T, "a < b", false)();
+ auto rt2 = new RedBlackTree!(T, "a < b", true)();
+ auto rt3 = new RedBlackTree!(T, "a > b", false)();
+ auto rt4 = new RedBlackTree!(T, "a > b", true)();
+ }
+
+ test!long();
+ test!ulong();
+ test!int();
+ test!uint();
+ test!short();
+ test!ushort();
+ test!byte();
+ 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...)
+{
+ return new RedBlackTree!E(elems);
+}
+
+/++ Ditto +/
+auto redBlackTree(bool allowDuplicates, E)(E[] elems...)
+{
+ return new RedBlackTree!(E, "a < b", allowDuplicates)(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))))
+{
+ //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!(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);
+}
+
+///
+@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.
+@safe pure unittest
+{
+ auto rbt1 = redBlackTree!(true, string)("hello", "hello");
+ auto rbt2 = redBlackTree!((a, b){return a < b;}, double)(5.1, 2.3);
+ auto rbt3 = redBlackTree!("a > b", true, string)("hello", "world");
+}
+
+//Range construction.
+@safe pure unittest
+{
+ 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]));
+}
+
+
+// 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]);
+
+ auto rt2 = redBlackTree!"a > b"(1.1, 2.1);
+ assert(rt2.length == 2);
+ assert(array(rt2[]) == [2.1, 1.1]);
+
+ auto rt3 = redBlackTree!true(5, 5, 4);
+ assert(rt3.length == 3);
+ assert(array(rt3[]) == [4, 5, 5]);
+
+ auto rt4 = redBlackTree!string("hello", "hello");
+ assert(rt4.length == 1);
+ 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
+@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;
+}
+
+@safe pure unittest // const/immutable elements (issue 17519)
+{
+ RedBlackTree!(immutable int) t1;
+ RedBlackTree!(const int) t2;
+
+ import std.algorithm.iteration : map;
+ static struct S { int* p; }
+ auto t3 = new RedBlackTree!(immutable S, (a, b) => *a.p < *b.p);
+ t3.insert([1, 2, 3].map!(x => immutable S(new int(x))));
+ static assert(!__traits(compiles, *t3.front.p = 4));
+ assert(*t3.front.p == 1);
+}
diff --git a/libphobos/src/std/container/slist.d b/libphobos/src/std/container/slist.d
new file mode 100644
index 0000000..820b0bb
--- /dev/null
+++ b/libphobos/src/std/container/slist.d
@@ -0,0 +1,846 @@
+/**
+This module implements a singly-linked list container.
+It can be used as a stack.
+
+This module is a submodule of $(MREF std, container).
+
+Source: $(PHOBOSSRC std/container/_slist.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.slist;
+
+///
+@safe unittest
+{
+ import std.algorithm.comparison : equal;
+ import std.container : SList;
+
+ 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.
+ */
+struct SList(T)
+{
+ import std.exception : enforce;
+ import std.range : Take;
+ import std.range.primitives : isInputRange, isForwardRange, ElementType;
+ import std.traits : isImplicitlyConvertible;
+
+ private struct Node
+ {
+ Node * _next;
+ T _payload;
+ }
+ private struct NodeWithoutPayload
+ {
+ Node* _next;
+ }
+ static assert(NodeWithoutPayload._next.offsetof == Node._next.offsetof);
+
+ private Node * _root;
+
+ private void initialize() @trusted nothrow pure
+ {
+ if (_root) return;
+ _root = cast (Node*) new NodeWithoutPayload();
+ }
+
+ private ref inout(Node*) _first() @property @safe nothrow pure inout
+ {
+ assert(_root);
+ return _root._next;
+ }
+
+ private static Node * findLastNode(Node * n)
+ {
+ assert(n);
+ auto ahead = n._next;
+ while (ahead)
+ {
+ n = ahead;
+ ahead = n._next;
+ }
+ return n;
+ }
+
+ private static Node * findLastNode(Node * n, size_t limit)
+ {
+ assert(n && limit);
+ auto ahead = n._next;
+ while (ahead)
+ {
+ if (!--limit) break;
+ n = ahead;
+ ahead = n._next;
+ }
+ return n;
+ }
+
+ private static Node * findNode(Node * n, Node * findMe)
+ {
+ assert(n);
+ auto ahead = n._next;
+ while (ahead != findMe)
+ {
+ n = ahead;
+ enforce(n);
+ ahead = n._next;
+ }
+ return n;
+ }
+
+/**
+Constructor taking a number of nodes
+ */
+ this(U)(U[] values...) if (isImplicitlyConvertible!(U, T))
+ {
+ insertFront(values);
+ }
+
+/**
+Constructor taking an input range
+ */
+ this(Stuff)(Stuff stuff)
+ if (isInputRange!Stuff
+ && isImplicitlyConvertible!(ElementType!Stuff, T)
+ && !is(Stuff == T[]))
+ {
+ insertFront(stuff);
+ }
+
+/**
+Comparison for equality.
+
+Complexity: $(BIGOH min(n, n1)) where $(D n1) is the number of
+elements in $(D rhs).
+ */
+ bool opEquals(const SList rhs) const
+ {
+ return opEquals(rhs);
+ }
+
+ /// ditto
+ bool opEquals(ref const SList rhs) const
+ {
+ if (_root is rhs._root) return true;
+ if (_root is null) return rhs._root is null || rhs._first is null;
+ if (rhs._root is null) return _root is null || _first is null;
+
+ const(Node) * n1 = _first, n2 = rhs._first;
+
+ for (;; n1 = n1._next, n2 = n2._next)
+ {
+ if (!n1) return !n2;
+ if (!n2 || n1._payload != n2._payload) return false;
+ }
+ }
+
+/**
+Defines the container's primary range, which embodies a forward range.
+ */
+ struct Range
+ {
+ private Node * _head;
+ private this(Node * p) { _head = p; }
+
+ /// Input range primitives.
+ @property bool empty() const { return !_head; }
+
+ /// ditto
+ @property ref T front()
+ {
+ assert(!empty, "SList.Range.front: Range is empty");
+ return _head._payload;
+ }
+
+ /// ditto
+ void popFront()
+ {
+ assert(!empty, "SList.Range.popFront: Range is empty");
+ _head = _head._next;
+ }
+
+ /// Forward range primitive.
+ @property Range save() { return this; }
+
+ T moveFront()
+ {
+ import std.algorithm.mutation : move;
+
+ assert(!empty, "SList.Range.moveFront: Range is empty");
+ return move(_head._payload);
+ }
+
+ bool sameHead(Range rhs)
+ {
+ return _head && _head == rhs._head;
+ }
+ }
+
+ @safe unittest
+ {
+ static assert(isForwardRange!Range);
+ }
+
+/**
+Property returning $(D true) if and only if the container has no
+elements.
+
+Complexity: $(BIGOH 1)
+ */
+ @property bool empty() const
+ {
+ return _root is null || _first is null;
+ }
+
+/**
+Duplicates the container. The elements themselves are not transitively
+duplicated.
+
+Complexity: $(BIGOH n).
+ */
+ @property SList dup()
+ {
+ return SList(this[]);
+ }
+
+/**
+Returns a range that iterates over all elements of the container, in
+forward order.
+
+Complexity: $(BIGOH 1)
+ */
+ Range opSlice()
+ {
+ if (empty)
+ return Range(null);
+ else
+ return Range(_first);
+ }
+
+/**
+Forward to $(D opSlice().front).
+
+Complexity: $(BIGOH 1)
+ */
+ @property ref T front()
+ {
+ assert(!empty, "SList.front: List is empty");
+ return _first._payload;
+ }
+
+ @safe unittest
+ {
+ auto s = SList!int(1, 2, 3);
+ s.front = 42;
+ assert(s == SList!int(42, 2, 3));
+ }
+
+/**
+Returns a new $(D SList) that's the concatenation of $(D this) and its
+argument. $(D opBinaryRight) is only defined if $(D Stuff) does not
+define $(D opBinary).
+ */
+ SList opBinary(string op, Stuff)(Stuff rhs)
+ if (op == "~" && is(typeof(SList(rhs))))
+ {
+ auto toAdd = SList(rhs);
+ if (empty) return toAdd;
+ // TODO: optimize
+ auto result = dup;
+ auto n = findLastNode(result._first);
+ n._next = toAdd._first;
+ return result;
+ }
+
+ /// ditto
+ SList opBinaryRight(string op, Stuff)(Stuff lhs)
+ if (op == "~" && !is(typeof(lhs.opBinary!"~"(this))) && is(typeof(SList(lhs))))
+ {
+ auto toAdd = SList(lhs);
+ if (empty) return toAdd;
+ auto result = dup;
+ result.insertFront(toAdd[]);
+ return result;
+ }
+
+/**
+Removes all contents from the $(D SList).
+
+Postcondition: $(D empty)
+
+Complexity: $(BIGOH 1)
+ */
+ void clear()
+ {
+ 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;
+ }
+ }
+
+/**
+Inserts $(D stuff) to the front 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), where $(D m) is the length of $(D stuff)
+ */
+ size_t insertFront(Stuff)(Stuff stuff)
+ if (isInputRange!Stuff && isImplicitlyConvertible!(ElementType!Stuff, T))
+ {
+ initialize();
+ size_t result;
+ Node * n, newRoot;
+ foreach (item; stuff)
+ {
+ auto newNode = new Node(null, item);
+ (newRoot ? n._next : newRoot) = newNode;
+ n = newNode;
+ ++result;
+ }
+ if (!n) return 0;
+ // Last node points to the old root
+ n._next = _first;
+ _first = newRoot;
+ return result;
+ }
+
+ /// ditto
+ size_t insertFront(Stuff)(Stuff stuff)
+ if (isImplicitlyConvertible!(Stuff, T))
+ {
+ initialize();
+ auto newRoot = new Node(_first, stuff);
+ _first = newRoot;
+ return 1;
+ }
+
+/// ditto
+ alias insert = insertFront;
+
+/// ditto
+ alias stableInsert = insert;
+
+ /// ditto
+ alias stableInsertFront = insertFront;
+
+/**
+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 1).
+ */
+ T removeAny()
+ {
+ import std.algorithm.mutation : move;
+
+ assert(!empty, "SList.removeAny: List is empty");
+ auto result = move(_first._payload);
+ _first = _first._next;
+ return result;
+ }
+ /// ditto
+ alias stableRemoveAny = removeAny;
+
+/**
+Removes the value at the front 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 1).
+ */
+ void removeFront()
+ {
+ assert(!empty, "SList.removeFront: List is empty");
+ _first = _first._next;
+ }
+
+ /// ditto
+ alias stableRemoveFront = removeFront;
+
+/**
+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)).
+ */
+ size_t removeFront(size_t howMany)
+ {
+ size_t result;
+ while (_first && result < howMany)
+ {
+ _first = _first._next;
+ ++result;
+ }
+ return result;
+ }
+
+ /// ditto
+ alias stableRemoveFront = removeFront;
+
+/**
+Inserts $(D stuff) after range $(D r), which must be a range
+previously extracted from this container. Given that all ranges for a
+list end at the end of the list, this function essentially appends to
+the list and uses $(D r) as a potentially fast way to reach the last
+node in the list. Ideally $(D r) is positioned near or at the last
+element of the list.
+
+$(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 k + m), where $(D k) is the number of elements in
+$(D r) and $(D m) is the length of $(D stuff).
+
+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"]));
+--------------------
+ */
+
+ size_t insertAfter(Stuff)(Range r, Stuff stuff)
+ {
+ initialize();
+ if (!_first)
+ {
+ enforce(!r._head);
+ return insertFront(stuff);
+ }
+ enforce(r._head);
+ auto n = findLastNode(r._head);
+ SList tmp;
+ auto result = tmp.insertFront(stuff);
+ n._next = tmp._first;
+ return result;
+ }
+
+/**
+Similar to $(D insertAfter) above, but accepts a range bounded in
+count. This is important for ensuring fast insertions in the middle of
+the list. For fast insertions after a specified position $(D r), use
+$(D insertAfter(take(r, 1), stuff)). The complexity of that operation
+only depends on the number of elements in $(D stuff).
+
+Precondition: $(D r.original.empty || r.maxLength > 0)
+
+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).
+ */
+ size_t insertAfter(Stuff)(Take!Range r, Stuff stuff)
+ {
+ auto orig = r.source;
+ if (!orig._head)
+ {
+ // Inserting after a null range counts as insertion to the
+ // front
+ return insertFront(stuff);
+ }
+ enforce(!r.empty);
+ // Find the last valid element in the range
+ foreach (i; 1 .. r.maxLength)
+ {
+ if (!orig._head._next) break;
+ orig.popFront();
+ }
+ // insert here
+ SList tmp;
+ tmp.initialize();
+ tmp._first = orig._head._next;
+ auto result = tmp.insertFront(stuff);
+ orig._head._next = tmp._first;
+ return result;
+ }
+
+/// ditto
+ alias stableInsertAfter = insertAfter;
+
+/**
+Removes a range from the list in linear time.
+
+Returns: An empty range.
+
+Complexity: $(BIGOH n)
+ */
+ Range linearRemove(Range r)
+ {
+ if (!_first)
+ {
+ enforce(!r._head);
+ return this[];
+ }
+ auto n = findNode(_root, r._head);
+ n._next = null;
+ return Range(null);
+ }
+
+/**
+Removes a $(D Take!Range) from the list in linear time.
+
+Returns: A range comprehending the elements after the removed range.
+
+Complexity: $(BIGOH n)
+ */
+ Range linearRemove(Take!Range r)
+ {
+ auto orig = r.source;
+ // We have something to remove here
+ if (orig._head == _first)
+ {
+ // remove straight from the head of the list
+ for (; !r.empty; r.popFront())
+ {
+ removeFront();
+ }
+ return this[];
+ }
+ if (!r.maxLength)
+ {
+ // Nothing to remove, return the range itself
+ return orig;
+ }
+ // Remove from somewhere in the middle of the list
+ enforce(_first);
+ auto n1 = findNode(_root, orig._head);
+ auto n2 = findLastNode(orig._head, r.maxLength);
+ n1._next = n2._next;
+ return Range(n1._next);
+ }
+
+/// ditto
+ alias stableLinearRemove = linearRemove;
+}
+
+@safe unittest
+{
+ import std.algorithm.comparison : equal;
+
+ auto a = SList!int(5);
+ auto b = a;
+ auto r = a[];
+ a.insertFront(1);
+ b.insertFront(2);
+ assert(equal(a[], [2, 1, 5]));
+ assert(equal(b[], [2, 1, 5]));
+ r.front = 9;
+ assert(equal(a[], [2, 1, 9]));
+ assert(equal(b[], [2, 1, 9]));
+}
+
+@safe unittest
+{
+ auto s = SList!int(1, 2, 3);
+ auto n = s.findLastNode(s._root);
+ assert(n && n._payload == 3);
+}
+
+@safe unittest
+{
+ import std.range.primitives;
+ auto s = SList!int(1, 2, 5, 10);
+ assert(walkLength(s[]) == 4);
+}
+
+@safe unittest
+{
+ import std.range : take;
+ auto src = take([0, 1, 2, 3], 3);
+ auto s = SList!int(src);
+ assert(s == SList!int(0, 1, 2));
+}
+
+@safe unittest
+{
+ auto a = SList!int();
+ auto b = SList!int();
+ auto c = a ~ b[];
+ assert(c.empty);
+}
+
+@safe unittest
+{
+ auto a = SList!int(1, 2, 3);
+ auto b = SList!int(4, 5, 6);
+ auto c = a ~ b[];
+ assert(c == SList!int(1, 2, 3, 4, 5, 6));
+}
+
+@safe unittest
+{
+ auto a = SList!int(1, 2, 3);
+ auto b = [4, 5, 6];
+ auto c = a ~ b;
+ assert(c == SList!int(1, 2, 3, 4, 5, 6));
+}
+
+@safe unittest
+{
+ auto a = SList!int(1, 2, 3);
+ auto c = a ~ 4;
+ assert(c == SList!int(1, 2, 3, 4));
+}
+
+@safe unittest
+{
+ auto a = SList!int(2, 3, 4);
+ auto b = 1 ~ a;
+ assert(b == SList!int(1, 2, 3, 4));
+}
+
+@safe unittest
+{
+ auto a = [1, 2, 3];
+ auto b = SList!int(4, 5, 6);
+ auto c = a ~ b;
+ assert(c == SList!int(1, 2, 3, 4, 5, 6));
+}
+
+@safe unittest
+{
+ auto s = SList!int(1, 2, 3, 4);
+ s.insertFront([ 42, 43 ]);
+ assert(s == SList!int(42, 43, 1, 2, 3, 4));
+}
+
+@safe unittest
+{
+ auto s = SList!int(1, 2, 3);
+ assert(s.removeAny() == 1);
+ assert(s == SList!int(2, 3));
+ assert(s.stableRemoveAny() == 2);
+ assert(s == SList!int(3));
+}
+
+@safe unittest
+{
+ import std.algorithm.comparison : equal;
+
+ auto s = SList!int(1, 2, 3);
+ s.removeFront();
+ assert(equal(s[], [2, 3]));
+ s.stableRemoveFront();
+ assert(equal(s[], [3]));
+}
+
+@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));
+}
+
+@safe unittest
+{
+ auto a = SList!int(1, 2, 3);
+ auto b = SList!int(1, 2, 3);
+ assert(a.insertAfter(a[], b[]) == 3);
+}
+
+@safe unittest
+{
+ import std.range : take;
+ auto s = SList!int(1, 2, 3, 4);
+ auto r = take(s[], 2);
+ assert(s.insertAfter(r, 5) == 1);
+ assert(s == SList!int(1, 2, 5, 3, 4));
+}
+
+@safe unittest
+{
+ 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(equal(sl[], ["a", "b", "d", "e"]));
+ sl.insertAfter(take(sl[], 2), "c"); // insert after "b"
+ assert(equal(sl[], ["a", "b", "c", "d", "e"]));
+}
+
+@safe unittest
+{
+ import std.range.primitives;
+ auto s = SList!int(1, 2, 3, 4, 5);
+ auto r = s[];
+ popFrontN(r, 3);
+ auto r1 = s.linearRemove(r);
+ assert(s == SList!int(1, 2, 3));
+ assert(r1.empty);
+}
+
+@safe unittest
+{
+ auto s = SList!int(1, 2, 3, 4, 5);
+ auto r = s[];
+ auto r1 = s.linearRemove(r);
+ assert(s == SList!int());
+ assert(r1.empty);
+}
+
+@safe unittest
+{
+ import std.algorithm.comparison : equal;
+ import std.range;
+
+ auto s = SList!int(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
+ auto r = s[];
+ popFrontN(r, 3);
+ auto r1 = take(r, 4);
+ assert(equal(r1, [4, 5, 6, 7]));
+ auto r2 = s.linearRemove(r1);
+ assert(s == SList!int(1, 2, 3, 8, 9, 10));
+ assert(equal(r2, [8, 9, 10]));
+}
+
+@safe unittest
+{
+ import std.range.primitives;
+ auto lst = SList!int(1, 5, 42, 9);
+ assert(!lst.empty);
+ assert(lst.front == 1);
+ assert(walkLength(lst[]) == 4);
+
+ auto lst2 = lst ~ [ 1, 2, 3 ];
+ assert(walkLength(lst2[]) == 7);
+
+ auto lst3 = lst ~ [ 7 ];
+ assert(walkLength(lst3[]) == 5);
+}
+
+@safe unittest
+{
+ auto s = make!(SList!int)(1, 2, 3);
+}
+
+@safe unittest
+{
+ // 5193
+ static struct Data
+ {
+ const int val;
+ }
+ SList!Data list;
+}
+
+@safe unittest
+{
+ auto s = SList!int([1, 2, 3]);
+ s.front = 5; //test frontAssign
+ assert(s.front == 5);
+ auto r = s[];
+ 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/libphobos/src/std/container/util.d b/libphobos/src/std/container/util.d
new file mode 100644
index 0000000..5be9e7d
--- /dev/null
+++ b/libphobos/src/std/container/util.d
@@ -0,0 +1,189 @@
+/**
+This module contains some common utilities used by containers.
+
+This module is a submodule of $(MREF std, container).
+
+Source: $(PHOBOSSRC std/container/_util.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.util;
+
+/**
+Returns an initialized object. This function is mainly for eliminating
+construction differences between structs and classes. It allows code to not
+worry about whether the type it's constructing is a struct or a class.
+ */
+template make(T)
+if (is(T == struct) || is(T == class))
+{
+ T make(Args...)(Args arguments)
+ if (is(T == struct) && __traits(compiles, T(arguments)))
+ {
+ // constructing an std.container.Array without arguments,
+ // does not initialize its payload and is equivalent
+ // 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)
+ {
+ import std.range.primitives : ElementType;
+ alias ET = ElementType!(T.Range);
+ return T(ET[].init);
+ }
+ else
+ return T(arguments);
+ }
+
+ T make(Args...)(Args arguments)
+ if (is(T == class) && __traits(compiles, new T(arguments)))
+ {
+ return new T(arguments);
+ }
+}
+
+
+///
+@system unittest
+{
+ import std.algorithm.comparison : equal;
+ import std.container;
+
+ auto arr = make!(Array!int)([4, 2, 3, 1]);
+ assert(equal(arr[], [4, 2, 3, 1]));
+
+ auto rbt = make!(RedBlackTree!(int, "a > b"))([4, 2, 3, 1]);
+ assert(equal(rbt[], [4, 3, 2, 1]));
+
+ alias makeList = make!(SList!int);
+ auto slist = makeList(1, 2, 3);
+ assert(equal(slist[], [1, 2, 3]));
+}
+
+@system unittest
+{
+ import std.algorithm.comparison : equal;
+ import std.container;
+
+ auto arr1 = make!(Array!dchar)();
+ assert(arr1.empty);
+ auto arr2 = make!(Array!dchar)("hello"d);
+ assert(equal(arr2[], "hello"d));
+
+ auto rtb1 = make!(RedBlackTree!dchar)();
+ assert(rtb1.empty);
+ auto rtb2 = make!(RedBlackTree!dchar)('h', 'e', 'l', 'l', 'o');
+ assert(equal(rtb2[], "ehlo"d));
+}
+
+// Issue 8895
+@safe unittest
+{
+ import std.algorithm.comparison : equal;
+ import std.container;
+
+ auto a = make!(DList!int)(1,2,3,4);
+ auto b = make!(DList!int)(1,2,3,4);
+ auto c = make!(DList!int)(1,2,3,5);
+ auto d = make!(DList!int)(1,2,3,4,5);
+ assert(a == b); // this better terminate!
+ assert(a != c);
+ assert(a != d);
+}
+
+/**
+ * Convenience function for constructing a generic container.
+ */
+template make(alias Container, Args...)
+if (!is(Container))
+{
+ import std.range : isInputRange, isInfinite;
+ import std.traits : isDynamicArray;
+
+ auto make(Range)(Range 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)); }));
+}
+
+///
+@system unittest
+{
+ import std.algorithm.comparison : equal;
+ import std.container.array, std.container.rbtree, std.container.slist;
+ import std.range : iota;
+
+ auto arr = make!Array(iota(5));
+ assert(equal(arr[], [0, 1, 2, 3, 4]));
+
+ auto rbtmax = make!(RedBlackTree, "a > b")(iota(5));
+ assert(equal(rbtmax[], [4, 3, 2, 1, 0]));
+
+ auto rbtmin = make!RedBlackTree(4, 1, 3, 2);
+ assert(equal(rbtmin[], [1, 2, 3, 4]));
+
+ alias makeList = make!SList;
+ auto list = makeList(1, 7, 42);
+ assert(equal(list[], [1, 7, 42]));
+}
+
+@safe unittest
+{
+ import std.algorithm.comparison : equal;
+ import std.container.rbtree;
+
+ auto rbtmin = make!(RedBlackTree, "a < b", false)(3, 2, 2, 1);
+ assert(equal(rbtmin[], [1, 2, 3]));
+}
+
+// Issue 13872
+@system unittest
+{
+ import std.container;
+
+ auto tree1 = make!(RedBlackTree!int)();
+ auto refToTree1 = tree1;
+ refToTree1.insert(1);
+ assert(1 in tree1);
+
+ auto array1 = make!(Array!int)();
+ auto refToArray1 = array1;
+ refToArray1.insertBack(1);
+ assert(!array1.empty);
+
+ auto slist = make!(SList!int)();
+ auto refToSlist = slist;
+ refToSlist.insert(1);
+ assert(!slist.empty);
+
+ auto dlist = make!(DList!int)();
+ auto refToDList = dlist;
+ refToDList.insert(1);
+ assert(!dlist.empty);
+}
diff --git a/libphobos/src/std/conv.d b/libphobos/src/std/conv.d
new file mode 100644
index 0000000..127849d
--- /dev/null
+++ b/libphobos/src/std/conv.d
@@ -0,0 +1,6290 @@
+// Written in the D programming language.
+
+/**
+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 asOriginalType)
+ $(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: $(HTTP boost.org/LICENSE_1_0.txt, Boost License 1.0).
+
+Authors: $(HTTP digitalmars.com, Walter Bright),
+ $(HTTP erdani.org, Andrei Alexandrescu),
+ Shin Fujishiro,
+ Adam D. Ruppe,
+ Kenji Hara
+
+Source: $(PHOBOSSRC std/_conv.d)
+
+*/
+module std.conv;
+
+public import std.ascii : LetterCase;
+
+import std.meta;
+import std.range.primitives;
+import std.traits;
+
+// 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;
+ alias convFormat = format;
+}
+
+/* ************* Exceptions *************** */
+
+/**
+ * Thrown on conversion errors.
+ */
+class ConvException : Exception
+{
+ import std.exception : basicExceptionCtors;
+ ///
+ mixin basicExceptionCtors;
+}
+
+private auto convError(S, T)(S source, string fn = __FILE__, size_t ln = __LINE__)
+{
+ string msg;
+
+ if (source.empty)
+ msg = "Unexpected end of input when converting from type " ~ S.stringof ~ " to type " ~ T.stringof;
+ else
+ {
+ ElementType!S el = source.front;
+
+ if (el == '\n')
+ msg = text("Unexpected '\\n' when converting from type " ~ S.stringof ~ " to type " ~ T.stringof);
+ else
+ msg = text("Unexpected '", el,
+ "' 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__)
+{
+ 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
+private auto parseError(lazy string msg, string fn = __FILE__, size_t ln = __LINE__)
+{
+ return new ConvException(text("Can't parse string: ", msg), fn, ln);
+}
+
+private void parseCheck(alias source)(dchar c, string fn = __FILE__, size_t ln = __LINE__)
+{
+ if (source.empty)
+ throw parseError(text("unexpected end of input when expecting", "\"", c, "\""));
+ if (source.front != c)
+ throw parseError(text("\"", c, "\" is missing"), fn, ln);
+ source.popFront();
+}
+
+private
+{
+ T toStr(T, S)(S src)
+ if (isSomeString!T)
+ {
+ // workaround for Bugzilla 14198
+ static if (is(S == bool) && is(typeof({ T s = "string"; })))
+ {
+ return src ? "true" : "false";
+ }
+ else
+ {
+ import std.array : appender;
+ import std.format : FormatSpec, formatValue;
+
+ auto w = appender!T();
+ FormatSpec!(ElementEncodingType!T) f;
+ formatValue(w, src, f);
+ return w.data;
+ }
+ }
+
+ template isExactSomeString(T)
+ {
+ enum isExactSomeString = isSomeString!T && !is(T == enum);
+ }
+
+ template isEnumStrToStr(S, T)
+ {
+ enum isEnumStrToStr = isImplicitlyConvertible!(S, T) &&
+ is(S == enum) && isExactSomeString!T;
+ }
+ template isNullToStr(S, T)
+ {
+ enum isNullToStr = isImplicitlyConvertible!(S, T) &&
+ (is(Unqual!S == typeof(null))) && isExactSomeString!T;
+ }
+}
+
+/**
+ * Thrown on conversion overflow errors.
+ */
+class ConvOverflowException : ConvException
+{
+ @safe pure nothrow
+ this(string s, string fn = __FILE__, size_t ln = __LINE__)
+ {
+ super(s, fn, ln);
+ }
+}
+
+/**
+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 -))
+
+For conversion _to unsigned types, the grammar recognized is:
+$(PRE $(I UnsignedInteger):
+ $(I DecimalDigit)
+ $(I DecimalDigit) $(I UnsignedInteger))
+ */
+template to(T)
+{
+ T to(A...)(A args)
+ if (A.length > 0)
+ {
+ return toImpl!T(args);
+ }
+
+ // Fix issue 6175
+ T to(S)(ref S arg)
+ if (isStaticArray!S)
+ {
+ return toImpl!T(arg);
+ }
+
+ // Fix issue 16108
+ T to(S)(ref S arg)
+ if (isAggregateType!S && !isCopyable!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
+{
+ char[9] sarr = "blablabla";
+ auto darr = to!(char[])(sarr);
+ assert(sarr.ptr == darr.ptr);
+ assert(sarr.length == darr.length);
+}
+
+// Tests for issue 7348
+@safe pure /+nothrow+/ unittest
+{
+ assert(to!string(null) == "null");
+ assert(text(null) == "null");
+}
+
+// Tests for issue 11390
+@safe pure /+nothrow+/ unittest
+{
+ const(typeof(null)) ctn;
+ immutable(typeof(null)) itn;
+ assert(to!string(ctn) == "null");
+ assert(to!string(itn) == "null");
+}
+
+// Tests for issue 8729: do NOT skip leading WS
+@safe pure unittest
+{
+ import std.exception;
+ foreach (T; AliasSeq!(byte, ubyte, short, ushort, int, uint, long, ulong))
+ {
+ assertThrown!ConvException(to!T(" 0"));
+ assertThrown!ConvException(to!T(" 0", 8));
+ }
+ foreach (T; AliasSeq!(float, double, real))
+ {
+ assertThrown!ConvException(to!T(" 0"));
+ }
+
+ assertThrown!ConvException(to!bool(" true"));
+
+ alias NullType = typeof(null);
+ assertThrown!ConvException(to!NullType(" null"));
+
+ alias ARR = int[];
+ assertThrown!ConvException(to!ARR(" [1]"));
+
+ alias AA = int[int];
+ assertThrown!ConvException(to!AA(" [1:1]"));
+}
+
+/**
+If the source type is implicitly convertible to the target type, $(D
+to) simply performs the implicit conversion.
+ */
+private T toImpl(T, S)(S value)
+if (isImplicitlyConvertible!(S, T) &&
+ !isEnumStrToStr!(S, T) && !isNullToStr!(S, T))
+{
+ template isSignedInt(T)
+ {
+ enum isSignedInt = isIntegral!T && isSigned!T;
+ }
+ alias isUnsignedInt = isUnsigned;
+
+ // Conversion from integer to integer, and changing its sign
+ 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,
+ new ConvOverflowException("Conversion positive overflow"));
+ }
+ else static if (isSignedInt!S && isUnsignedInt!T)
+ { // signed to unsigned
+ import std.exception : enforce;
+ enforce(0 <= value,
+ new ConvOverflowException("Conversion negative overflow"));
+ }
+
+ return value;
+}
+
+@safe pure nothrow unittest
+{
+ enum E { a } // Issue 9523 - Allow identity enum conversion
+ auto e = to!E(E.a);
+ assert(e == E.a);
+}
+
+@safe pure nothrow unittest
+{
+ int a = 42;
+ auto b = to!long(a);
+ assert(a == b);
+}
+
+// Tests for issue 6377
+@safe pure unittest
+{
+ import std.exception;
+ // Conversion between same size
+ foreach (S; AliasSeq!(byte, short, int, long))
+ (){ // avoid slow optimizations for large functions @@@BUG@@@ 2396
+ alias U = Unsigned!S;
+
+ foreach (Sint; AliasSeq!(S, const S, immutable S))
+ foreach (Uint; AliasSeq!(U, const U, immutable U))
+ {
+ // positive overflow
+ Uint un = Uint.max;
+ assertThrown!ConvOverflowException(to!Sint(un),
+ text(Sint.stringof, ' ', Uint.stringof, ' ', un));
+
+ // negative overflow
+ Sint sn = -1;
+ assertThrown!ConvOverflowException(to!Uint(sn),
+ text(Sint.stringof, ' ', Uint.stringof, ' ', un));
+ }
+ }();
+
+ // Conversion between different size
+ 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;
+
+ static assert(U1.sizeof < S2.sizeof);
+
+ // small unsigned to big signed
+ foreach (Uint; AliasSeq!(U1, const U1, immutable U1))
+ foreach (Sint; AliasSeq!(S2, const S2, immutable S2))
+ {
+ Uint un = Uint.max;
+ assertNotThrown(to!Sint(un));
+ assert(to!Sint(un) == un);
+ }
+
+ // big unsigned to small signed
+ foreach (Uint; AliasSeq!(U2, const U2, immutable U2))
+ foreach (Sint; AliasSeq!(S1, const S1, immutable S1))
+ {
+ Uint un = Uint.max;
+ assertThrown(to!Sint(un));
+ }
+
+ static assert(S1.sizeof < U2.sizeof);
+
+ // small signed to big unsigned
+ 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; AliasSeq!(S2, const S2, immutable S2))
+ foreach (Uint; AliasSeq!(U1, const U1, immutable U1))
+ {
+ Sint sn = -1;
+ assertThrown!ConvOverflowException(to!Uint(sn));
+ }
+ }();
+}
+
+/*
+ Converting static arrays forwards to their dynamic counterparts.
+ */
+private T toImpl(T, S)(ref S s)
+if (isStaticArray!S)
+{
+ return toImpl!(T, typeof(s[0])[])(s);
+}
+
+@safe pure nothrow unittest
+{
+ char[4] test = ['a', 'b', 'c', 'd'];
+ static assert(!isInputRange!(Unqual!(char[4])));
+ assert(to!string(test) == test);
+}
+
+/**
+When source type supports member template function opCast, it is used.
+*/
+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();
+}
+
+@safe pure unittest
+{
+ static struct Test
+ {
+ struct T
+ {
+ this(S s) @safe pure { }
+ }
+ struct S
+ {
+ T opCast(U)() @safe pure { assert(false); }
+ }
+ }
+ cast(void) to!(Test.T)(Test.S());
+
+ // make sure std.conv.to is doing the same thing as initialization
+ Test.S s;
+ Test.T t = s;
+}
+
+@safe pure unittest
+{
+ class B
+ {
+ T opCast(T)() { return 43; }
+ }
+ auto b = new B;
+ assert(to!int(b) == 43);
+
+ struct S
+ {
+ T opCast(T)() { return 43; }
+ }
+ auto s = S();
+ assert(to!int(s) == 43);
+}
+
+/**
+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.))
+*/
+private T toImpl(T, S)(S value)
+if (!isImplicitlyConvertible!(S, T) &&
+ is(T == struct) && is(typeof(T(value))))
+{
+ return T(value);
+}
+
+// Bugzilla 3961
+@safe pure unittest
+{
+ struct Int
+ {
+ int x;
+ }
+ Int i = to!Int(1);
+
+ static struct Int2
+ {
+ int x;
+ this(int x) @safe pure { this.x = x; }
+ }
+ Int2 i2 = to!Int2(1);
+
+ static struct Int3
+ {
+ int x;
+ static Int3 opCall(int x) @safe pure
+ {
+ Int3 i;
+ i.x = x;
+ return i;
+ }
+ }
+ Int3 i3 = to!Int3(1);
+}
+
+// Bugzilla 6808
+@safe pure unittest
+{
+ static struct FakeBigInt
+ {
+ this(string s) @safe pure {}
+ }
+
+ string s = "101";
+ auto i3 = to!FakeBigInt(s);
+}
+
+/// ditto
+private T toImpl(T, S)(S value)
+if (!isImplicitlyConvertible!(S, T) &&
+ is(T == class) && is(typeof(new T(value))))
+{
+ return new T(value);
+}
+
+@safe pure unittest
+{
+ static struct S
+ {
+ int x;
+ }
+ static class C
+ {
+ int x;
+ this(int x) @safe pure { this.x = x; }
+ }
+
+ static class B
+ {
+ int value;
+ this(S src) @safe pure { value = src.x; }
+ this(C src) @safe pure { value = src.x; }
+ }
+
+ S s = S(1);
+ auto b1 = to!B(s); // == new B(s)
+ assert(b1.value == 1);
+
+ C c = new C(2);
+ auto b2 = to!B(c); // == new B(c)
+ assert(b2.value == 2);
+
+ auto c2 = to!C(3); // == new C(3)
+ assert(c2.x == 3);
+}
+
+@safe pure unittest
+{
+ struct S
+ {
+ class A
+ {
+ this(B b) @safe pure {}
+ }
+ class B : A
+ {
+ this() @safe pure { super(this); }
+ }
+ }
+
+ S.B b = new S.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);
+
+ static class C : Object
+ {
+ this() @safe pure {}
+ this(Object o) @safe pure {}
+ }
+
+ Object oc = new C();
+ C a2 = to!C(oc); // == new C(a)
+ // Construction conversion overrides down-casting conversion
+ assert(a2 !is a); //
+}
+
+/**
+Object-to-object conversions by dynamic casting throw exception when the source is
+non-null and the target is null.
+ */
+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))
+ {
+ // immutable <- immutable
+ enum isModConvertible = is(S == immutable);
+ }
+ else static if (is(T == const))
+ {
+ static if (is(T == shared))
+ {
+ // shared const <- shared
+ // shared const <- shared const
+ // shared const <- immutable
+ enum isModConvertible = is(S == shared) || is(S == immutable);
+ }
+ else
+ {
+ // const <- mutable
+ // const <- immutable
+ enum isModConvertible = !is(S == shared);
+ }
+ }
+ else
+ {
+ static if (is(T == shared))
+ {
+ // shared <- shared mutable
+ enum isModConvertible = is(S == shared) && !is(S == const);
+ }
+ else
+ {
+ // (mutable) <- (mutable)
+ enum isModConvertible = is(Unqual!S == S);
+ }
+ }
+ static assert(isModConvertible, "Bad modifier conversion: "~S.stringof~" to "~T.stringof);
+
+ auto result = ()@trusted{ return cast(T) value; }();
+ if (!result && value)
+ {
+ throw new ConvException("Cannot convert object of static type "
+ ~S.classinfo.name~" and dynamic type "~value.classinfo.name
+ ~" to type "~T.classinfo.name);
+ }
+ return result;
+}
+
+// Unittest for 6288
+@safe pure unittest
+{
+ import std.exception;
+
+ alias Identity(T) = T;
+ alias toConst(T) = const T;
+ 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)
+ {
+ static if (n == 0) alias AddModifier = Identity;
+ else static if (n == 1) alias AddModifier = toConst;
+ else static if (n == 2) alias AddModifier = toShared;
+ else static if (n == 3) alias AddModifier = toSharedConst;
+ else static if (n == 4) alias AddModifier = toImmutable;
+ }
+
+ interface I {}
+ interface J {}
+
+ class A {}
+ class B : A {}
+ class C : B, I, J {}
+ class D : I {}
+
+ 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;
+
+ // Compile time convertible equals to modifier convertible.
+ static if (isImplicitlyConvertible!(srcmod!Object, tgtmod!Object))
+ {
+ // Test runtime conversions: class to class, class to interface,
+ // interface to class, and interface to interface
+
+ // Check that the runtime conversion to succeed
+ srcmod!A ac = new srcmod!C();
+ srcmod!I ic = new srcmod!C();
+ assert(to!(tgtmod!C)(ac) !is null); // A(c) to C
+ assert(to!(tgtmod!I)(ac) !is null); // A(c) to I
+ assert(to!(tgtmod!C)(ic) !is null); // I(c) to C
+ assert(to!(tgtmod!J)(ic) !is null); // I(c) to J
+
+ // Check that the runtime conversion fails
+ srcmod!A ab = new srcmod!B();
+ srcmod!I id = new srcmod!D();
+ assertThrown(to!(tgtmod!C)(ab)); // A(b) to C
+ assertThrown(to!(tgtmod!I)(ab)); // A(b) to I
+ assertThrown(to!(tgtmod!C)(id)); // I(d) to C
+ assertThrown(to!(tgtmod!J)(id)); // I(d) to J
+ }
+ else
+ {
+ // Check that the conversion is rejected statically
+ static assert(!is(typeof(to!(tgtmod!C)(srcmod!A.init)))); // A to C
+ static assert(!is(typeof(to!(tgtmod!I)(srcmod!A.init)))); // A to I
+ static assert(!is(typeof(to!(tgtmod!C)(srcmod!I.init)))); // I to C
+ static assert(!is(typeof(to!(tgtmod!J)(srcmod!I.init)))); // I to J
+ }
+ }();
+}
+
+/**
+Handles type _to string conversions
+*/
+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)
+ {
+ // string-to-string with incompatible qualifier conversion
+ static if (is(ElementEncodingType!T == immutable))
+ {
+ // conversion (mutable|const) -> immutable
+ return value.idup;
+ }
+ else
+ {
+ // conversion (immutable|const) -> mutable
+ return value.dup;
+ }
+ }
+ else static if (isExactSomeString!S)
+ {
+ import std.array : appender;
+ // other string-to-string
+ //Use Appender directly instead of toStr, which also uses a formatedWrite
+ auto w = appender!T();
+ w.put(value);
+ return w.data;
+ }
+ else static if (isIntegral!S && !is(S == enum))
+ {
+ // other integral-to-string conversions with default radix
+ return toImpl!(T, S)(value, 10);
+ }
+ else static if (is(S == void[]) || is(S == const(void)[]) || is(S == immutable(void)[]))
+ {
+ import core.stdc.string : memcpy;
+ import std.exception : enforce;
+ // Converting void array to string
+ alias Char = Unqual!(ElementEncodingType!T);
+ auto raw = cast(const(ubyte)[]) value;
+ enforce(raw.length % Char.sizeof == 0,
+ new ConvException("Alignment mismatch in converting a "
+ ~ S.stringof ~ " to a "
+ ~ T.stringof));
+ auto result = new Char[raw.length / Char.sizeof];
+ ()@trusted{ memcpy(result.ptr, value.ptr, value.length); }();
+ return cast(T) result;
+ }
+ else static if (isPointer!S && isSomeChar!(PointerTarget!S))
+ {
+ // 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)
+ {
+ foreach (member; NoDuplicates!(EnumMembers!S))
+ {
+ case member:
+ return to!T(enumRep!(immutable(T), S, member));
+ }
+ default:
+ }
+ }
+ else
+ {
+ foreach (member; EnumMembers!S)
+ {
+ if (value == member)
+ return to!T(enumRep!(immutable(T), S, member));
+ }
+ }
+
+ import std.array : appender;
+ import std.format : FormatSpec, formatValue;
+
+ //Default case, delegate to format
+ //Note: we don't call toStr directly, to avoid duplicate work.
+ auto app = appender!T();
+ app.put("cast(" ~ S.stringof ~ ")");
+ FormatSpec!char f;
+ formatValue(app, cast(OriginalType!S) value, f);
+ return app.data;
+ }
+ else
+ {
+ // other non-string values runs formatting
+ return toStr!T(value);
+ }
+}
+
+// Bugzilla 14042
+@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, "");
+ }
+}
+
+/*
+ To string conversion for non copy-able structs
+ */
+private T toImpl(T, S)(ref S value)
+if (!(isImplicitlyConvertible!(S, T) &&
+ !isEnumStrToStr!(S, T) && !isNullToStr!(S, T)) &&
+ !isInfinite!S && isExactSomeString!T && !isCopyable!S)
+{
+ import std.array : appender;
+ import std.format : FormatSpec, formatValue;
+
+ auto w = appender!T();
+ FormatSpec!(ElementEncodingType!T) f;
+ formatValue(w, value, f);
+ return w.data;
+}
+
+// Bugzilla 16108
+@system unittest
+{
+ static struct A
+ {
+ int val;
+ bool flag;
+
+ string toString() { return text(val, ":", flag); }
+
+ @disable this(this);
+ }
+
+ auto a = A();
+ assert(to!string(a) == "0:false");
+
+ static struct B
+ {
+ int val;
+ bool flag;
+
+ @disable this(this);
+ }
+
+ auto b = B();
+ assert(to!string(b) == "B(0, false)");
+}
+
+/*
+ Check whether type $(D T) can be used in a switch statement.
+ This is useful for compile-time generation of switch case statements.
+*/
+private template isSwitchable(E)
+{
+ enum bool isSwitchable = is(typeof({
+ switch (E.init) { default: }
+ }));
+}
+
+//
+@safe unittest
+{
+ static assert(isSwitchable!int);
+ static assert(!isSwitchable!double);
+ static assert(!isSwitchable!real);
+}
+
+//Static representation of the index I of the enum S,
+//In representation T.
+//T must be an immutable string (avoids un-necessary initializations).
+private template enumRep(T, S, S value)
+if (is (T == immutable) && isExactSomeString!T && is(S == enum))
+{
+ static T enumRep = toStr!T(value);
+}
+
+@safe pure unittest
+{
+ import std.exception;
+ void dg()
+ {
+ // string to string conversion
+ alias Chars = AliasSeq!(char, wchar, dchar);
+ foreach (LhsC; Chars)
+ {
+ alias LhStrings = AliasSeq!(LhsC[], const(LhsC)[], immutable(LhsC)[]);
+ foreach (Lhs; LhStrings)
+ {
+ foreach (RhsC; Chars)
+ {
+ alias RhStrings = AliasSeq!(RhsC[], const(RhsC)[], immutable(RhsC)[]);
+ foreach (Rhs; RhStrings)
+ {
+ Lhs s1 = to!Lhs("wyda");
+ Rhs s2 = to!Rhs(s1);
+ //writeln(Lhs.stringof, " -> ", Rhs.stringof);
+ assert(s1 == to!Lhs(s2));
+ }
+ }
+ }
+ }
+
+ foreach (T; Chars)
+ {
+ foreach (U; Chars)
+ {
+ T[] s1 = to!(T[])("Hello, world!");
+ auto s2 = to!(U[])(s1);
+ assert(s1 == to!(T[])(s2));
+ auto s3 = to!(const(U)[])(s1);
+ assert(s1 == to!(T[])(s3));
+ auto s4 = to!(immutable(U)[])(s1);
+ assert(s1 == to!(T[])(s4));
+ }
+ }
+ }
+ dg();
+ assertCTFEable!dg;
+}
+
+@safe pure unittest
+{
+ // Conversion representing bool value with string
+ bool b;
+ assert(to!string(b) == "false");
+ b = true;
+ assert(to!string(b) == "true");
+}
+
+@safe pure unittest
+{
+ // Conversion representing character value with string
+ alias AllChars =
+ AliasSeq!( char, const( char), immutable( char),
+ wchar, const(wchar), immutable(wchar),
+ dchar, const(dchar), immutable(dchar));
+ foreach (Char1; AllChars)
+ {
+ foreach (Char2; AllChars)
+ {
+ Char1 c = 'a';
+ assert(to!(Char2[])(c)[0] == c);
+ }
+ uint x = 4;
+ assert(to!(Char1[])(x) == "4");
+ }
+
+ string s = "foo";
+ string s2;
+ foreach (char c; s)
+ {
+ s2 ~= to!string(c);
+ }
+ assert(s2 == "foo");
+}
+
+@safe pure nothrow unittest
+{
+ import std.exception;
+ // Conversion representing integer values with string
+
+ 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; AliasSeq!(byte, short, int, long))
+ {
+ assert(to!string(Int(0)) == "0");
+ assert(to!string(Int(9)) == "9");
+ assert(to!string(Int(123)) == "123");
+ assert(to!string(Int(-0)) == "0");
+ assert(to!string(Int(-9)) == "-9");
+ assert(to!string(Int(-123)) == "-123");
+ assert(to!string(const(Int)(6)) == "6");
+ }
+
+ assert(wtext(int.max) == "2147483647"w);
+ assert(wtext(int.min) == "-2147483648"w);
+ assert(to!string(0L) == "0");
+
+ assertCTFEable!(
+ {
+ assert(to!string(1uL << 62) == "4611686018427387904");
+ assert(to!string(0x100000000) == "4294967296");
+ assert(to!string(-138L) == "-138");
+ });
+}
+
+@safe unittest // sprintf issue
+{
+ double[2] a = [ 1.5, 2.5 ];
+ assert(to!string(a) == "[1.5, 2.5]");
+}
+
+@system unittest
+{
+ // Conversion representing class object with string
+ class A
+ {
+ override string toString() const { return "an A"; }
+ }
+ A a;
+ assert(to!string(a) == "null");
+ a = new A;
+ assert(to!string(a) == "an A");
+
+ // Bug 7660
+ class C { override string toString() const { return "C"; } }
+ struct S { C c; alias c this; }
+ S s; s.c = new C();
+ assert(to!string(s) == "C");
+}
+
+@safe unittest
+{
+ // Conversion representing struct object with string
+ struct S1
+ {
+ string toString() { return "wyda"; }
+ }
+ assert(to!string(S1()) == "wyda");
+
+ struct S2
+ {
+ int a = 42;
+ float b = 43.5;
+ }
+ S2 s2;
+ assert(to!string(s2) == "S2(42, 43.5)");
+
+ // Test for issue 8080
+ struct S8080
+ {
+ short[4] data;
+ alias data this;
+ string toString() { return "<S>"; }
+ }
+ S8080 s8080;
+ assert(to!string(s8080) == "<S>");
+}
+
+@safe unittest
+{
+ // Conversion representing enum value with string
+ enum EB : bool { a = true }
+ enum EU : uint { a = 0, b = 1, c = 2 } // base type is unsigned
+ enum EI : int { a = -1, b = 0, c = 1 } // base type is signed (bug 7909)
+ enum EF : real { a = 1.414, b = 1.732, c = 2.236 }
+ enum EC : char { a = 'x', b = 'y' }
+ enum ES : string { a = "aaa", b = "bbb" }
+
+ foreach (E; AliasSeq!(EB, EU, EI, EF, EC, ES))
+ {
+ assert(to! string(E.a) == "a"c);
+ assert(to!wstring(E.a) == "a"w);
+ assert(to!dstring(E.a) == "a"d);
+ }
+
+ // Test an value not corresponding to an enum member.
+ 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);
+}
+
+@safe unittest
+{
+ enum E
+ {
+ foo,
+ doo = foo, // check duplicate switch statements
+ bar,
+ }
+
+ //Test regression 12494
+ assert(to!string(E.foo) == "foo");
+ assert(to!string(E.doo) == "foo");
+ assert(to!string(E.bar) == "bar");
+
+ foreach (S; AliasSeq!(string, wstring, dstring, const(char[]), const(wchar[]), const(dchar[])))
+ {
+ auto s1 = to!S(E.foo);
+ auto s2 = to!S(E.foo);
+ assert(s1 == s2);
+ // ensure we don't allocate when it's unnecessary
+ assert(s1 is s2);
+ }
+
+ foreach (S; AliasSeq!(char[], wchar[], dchar[]))
+ {
+ auto s1 = to!S(E.foo);
+ auto s2 = to!S(E.foo);
+ assert(s1 == s2);
+ // ensure each mutable array is unique
+ assert(s1 !is s2);
+ }
+}
+
+// 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);
+}
+body
+{
+ alias EEType = Unqual!(ElementEncodingType!T);
+
+ T toStringRadixConvert(size_t bufLen)(uint runtimeRadix = 0)
+ {
+ Unsigned!(Unqual!S) div = void, mValue = unsigned(value);
+
+ size_t index = bufLen;
+ EEType[bufLen] buffer = void;
+ char baseChar = letterCase == LetterCase.lower ? 'a' : 'A';
+ char mod = void;
+
+ do
+ {
+ 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);
+
+ return cast(T) buffer[index .. $].dup;
+ }
+
+ import std.array : array;
+ switch (radix)
+ {
+ case 10:
+ // The (value+0) is so integral promotions happen to the type
+ return toChars!(10, EEType)(value + 0).array;
+ case 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 toChars!(2, EEType)(unsigned(unsigned(value) + 0)).array;
+ case 8:
+ return toChars!(8, EEType)(unsigned(unsigned(value) + 0)).array;
+
+ default:
+ return toStringRadixConvert!(S.sizeof * 6)(radix);
+ }
+}
+
+@safe pure nothrow unittest
+{
+ foreach (Int; AliasSeq!(uint, ulong))
+ {
+ assert(to!string(Int(16), 16) == "10");
+ assert(to!string(Int(15), 2u) == "1111");
+ assert(to!string(Int(1), 2u) == "1");
+ assert(to!string(Int(0x1234AF), 16u) == "1234AF");
+ assert(to!string(Int(0x1234BCD), 16u, LetterCase.upper) == "1234BCD");
+ assert(to!string(Int(0x1234AF), 16u, LetterCase.lower) == "1234af");
+ }
+
+ foreach (Int; AliasSeq!(int, long))
+ {
+ assert(to!string(Int(-10), 10u) == "-10");
+ }
+
+ assert(to!string(byte(-10), 16) == "F6");
+ assert(to!string(long.min) == "-9223372036854775808");
+ assert(to!string(long.max) == "9223372036854775807");
+}
+
+/**
+Narrowing numeric-numeric conversions throw when the value does not
+fit in the narrower type.
+ */
+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;
+ static if (sSmallest < 0)
+ {
+ // possible underflow converting from a signed
+ static if (tSmallest == 0)
+ {
+ immutable good = value >= 0;
+ }
+ else
+ {
+ static assert(tSmallest < 0);
+ immutable good = value >= tSmallest;
+ }
+ if (!good)
+ throw new ConvOverflowException("Conversion negative overflow");
+ }
+ static if (S.max > T.max)
+ {
+ // possible overflow
+ if (value > T.max)
+ throw new ConvOverflowException("Conversion positive overflow");
+ }
+ return (ref value)@trusted{ return cast(T) value; }(value);
+}
+
+@safe pure unittest
+{
+ import std.exception;
+
+ dchar a = ' ';
+ assert(to!char(a) == ' ');
+ a = 300;
+ assert(collectException(to!char(a)));
+
+ dchar from0 = 'A';
+ char to0 = to!char(from0);
+
+ wchar from1 = 'A';
+ char to1 = to!char(from1);
+
+ char from2 = 'A';
+ char to2 = to!char(from2);
+
+ char from3 = 'A';
+ wchar to3 = to!wchar(from3);
+
+ char from4 = 'A';
+ dchar to4 = to!dchar(from4);
+}
+
+@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 }
+ 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 }
+ 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
+
+ enum E3 : int { A = -1, B = 1, C = 255, D = 0 }
+ assertThrown!ConvOverflowException(to!ubyte(E3.A));
+ assertThrown!ConvOverflowException(to!bool(E3.A));
+ assert(to!byte(E3.A) == -1);
+ assert(to!byte(E3.B) == 1);
+ assert(to!ubyte(E3.C) == 255);
+ assert(to!bool(E3.B) == true);
+ assertThrown!ConvOverflowException(to!byte(E3.C));
+ assertThrown!ConvOverflowException(to!bool(E3.C));
+ assert(to!bool(E3.D) == false);
+
+}
+
+/**
+Array-to-array conversion (except when target is a string type)
+converts each element in turn by using $(D to).
+ */
+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]);
+
+ static if (isStaticArray!T)
+ {
+ import std.exception : enforce;
+ auto res = to!(E[])(value);
+ enforce!ConvException(T.length == res.length,
+ convFormat("Length mismatch when converting to static array: %s vs %s", T.length, res.length));
+ return res[0 .. T.length];
+ }
+ else
+ {
+ import std.array : appender;
+ auto w = appender!(E[])();
+ w.reserve(value.length);
+ foreach (i, ref e; value)
+ {
+ w.put(to!E(e));
+ }
+ return w.data;
+ }
+}
+
+@safe pure unittest
+{
+ import std.exception;
+
+ // array to array conversions
+ uint[] a = [ 1u, 2, 3 ];
+ auto b = to!(float[])(a);
+ assert(b == [ 1.0f, 2, 3 ]);
+
+ immutable(int)[3] d = [ 1, 2, 3 ];
+ b = to!(float[])(d);
+ assert(b == [ 1.0f, 2, 3 ]);
+
+ uint[][] e = [ a, a ];
+ auto f = to!(float[][])(e);
+ assert(f[0] == b && f[1] == b);
+
+ // Test for bug 8264
+ struct Wrap
+ {
+ string wrap;
+ alias wrap this;
+ }
+ Wrap[] warr = to!(Wrap[])(["foo", "bar"]); // should work
+
+ // Issue 12633
+ import std.conv : to;
+ const s2 = ["10", "20"];
+
+ immutable int[2] a3 = s2.to!(int[2]);
+ assert(a3 == [10, 20]);
+
+ // verify length mismatches are caught
+ immutable s4 = [1, 2, 3, 4];
+ foreach (i; [1, 4])
+ {
+ auto ex = collectException(s4[0 .. i].to!(int[2]));
+ assert(ex && ex.msg == "Length mismatch when converting to static array: 2 vs " ~ [cast(char)(i + '0')],
+ ex ? ex.msg : "Exception was not thrown!");
+ }
+}
+
+@safe unittest
+{
+ auto b = [ 1.0f, 2, 3 ];
+
+ auto c = to!(string[])(b);
+ assert(c[0] == "1" && c[1] == "2" && c[2] == "3");
+}
+
+/**
+Associative array to associative array conversion converts each key
+and each value in turn.
+ */
+private T toImpl(T, S)(S value)
+if (isAssociativeArray!S &&
+ isAssociativeArray!T && !is(T == enum))
+{
+ /* This code is potentially unsafe.
+ */
+ alias K2 = KeyType!T;
+ alias V2 = ValueType!T;
+
+ // While we are "building" the AA, we need to unqualify its values, and only re-qualify at the end
+ Unqual!V2[K2] result;
+
+ foreach (k1, v1; value)
+ {
+ // Cast values temporarily to Unqual!V2 to store them to result variable
+ result[to!K2(k1)] = cast(Unqual!V2) to!V2(v1);
+ }
+ // Cast back to original type
+ return cast(T) result;
+}
+
+@safe unittest
+{
+ // hash to hash conversions
+ int[string] a;
+ a["0"] = 1;
+ a["1"] = 2;
+ auto b = to!(double[dstring])(a);
+ assert(b["0"d] == 1 && b["1"d] == 2);
+}
+@safe unittest // Bugzilla 8705, from doc
+{
+ import std.exception;
+ int[string][double[int[]]] a;
+ auto b = to!(short[wstring][string[double[]]])(a);
+ a = [null:["hello":int.max]];
+ assertThrown!ConvOverflowException(to!(short[wstring][string[double[]]])(a));
+}
+@system unittest // Extra cases for AA with qualifiers conversion
+{
+ int[][int[]] a;// = [[], []];
+ auto b = to!(immutable(short[])[immutable short[]])(a);
+
+ double[dstring][int[long[]]] c;
+ auto d = to!(immutable(short[immutable wstring])[immutable string[double[]]])(c);
+}
+
+private void testIntegralToFloating(Integral, Floating)()
+{
+ Integral a = 42;
+ auto b = to!Floating(a);
+ assert(a == b);
+ assert(a == to!Integral(b));
+}
+
+private void testFloatingToIntegral(Floating, Integral)()
+{
+ bool convFails(Source, Target, E)(Source src)
+ {
+ try
+ auto t = to!Target(src);
+ catch (E)
+ return true;
+ return false;
+ }
+
+ // convert some value
+ Floating a = 4.2e1;
+ auto b = to!Integral(a);
+ assert(is(typeof(b) == Integral) && b == 42);
+ // convert some negative value (if applicable)
+ a = -4.2e1;
+ static if (Integral.min < 0)
+ {
+ b = to!Integral(a);
+ assert(is(typeof(b) == Integral) && b == -42);
+ }
+ else
+ {
+ // no go for unsigned types
+ assert(convFails!(Floating, Integral, ConvOverflowException)(a));
+ }
+ // convert to the smallest integral value
+ a = 0.0 + Integral.min;
+ static if (Integral.min < 0)
+ {
+ a = -a; // -Integral.min not representable as an Integral
+ assert(convFails!(Floating, Integral, ConvOverflowException)(a)
+ || Floating.sizeof <= Integral.sizeof);
+ }
+ a = 0.0 + Integral.min;
+ assert(to!Integral(a) == Integral.min);
+ --a; // no more representable as an Integral
+ assert(convFails!(Floating, Integral, ConvOverflowException)(a)
+ || Floating.sizeof <= Integral.sizeof);
+ a = 0.0 + Integral.max;
+ assert(to!Integral(a) == Integral.max || Floating.sizeof <= Integral.sizeof);
+ ++a; // no more representable as an Integral
+ assert(convFails!(Floating, Integral, ConvOverflowException)(a)
+ || Floating.sizeof <= Integral.sizeof);
+ // convert a value with a fractional part
+ a = 3.14;
+ assert(to!Integral(a) == 3);
+ a = 3.99;
+ assert(to!Integral(a) == 3);
+ static if (Integral.min < 0)
+ {
+ a = -3.14;
+ assert(to!Integral(a) == -3);
+ a = -3.99;
+ assert(to!Integral(a) == -3);
+ }
+}
+
+@safe pure unittest
+{
+ 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)
+ {
+ T a = 42;
+ auto b = to!T(a);
+ assert(is(typeof(a) == typeof(b)) && a == b);
+ }
+ }
+ // test that floating-point numbers convert properly to largest ints
+ // see http://oregonstate.edu/~peterseb/mth351/docs/351s2001_fp80x87.html
+ // look for "largest fp integer with a predecessor"
+ {
+ // float
+ int a = 16_777_215; // 2^24 - 1
+ assert(to!int(to!float(a)) == a);
+ assert(to!int(to!float(-a)) == -a);
+ // double
+ long b = 9_007_199_254_740_991; // 2^53 - 1
+ assert(to!long(to!double(b)) == b);
+ assert(to!long(to!double(-b)) == -b);
+ // real
+ 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
+ {
+ // AllInts[0 .. $ - 1] should be AllInts
+ // @@@ BUG IN COMPILER @@@
+ foreach (Integral; AllInts[0 .. $ - 1])
+ {
+ foreach (Floating; AllFloats)
+ {
+ testFloatingToIntegral!(Floating, Integral)();
+ }
+ }
+ }
+ // test conversion integral => floating
+ {
+ foreach (Integral; AllInts[0 .. $ - 1])
+ {
+ foreach (Floating; AllFloats)
+ {
+ testIntegralToFloating!(Integral, Floating)();
+ }
+ }
+ }
+ // test parsing
+ {
+ foreach (T; AllNumerics)
+ {
+ // from type immutable(char)[2]
+ auto a = to!T("42");
+ assert(a == 42);
+ // from type char[]
+ char[] s1 = "42".dup;
+ a = to!T(s1);
+ assert(a == 42);
+ // from type char[2]
+ char[2] s2;
+ s2[] = "42";
+ a = to!T(s2);
+ assert(a == 42);
+ // from type immutable(wchar)[2]
+ a = to!T("42"w);
+ assert(a == 42);
+ }
+ }
+}
+
+@safe unittest
+{
+ 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);
+ // array test
+ T[] b = new T[2];
+ b[0] = 42;
+ b[1] = 33;
+ assert(to!string(b) == "[42, 33]");
+ }
+ }
+ // test array to string conversion
+ foreach (T ; AllNumerics)
+ {
+ auto a = [to!T(1), 2, 3];
+ 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 == "Test1");
+}
+
+
+/**
+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.))
+*/
+private T toImpl(T, S)(S value)
+if (isInputRange!S && isSomeChar!(ElementEncodingType!S) &&
+ !isExactSomeString!T && is(typeof(parse!T(value))))
+{
+ scope(success)
+ {
+ if (!value.empty)
+ {
+ throw convError!(S, T)(value);
+ }
+ }
+ return parse!T(value);
+}
+
+/// ditto
+private T toImpl(T, S)(S value, uint radix)
+if (isInputRange!S && !isInfinite!S && isSomeChar!(ElementEncodingType!S) &&
+ isIntegral!T && is(typeof(parse!T(value, radix))))
+{
+ scope(success)
+ {
+ if (!value.empty)
+ {
+ throw convError!(S, T)(value);
+ }
+ }
+ return parse!T(value, radix);
+}
+
+@safe pure unittest
+{
+ // Issue 6668 - ensure no collaterals thrown
+ try { to!uint("-1"); }
+ catch (ConvException e) { assert(e.next is null); }
+}
+
+@safe pure unittest
+{
+ foreach (Str; AliasSeq!(string, wstring, dstring))
+ {
+ Str a = "123";
+ assert(to!int(a) == 123);
+ assert(to!double(a) == 123);
+ }
+
+ // 6255
+ auto n = to!int("FF", 16);
+ 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.
+*/
+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)
+ {
+ if (Member == value)
+ return Member;
+ }
+ throw new ConvException(convFormat("Value (%s) does not match any member value of enum '%s'", value, T.stringof));
+}
+
+@safe pure unittest
+{
+ import std.exception;
+ enum En8143 : int { A = 10, B = 20, C = 30, D = 20 }
+ enum En8143[][] m3 = to!(En8143[][])([[10, 30], [30, 10]]);
+ static assert(m3 == [[En8143.A, En8143.C], [En8143.C, En8143.A]]);
+
+ En8143 en1 = to!En8143(10);
+ assert(en1 == En8143.A);
+ assertThrown!ConvException(to!En8143(5)); // matches none
+ En8143[][] m1 = to!(En8143[][])([[10, 30], [30, 10]]);
+ assert(m1 == [[En8143.A, En8143.C], [En8143.C, En8143.A]]);
+}
+
+/***************************************************************
+ Rounded conversion from floating point to integral.
+
+Rounded conversions do not work with non-integral target types.
+ */
+
+template roundTo(Target)
+{
+ Target roundTo(Source)(Source value)
+ {
+ import std.math : trunc;
+
+ static assert(isFloatingPoint!Source);
+ static assert(isIntegral!Target);
+ return to!Target(trunc(value + (value < 0 ? -0.5L : 0.5L)));
+ }
+}
+
+///
+@safe unittest
+{
+ assert(roundTo!int(3.14) == 3);
+ assert(roundTo!int(3.49) == 3);
+ assert(roundTo!int(3.5) == 4);
+ assert(roundTo!int(3.999) == 4);
+ assert(roundTo!int(-3.14) == -3);
+ assert(roundTo!int(-3.49) == -3);
+ assert(roundTo!int(-3.5) == -4);
+ assert(roundTo!int(-3.999) == -4);
+ assert(roundTo!(const int)(to!(const double)(-3.999)) == -4);
+}
+
+@safe unittest
+{
+ import std.exception;
+ // boundary values
+ 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);
+ assertThrown!ConvOverflowException(roundTo!Int(Int.min - 0.5L));
+ assertThrown!ConvOverflowException(roundTo!Int(Int.max + 0.5L));
+ }
+}
+
+/**
+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;
+ auto s = source.representation;
+ }
+ else
+ {
+ alias s = source;
+ }
+
+ if (!s.empty)
+ {
+ auto c1 = toLower(s.front);
+ bool result = c1 == 't';
+ if (result || c1 == 'f')
+ {
+ s.popFront();
+ foreach (c; result ? "rue" : "alse")
+ {
+ if (s.empty || toLower(s.front) != c)
+ goto Lerr;
+ s.popFront();
+ }
+
+ static if (isNarrowString!Source)
+ source = cast(Source) s;
+
+ return result;
+ }
+ }
+Lerr:
+ throw parseError("bool should be case-insensitive 'true' or 'false'");
+}
+
+///
+@safe unittest
+{
+ auto s = "true";
+ bool b = parse!bool(s);
+ assert(b);
+}
+
+@safe unittest
+{
+ import std.algorithm.comparison : equal;
+ import std.exception;
+ struct InputString
+ {
+ string _s;
+ @property auto front() { return _s.front; }
+ @property bool empty() { return _s.empty; }
+ void popFront() { _s.popFront(); }
+ }
+
+ auto s = InputString("trueFALSETrueFalsetRUEfALSE");
+ assert(parse!bool(s) == true);
+ assert(s.equal("FALSETrueFalsetRUEfALSE"));
+ assert(parse!bool(s) == false);
+ assert(s.equal("TrueFalsetRUEfALSE"));
+ assert(parse!bool(s) == true);
+ assert(s.equal("FalsetRUEfALSE"));
+ assert(parse!bool(s) == false);
+ assert(s.equal("tRUEfALSE"));
+ assert(parse!bool(s) == true);
+ assert(s.equal("fALSE"));
+ assert(parse!bool(s) == false);
+ assert(s.empty);
+
+ foreach (ss; ["tfalse", "ftrue", "t", "f", "tru", "fals", ""])
+ {
+ s = InputString(ss);
+ assertThrown!ConvException(parse!bool(s));
+ }
+}
+
+/**
+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))
+{
+ static if (Target.sizeof < int.sizeof)
+ {
+ // smaller types are handled like integers
+ auto v = .parse!(Select!(Target.min < 0, int, uint))(s);
+ auto result = ()@trusted{ return cast(Target) v; }();
+ if (result == v)
+ return result;
+ throw new ConvOverflowException("Overflow in integral conversion");
+ }
+ else
+ {
+ // int or larger types
+
+ static if (Target.min < 0)
+ bool sign = false;
+ else
+ enum bool sign = false;
+
+ enum char maxLastDigit = Target.min < 0 ? 7 : 5;
+ uint c;
+
+ static if (isNarrowString!Source)
+ {
+ import std.string : representation;
+ auto source = s.representation;
+ }
+ else
+ {
+ alias source = s;
+ }
+
+ if (source.empty)
+ goto Lerr;
+
+ c = source.front;
+
+ static if (Target.min < 0)
+ {
+ switch (c)
+ {
+ case '-':
+ sign = true;
+ goto case '+';
+ case '+':
+ source.popFront();
+
+ if (source.empty)
+ goto Lerr;
+
+ c = source.front;
+
+ break;
+
+ default:
+ break;
+ }
+ }
+ c -= '0';
+ if (c <= 9)
+ {
+ Target v = cast(Target) c;
+
+ source.popFront();
+
+ while (!source.empty)
+ {
+ c = cast(typeof(c)) (source.front - '0');
+
+ if (c > 9)
+ break;
+
+ if (v >= 0 && (v < Target.max/10 ||
+ (v == Target.max/10 && c <= maxLastDigit + sign)))
+ {
+ // Note: `v` can become negative here in case of parsing
+ // the most negative value:
+ v = cast(Target) (v * 10 + c);
+
+ source.popFront();
+ }
+ else
+ throw new ConvOverflowException("Overflow in integral conversion");
+ }
+
+ if (sign)
+ v = -v;
+
+ static if (isNarrowString!Source)
+ s = cast(Source) source;
+
+ return v;
+ }
+Lerr:
+ static if (isNarrowString!Source)
+ throw convError!(Source, Target)(cast(Source) source);
+ 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
+{
+ import std.string : tr;
+ string test = "123 \t 76.14";
+ auto a = parse!uint(test);
+ assert(a == 123);
+ assert(test == " \t 76.14"); // parse bumps string
+ test = tr(test, " \t\n\r", "", "d"); // 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)
+ {
+ assert(to!Int("+0") == 0);
+ assert(to!Int("-0") == 0);
+ }
+ }
+
+ static if (Int.sizeof >= byte.sizeof)
+ {
+ assert(to!Int("6") == 6);
+ assert(to!Int("23") == 23);
+ assert(to!Int("68") == 68);
+ assert(to!Int("127") == 0x7F);
+
+ static if (isUnsigned!Int)
+ {
+ assert(to!Int("255") == 0xFF);
+ }
+ static if (isSigned!Int)
+ {
+ assert(to!Int("+6") == 6);
+ assert(to!Int("+23") == 23);
+ assert(to!Int("+68") == 68);
+ assert(to!Int("+127") == 0x7F);
+
+ assert(to!Int("-6") == -6);
+ assert(to!Int("-23") == -23);
+ assert(to!Int("-68") == -68);
+ assert(to!Int("-128") == -128);
+ }
+ }
+
+ static if (Int.sizeof >= short.sizeof)
+ {
+ assert(to!Int("468") == 468);
+ assert(to!Int("32767") == 0x7FFF);
+
+ static if (isUnsigned!Int)
+ {
+ assert(to!Int("65535") == 0xFFFF);
+ }
+ static if (isSigned!Int)
+ {
+ assert(to!Int("+468") == 468);
+ assert(to!Int("+32767") == 0x7FFF);
+
+ assert(to!Int("-468") == -468);
+ assert(to!Int("-32768") == -32768);
+ }
+ }
+
+ static if (Int.sizeof >= int.sizeof)
+ {
+ assert(to!Int("2147483647") == 0x7FFFFFFF);
+
+ static if (isUnsigned!Int)
+ {
+ assert(to!Int("4294967295") == 0xFFFFFFFF);
+ }
+
+ static if (isSigned!Int)
+ {
+ assert(to!Int("+2147483647") == 0x7FFFFFFF);
+
+ assert(to!Int("-2147483648") == -2147483648);
+ }
+ }
+
+ static if (Int.sizeof >= long.sizeof)
+ {
+ assert(to!Int("9223372036854775807") == 0x7FFFFFFFFFFFFFFF);
+
+ static if (isUnsigned!Int)
+ {
+ assert(to!Int("18446744073709551615") == 0xFFFFFFFFFFFFFFFF);
+ }
+
+ static if (isSigned!Int)
+ {
+ assert(to!Int("+9223372036854775807") == 0x7FFFFFFFFFFFFFFF);
+
+ assert(to!Int("-9223372036854775808") == 0x8000000000000000);
+ }
+ }
+ }
+}
+
+@safe pure unittest
+{
+ import std.exception;
+ // parsing error check
+ foreach (Int; AliasSeq!(byte, ubyte, short, ushort, int, uint, long, ulong))
+ {
+ {
+ immutable string[] errors1 =
+ [
+ "",
+ "-",
+ "+",
+ "-+",
+ " ",
+ " 0",
+ "0 ",
+ "- 0",
+ "1-",
+ "xx",
+ "123h",
+ "-+1",
+ "--1",
+ "+-1",
+ "++1",
+ ];
+ foreach (j, s; errors1)
+ assertThrown!ConvException(to!Int(s));
+ }
+
+ // parse!SomeUnsigned cannot parse head sign.
+ static if (isUnsigned!Int)
+ {
+ immutable string[] errors2 =
+ [
+ "+5",
+ "-78",
+ ];
+ foreach (j, s; errors2)
+ assertThrown!ConvException(to!Int(s));
+ }
+ }
+
+ // positive overflow check
+ foreach (i, Int; AliasSeq!(byte, ubyte, short, ushort, int, uint, long, ulong))
+ {
+ immutable string[] errors =
+ [
+ "128", // > byte.max
+ "256", // > ubyte.max
+ "32768", // > short.max
+ "65536", // > ushort.max
+ "2147483648", // > int.max
+ "4294967296", // > uint.max
+ "9223372036854775808", // > long.max
+ "18446744073709551616", // > ulong.max
+ ];
+ foreach (j, s; errors[i..$])
+ assertThrown!ConvOverflowException(to!Int(s));
+ }
+
+ // negative overflow check
+ foreach (i, Int; AliasSeq!(byte, short, int, long))
+ {
+ immutable string[] errors =
+ [
+ "-129", // < byte.min
+ "-32769", // < short.min
+ "-2147483649", // < int.min
+ "-9223372036854775809", // < long.min
+ ];
+ foreach (j, s; errors[i..$])
+ assertThrown!ConvOverflowException(to!Int(s));
+ }
+}
+
+@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;
+ assertCTFEable!({ string s = "1234abc"; assert(parse! int(s) == 1234 && s == "abc"); });
+ assertCTFEable!({ string s = "-1234abc"; assert(parse! int(s) == -1234 && s == "abc"); });
+ assertCTFEable!({ string s = "1234abc"; assert(parse!uint(s) == 1234 && s == "abc"); });
+}
+
+// Issue 13931
+@safe pure unittest
+{
+ import std.exception;
+
+ assertThrown!ConvOverflowException("-21474836480".to!int());
+ 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 source, uint radix)
+if (isSomeChar!(ElementType!Source) &&
+ isIntegral!Target && !is(Target == enum))
+in
+{
+ assert(radix >= 2 && radix <= 36);
+}
+body
+{
+ import core.checkedint : mulu, addu;
+ import std.exception : enforce;
+
+ if (radix == 10)
+ return parse!Target(source);
+
+ enforce!ConvException(!source.empty, "s must not be empty in integral parse");
+
+ immutable uint beyond = (radix < 10 ? '0' : 'a'-10) + radix;
+ Target v = 0;
+
+ static if (isNarrowString!Source)
+ {
+ import std.string : representation;
+ auto s = source.representation;
+ }
+ else
+ {
+ alias s = source;
+ }
+
+ do
+ {
+ uint c = s.front;
+ if (c < '0')
+ break;
+ if (radix < 10)
+ {
+ if (c >= beyond)
+ break;
+ }
+ else
+ {
+ if (c > '9')
+ {
+ c |= 0x20;//poorman's tolower
+ if (c < 'a' || c >= beyond)
+ break;
+ c -= 'a'-10-'0';
+ }
+ }
+
+ bool overflow = false;
+ auto nextv = v.mulu(radix, overflow).addu(c - '0', overflow);
+ enforce!ConvOverflowException(!overflow && nextv <= Target.max, "Overflow in integral conversion");
+ v = cast(Target) nextv;
+ s.popFront();
+ } while (!s.empty);
+
+ static if (isNarrowString!Source)
+ source = cast(Source) s;
+
+ return v;
+}
+
+@safe pure unittest
+{
+ 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
+ assert(parse!int(s = "-42", 10) == -42);
+
+ assert(parse!ubyte(s = "ff", 16) == 0xFF);
+}
+
+@safe pure unittest // bugzilla 7302
+{
+ import std.range : cycle;
+ auto r = cycle("2A!");
+ auto u = parse!uint(r, 16);
+ assert(u == 42);
+ assert(r.front == '!');
+}
+
+@safe pure unittest // bugzilla 13163
+{
+ import std.exception;
+ foreach (s; ["fff", "123"])
+ assertThrown!ConvOverflowException(s.parse!ubyte(16));
+}
+
+@safe pure unittest // bugzilla 17282
+{
+ auto str = "0=\x00\x02\x55\x40&\xff\xf0\n\x00\x04\x55\x40\xff\xf0~4+10\n";
+ assert(parse!uint(str) == 0);
+}
+
+/**
+ * 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 (isSomeString!Source && !is(Source == enum) &&
+ is(Target == enum))
+{
+ import std.algorithm.searching : startsWith;
+ Target result;
+ size_t longest_match = 0;
+
+ foreach (i, e; EnumMembers!Target)
+ {
+ auto ident = __traits(allMembers, Target)[i];
+ if (longest_match < ident.length && s.startsWith(ident))
+ {
+ result = e;
+ longest_match = ident.length ;
+ }
+ }
+
+ if (longest_match > 0)
+ {
+ s = s[longest_match .. $];
+ return result ;
+ }
+
+ throw new ConvException(
+ Target.stringof ~ " does not have a member named '"
+ ~ to!string(s) ~ "'");
+}
+
+///
+@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;
+
+ enum EB : bool { a = true, b = false, c = a }
+ enum EU { a, b, c }
+ enum EI { a = -1, b = 0, c = 1 }
+ enum EF : real { a = 1.414, b = 1.732, c = 2.236 }
+ enum EC : char { a = 'a', b = 'b', c = 'c' }
+ enum ES : string { a = "aaa", b = "bbb", c = "ccc" }
+
+ foreach (E; AliasSeq!(EB, EU, EI, EF, EC, ES))
+ {
+ assert(to!E("a"c) == E.a);
+ assert(to!E("b"w) == E.b);
+ assert(to!E("c"d) == E.c);
+
+ assertThrown!ConvException(to!E("d"));
+ }
+}
+
+@safe pure unittest // bugzilla 4744
+{
+ enum A { member1, member11, member111 }
+ assert(to!A("member1" ) == A.member1 );
+ assert(to!A("member11" ) == A.member11 );
+ assert(to!A("member111") == A.member111);
+ auto s = "member1111";
+ assert(parse!A(s) == A.member111 && s == "1");
+}
+
+/**
+ * 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 core.stdc.math : HUGE_VAL;
+ import std.ascii : isDigit, isAlpha, toLower, toUpper, isHexDigit;
+ import std.exception : enforce;
+
+ static if (isNarrowString!Source)
+ {
+ import std.string : representation;
+ 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 ];
+
+ ConvException bailOut()(string msg = null, string fn = __FILE__, size_t ln = __LINE__)
+ {
+ if (msg == null)
+ msg = "Floating point conversion error";
+ return new ConvException(text(msg, " for input \"", p, "\"."), fn, ln);
+ }
+
+
+ enforce(!p.empty, bailOut());
+
+ bool sign = false;
+ switch (p.front)
+ {
+ case '-':
+ sign = true;
+ p.popFront();
+ enforce(!p.empty, bailOut());
+ if (toLower(p.front) == 'i')
+ goto case 'i';
+ enforce(!p.empty, bailOut());
+ break;
+ case '+':
+ p.popFront();
+ enforce(!p.empty, bailOut());
+ break;
+ case 'i': case 'I':
+ p.popFront();
+ enforce(!p.empty, bailOut());
+ if (toLower(p.front) == 'n')
+ {
+ p.popFront();
+ enforce(!p.empty, bailOut());
+ if (toLower(p.front) == 'f')
+ {
+ // 'inf'
+ p.popFront();
+ static if (isNarrowString!Source)
+ source = cast(Source) p;
+ return sign ? -Target.infinity : Target.infinity;
+ }
+ }
+ goto default;
+ default: {}
+ }
+
+ bool isHex = false;
+ bool startsWithZero = p.front == '0';
+ if (startsWithZero)
+ {
+ p.popFront();
+ if (p.empty)
+ {
+ static if (isNarrowString!Source)
+ source = cast(Source) p;
+ return sign ? -0.0 : 0.0;
+ }
+
+ isHex = p.front == 'x' || p.front == 'X';
+ }
+
+ real ldval = 0.0;
+ char dot = 0; /* if decimal point has been seen */
+ int exp = 0;
+ long msdec = 0, lsdec = 0;
+ ulong msscale = 1;
+
+ if (isHex)
+ {
+ int guard = 0;
+ int anydigits = 0;
+ uint ndigits = 0;
+
+ p.popFront();
+ while (!p.empty)
+ {
+ int i = p.front;
+ while (isHexDigit(i))
+ {
+ anydigits = 1;
+ i = isAlpha(i) ? ((i & ~0x20) - ('A' - 10)) : i - '0';
+ if (ndigits < 16)
+ {
+ msdec = msdec * 16 + i;
+ if (msdec)
+ ndigits++;
+ }
+ else if (ndigits == 16)
+ {
+ while (msdec >= 0)
+ {
+ exp--;
+ msdec <<= 1;
+ i <<= 1;
+ if (i & 0x10)
+ msdec |= 1;
+ }
+ guard = i << 4;
+ ndigits++;
+ exp += 4;
+ }
+ else
+ {
+ guard |= i;
+ exp += 4;
+ }
+ exp -= dot;
+ p.popFront();
+ if (p.empty)
+ break;
+ i = p.front;
+ if (i == '_')
+ {
+ p.popFront();
+ if (p.empty)
+ break;
+ i = p.front;
+ }
+ }
+ if (i == '.' && !dot)
+ {
+ p.popFront();
+ dot = 4;
+ }
+ else
+ break;
+ }
+
+ // Round up if (guard && (sticky || odd))
+ if (guard & 0x80 && (guard & 0x7F || msdec & 1))
+ {
+ msdec++;
+ if (msdec == 0) // overflow
+ {
+ msdec = 0x8000000000000000L;
+ exp++;
+ }
+ }
+
+ enforce(anydigits, bailOut());
+ enforce(!p.empty && (p.front == 'p' || p.front == 'P'),
+ bailOut("Floating point parsing: exponent is required"));
+ char sexp;
+ int e;
+
+ sexp = 0;
+ p.popFront();
+ if (!p.empty)
+ {
+ switch (p.front)
+ {
+ case '-': sexp++;
+ goto case;
+ case '+': p.popFront(); enforce(!p.empty,
+ new ConvException("Error converting input"~
+ " to floating point"));
+ break;
+ default: {}
+ }
+ }
+ ndigits = 0;
+ e = 0;
+ while (!p.empty && isDigit(p.front))
+ {
+ if (e < 0x7FFFFFFF / 10 - 10) // prevent integer overflow
+ {
+ e = e * 10 + p.front - '0';
+ }
+ p.popFront();
+ ndigits = 1;
+ }
+ exp += (sexp) ? -e : e;
+ enforce(ndigits, new ConvException("Error converting input"~
+ " to floating point"));
+
+ static if (real.mant_dig == 64)
+ {
+ if (msdec)
+ {
+ int e2 = 0x3FFF + 63;
+
+ // left justify mantissa
+ while (msdec >= 0)
+ {
+ msdec <<= 1;
+ e2--;
+ }
+
+ // Stuff mantissa directly into real
+ ()@trusted{ *cast(long*)&ldval = msdec; }();
+ ()@trusted{ (cast(ushort*)&ldval)[4] = cast(ushort) e2; }();
+
+ import std.math : ldexp;
+
+ // Exponent is power of 2, not power of 10
+ ldval = ldexp(ldval,exp);
+ }
+ }
+ else static if (real.mant_dig == 53)
+ {
+ if (msdec)
+ {
+ //Exponent bias + 52:
+ //After shifting 52 times left, exp must be 1
+ int e2 = 0x3FF + 52;
+
+ // right justify mantissa
+ // first 11 bits must be zero, rest is implied bit + mantissa
+ // shift one time less, do rounding, shift again
+ while ((msdec & 0xFFC0_0000_0000_0000) != 0)
+ {
+ msdec = ((cast(ulong) msdec) >> 1);
+ e2++;
+ }
+
+ //Have to shift one more time
+ //and do rounding
+ if ((msdec & 0xFFE0_0000_0000_0000) != 0)
+ {
+ auto roundUp = (msdec & 0x1);
+
+ msdec = ((cast(ulong) msdec) >> 1);
+ e2++;
+ 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)
+ {
+ msdec = 0x0010_0000_0000_0000;
+ e2++;
+ }
+ }
+ }
+
+
+ // left justify mantissa
+ // bit 11 must be 1
+ while ((msdec & 0x0010_0000_0000_0000) == 0)
+ {
+ msdec <<= 1;
+ e2--;
+ }
+
+ // Stuff mantissa directly into double
+ // (first including implicit bit)
+ ()@trusted{ *cast(long *)&ldval = msdec; }();
+ //Store exponent, now overwriting implicit bit
+ ()@trusted{ *cast(long *)&ldval &= 0x000F_FFFF_FFFF_FFFF; }();
+ ()@trusted{ *cast(long *)&ldval |= ((e2 & 0xFFFUL) << 52); }();
+
+ import std.math : ldexp;
+
+ // Exponent is power of 2, not power of 10
+ ldval = ldexp(ldval,exp);
+ }
+ }
+ else
+ static assert(false, "Floating point format of real type not supported");
+
+ goto L6;
+ }
+ else // not hex
+ {
+ if (toUpper(p.front) == 'N' && !startsWithZero)
+ {
+ // nan
+ p.popFront();
+ enforce(!p.empty && toUpper(p.front) == 'A',
+ new ConvException("error converting input to floating point"));
+ p.popFront();
+ enforce(!p.empty && toUpper(p.front) == 'N',
+ new ConvException("error converting input to floating point"));
+ // skip past the last 'n'
+ p.popFront();
+ static if (isNarrowString!Source)
+ source = cast(Source) p;
+ return typeof(return).nan;
+ }
+
+ bool sawDigits = startsWithZero;
+
+ while (!p.empty)
+ {
+ int i = p.front;
+ while (isDigit(i))
+ {
+ sawDigits = true; /* must have at least 1 digit */
+ if (msdec < (0x7FFFFFFFFFFFL-10)/10)
+ msdec = msdec * 10 + (i - '0');
+ else if (msscale < (0xFFFFFFFF-10)/10)
+ {
+ lsdec = lsdec * 10 + (i - '0');
+ msscale *= 10;
+ }
+ else
+ {
+ exp++;
+ }
+ exp -= dot;
+ p.popFront();
+ if (p.empty)
+ break;
+ i = p.front;
+ if (i == '_')
+ {
+ p.popFront();
+ if (p.empty)
+ break;
+ i = p.front;
+ }
+ }
+ if (i == '.' && !dot)
+ {
+ p.popFront();
+ dot++;
+ }
+ else
+ {
+ break;
+ }
+ }
+ enforce(sawDigits, new ConvException("no digits seen"));
+ }
+ if (!p.empty && (p.front == 'e' || p.front == 'E'))
+ {
+ char sexp;
+ int e;
+
+ sexp = 0;
+ p.popFront();
+ enforce(!p.empty, new ConvException("Unexpected end of input"));
+ switch (p.front)
+ {
+ case '-': sexp++;
+ goto case;
+ case '+': p.popFront();
+ break;
+ default: {}
+ }
+ bool sawDigits = 0;
+ e = 0;
+ while (!p.empty && isDigit(p.front))
+ {
+ if (e < 0x7FFFFFFF / 10 - 10) // prevent integer overflow
+ {
+ e = e * 10 + p.front - '0';
+ }
+ p.popFront();
+ sawDigits = 1;
+ }
+ exp += (sexp) ? -e : e;
+ enforce(sawDigits, new ConvException("No digits seen."));
+ }
+
+ ldval = msdec;
+ if (msscale != 1) /* if stuff was accumulated in lsdec */
+ ldval = ldval * msscale + lsdec;
+ if (ldval)
+ {
+ uint u = 0;
+ int pow = 4096;
+
+ while (exp > 0)
+ {
+ while (exp >= pow)
+ {
+ ldval *= postab[u];
+ exp -= pow;
+ }
+ pow >>= 1;
+ u++;
+ }
+ while (exp < 0)
+ {
+ while (exp <= -pow)
+ {
+ ldval *= negtab[u];
+ enforce(ldval != 0, new ConvException("Range error"));
+ exp += pow;
+ }
+ pow >>= 1;
+ u++;
+ }
+ }
+ L6: // if overflow occurred
+ enforce(ldval != HUGE_VAL, new ConvException("Range error"));
+
+ L1:
+ static if (isNarrowString!Source)
+ source = cast(Source) p;
+ return sign ? -ldval : ldval;
+}
+
+///
+@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;
+
+ // Compare reals with given precision
+ bool feq(in real rx, in real ry, in real precision = 0.000001L)
+ {
+ if (rx == ry)
+ return 1;
+
+ if (isNaN(rx))
+ return cast(bool) isNaN(ry);
+
+ if (isNaN(ry))
+ return 0;
+
+ return cast(bool)(fabs(rx - ry) <= precision);
+ }
+
+ // Make given typed literal
+ F Literal(F)(F f)
+ {
+ return f;
+ }
+
+ foreach (Float; AliasSeq!(float, double, real))
+ {
+ assert(to!Float("123") == Literal!Float(123));
+ assert(to!Float("+123") == Literal!Float(+123));
+ assert(to!Float("-123") == Literal!Float(-123));
+ assert(to!Float("123e2") == Literal!Float(123e2));
+ assert(to!Float("123e+2") == Literal!Float(123e+2));
+ assert(to!Float("123e-2") == Literal!Float(123e-2));
+ assert(to!Float("123.") == Literal!Float(123.0));
+ assert(to!Float(".375") == Literal!Float(.375));
+
+ assert(to!Float("1.23375E+2") == Literal!Float(1.23375E+2));
+
+ assert(to!Float("0") is 0.0);
+ assert(to!Float("-0") is -0.0);
+
+ assert(isNaN(to!Float("nan")));
+
+ assertThrown!ConvException(to!Float("\x00"));
+ }
+
+ // 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));
+ 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));
+ d = to!double("1.79769e+308");
+ assert(to!string(d) == to!string(1.79769e+308));
+ assert(to!string(d) == to!string(double.max));
+
+ assert(to!string(to!real(to!string(real.max / 2L))) == to!string(real.max / 2L));
+
+ // min and max
+ real r = to!real(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
+@system unittest
+{
+ // @system because strtod is not @safe.
+ 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);
+ assert(x == 0x1A_BCDE_F012_3456p10L);
+ //1 bit is implicit
+ assert(((*cast(ulong*)&x) & 0x000F_FFFF_FFFF_FFFF) == 0xA_BCDE_F012_3456);
+ assert(strtod("0x1ABCDEF0123456p10", null) == x);
+
+ //Should be parsed exactly: 10 bit mantissa
+ s = "0x3FFp10";
+ x = parse!real(s);
+ assert(x == 0x03FFp10);
+ //1 bit is implicit
+ assert(((*cast(ulong*)&x) & 0x000F_FFFF_FFFF_FFFF) == 0x000F_F800_0000_0000);
+ assert(strtod("0x3FFp10", null) == x);
+
+ //60 bit mantissa, round up
+ s = "0xFFF_FFFF_FFFF_FFFFp10";
+ x = parse!real(s);
+ assert(approxEqual(x, 0xFFF_FFFF_FFFF_FFFFp10));
+ //1 bit is implicit
+ assert(((*cast(ulong*)&x) & 0x000F_FFFF_FFFF_FFFF) == 0x0000_0000_0000_0000);
+ assert(strtod("0xFFFFFFFFFFFFFFFp10", null) == x);
+
+ //60 bit mantissa, round down
+ s = "0xFFF_FFFF_FFFF_FF90p10";
+ x = parse!real(s);
+ assert(approxEqual(x, 0xFFF_FFFF_FFFF_FF90p10));
+ //1 bit is implicit
+ assert(((*cast(ulong*)&x) & 0x000F_FFFF_FFFF_FFFF) == 0x000F_FFFF_FFFF_FFFF);
+ assert(strtod("0xFFFFFFFFFFFFF90p10", null) == x);
+
+ //61 bit mantissa, round up 2
+ s = "0x1F0F_FFFF_FFFF_FFFFp10";
+ x = parse!real(s);
+ assert(approxEqual(x, 0x1F0F_FFFF_FFFF_FFFFp10));
+ //1 bit is implicit
+ assert(((*cast(ulong*)&x) & 0x000F_FFFF_FFFF_FFFF) == 0x000F_1000_0000_0000);
+ assert(strtod("0x1F0FFFFFFFFFFFFFp10", null) == x);
+
+ //61 bit mantissa, round down 2
+ s = "0x1F0F_FFFF_FFFF_FF10p10";
+ x = parse!real(s);
+ assert(approxEqual(x, 0x1F0F_FFFF_FFFF_FF10p10));
+ //1 bit is implicit
+ assert(((*cast(ulong*)&x) & 0x000F_FFFF_FFFF_FFFF) == 0x000F_0FFF_FFFF_FFFF);
+ assert(strtod("0x1F0FFFFFFFFFFF10p10", null) == x);
+
+ //Huge exponent
+ s = "0x1F_FFFF_FFFF_FFFFp900";
+ x = parse!real(s);
+ assert(strtod("0x1FFFFFFFFFFFFFp900", null) == x);
+
+ //exponent too big -> converror
+ s = "";
+ assertThrown!ConvException(x = parse!real(s));
+ assert(strtod("0x1FFFFFFFFFFFFFp1024", null) == real.infinity);
+
+ //-exponent too big -> 0
+ s = "0x1FFFFFFFFFFFFFp-2000";
+ x = parse!real(s);
+ assert(x == 0);
+ assert(strtod("0x1FFFFFFFFFFFFFp-2000", null) == x);
+ }
+}
+
+@system unittest
+{
+ import core.stdc.errno;
+ import core.stdc.stdlib;
+
+ errno = 0; // In case it was set by another unittest in a different module.
+ struct longdouble
+ {
+ static if (real.mant_dig == 64)
+ {
+ ushort[5] value;
+ }
+ else static if (real.mant_dig == 53)
+ {
+ ushort[4] value;
+ }
+ else
+ static assert(false, "Not implemented");
+ }
+
+ real ld;
+ longdouble x;
+ real ld1;
+ longdouble x1;
+ int i;
+
+ static if (real.mant_dig == 64)
+ enum s = "0x1.FFFFFFFFFFFFFFFEp-16382";
+ else static if (real.mant_dig == 53)
+ enum s = "0x1.FFFFFFFFFFFFFFFEp-1000";
+ else
+ static assert(false, "Floating point format for real not supported");
+
+ auto s2 = s.idup;
+ ld = parse!real(s2);
+ assert(s2.empty);
+ x = *cast(longdouble *)&ld;
+
+ 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);
+
+ assert(!errno);
+
+ s2 = "1.0e5";
+ ld = parse!real(s2);
+ assert(s2.empty);
+ x = *cast(longdouble *)&ld;
+ ld1 = strtold("1.0e5", null);
+ x1 = *cast(longdouble *)&ld1;
+}
+
+@safe pure unittest
+{
+ import std.exception;
+
+ // Bugzilla 4959
+ {
+ auto s = "0 ";
+ auto x = parse!double(s);
+ assert(s == " ");
+ assert(x == 0.0);
+ }
+
+ // Bugzilla 3369
+ assert(to!float("inf") == float.infinity);
+ assert(to!float("-inf") == -float.infinity);
+
+ // Bugzilla 6160
+ assert(6_5.536e3L == to!real("6_5.536e3")); // 2^16
+ assert(0x1000_000_000_p10 == to!real("0x1000_000_000_p10")); // 7.03687e+13
+
+ // Bugzilla 6258
+ assertThrown!ConvException(to!real("-"));
+ assertThrown!ConvException(to!real("in"));
+
+ // Bugzilla 7055
+ assertThrown!ConvException(to!float("INF2"));
+
+ //extra stress testing
+ auto ssOK = ["1.", "1.1.1", "1.e5", "2e1e", "2a", "2e1_1",
+ "inf", "-inf", "infa", "-infa", "inf2e2", "-inf2e2"];
+ auto ssKO = ["", " ", "2e", "2e+", "2e-", "2ee", "2e++1", "2e--1", "2e_1", "+inf"];
+ foreach (s; ssOK)
+ parse!double(s);
+ foreach (s; ssKO)
+ assertThrown!ConvException(parse!double(s));
+}
+
+/**
+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 (isSomeString!Source && !is(Source == enum) &&
+ staticIndexOf!(Unqual!Target, dchar, Unqual!(ElementEncodingType!Source)) >= 0)
+{
+ if (s.empty)
+ throw convError!(Source, Target)(s);
+ static if (is(Unqual!Target == dchar))
+ {
+ Target result = s.front;
+ s.popFront();
+ return result;
+ }
+ else
+ {
+ // Special case: okay so parse a Char off a Char[]
+ Target result = s[0];
+ s = s[1 .. $];
+ return result;
+ }
+}
+
+@safe pure unittest
+{
+ foreach (Str; AliasSeq!(string, wstring, dstring))
+ {
+ foreach (Char; AliasSeq!(char, wchar, dchar))
+ {
+ static if (is(Unqual!Char == dchar) ||
+ Char.sizeof == ElementEncodingType!Str.sizeof)
+ {
+ Str s = "aaa";
+ assert(parse!Char(s) == 'a');
+ assert(s == "aa");
+ }
+ }
+ }
+}
+
+/// 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 (s.empty)
+ throw convError!(Source, Target)(s);
+ Target result = s.front;
+ s.popFront();
+ 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
+*/
+@safe pure unittest
+{
+ import std.exception;
+
+ 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");
+
+ auto f = "False 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
+
+ auto s = "true";
+ auto b = parse!(const(bool))(s);
+ assert(b == true);
+}
+
+/**
+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)))
+{
+ import std.ascii : toLower;
+ foreach (c; "null")
+ {
+ if (s.empty || toLower(s.front) != c)
+ throw parseError("null should be case-insensitive 'null'");
+ s.popFront();
+ }
+ return null;
+}
+
+///
+@safe pure unittest
+{
+ import std.exception : assertThrown;
+
+ alias NullType = typeof(null);
+ auto s1 = "null";
+ assert(parse!NullType(s1) is null);
+ assert(s1 == "");
+
+ auto s2 = "NUll"d;
+ assert(parse!NullType(s2) is null);
+ assert(s2 == "");
+
+ auto m = "maybe";
+ assertThrown!ConvException(parse!NullType(m));
+ assert(m == "maybe"); // m shouldn't change on failure
+
+ auto s = "NULL";
+ assert(parse!(const NullType)(s) is null);
+}
+
+//Used internally by parse Array/AA, to remove ascii whites
+package void skipWS(R)(ref R r)
+{
+ import std.ascii : isWhite;
+ static if (isSomeString!R)
+ {
+ //Implementation inspired from stripLeft.
+ foreach (i, c; r)
+ {
+ if (!isWhite(c))
+ {
+ r = r[i .. $];
+ return;
+ }
+ }
+ r = r[0 .. 0]; //Empty string with correct type.
+ return;
+ }
+ else
+ {
+ for (; !r.empty && isWhite(r.front); r.popFront())
+ {}
+ }
+}
+
+/**
+ * Parses an array from a string given the left bracket (default $(D
+ * '[')), right bracket (default $(D ']')), and element separator (by
+ * 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 (isSomeString!Source && !is(Source == enum) &&
+ isDynamicArray!Target && !is(Target == enum))
+{
+ import std.array : appender;
+
+ auto result = appender!Target();
+
+ parseCheck!s(lbracket);
+ skipWS(s);
+ if (s.empty)
+ throw convError!(Source, Target)(s);
+ if (s.front == rbracket)
+ {
+ s.popFront();
+ 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)
+ throw convError!(Source, Target)(s);
+ if (s.front != comma)
+ break;
+ }
+ parseCheck!s(rbracket);
+
+ return result.data;
+}
+
+///
+@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);
+}
+
+@safe unittest
+{
+ int[][] a = [ [1, 2] , [3], [4, 5] ];
+ auto s = to!string(a);
+ assert(to!(int[][])(s) == a);
+}
+
+@safe unittest
+{
+ int[][][] ia = [ [[1,2],[3,4],[5]] , [[6],[],[7,8,9]] , [[]] ];
+
+ char[] s = to!(char[])(ia);
+ int[][][] ia2;
+
+ ia2 = to!(typeof(ia2))(s);
+ assert( ia == ia2);
+}
+
+@safe pure unittest
+{
+ import std.exception;
+
+ //Check proper failure
+ auto s = "[ 1 , 2 , 3 ]";
+ foreach (i ; 0 .. s.length-1)
+ {
+ auto ss = s[0 .. i];
+ assertThrown!ConvException(parse!(int[])(ss));
+ }
+ int[] arr = parse!(int[])(s);
+}
+
+@safe pure unittest
+{
+ //Checks parsing of strings with escaped characters
+ string s1 = `[
+ "Contains a\0null!",
+ "tab\there",
+ "line\nbreak",
+ "backslash \\ slash / question \?",
+ "number \x35 five",
+ "unicode \u65E5 sun",
+ "very long \U000065E5 sun"
+ ]`;
+
+ //Note: escaped characters purposefully replaced and isolated to guarantee
+ //there are no typos in the escape syntax
+ string[] s2 = [
+ "Contains a" ~ '\0' ~ "null!",
+ "tab" ~ '\t' ~ "here",
+ "line" ~ '\n' ~ "break",
+ "backslash " ~ '\\' ~ " slash / question ?",
+ "number 5 five",
+ "unicode 日 sun",
+ "very long 日 sun"
+ ];
+ assert(s2 == parse!(string[])(s1));
+ assert(s1.empty);
+}
+
+/// ditto
+Target parse(Target, Source)(ref Source s, dchar lbracket = '[', dchar rbracket = ']', dchar comma = ',')
+if (isExactSomeString!Source &&
+ isStaticArray!Target && !is(Target == enum))
+{
+ static if (hasIndirections!Target)
+ Target result = Target.init[0].init;
+ else
+ Target result = void;
+
+ parseCheck!s(lbracket);
+ skipWS(s);
+ if (s.empty)
+ throw convError!(Source, Target)(s);
+ if (s.front == rbracket)
+ {
+ static if (result.length != 0)
+ goto Lmanyerr;
+ else
+ {
+ s.popFront();
+ return result;
+ }
+ }
+ for (size_t i = 0; ; s.popFront(), skipWS(s))
+ {
+ if (i == result.length)
+ goto Lmanyerr;
+ result[i++] = parseElement!(ElementType!Target)(s);
+ skipWS(s);
+ if (s.empty)
+ throw convError!(Source, Target)(s);
+ if (s.front != comma)
+ {
+ if (i != result.length)
+ goto Lfewerr;
+ break;
+ }
+ }
+ parseCheck!s(rbracket);
+
+ return result;
+
+Lmanyerr:
+ throw parseError(text("Too many elements in input, ", result.length, " elements expected."));
+
+Lfewerr:
+ throw parseError(text("Too few elements in input, ", result.length, " elements expected."));
+}
+
+@safe pure unittest
+{
+ import std.exception;
+
+ auto s1 = "[1,2,3,4]";
+ auto sa1 = parse!(int[4])(s1);
+ assert(sa1 == [1,2,3,4]);
+
+ auto s2 = "[[1],[2,3],[4]]";
+ auto sa2 = parse!(int[][3])(s2);
+ assert(sa2 == [[1],[2,3],[4]]);
+
+ auto s3 = "[1,2,3]";
+ assertThrown!ConvException(parse!(int[4])(s3));
+
+ auto s4 = "[1,2,3,4,5]";
+ assertThrown!ConvException(parse!(int[4])(s4));
+}
+
+/**
+ * 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 (isSomeString!Source && !is(Source == enum) &&
+ isAssociativeArray!Target && !is(Target == enum))
+{
+ alias KeyType = typeof(Target.init.keys[0]);
+ alias ValType = typeof(Target.init.values[0]);
+
+ Target result;
+
+ parseCheck!s(lbracket);
+ skipWS(s);
+ if (s.empty)
+ throw convError!(Source, Target)(s);
+ if (s.front == rbracket)
+ {
+ s.popFront();
+ return result;
+ }
+ for (;; s.popFront(), skipWS(s))
+ {
+ auto key = parseElement!KeyType(s);
+ skipWS(s);
+ parseCheck!s(keyval);
+ skipWS(s);
+ auto val = parseElement!ValType(s);
+ skipWS(s);
+ result[key] = val;
+ if (s.empty)
+ throw convError!(Source, Target)(s);
+ if (s.front != comma)
+ break;
+ }
+ parseCheck!s(rbracket);
+
+ return result;
+}
+
+///
+@safe pure unittest
+{
+ auto s1 = "[1:10, 2:20, 3:30]";
+ auto aa1 = parse!(int[int])(s1);
+ assert(aa1 == [1:10, 2:20, 3:30]);
+
+ auto s2 = `["aaa":10, "bbb":20, "ccc":30]`;
+ auto aa2 = parse!(int[string])(s2);
+ assert(aa2 == ["aaa":10, "bbb":20, "ccc":30]);
+
+ auto s3 = `["aaa":[1], "bbb":[2,3], "ccc":[4,5,6]]`;
+ auto aa3 = parse!(int[][string])(s3);
+ assert(aa3 == ["aaa":[1], "bbb":[2,3], "ccc":[4,5,6]]);
+}
+
+@safe pure unittest
+{
+ import std.exception;
+
+ //Check proper failure
+ auto s = "[1:10, 2:20, 3:30]";
+ foreach (i ; 0 .. s.length-1)
+ {
+ auto ss = s[0 .. i];
+ assertThrown!ConvException(parse!(int[int])(ss));
+ }
+ int[int] aa = parse!(int[int])(s);
+}
+
+private dchar parseEscape(Source)(ref Source s)
+if (isInputRange!Source && isSomeChar!(ElementType!Source))
+{
+ parseCheck!s('\\');
+ if (s.empty)
+ throw parseError("Unterminated escape sequence");
+
+ dchar getHexDigit()(ref Source s_ = s) // workaround
+ {
+ import std.ascii : isAlpha, isHexDigit;
+ if (s_.empty)
+ throw parseError("Unterminated escape sequence");
+ s_.popFront();
+ if (s_.empty)
+ throw parseError("Unterminated escape sequence");
+ dchar c = s_.front;
+ if (!isHexDigit(c))
+ throw parseError("Hex digit is missing");
+ return isAlpha(c) ? ((c & ~0x20) - ('A' - 10)) : c - '0';
+ }
+
+ dchar result;
+
+ switch (s.front)
+ {
+ case '"': result = '\"'; break;
+ case '\'': result = '\''; break;
+ case '0': result = '\0'; break;
+ case '?': result = '\?'; break;
+ case '\\': result = '\\'; break;
+ case 'a': result = '\a'; break;
+ case 'b': result = '\b'; break;
+ case 'f': result = '\f'; break;
+ case 'n': result = '\n'; break;
+ case 'r': result = '\r'; break;
+ case 't': result = '\t'; break;
+ case 'v': result = '\v'; break;
+ case 'x':
+ result = getHexDigit() << 4;
+ result |= getHexDigit();
+ break;
+ case 'u':
+ result = getHexDigit() << 12;
+ result |= getHexDigit() << 8;
+ result |= getHexDigit() << 4;
+ result |= getHexDigit();
+ break;
+ case 'U':
+ result = getHexDigit() << 28;
+ result |= getHexDigit() << 24;
+ result |= getHexDigit() << 20;
+ result |= getHexDigit() << 16;
+ result |= getHexDigit() << 12;
+ result |= getHexDigit() << 8;
+ result |= getHexDigit() << 4;
+ result |= getHexDigit();
+ break;
+ default:
+ throw parseError("Unknown escape character " ~ to!string(s.front));
+ }
+ if (s.empty)
+ throw parseError("Unterminated escape sequence");
+
+ s.popFront();
+
+ return result;
+}
+
+@safe pure unittest
+{
+ string[] s1 = [
+ `\"`, `\'`, `\?`, `\\`, `\a`, `\b`, `\f`, `\n`, `\r`, `\t`, `\v`, //Normal escapes
+ //`\141`, //@@@9621@@@ Octal escapes.
+ `\x61`,
+ `\u65E5`, `\U00012456`
+ //`\&amp;`, `\&quot;`, //@@@9621@@@ Named Character Entities.
+ ];
+
+ const(dchar)[] s2 = [
+ '\"', '\'', '\?', '\\', '\a', '\b', '\f', '\n', '\r', '\t', '\v', //Normal escapes
+ //'\141', //@@@9621@@@ Octal escapes.
+ '\x61',
+ '\u65E5', '\U00012456'
+ //'\&amp;', '\&quot;', //@@@9621@@@ Named Character Entities.
+ ];
+
+ foreach (i ; 0 .. s1.length)
+ {
+ assert(s2[i] == parseEscape(s1[i]));
+ assert(s1[i].empty);
+ }
+}
+
+@safe pure unittest
+{
+ import std.exception;
+
+ string[] ss = [
+ `hello!`, //Not an escape
+ `\`, //Premature termination
+ `\/`, //Not an escape
+ `\gggg`, //Not an escape
+ `\xzz`, //Not an hex
+ `\x0`, //Premature hex end
+ `\XB9`, //Not legal hex syntax
+ `\u!!`, //Not a unicode hex
+ `\777`, //Octal is larger than a byte //Note: Throws, but simply because octals are unsupported
+ `\u123`, //Premature hex end
+ `\U123123` //Premature hex end
+ ];
+ foreach (s ; ss)
+ assertThrown!ConvException(parseEscape(s));
+}
+
+// Undocumented
+Target parseElement(Target, Source)(ref Source s)
+if (isInputRange!Source && isSomeChar!(ElementType!Source) && !is(Source == enum) &&
+ isExactSomeString!Target)
+{
+ import std.array : appender;
+ auto result = appender!Target();
+
+ // parse array of chars
+ if (s.empty)
+ throw convError!(Source, Target)(s);
+ if (s.front == '[')
+ return parse!Target(s);
+
+ parseCheck!s('\"');
+ if (s.empty)
+ throw convError!(Source, Target)(s);
+ if (s.front == '\"')
+ {
+ s.popFront();
+ return result.data;
+ }
+ while (true)
+ {
+ if (s.empty)
+ throw parseError("Unterminated quoted string");
+ switch (s.front)
+ {
+ case '\"':
+ s.popFront();
+ return result.data;
+ case '\\':
+ result.put(parseEscape(s));
+ break;
+ default:
+ result.put(s.front);
+ s.popFront();
+ break;
+ }
+ }
+ assert(0);
+}
+
+// ditto
+Target parseElement(Target, Source)(ref Source s)
+if (isInputRange!Source && isSomeChar!(ElementType!Source) && !is(Source == enum) &&
+ isSomeChar!Target && !is(Target == enum))
+{
+ Target c;
+
+ parseCheck!s('\'');
+ if (s.empty)
+ throw convError!(Source, Target)(s);
+ if (s.front != '\\')
+ {
+ c = s.front;
+ s.popFront();
+ }
+ else
+ c = parseEscape(s);
+ parseCheck!s('\'');
+
+ return c;
+}
+
+// ditto
+Target parseElement(Target, Source)(ref Source s)
+if (isInputRange!Source && isSomeChar!(ElementType!Source) &&
+ !isSomeString!Target && !isSomeChar!Target)
+{
+ return parse!Target(s);
+}
+
+
+/***************************************************************
+ * Convenience functions for converting one or more arguments
+ * of any type into _text (the three character widths).
+ */
+string text(T...)(T args)
+if (T.length > 0) { return textImpl!string(args); }
+
+// @@@DEPRECATED_2018-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)
+if (T.length > 0) { return textImpl!wstring(args); }
+
+// @@@DEPRECATED_2018-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)
+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_2018-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)
+{
+ static if (U.length == 0)
+ {
+ return null;
+ }
+ else static if (U.length == 1)
+ {
+ return to!S(args[0]);
+ }
+ else
+ {
+ import std.array : appender;
+
+ auto app = appender!S();
+
+ foreach (arg; args)
+ app.put(to!S(arg));
+ return app.data;
+ }
+}
+
+
+/***************************************************************
+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
+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.
+
+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 decimalInteger)
+if (isIntegral!(typeof(decimalInteger)))
+{
+ enum octal = octal!(typeof(decimalInteger))(to!string(decimalInteger));
+}
+
+///
+@safe unittest
+{
+ // same as 0177
+ auto x = octal!177;
+ // octal is a compile-time device
+ enum y = octal!160;
+ // Create an unsigned octal
+ auto z = octal!"1_000_000u";
+}
+
+/*
+ Takes a string, num, which is an octal literal, and returns its
+ value, in the type T specified.
+*/
+private T octal(T)(const string num)
+{
+ assert(isOctalLiteral(num));
+
+ T value = 0;
+
+ foreach (const char s; num)
+ {
+ if (s < '0' || s > '7') // we only care about digits; skip the rest
+ // safe to skip - this is checked out in the assert so these
+ // are just suffixes
+ continue;
+
+ value *= 8;
+ value += s - '0';
+ }
+
+ return value;
+}
+
+@safe unittest
+{
+ int a = octal!int("10");
+ assert(a == 8);
+}
+
+/*
+Take a look at int.max and int.max+1 in octal and the logic for this
+function follows directly.
+ */
+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
+ // the number of digits here that we depend on.
+ enum bool octalFitsInInt = strippedOctalLiteral(octalNum).length < 11 ||
+ strippedOctalLiteral(octalNum).length == 11 &&
+ strippedOctalLiteral(octalNum)[0] == '1';
+}
+
+private string strippedOctalLiteral(string original)
+{
+ string stripped = "";
+ foreach (c; original)
+ if (c >= '0' && c <= '7')
+ stripped ~= c;
+ return stripped;
+}
+
+private template literalIsLong(string num)
+{
+ static if (num.length > 1)
+ // can be xxL or xxLu according to spec
+ enum literalIsLong = (num[$-1] == 'L' || num[$-2] == 'L');
+ else
+ enum literalIsLong = false;
+}
+
+private template literalIsUnsigned(string num)
+{
+ static if (num.length > 1)
+ // can be xxU or xxUL according to spec
+ enum literalIsUnsigned = (num[$-1] == 'u' || num[$-2] == 'u')
+ // both cases are allowed too
+ || (num[$-1] == 'U' || num[$-2] == 'U');
+ else
+ enum literalIsUnsigned = false;
+}
+
+/*
+Returns if the given string is a correctly formatted octal literal.
+
+The format is specified in spec/lex.html. The leading zero is allowed, but
+not required.
+ */
+@safe pure nothrow @nogc
+private bool isOctalLiteral(const string num)
+{
+ if (num.length == 0)
+ return false;
+
+ // Must start with a number. To avoid confusion, literals that
+ // start with a '0' are not allowed
+ if (num[0] == '0' && num.length > 1)
+ return false;
+ if (num[0] < '0' || num[0] > '7')
+ return false;
+
+ foreach (i, c; num)
+ {
+ if ((c < '0' || c > '7') && c != '_') // not a legal character
+ {
+ if (i < num.length - 2)
+ return false;
+ else // gotta check for those suffixes
+ {
+ if (c != 'U' && c != 'u' && c != 'L')
+ return false;
+ if (i != num.length - 1)
+ {
+ // if we're not the last one, the next one must
+ // also be a suffix to be valid
+ char c2 = num[$-1];
+ if (c2 != 'U' && c2 != 'u' && c2 != 'L')
+ return false; // spam at the end of the string
+ if (c2 == c)
+ return false; // repeats are disallowed
+ }
+ }
+ }
+ }
+
+ return true;
+}
+
+@safe unittest
+{
+ // ensure that you get the right types, even with embedded underscores
+ auto w = octal!"100_000_000_000";
+ static assert(!is(typeof(w) == int));
+ auto w2 = octal!"1_000_000_000";
+ static assert(is(typeof(w2) == int));
+
+ static assert(octal!"45" == 37);
+ static assert(octal!"0" == 0);
+ static assert(octal!"7" == 7);
+ static assert(octal!"10" == 8);
+ static assert(octal!"666" == 438);
+
+ static assert(octal!45 == 37);
+ static assert(octal!0 == 0);
+ static assert(octal!7 == 7);
+ static assert(octal!10 == 8);
+ static assert(octal!666 == 438);
+
+ static assert(octal!"66_6" == 438);
+
+ static assert(octal!2520046213 == 356535435);
+ static assert(octal!"2520046213" == 356535435);
+
+ static assert(octal!17777777777 == int.max);
+
+ static assert(!__traits(compiles, octal!823));
+
+ static assert(!__traits(compiles, octal!"823"));
+
+ static assert(!__traits(compiles, octal!"_823"));
+ 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
+ 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
+ b = octal!"20000000000";
+ assert(b == 1L + int.max);
+
+ b = octal!"1L";
+ assert(b == 1);
+ b = octal!1L;
+ assert(b == 1);
+}
+
+/+
+emplaceRef is a package function for phobos internal use. It works like
+emplace, but takes its argument by ref (as opposed to "by pointer").
+
+This makes it easier to use, easier to be safe, and faster in a non-inline
+build.
+
+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 void emplaceRef(T, UT, Args...)(ref UT chunk, auto ref Args args)
+{
+ static if (args.length == 0)
+ {
+ static assert(is(typeof({static T i;})),
+ convFormat("Cannot emplace a %1$s because %1$s.this() is annotated with @disable.", T.stringof));
+ 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
+ {
+ T payload;
+ this(ref Args x)
+ {
+ static if (Args.length == 1)
+ static if (is(typeof(payload = x[0])))
+ payload = x[0];
+ else
+ payload = T(x[0]);
+ else
+ payload = T(x);
+ }
+ }
+ if (__ctfe)
+ {
+ 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
+ {
+ 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));
+
+ //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 void emplaceInitializer(T)(ref T chunk) @trusted pure nothrow
+{
+ static if (!hasElaborateAssign!T && isAssignable!T)
+ chunk = T.init;
+ else
+ {
+ import core.stdc.string : memcpy;
+ static immutable T init = T.init;
+ memcpy(&chunk, &init, T.sizeof);
+ }
+}
+
+// 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. 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
+{
+ 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). 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).
+
+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)
+{
+ emplaceRef!T(*chunk, args);
+ return chunk;
+}
+
+///
+@system unittest
+{
+ 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
+{
+ int i = 3;
+ this(int i)
+ {
+ assert(this.i == 3 && i == 5);
+ this.i = i;
+ }
+ this(int i, ref int j)
+ {
+ assert(i == 5 && j == 6);
+ this.i = i;
+ ++j;
+ }
+
+@disable:
+ this();
+ this(this);
+ void opAssign();
+}
+
+version (unittest) private class __conv_EmplaceTestClass
+{
+ int i = 3;
+ this(int i) @nogc @safe pure nothrow
+ {
+ assert(this.i == 3 && i == 5);
+ this.i = i;
+ }
+ this(int i, ref int j) @nogc @safe pure nothrow
+ {
+ assert(i == 5 && j == 6);
+ this.i = i;
+ ++j;
+ }
+}
+
+@system unittest // bugzilla 15772
+{
+ 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))));
+}
+
+@system unittest
+{
+ struct S { @disable this(); }
+ S s = void;
+ static assert(!__traits(compiles, emplace(&s)));
+ emplace(&s, S.init);
+}
+
+@system unittest
+{
+ struct S1
+ {}
+
+ struct S2
+ {
+ void opAssign(S2);
+ }
+
+ S1 s1 = void;
+ S2 s2 = void;
+ S1[2] as1 = void;
+ S2[2] as2 = void;
+ emplace(&s1);
+ emplace(&s2);
+ emplace(&as1);
+ emplace(&as2);
+}
+
+@system unittest
+{
+ static struct S1
+ {
+ this(this) @disable;
+ }
+ static struct S2
+ {
+ this() @disable;
+ }
+ S1[2] ss1 = void;
+ S2[2] ss2 = void;
+ emplace(&ss1);
+ static assert(!__traits(compiles, emplace(&ss2)));
+ S1 s1 = S1.init;
+ S2 s2 = S2.init;
+ static assert(!__traits(compiles, emplace(&ss1, s1)));
+ emplace(&ss2, s2);
+}
+
+@system unittest
+{
+ struct S
+ {
+ immutable int i;
+ }
+ S s = void;
+ 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
+
+@system unittest
+{
+ interface I {}
+ class K : I {}
+
+ K k = null, k2 = new K;
+ assert(k !is k2);
+ emplace!K(&k, k2);
+ assert(k is k2);
+
+ I i = null;
+ assert(i !is k);
+ emplace!I(&i, k);
+ assert(i is k);
+}
+
+@system unittest
+{
+ static struct S
+ {
+ int i = 5;
+ void opAssign(S){assert(0);}
+ }
+ S[2] sa = void;
+ S[2] sb;
+ emplace(&sa, sb);
+ assert(sa[0].i == 5 && sa[1].i == 5);
+}
+
+//Start testing emplace-struct here
+
+// Test constructor branch
+@system unittest
+{
+ struct S
+ {
+ double x = 5, y = 6;
+ this(int a, int b)
+ {
+ assert(x == 5 && y == 6);
+ x = a;
+ y = b;
+ }
+ }
+
+ auto s1 = new void[S.sizeof];
+ auto s2 = S(42, 43);
+ assert(*emplace!S(cast(S*) s1.ptr, s2) == s2);
+ assert(*emplace!S(cast(S*) s1, 44, 45) == S(44, 45));
+}
+
+@system unittest
+{
+ __conv_EmplaceTest k = void;
+ emplace(&k, 5);
+ assert(k.i == 5);
+}
+
+@system unittest
+{
+ int var = 6;
+ __conv_EmplaceTest k = void;
+ emplace(&k, 5, var);
+ assert(k.i == 5);
+ assert(var == 7);
+}
+
+// Test matching fields branch
+@system unittest
+{
+ struct S { uint n; }
+ S s;
+ emplace!S(&s, 2U);
+ assert(s.n == 2);
+}
+
+@safe unittest
+{
+ struct S { int a, b; this(int){} }
+ S s;
+ static assert(!__traits(compiles, emplace!S(&s, 2, 3)));
+}
+
+@system unittest
+{
+ struct S { int a, b = 7; }
+ S s1 = void, s2 = void;
+
+ emplace!S(&s1, 2);
+ assert(s1.a == 2 && s1.b == 7);
+
+ emplace!S(&s2, 2, 3);
+ assert(s2.a == 2 && s2.b == 3);
+}
+
+//opAssign
+@system unittest
+{
+ static struct S
+ {
+ int i = 5;
+ void opAssign(int){assert(0);}
+ void opAssign(S){assert(0);}
+ }
+ S sa1 = void;
+ S sa2 = void;
+ S sb1 = S(1);
+ emplace(&sa1, sb1);
+ emplace(&sa2, 2);
+ assert(sa1.i == 1);
+ assert(sa2.i == 2);
+}
+
+//postblit precedence
+@system unittest
+{
+ //Works, but breaks in "-w -O" because of @@@9332@@@.
+ //Uncomment test when 9332 is fixed.
+ static struct S
+ {
+ int i;
+
+ this(S other){assert(false);}
+ this(int i){this.i = i;}
+ this(this){}
+ }
+ S a = void;
+ assert(is(typeof({S b = a;}))); //Postblit
+ assert(is(typeof({S b = S(a);}))); //Constructor
+ auto b = S(5);
+ emplace(&a, b);
+ assert(a.i == 5);
+
+ static struct S2
+ {
+ int* p;
+ this(const S2){}
+ }
+ static assert(!is(immutable S2 : S2));
+ S2 s2 = void;
+ immutable is2 = (immutable S2).init;
+ emplace(&s2, is2);
+}
+
+//nested structs and postblit
+@system unittest
+{
+ static struct S
+ {
+ int* p;
+ this(int i){p = [i].ptr;}
+ this(this)
+ {
+ if (p)
+ p = [*p].ptr;
+ }
+ }
+ static struct SS
+ {
+ S s;
+ void opAssign(const SS)
+ {
+ assert(0);
+ }
+ }
+ SS ssa = void;
+ SS ssb = SS(S(5));
+ emplace(&ssa, ssb);
+ assert(*ssa.s.p == 5);
+ assert(ssa.s.p != ssb.s.p);
+}
+
+//disabled postblit
+@system unittest
+{
+ static struct S1
+ {
+ int i;
+ @disable this(this);
+ }
+ S1 s1 = void;
+ emplace(&s1, 1);
+ assert(s1.i == 1);
+ static assert(!__traits(compiles, emplace(&s1, S1.init)));
+
+ static struct S2
+ {
+ int i;
+ @disable this(this);
+ this(ref S2){}
+ }
+ S2 s2 = void;
+ static assert(!__traits(compiles, emplace(&s2, 1)));
+ emplace(&s2, S2.init);
+
+ static struct SS1
+ {
+ S1 s;
+ }
+ SS1 ss1 = void;
+ emplace(&ss1);
+ static assert(!__traits(compiles, emplace(&ss1, SS1.init)));
+
+ static struct SS2
+ {
+ S2 s;
+ }
+ SS2 ss2 = void;
+ emplace(&ss2);
+ static assert(!__traits(compiles, emplace(&ss2, SS2.init)));
+
+
+ // SS1 sss1 = s1; //This doesn't compile
+ // SS1 sss1 = SS1(s1); //This doesn't compile
+ // So emplace shouldn't compile either
+ static assert(!__traits(compiles, emplace(&sss1, s1)));
+ static assert(!__traits(compiles, emplace(&sss2, s2)));
+}
+
+//Imutability
+@system unittest
+{
+ //Castable immutability
+ {
+ static struct S1
+ {
+ int i;
+ }
+ static assert(is( immutable(S1) : S1));
+ S1 sa = void;
+ auto sb = immutable(S1)(5);
+ emplace(&sa, sb);
+ assert(sa.i == 5);
+ }
+ //Un-castable immutability
+ {
+ static struct S2
+ {
+ int* p;
+ }
+ static assert(!is(immutable(S2) : S2));
+ S2 sa = void;
+ auto sb = immutable(S2)(null);
+ assert(!__traits(compiles, emplace(&sa, sb)));
+ }
+}
+
+@system unittest
+{
+ static struct S
+ {
+ immutable int i;
+ immutable(int)* j;
+ }
+ S s = void;
+ emplace(&s, 1, null);
+ emplace(&s, 2, &s.i);
+ assert(s is S(2, &s.i));
+}
+
+//Context pointer
+@system unittest
+{
+ int i = 0;
+ {
+ struct S1
+ {
+ void foo(){++i;}
+ }
+ S1 sa = void;
+ S1 sb;
+ emplace(&sa, sb);
+ sa.foo();
+ assert(i == 1);
+ }
+ {
+ struct S2
+ {
+ void foo(){++i;}
+ this(this){}
+ }
+ S2 sa = void;
+ S2 sb;
+ emplace(&sa, sb);
+ sa.foo();
+ assert(i == 2);
+ }
+}
+
+//Alias this
+@system unittest
+{
+ static struct S
+ {
+ int i;
+ }
+ //By Ref
+ {
+ static struct SS1
+ {
+ int j;
+ S s;
+ alias s this;
+ }
+ S s = void;
+ SS1 ss = SS1(1, S(2));
+ emplace(&s, ss);
+ assert(s.i == 2);
+ }
+ //By Value
+ {
+ static struct SS2
+ {
+ int j;
+ S s;
+ S foo() @property{return s;}
+ alias foo this;
+ }
+ S s = void;
+ SS2 ss = SS2(1, S(2));
+ emplace(&s, ss);
+ assert(s.i == 2);
+ }
+}
+version (unittest)
+{
+ //Ambiguity
+ struct __std_conv_S
+ {
+ int i;
+ this(__std_conv_SS ss) {assert(0);}
+ static opCall(__std_conv_SS ss)
+ {
+ __std_conv_S s; s.i = ss.j;
+ return s;
+ }
+ }
+ struct __std_conv_SS
+ {
+ int j;
+ __std_conv_S s;
+ ref __std_conv_S foo() return @property {s.i = j; return s;}
+ alias foo this;
+ }
+ static assert(is(__std_conv_SS : __std_conv_S));
+ @system unittest
+ {
+ __std_conv_S s = void;
+ __std_conv_SS ss = __std_conv_SS(1);
+
+ __std_conv_S sTest1 = ss; //this calls "SS alias this" (and not "S.this(SS)")
+ emplace(&s, ss); //"alias this" should take precedence in emplace over "opCall"
+ assert(s.i == 1);
+ }
+}
+
+//Nested classes
+@system unittest
+{
+ class A{}
+ static struct S
+ {
+ A a;
+ }
+ S s1 = void;
+ S s2 = S(new A);
+ emplace(&s1, s2);
+ assert(s1.a is s2.a);
+}
+
+//safety & nothrow & CTFE
+@system unittest
+{
+ //emplace should be safe for anything with no elaborate opassign
+ static struct S1
+ {
+ int i;
+ }
+ static struct S2
+ {
+ int i;
+ this(int j)@safe nothrow{i = j;}
+ }
+
+ int i;
+ S1 s1 = void;
+ S2 s2 = void;
+
+ auto pi = &i;
+ auto ps1 = &s1;
+ auto ps2 = &s2;
+
+ void foo() @safe nothrow
+ {
+ emplace(pi);
+ emplace(pi, 5);
+ emplace(ps1);
+ emplace(ps1, 5);
+ emplace(ps1, S1.init);
+ emplace(ps2);
+ emplace(ps2, 5);
+ emplace(ps2, S2.init);
+ }
+ foo();
+
+ T bar(T)() @property
+ {
+ T t/+ = void+/; //CTFE void illegal
+ 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);
+}
+
+
+@system unittest
+{
+ struct S
+ {
+ int[2] get(){return [1, 2];}
+ alias get this;
+ }
+ struct SS
+ {
+ int[2] ii;
+ }
+ struct ISS
+ {
+ int[2] ii;
+ }
+ S s;
+ SS ss = void;
+ ISS iss = void;
+ emplace(&ss, s);
+ emplace(&iss, s);
+ assert(ss.ii == [1, 2]);
+ assert(iss.ii == [1, 2]);
+}
+
+//disable opAssign
+@system unittest
+{
+ static struct S
+ {
+ @disable void opAssign(S);
+ }
+ S s;
+ emplace(&s, S.init);
+}
+
+//opCall
+@system unittest
+{
+ int i;
+ //Without constructor
+ {
+ static struct S1
+ {
+ int i;
+ static S1 opCall(int*){assert(0);}
+ }
+ S1 s = void;
+ static assert(!__traits(compiles, emplace(&s, 1)));
+ }
+ //With constructor
+ {
+ static struct S2
+ {
+ int i = 0;
+ static S2 opCall(int*){assert(0);}
+ static S2 opCall(int){assert(0);}
+ this(int i){this.i = i;}
+ }
+ S2 s = void;
+ emplace(&s, 1);
+ assert(s.i == 1);
+ }
+ //With postblit ambiguity
+ {
+ static struct S3
+ {
+ int i = 0;
+ static S3 opCall(ref S3){assert(0);}
+ }
+ S3 s = void;
+ emplace(&s, S3.init);
+ }
+}
+
+@safe unittest //@@@9559@@@
+{
+ import std.algorithm.iteration : map;
+ import std.array : array;
+ import std.typecons : Nullable;
+ alias I = Nullable!int;
+ auto ints = [0, 1, 2].map!(i => i & 1 ? I.init : I(i))();
+ auto asArray = array(ints);
+}
+
+@system unittest //http://forum.dlang.org/post/nxbdgtdlmwscocbiypjs@forum.dlang.org
+{
+ import std.array : array;
+ import std.datetime : SysTime, UTC;
+ import std.math : isNaN;
+
+ static struct A
+ {
+ double i;
+ }
+
+ static struct B
+ {
+ invariant()
+ {
+ if (j == 0)
+ assert(a.i.isNaN(), "why is 'j' zero?? and i is not NaN?");
+ else
+ assert(!a.i.isNaN());
+ }
+ SysTime when; // comment this line avoid the breakage
+ int j;
+ A a;
+ }
+
+ B b1 = B.init;
+ assert(&b1); // verify that default eyes invariants are ok;
+
+ auto b2 = B(SysTime(0, UTC()), 1, A(1));
+ assert(&b2);
+ auto b3 = B(SysTime(0, UTC()), 1, A(1));
+ assert(&b3);
+
+ auto arr = [b2, b3];
+
+ assert(arr[0].j == 1);
+ assert(arr[1].j == 1);
+ auto a2 = arr.array(); // << bang, invariant is raised, also if b2 and b3 are good
+}
+
+//static arrays
+@system unittest
+{
+ static struct S
+ {
+ int[2] ii;
+ }
+ static struct IS
+ {
+ immutable int[2] ii;
+ }
+ int[2] ii;
+ S s = void;
+ IS ims = void;
+ ubyte ub = 2;
+ emplace(&s, ub);
+ emplace(&s, ii);
+ emplace(&ims, ub);
+ emplace(&ims, ii);
+ uint[2] uu;
+ static assert(!__traits(compiles, {S ss = S(uu);}));
+ static assert(!__traits(compiles, emplace(&s, uu)));
+}
+
+@system unittest
+{
+ int[2] sii;
+ int[2] sii2;
+ uint[2] uii;
+ uint[2] uii2;
+ emplace(&sii, 1);
+ emplace(&sii, 1U);
+ emplace(&uii, 1);
+ emplace(&uii, 1U);
+ emplace(&sii, sii2);
+ //emplace(&sii, uii2); //Sorry, this implementation doesn't know how to...
+ //emplace(&uii, sii2); //Sorry, this implementation doesn't know how to...
+ emplace(&uii, uii2);
+ emplace(&sii, sii2[]);
+ //emplace(&sii, uii2[]); //Sorry, this implementation doesn't know how to...
+ //emplace(&uii, sii2[]); //Sorry, this implementation doesn't know how to...
+ emplace(&uii, uii2[]);
+}
+
+@system unittest
+{
+ bool allowDestruction = false;
+ struct S
+ {
+ int i;
+ this(this){}
+ ~this(){assert(allowDestruction);}
+ }
+ S s = S(1);
+ S[2] ss1 = void;
+ S[2] ss2 = void;
+ S[2] ss3 = void;
+ emplace(&ss1, s);
+ emplace(&ss2, ss1);
+ emplace(&ss3, ss2[]);
+ assert(ss1[1] == s);
+ assert(ss2[1] == s);
+ assert(ss3[1] == s);
+ allowDestruction = true;
+}
+
+@system unittest
+{
+ //Checks postblit, construction, and context pointer
+ int count = 0;
+ struct S
+ {
+ this(this)
+ {
+ ++count;
+ }
+ ~this()
+ {
+ --count;
+ }
+ }
+
+ S s;
+ {
+ S[4] ss = void;
+ emplace(&ss, s);
+ assert(count == 4);
+ }
+ assert(count == 0);
+}
+
+@system unittest
+{
+ struct S
+ {
+ int i;
+ }
+ S s;
+ S[2][2][2] sss = void;
+ emplace(&sss, s);
+}
+
+@system unittest //Constness
+{
+ import std.stdio;
+
+ int a = void;
+ emplaceRef!(const int)(a, 5);
+
+ immutable i = 5;
+ const(int)* p = void;
+ emplaceRef!(const int*)(p, &i);
+
+ struct S
+ {
+ int* p;
+ }
+ alias IS = immutable(S);
+ S s = void;
+ emplaceRef!IS(s, IS());
+ S[2] ss = void;
+ emplaceRef!(IS[2])(ss, IS());
+
+ IS[2] iss = IS.init;
+ emplaceRef!(IS[2])(ss, iss);
+ emplaceRef!(IS[2])(ss, iss[]);
+}
+
+pure nothrow @safe @nogc unittest
+{
+ int i;
+ emplaceRef(i);
+ emplaceRef!int(i);
+ emplaceRef(i, 5);
+ emplaceRef!int(i, 5);
+}
+
+// Test attribute propagation for UDTs
+pure nothrow @safe /* @nogc */ unittest
+{
+ static struct Safe
+ {
+ this(this) pure nothrow @safe @nogc {}
+ }
+
+ Safe safe = void;
+ emplaceRef(safe, Safe());
+
+ Safe[1] safeArr = [Safe()];
+ Safe[1] uninitializedSafeArr = void;
+ emplaceRef(uninitializedSafeArr, safe);
+ emplaceRef(uninitializedSafeArr, safeArr);
+
+ static struct Unsafe
+ {
+ this(this) @system {}
+ }
+
+ Unsafe unsafe = void;
+ static assert(!__traits(compiles, emplaceRef(unsafe, Unsafe())));
+
+ Unsafe[1] unsafeArr = [Unsafe()];
+ Unsafe[1] uninitializedUnsafeArr = void;
+ static assert(!__traits(compiles, emplaceRef(uninitializedUnsafeArr, unsafe)));
+ static assert(!__traits(compiles, emplaceRef(uninitializedUnsafeArr, unsafeArr)));
+}
+
+@system unittest
+{
+ // Issue 15313
+ static struct Node
+ {
+ int payload;
+ Node* next;
+ uint refs;
+ }
+
+ 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);
+}
+
+@system unittest
+{
+ int var = 6;
+ auto k = emplace!__conv_EmplaceTest(new void[__conv_EmplaceTest.sizeof], 5, var);
+ assert(k.i == 5);
+ assert(var == 7);
+}
+
+@system unittest
+{
+ class A
+ {
+ int x = 5;
+ int y = 42;
+ this(int z)
+ {
+ assert(x == 5 && y == 42);
+ x = y = z;
+ }
+ }
+ void[] buf;
+
+ static align(A.alignof) byte[__traits(classInstanceSize, A)] sbuf;
+ buf = sbuf[];
+ auto a = emplace!A(buf, 55);
+ assert(a.x == 55 && a.y == 55);
+
+ // emplace in bigger buffer
+ buf = new byte[](__traits(classInstanceSize, A) + 10);
+ a = emplace!A(buf, 55);
+ assert(a.x == 55 && a.y == 55);
+
+ // need ctor args
+ static assert(!is(typeof(emplace!A(buf))));
+}
+// Bulk of emplace unittests ends here
+
+@safe unittest
+{
+ 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))
+{
+ import core.internal.string : SignedStringBuf, signedToTempString,
+ UnsignedStringBuf, unsignedToTempString;
+
+ if (value < 0)
+ {
+ SignedStringBuf buf = void;
+ put(writer, signedToTempString(value, buf, 10));
+ }
+ else
+ {
+ UnsignedStringBuf buf = void;
+ put(writer, unsignedToTempString(value, buf, 10));
+ }
+}
+
+@safe unittest
+{
+ import std.array : appender;
+ auto result = appender!(char[])();
+ toTextRange(-1, result);
+ assert(result.data == "-1");
+}
+
+
+/**
+ Returns the corresponding _unsigned value for $(D x) (e.g. if $(D x) has type
+ $(D int), it returns $(D cast(uint) x)). The advantage compared to the cast
+ is that you do not need to rewrite the cast if $(D x) later changes type
+ (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 $(REF Unsigned, std,traits).
+ */
+auto unsigned(T)(T x)
+if (isIntegral!T)
+{
+ return cast(Unqual!(Unsigned!T))x;
+}
+
+///
+@safe unittest
+{
+ import std.traits : Unsigned;
+ immutable int s = 42;
+ auto u1 = unsigned(s); //not qualified
+ static assert(is(typeof(u1) == uint));
+ Unsigned!(typeof(s)) u2 = unsigned(s); //same qualification
+ static assert(is(typeof(u2) == immutable uint));
+ immutable u3 = unsigned(s); //explicitly qualified
+}
+
+@safe unittest
+{
+ 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));
+ }
+
+ 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));
+ }
+
+ 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));
+ }
+
+ 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));
+ }
+}
+
+auto unsigned(T)(T x)
+if (isSomeChar!T)
+{
+ // All characters are unsigned
+ static assert(T.min == 0);
+ return cast(Unqual!T) x;
+}
+
+@safe unittest
+{
+ 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));
+ static assert(is(typeof(unsigned(cast(immutable T)'A')) == T));
+ }
+}
+
+
+/**
+ Returns the corresponding _signed value for $(D x) (e.g. if $(D x) has type
+ $(D uint), it returns $(D cast(int) x)). The advantage compared to the cast
+ is that you do not need to rewrite the cast if $(D x) later changes type
+ (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 $(REF Signed, std,traits).
+ */
+auto signed(T)(T x)
+if (isIntegral!T)
+{
+ return cast(Unqual!(Signed!T))x;
+}
+
+///
+@safe unittest
+{
+ import std.traits : Signed;
+
+ immutable uint u = 42;
+ auto s1 = signed(u); //not qualified
+ static assert(is(typeof(s1) == int));
+ Signed!(typeof(u)) s2 = signed(u); //same qualification
+ static assert(is(typeof(s2) == immutable int));
+ immutable s3 = signed(u); //explicitly qualified
+}
+
+@system unittest
+{
+ 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));
+ }
+
+ 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));
+ }
+
+ 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));
+ }
+
+ 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));
+ }
+}
+
+@safe unittest
+{
+ // issue 10874
+ enum Test { a = 0 }
+ ulong l = 0;
+ auto t = l.to!Test;
+}
+
+// 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;
+}
+
+///
+@safe 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);
+}
+
+/**
+ A wrapper on top of the built-in cast operator that allows one to restrict
+ casting of the original type of the value.
+
+ A common issue with using a raw cast is that it may silently continue to
+ compile even if the value's type has changed during refactoring,
+ which breaks the initial assumption about the cast.
+
+ Params:
+ From = The type to cast from. The programmer must ensure it is legal
+ to make this cast.
+ */
+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(
+ 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)),
+ "can't cast from '" ~ From.stringof ~ "' to '" ~ To.stringof ~ "'"
+ );
+
+ return cast(To) value;
+ }
+}
+
+///
+@system unittest
+{
+ // Regular cast, which has been verified to be legal by the programmer:
+ {
+ long x;
+ auto y = cast(int) x;
+ }
+
+ // However this will still compile if 'x' is changed to be a pointer:
+ {
+ long* x;
+ auto y = cast(int) x;
+ }
+
+ // castFrom provides a more reliable alternative to casting:
+ {
+ long x;
+ auto y = castFrom!long.to!int(x);
+ }
+
+ // Changing the type of 'x' will now issue a compiler error,
+ // allowing bad casts to be caught before it's too late:
+ {
+ long* x;
+ static assert(
+ !__traits(compiles, castFrom!long.to!int(x))
+ );
+
+ // if cast is still needed, must be changed to:
+ 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/libphobos/src/std/csv.d b/libphobos/src/std/csv.d
new file mode 100644
index 0000000..b10f1e6
--- /dev/null
+++ b/libphobos/src/std/csv.d
@@ -0,0 +1,1701 @@
+//Written in the D programming language
+
+/**
+ * Implements functionality to read Comma Separated Values and its variants
+ * 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
+ * $(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))
+ * $(LI A final record may end with a new line)
+ * $(LI A header may be provided as the first record in input)
+ * $(LI A record has fields separated by a comma (customizable))
+ * $(LI A field containing new lines, commas, or double quotes
+ * should be enclosed in double quotes (customizable))
+ * $(LI Double quotes in a field are escaped with a double quote)
+ * $(LI Each record should contain the same number of fields)
+ * )
+ *
+ * Example:
+ *
+ * -------
+ * import std.algorithm;
+ * import std.array;
+ * import std.csv;
+ * import std.stdio;
+ * import std.typecons;
+ *
+ * void main()
+ * {
+ * auto text = "Joe,Carpenter,300000\nFred,Blacksmith,400000\r\n";
+ *
+ * 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]);
+ * }
+ }
+ * }
+ * -------
+ *
+ * When an input contains a header the $(D Contents) can be specified as an
+ * associative array. Passing null to signify that a header is present.
+ *
+ * -------
+ * auto text = "Name,Occupation,Salary\r"
+ * "Joe,Carpenter,300000\nFred,Blacksmith,400000\r\n";
+ *
+ * foreach (record; csvReader!(string[string])
+ * (text, null))
+ * {
+ * writefln("%s works as a %s and earns $%s per year.",
+ * record["Name"], record["Occupation"],
+ * record["Salary"]);
+ * }
+ * -------
+ *
+ * This module allows content to be iterated by record stored in a struct,
+ * class, associative array, or as a range of fields. Upon detection of an
+ * error an CSVException is thrown (can be disabled). csvNextToken has been
+ * made public to allow for attempted recovery.
+ *
+ * Disabling exceptions will lift many restrictions specified above. A quote
+ * can appear in a field if the field was not quoted. If in a quoted field any
+ * quote by itself, not at the end of a field, will end processing for that
+ * field. The field is ended when there is no input, even if the quote was not
+ * closed.
+ *
+ * See_Also:
+ * $(HTTP en.wikipedia.org/wiki/Comma-separated_values, Wikipedia
+ * Comma-separated values)
+ *
+ * Copyright: Copyright 2011
+ * License: $(HTTP www.boost.org/LICENSE_1_0.txt, Boost License 1.0).
+ * Authors: Jesse Phillips
+ * Source: $(PHOBOSSRC std/_csv.d)
+ */
+module std.csv;
+
+import std.conv;
+import std.exception; // basicExceptionCtors
+import std.range.primitives;
+import std.traits;
+
+/**
+ * Exception containing the row and column for when an exception was thrown.
+ *
+ * Numbering of both row and col start at one and corresponds to the location
+ * in the file rather than any specified header. Special consideration should
+ * be made when there is failure to match the header see $(LREF
+ * HeaderMismatchException) for details.
+ *
+ * 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) @nogc @safe pure nothrow
+ {
+ super(msg, file, line, next);
+ }
+
+ this(string msg, Throwable next, string file = __FILE__,
+ 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__) @nogc @safe pure nothrow
+ {
+ super(msg, next, file, line);
+ this.row = row;
+ this.col = col;
+ }
+
+ override string toString() @safe pure const
+ {
+ return "(Row: " ~ to!string(row) ~
+ ", Col: " ~ to!string(col) ~ ") " ~ msg;
+ }
+}
+
+@safe pure unittest
+{
+ import std.string;
+ auto e1 = new Exception("Foobar");
+ auto e2 = new CSVException("args", e1);
+ assert(e2.next is e1);
+
+ size_t r = 13;
+ size_t c = 37;
+
+ auto e3 = new CSVException("argv", r, c);
+ assert(e3.row == r);
+ assert(e3.col == c);
+
+ auto em = e3.toString();
+ assert(em.indexOf("13") != -1);
+ assert(em.indexOf("37") != -1);
+}
+
+/**
+ * Exception thrown when a Token is identified to not be completed: a quote is
+ * found in an unquoted field, data continues after a closing quote, or the
+ * quoted field was not closed before data was empty.
+ */
+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.
+ */
+ dstring partialData;
+
+ mixin basicExceptionCtors;
+}
+
+@safe pure unittest
+{
+ auto e1 = new Exception("Foobar");
+ auto e2 = new IncompleteCellException("args", e1);
+ assert(e2.next is e1);
+}
+
+/**
+ * Exception thrown under different conditions based on the type of $(D
+ * Contents).
+ *
+ * Structure, Class, and Associative Array
+ * $(UL
+ * $(LI When a header is provided but a matching column is not found)
+ * )
+ *
+ * Other
+ * $(UL
+ * $(LI When a header is provided but a matching column is not found)
+ * $(LI Order did not match that found in the input)
+ * )
+ *
+ * Since a row and column is not meaningful when a column specified by the
+ * header is not found in the data, both row and col will be zero. Otherwise
+ * row is always one and col is the first instance found in header that
+ * occurred before the previous starting at one.
+ */
+class HeaderMismatchException : CSVException
+{
+ mixin basicExceptionCtors;
+}
+
+@safe pure unittest
+{
+ auto e1 = new Exception("Foobar");
+ auto e2 = new HeaderMismatchException("args", e1);
+ assert(e2.next is e1);
+}
+
+/**
+ * Determines the behavior for when an error is detected.
+ *
+ * Disabling exception will follow these rules:
+ * $(UL
+ * $(LI A quote can appear in a field if the field was not quoted.)
+ * $(LI If in a quoted field any quote by itself, not at the end of a
+ * field, will end processing for that field.)
+ * $(LI The field is ended when there is no input, even if the quote was
+ * not closed.)
+ * $(LI If the given header does not match the order in the input, the
+ * content will return as it is found in the input.)
+ * $(LI If the given header contains columns not found in the input they
+ * will be ignored.)
+ * )
+*/
+enum Malformed
+{
+ ignore, /// No exceptions are thrown due to incorrect CSV.
+ throwException /// Use exceptions when input has incorrect CSV.
+}
+
+/**
+ * Returns an input range for iterating over records found in $(D
+ * input).
+ *
+ * The $(D Contents) of the input can be provided if all the records are the
+ * same type such as all integer data:
+ *
+ * -------
+ * string str = `76,26,22`;
+ * int[] ans = [76,26,22];
+ * auto records = csvReader!int(str);
+ *
+ * foreach (record; records)
+ * {
+ * assert(equal(record, ans));
+ * }
+ * -------
+ *
+ * Example using a struct with modified delimiter:
+ *
+ * -------
+ * string str = "Hello;65;63.63\nWorld;123;3673.562";
+ * struct Layout
+ * {
+ * string name;
+ * int value;
+ * double other;
+ * }
+ *
+ * auto records = csvReader!Layout(str,';');
+ *
+ * foreach (record; records)
+ * {
+ * writeln(record.name);
+ * writeln(record.value);
+ * writeln(record.other);
+ * }
+ * -------
+ *
+ * Specifying $(D ErrorLevel) as Malformed.ignore will lift restrictions
+ * on the format. This example shows that an exception is not thrown when
+ * finding a quote in a field not quoted.
+ *
+ * -------
+ * string str = "A \" is now part of the data";
+ * auto records = csvReader!(string,Malformed.ignore)(str);
+ * auto record = records.front;
+ *
+ * assert(record.front == str);
+ * -------
+ *
+ * Returns:
+ * 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,
+ * data continues after a closing quote, the quoted field was not
+ * closed before data was empty, a conversion failed, or when the row's
+ * length does not match the previous length.
+ *
+ * $(LREF HeaderMismatchException) when a header is provided but a
+ * matching column is not found or the order did not match that found in
+ * the input. Read the exception documentation for specific details of
+ * when the exception is thrown for different types of $(D Contents).
+ */
+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))
+{
+ return CsvReader!(Contents,ErrorLevel,Range,
+ Unqual!(ElementType!Range),string[])
+ (input, delimiter, quote);
+}
+
+/**
+ * An optional $(D header) can be provided. The first record will be read in
+ * as the header. If $(D Contents) is a struct then the header provided is
+ * expected to correspond to the fields in the struct. When $(D Contents) is
+ * not a type which can contain the entire record, the $(D header) must be
+ * provided in the same order as the input or an exception is thrown.
+ *
+ * Read only column "b":
+ *
+ * -------
+ * string str = "a,b,c\nHello,65,63.63\nWorld,123,3673.562";
+ * auto records = csvReader!int(str, ["b"]);
+ *
+ * auto ans = [[65],[123]];
+ * foreach (record; records)
+ * {
+ * assert(equal(record, ans.front));
+ * ans.popFront();
+ * }
+ * -------
+ *
+ * Read from header of different order:
+ *
+ * -------
+ * string str = "a,b,c\nHello,65,63.63\nWorld,123,3673.562";
+ * struct Layout
+ * {
+ * int value;
+ * double other;
+ * string name;
+ * }
+ *
+ * auto records = csvReader!Layout(str, ["b","c","a"]);
+ * -------
+ *
+ * The header can also be left empty if the input contains a header but
+ * all columns should be iterated. The header from the input can always
+ * be accessed from the header field.
+ *
+ * -------
+ * string str = "a,b,c\nHello,65,63.63\nWorld,123,3673.562";
+ * auto records = csvReader(str, null);
+ *
+ * assert(records.header == ["a","b","c"]);
+ * -------
+ *
+ * Returns:
+ * 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.
+ *
+ * -------
+ * string str = "a,b,c\nHello,65,63.63";
+ * auto records = csvReader(str, ["a"]);
+ *
+ * assert(records.header == ["a","b","c"]);
+ * -------
+ *
+ * Throws:
+ * $(LREF CSVException) When a quote is found in an unquoted field,
+ * data continues after a closing quote, the quoted field was not
+ * closed before data was empty, a conversion failed, or when the row's
+ * length does not match the previous length.
+ *
+ * $(LREF HeaderMismatchException) when a header is provided but a
+ * matching column is not found or the order did not match that found in
+ * the input. Read the exception documentation for specific details of
+ * when the exception is thrown for different types of $(D Contents).
+ */
+auto csvReader(Contents = string,
+ Malformed ErrorLevel = Malformed.throwException,
+ 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))
+{
+ return CsvReader!(Contents,ErrorLevel,Range,
+ Unqual!(ElementType!Range),Header)
+ (input, header, delimiter, quote);
+}
+
+///
+auto csvReader(Contents = string,
+ Malformed ErrorLevel = Malformed.throwException,
+ 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)))
+{
+ return CsvReader!(Contents,ErrorLevel,Range,
+ Unqual!(ElementType!Range),string[])
+ (input, cast(string[]) null, delimiter, quote);
+}
+
+// Test standard iteration over input.
+@safe pure unittest
+{
+ string str = `one,"two ""quoted"""` ~ "\n\"three\nnew line\",\nfive,six";
+ auto records = csvReader(str);
+
+ int count;
+ foreach (record; records)
+ {
+ foreach (cell; record)
+ {
+ count++;
+ }
+ }
+ assert(count == 6);
+}
+
+// Test newline on last record
+@safe pure unittest
+{
+ string str = "one,two\nthree,four\n";
+ auto records = csvReader(str);
+ records.popFront();
+ records.popFront();
+ assert(records.empty);
+}
+
+// Test shorter row length
+@safe pure unittest
+{
+ wstring str = "one,1\ntwo\nthree"w;
+ struct Layout
+ {
+ string name;
+ int value;
+ }
+
+ Layout[3] ans;
+ ans[0].name = "one";
+ ans[0].value = 1;
+ ans[1].name = "two";
+ ans[1].value = 0;
+ ans[2].name = "three";
+ ans[2].value = 0;
+
+ auto records = csvReader!(Layout,Malformed.ignore)(str);
+
+ int count;
+ foreach (record; records)
+ {
+ assert(ans[count].name == record.name);
+ assert(ans[count].value == record.value);
+ count++;
+ }
+}
+
+// Test shorter row length exception
+@safe pure unittest
+{
+ import std.exception;
+
+ struct A
+ {
+ string a,b,c;
+ }
+
+ auto strs = ["one,1\ntwo",
+ "one\ntwo,2,二\nthree,3,三",
+ "one\ntwo,2\nthree,3",
+ "one,1\ntwo\nthree,3"];
+
+ foreach (str; strs)
+ {
+ auto records = csvReader!A(str);
+ assertThrown!CSVException((){foreach (record; records) { }}());
+ }
+}
+
+
+// Test structure conversion interface with unicode.
+@safe pure unittest
+{
+ import std.math : abs;
+
+ wstring str = "\U00010143Hello,65,63.63\nWorld,123,3673.562"w;
+ struct Layout
+ {
+ string name;
+ int value;
+ double other;
+ }
+
+ Layout[2] ans;
+ ans[0].name = "\U00010143Hello";
+ ans[0].value = 65;
+ ans[0].other = 63.63;
+ ans[1].name = "World";
+ ans[1].value = 123;
+ ans[1].other = 3673.562;
+
+ auto records = csvReader!Layout(str);
+
+ int count;
+ foreach (record; records)
+ {
+ assert(ans[count].name == record.name);
+ assert(ans[count].value == record.value);
+ assert(abs(ans[count].other - record.other) < 0.00001);
+ count++;
+ }
+ assert(count == ans.length);
+}
+
+// Test input conversion interface
+@safe pure unittest
+{
+ import std.algorithm;
+ string str = `76,26,22`;
+ int[] ans = [76,26,22];
+ auto records = csvReader!int(str);
+
+ foreach (record; records)
+ {
+ assert(equal(record, ans));
+ }
+}
+
+// Test struct & header interface and same unicode
+@safe unittest
+{
+ import std.math : abs;
+
+ string str = "a,b,c\nHello,65,63.63\n➊➋➂❹,123,3673.562";
+ struct Layout
+ {
+ int value;
+ double other;
+ string name;
+ }
+
+ auto records = csvReader!Layout(str, ["b","c","a"]);
+
+ Layout[2] ans;
+ ans[0].name = "Hello";
+ ans[0].value = 65;
+ ans[0].other = 63.63;
+ ans[1].name = "➊➋➂❹";
+ ans[1].value = 123;
+ ans[1].other = 3673.562;
+
+ int count;
+ foreach (record; records)
+ {
+ assert(ans[count].name == record.name);
+ assert(ans[count].value == record.value);
+ assert(abs(ans[count].other - record.other) < 0.00001);
+ count++;
+ }
+ assert(count == ans.length);
+
+}
+
+// Test header interface
+@safe unittest
+{
+ import std.algorithm;
+
+ string str = "a,b,c\nHello,65,63.63\nWorld,123,3673.562";
+ auto records = csvReader!int(str, ["b"]);
+
+ auto ans = [[65],[123]];
+ foreach (record; records)
+ {
+ assert(equal(record, ans.front));
+ ans.popFront();
+ }
+
+ try
+ {
+ csvReader(str, ["c","b"]);
+ assert(0);
+ }
+ catch (HeaderMismatchException e)
+ {
+ assert(e.col == 2);
+ }
+ auto records2 = csvReader!(string,Malformed.ignore)
+ (str, ["b","a"], ',', '"');
+
+ auto ans2 = [["Hello","65"],["World","123"]];
+ foreach (record; records2)
+ {
+ assert(equal(record, ans2.front));
+ ans2.popFront();
+ }
+
+ str = "a,c,e\nJoe,Carpenter,300000\nFred,Fly,4";
+ records2 = csvReader!(string,Malformed.ignore)
+ (str, ["a","b","c","d"], ',', '"');
+
+ ans2 = [["Joe","Carpenter"],["Fred","Fly"]];
+ foreach (record; records2)
+ {
+ assert(equal(record, ans2.front));
+ ans2.popFront();
+ }
+}
+
+// Test null header interface
+@safe unittest
+{
+ string str = "a,b,c\nHello,65,63.63\nWorld,123,3673.562";
+ auto records = csvReader(str, ["a"]);
+
+ assert(records.header == ["a","b","c"]);
+}
+
+// Test unchecked read
+@safe pure unittest
+{
+ string str = "one \"quoted\"";
+ foreach (record; csvReader!(string,Malformed.ignore)(str))
+ {
+ foreach (cell; record)
+ {
+ assert(cell == "one \"quoted\"");
+ }
+ }
+
+ str = "one \"quoted\",two \"quoted\" end";
+ struct Ans
+ {
+ string a,b;
+ }
+ foreach (record; csvReader!(Ans,Malformed.ignore)(str))
+ {
+ assert(record.a == "one \"quoted\"");
+ assert(record.b == "two \"quoted\" end");
+ }
+}
+
+// Test partial data returned
+@safe pure unittest
+{
+ string str = "\"one\nnew line";
+
+ try
+ {
+ foreach (record; csvReader(str))
+ {}
+ assert(0);
+ }
+ catch (IncompleteCellException ice)
+ {
+ assert(ice.partialData == "one\nnew line");
+ }
+}
+
+// Test Windows line break
+@safe pure unittest
+{
+ string str = "one,two\r\nthree";
+
+ auto records = csvReader(str);
+ auto record = records.front;
+ assert(record.front == "one");
+ record.popFront();
+ assert(record.front == "two");
+ records.popFront();
+ record = records.front;
+ assert(record.front == "three");
+}
+
+
+// Test associative array support with unicode separator
+@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)
+ {
+ count++;
+ assert(record["1"] == "34");
+ assert(record["3"] == "63");
+ }
+ assert(count == 2);
+}
+
+// Test restricted range
+@safe unittest
+{
+ import std.typecons;
+ struct InputRange
+ {
+ dstring text;
+
+ this(dstring txt)
+ {
+ text = txt;
+ }
+
+ @property auto empty()
+ {
+ return text.empty;
+ }
+
+ void popFront()
+ {
+ text.popFront();
+ }
+
+ @property dchar front()
+ {
+ return text[0];
+ }
+ }
+ 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)) {}
+}
+
+@safe unittest // const/immutable dchars
+{
+ 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";
+ assert(csvReader(i).map!array.array == [["foo", "bar"]]);
+}
+
+/*
+ * This struct is stored on the heap for when the structures
+ * are passed around.
+ */
+private pure struct Input(Range, Malformed ErrorLevel)
+{
+ Range range;
+ size_t row, col;
+ static if (ErrorLevel == Malformed.throwException)
+ size_t rowLength;
+}
+
+/*
+ * Range for iterating CSV records.
+ *
+ * This range is returned by the $(LREF csvReader) functions. It can be
+ * created in a similar manner to allow $(D ErrorLevel) be set to $(LREF
+ * 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))
+{
+private:
+ Input!(Range, ErrorLevel)* _input;
+ Separator _separator;
+ Separator _quote;
+ size_t[] indices;
+ bool _empty;
+ static if (is(Contents == struct) || is(Contents == class))
+ {
+ Contents recordContent;
+ CsvRecord!(string, ErrorLevel, Range, Separator) recordRange;
+ }
+ else static if (is(Contents T : T[U], U : string))
+ {
+ Contents recordContent;
+ CsvRecord!(T, ErrorLevel, Range, Separator) recordRange;
+ }
+ else
+ CsvRecord!(Contents, ErrorLevel, Range, Separator) recordRange;
+public:
+ /**
+ * Header from the input in array form.
+ *
+ * -------
+ * string str = "a,b,c\nHello,65,63.63";
+ * auto records = csvReader(str, ["a"]);
+ *
+ * assert(records.header == ["a","b","c"]);
+ * -------
+ */
+ string[] header;
+
+ /**
+ * Constructor to initialize the input, delimiter and quote for input
+ * without a header.
+ *
+ * -------
+ * string str = `76;^26^;22`;
+ * int[] ans = [76,26,22];
+ * auto records = CsvReader!(int,Malformed.ignore,string,char,string[])
+ * (str, ';', '^');
+ *
+ * foreach (record; records)
+ * {
+ * assert(equal(record, ans));
+ * }
+ * -------
+ */
+ this(Range input, Separator delimiter, Separator quote)
+ {
+ _input = new Input!(Range, ErrorLevel)(input);
+ _separator = delimiter;
+ _quote = quote;
+
+ prime();
+ }
+
+ /**
+ * Constructor to initialize the input, delimiter and quote for input
+ * with a header.
+ *
+ * -------
+ * string str = `high;mean;low\n76;^26^;22`;
+ * auto records = CsvReader!(int,Malformed.ignore,string,char,string[])
+ * (str, ["high","low"], ';', '^');
+ *
+ * int[] ans = [76,22];
+ * foreach (record; records)
+ * {
+ * assert(equal(record, ans));
+ * }
+ * -------
+ *
+ * Throws:
+ * $(LREF HeaderMismatchException) when a header is provided but a
+ * matching column is not found or the order did not match that found
+ * in the input (non-struct).
+ */
+ this(Range input, Header colHeaders, Separator delimiter, Separator quote)
+ {
+ _input = new Input!(Range, ErrorLevel)(input);
+ _separator = delimiter;
+ _quote = quote;
+
+ size_t[string] colToIndex;
+ foreach (h; colHeaders)
+ {
+ colToIndex[h] = size_t.max;
+ }
+
+ auto r = CsvRecord!(string, ErrorLevel, Range, Separator)
+ (_input, _separator, _quote, indices);
+
+ size_t colIndex;
+ foreach (col; r)
+ {
+ header ~= col;
+ auto ptr = col in colToIndex;
+ if (ptr)
+ *ptr = colIndex;
+ colIndex++;
+ }
+ // The above loop empties the header row.
+ recordRange._empty = true;
+
+ indices.length = colToIndex.length;
+ int i;
+ foreach (h; colHeaders)
+ {
+ immutable index = colToIndex[h];
+ static if (ErrorLevel != Malformed.ignore)
+ if (index == size_t.max)
+ throw new HeaderMismatchException
+ ("Header not found: " ~ to!string(h));
+ indices[i++] = index;
+ }
+
+ static if (!is(Contents == struct) && !is(Contents == class))
+ {
+ static if (is(Contents T : T[U], U : string))
+ {
+ import std.algorithm.sorting : sort;
+ sort(indices);
+ }
+ else static if (ErrorLevel == Malformed.ignore)
+ {
+ import std.algorithm.sorting : sort;
+ sort(indices);
+ }
+ else
+ {
+ import std.algorithm.searching : findAdjacent;
+ import std.algorithm.sorting : isSorted;
+ if (!isSorted(indices))
+ {
+ auto ex = new HeaderMismatchException
+ ("Header in input does not match specified header.");
+ findAdjacent!"a > b"(indices);
+ ex.row = 1;
+ ex.col = indices.front;
+
+ throw ex;
+ }
+ }
+ }
+
+ popFront();
+ }
+
+ /**
+ * 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.
+ *
+ * If $(D Contents) is a class, will be filled with record data.
+ *
+ * If $(D Contents) is a associative array, will be filled
+ * with record data.
+ *
+ * If $(D Contents) is non-struct, a $(LREF CsvRecord) will be
+ * returned.
+ */
+ @property auto front()
+ {
+ assert(!empty);
+ static if (is(Contents == struct) || is(Contents == class))
+ {
+ return recordContent;
+ }
+ else static if (is(Contents T : T[U], U : string))
+ {
+ return recordContent;
+ }
+ else
+ {
+ return recordRange;
+ }
+ }
+
+ /**
+ * Part of an input range as defined by
+ * $(REF isInputRange, std,range,primitives).
+ */
+ @property bool empty() @safe @nogc pure nothrow const
+ {
+ return _empty;
+ }
+
+ /**
+ * 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,
+ * data continues after a closing quote, the quoted field was not
+ * closed before data was empty, a conversion failed, or when the
+ * row's length does not match the previous length.
+ */
+ void popFront()
+ {
+ while (!recordRange.empty)
+ {
+ recordRange.popFront();
+ }
+
+ static if (ErrorLevel == Malformed.throwException)
+ if (_input.rowLength == 0)
+ _input.rowLength = _input.col;
+
+ _input.col = 0;
+
+ if (!_input.range.empty)
+ {
+ 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)
+ {
+ _empty = true;
+ return;
+ }
+
+ prime();
+ }
+
+ private void prime()
+ {
+ if (_empty)
+ return;
+ _input.row++;
+ static if (is(Contents == struct) || is(Contents == class))
+ {
+ recordRange = typeof(recordRange)
+ (_input, _separator, _quote, null);
+ }
+ else
+ {
+ recordRange = typeof(recordRange)
+ (_input, _separator, _quote, indices);
+ }
+
+ static if (is(Contents T : T[U], U : string))
+ {
+ T[U] aa;
+ try
+ {
+ for (; !recordRange.empty; recordRange.popFront())
+ {
+ aa[header[_input.col-1]] = recordRange.front;
+ }
+ }
+ catch (ConvException e)
+ {
+ throw new CSVException(e.msg, _input.row, _input.col, e);
+ }
+
+ recordContent = aa;
+ }
+ else static if (is(Contents == struct) || is(Contents == class))
+ {
+ static if (is(Contents == class))
+ recordContent = new typeof(recordContent)();
+ else
+ recordContent = typeof(recordContent).init;
+ size_t colIndex;
+ try
+ {
+ for (; !recordRange.empty;)
+ {
+ auto colData = recordRange.front;
+ scope(exit) colIndex++;
+ if (indices.length > 0)
+ {
+ foreach (ti, ToType; Fields!(Contents))
+ {
+ if (indices[ti] == colIndex)
+ {
+ static if (!isSomeString!ToType) skipWS(colData);
+ recordContent.tupleof[ti] = to!ToType(colData);
+ }
+ }
+ }
+ else
+ {
+ foreach (ti, ToType; Fields!(Contents))
+ {
+ if (ti == colIndex)
+ {
+ static if (!isSomeString!ToType) skipWS(colData);
+ recordContent.tupleof[ti] = to!ToType(colData);
+ }
+ }
+ }
+ recordRange.popFront();
+ }
+ }
+ catch (ConvException e)
+ {
+ throw new CSVException(e.msg, _input.row, colIndex, e);
+ }
+ }
+ }
+}
+
+@safe pure unittest
+{
+ 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)
+ {
+ 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))
+{
+ import std.array : appender;
+private:
+ Input!(Range, ErrorLevel)* _input;
+ Separator _separator;
+ Separator _quote;
+ Contents curContentsoken;
+ typeof(appender!(dchar[])()) _front;
+ bool _empty;
+ size_t[] _popCount;
+public:
+ /*
+ * Params:
+ * input = Pointer to a character input range
+ * delimiter = Separator for each column
+ * quote = Character used for quotation
+ * indices = An array containing which columns will be returned.
+ * If empty, all columns are returned. List must be in order.
+ */
+ this(Input!(Range, ErrorLevel)* input, Separator delimiter,
+ Separator quote, size_t[] indices)
+ {
+ _input = input;
+ _separator = delimiter;
+ _quote = quote;
+ _front = appender!(dchar[])();
+ _popCount = indices.dup;
+
+ // If a header was given, each call to popFront will need
+ // 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)
+ {
+ static if (ErrorLevel == Malformed.ignore)
+ {
+ // If we are not throwing exceptions
+ // a header may not exist, indices are sorted
+ // and will be size_t.max if not found.
+ if (c == size_t.max)
+ break;
+ }
+ c -= normalizer;
+ normalizer += c + 1;
+ }
+
+ prime();
+ }
+
+ /**
+ * Part of an input range as defined by
+ * $(REF isInputRange, std,range,primitives).
+ */
+ @property Contents front() @safe pure
+ {
+ assert(!empty);
+ return curContentsoken;
+ }
+
+ /**
+ * Part of an input range as defined by
+ * $(REF isInputRange, std,range,primitives).
+ */
+ @property bool empty() @safe pure nothrow @nogc const
+ {
+ return _empty;
+ }
+
+ /*
+ * CsvRecord is complete when input
+ * is empty or starts with record break
+ */
+ private bool recordEnd()
+ {
+ if (_input.range.empty
+ || _input.range.front == '\n'
+ || _input.range.front == '\r')
+ {
+ return true;
+ }
+ return false;
+ }
+
+
+ /**
+ * 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,
+ * data continues after a closing quote, the quoted field was not
+ * closed before data was empty, a conversion failed, or when the
+ * row's length does not match the previous length.
+ */
+ void popFront()
+ {
+ static if (ErrorLevel == Malformed.throwException)
+ import std.format : format;
+ // Skip last of record when header is depleted.
+ if (_popCount.ptr && _popCount.empty)
+ while (!recordEnd())
+ {
+ prime(1);
+ }
+
+ if (recordEnd())
+ {
+ _empty = true;
+ static if (ErrorLevel == Malformed.throwException)
+ if (_input.rowLength != 0)
+ if (_input.col != _input.rowLength)
+ throw new CSVException(
+ format("Row %s's length %s does not match "~
+ "previous length of %s.", _input.row,
+ _input.col, _input.rowLength));
+ return;
+ }
+ else
+ {
+ static if (ErrorLevel == Malformed.throwException)
+ if (_input.rowLength != 0)
+ if (_input.col > _input.rowLength)
+ throw new CSVException(
+ format("Row %s's length %s does not match "~
+ "previous length of %s.", _input.row,
+ _input.col, _input.rowLength));
+ }
+
+ // Separator is left on the end of input from the last call.
+ // This cannot be moved to after the call to csvNextToken as
+ // there may be an empty record after it.
+ if (_input.range.front == _separator)
+ _input.range.popFront();
+
+ _front.shrinkTo(0);
+
+ prime();
+ }
+
+ /*
+ * Handles moving to the next skipNum token.
+ */
+ private void prime(size_t skipNum)
+ {
+ foreach (i; 0 .. skipNum)
+ {
+ _input.col++;
+ _front.shrinkTo(0);
+ if (_input.range.front == _separator)
+ _input.range.popFront();
+
+ try
+ csvNextToken!(Range, ErrorLevel, Separator)
+ (_input.range, _front, _separator, _quote,false);
+ catch (IncompleteCellException ice)
+ {
+ ice.row = _input.row;
+ ice.col = _input.col;
+ ice.partialData = _front.data.idup;
+ throw ice;
+ }
+ catch (ConvException e)
+ {
+ throw new CSVException(e.msg, _input.row, _input.col, e);
+ }
+ }
+ }
+
+ private void prime()
+ {
+ try
+ {
+ _input.col++;
+ csvNextToken!(Range, ErrorLevel, Separator)
+ (_input.range, _front, _separator, _quote,false);
+ }
+ catch (IncompleteCellException ice)
+ {
+ ice.row = _input.row;
+ ice.col = _input.col;
+ ice.partialData = _front.data.idup;
+ throw ice;
+ }
+
+ auto skipNum = _popCount.empty ? 0 : _popCount.front;
+ if (!_popCount.empty)
+ _popCount.popFront();
+
+ if (skipNum == size_t.max)
+ {
+ while (!recordEnd())
+ prime(1);
+ _empty = true;
+ return;
+ }
+
+ if (skipNum)
+ prime(skipNum);
+
+ auto data = _front.data;
+ static if (!isSomeString!Contents) skipWS(data);
+ try curContentsoken = to!Contents(data);
+ catch (ConvException e)
+ {
+ throw new CSVException(e.msg, _input.row, _input.col, e);
+ }
+ }
+}
+
+/**
+ * Lower level control over parsing CSV
+ *
+ * This function consumes the input. After each call the input will
+ * start with either a delimiter or record break (\n, \r\n, \r) which
+ * must be removed for subsequent calls.
+ *
+ * Params:
+ * input = Any CSV input
+ * ans = The first field in the input
+ * sep = The character to represent a comma in the specification
+ * quote = The character to represent a quote in the specification
+ * startQuoted = Whether the input should be considered to already be in
+ * quotes
+ *
+ * Throws:
+ * $(LREF IncompleteCellException) When a quote is found in an unquoted
+ * field, data continues after a closing quote, or the quoted field was
+ * not closed before data was empty.
+ */
+void csvNextToken(Range, Malformed ErrorLevel = Malformed.throwException,
+ Separator, Output)
+ (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))
+{
+ bool quoted = startQuoted;
+ bool escQuote;
+ if (input.empty)
+ return;
+
+ if (input.front == '\n')
+ return;
+ if (input.front == '\r')
+ return;
+
+ if (input.front == quote)
+ {
+ quoted = true;
+ input.popFront();
+ }
+
+ while (!input.empty)
+ {
+ assert(!(quoted && escQuote));
+ if (!quoted)
+ {
+ // When not quoted the token ends at sep
+ if (input.front == sep)
+ break;
+ if (input.front == '\r')
+ break;
+ if (input.front == '\n')
+ break;
+ }
+ if (!quoted && !escQuote)
+ {
+ if (input.front == quote)
+ {
+ // Not quoted, but quote found
+ static if (ErrorLevel == Malformed.throwException)
+ throw new IncompleteCellException(
+ "Quote located in unquoted token");
+ else static if (ErrorLevel == Malformed.ignore)
+ ans.put(quote);
+ }
+ else
+ {
+ // Not quoted, non-quote character
+ ans.put(input.front);
+ }
+ }
+ else
+ {
+ if (input.front == quote)
+ {
+ // Quoted, quote found
+ // By turning off quoted and turning on escQuote
+ // I can tell when to add a quote to the string
+ // escQuote is turned to false when it escapes a
+ // quote or is followed by a non-quote (see outside else).
+ // They are mutually exclusive, but provide different
+ // information.
+ if (escQuote)
+ {
+ escQuote = false;
+ quoted = true;
+ ans.put(quote);
+ } else
+ {
+ escQuote = true;
+ quoted = false;
+ }
+ }
+ else
+ {
+ // Quoted, non-quote character
+ if (escQuote)
+ {
+ static if (ErrorLevel == Malformed.throwException)
+ throw new IncompleteCellException(
+ "Content continues after end quote, " ~
+ "or needs to be escaped.");
+ else static if (ErrorLevel == Malformed.ignore)
+ break;
+ }
+ ans.put(input.front);
+ }
+ }
+ input.popFront();
+ }
+
+ static if (ErrorLevel == Malformed.throwException)
+ if (quoted && (input.empty || input.front == '\n' || input.front == '\r'))
+ throw new IncompleteCellException(
+ "Data continues on future lines or trailing quote");
+
+}
+
+///
+@safe unittest
+{
+ import std.array : appender;
+ import std.range.primitives : popFront;
+
+ string str = "65,63\n123,3673";
+
+ auto a = appender!(char[])();
+
+ csvNextToken(str,a,',','"');
+ assert(a.data == "65");
+ assert(str == ",63\n123,3673");
+
+ str.popFront();
+ a.shrinkTo(0);
+ csvNextToken(str,a,',','"');
+ assert(a.data == "63");
+ assert(str == "\n123,3673");
+
+ str.popFront();
+ a.shrinkTo(0);
+ csvNextToken(str,a,',','"');
+ assert(a.data == "123");
+ assert(str == ",3673");
+}
+
+// Test csvNextToken on simplest form and correct format.
+@safe pure unittest
+{
+ import std.array;
+
+ string str = "\U00010143Hello,65,63.63\nWorld,123,3673.562";
+
+ auto a = appender!(dchar[])();
+ csvNextToken!string(str,a,',','"');
+ assert(a.data == "\U00010143Hello");
+ assert(str == ",65,63.63\nWorld,123,3673.562");
+
+ str.popFront();
+ a.shrinkTo(0);
+ csvNextToken(str,a,',','"');
+ assert(a.data == "65");
+ assert(str == ",63.63\nWorld,123,3673.562");
+
+ str.popFront();
+ a.shrinkTo(0);
+ csvNextToken(str,a,',','"');
+ assert(a.data == "63.63");
+ assert(str == "\nWorld,123,3673.562");
+
+ str.popFront();
+ a.shrinkTo(0);
+ csvNextToken(str,a,',','"');
+ assert(a.data == "World");
+ assert(str == ",123,3673.562");
+
+ str.popFront();
+ a.shrinkTo(0);
+ csvNextToken(str,a,',','"');
+ assert(a.data == "123");
+ assert(str == ",3673.562");
+
+ str.popFront();
+ a.shrinkTo(0);
+ csvNextToken(str,a,',','"');
+ assert(a.data == "3673.562");
+ assert(str == "");
+}
+
+// Test quoted tokens
+@safe pure unittest
+{
+ import std.array;
+
+ string str = `one,two,"three ""quoted""","",` ~ "\"five\nnew line\"\nsix";
+
+ auto a = appender!(dchar[])();
+ csvNextToken!string(str,a,',','"');
+ assert(a.data == "one");
+ assert(str == `,two,"three ""quoted""","",` ~ "\"five\nnew line\"\nsix");
+
+ str.popFront();
+ a.shrinkTo(0);
+ csvNextToken(str,a,',','"');
+ assert(a.data == "two");
+ assert(str == `,"three ""quoted""","",` ~ "\"five\nnew line\"\nsix");
+
+ str.popFront();
+ a.shrinkTo(0);
+ csvNextToken(str,a,',','"');
+ assert(a.data == "three \"quoted\"");
+ assert(str == `,"",` ~ "\"five\nnew line\"\nsix");
+
+ str.popFront();
+ a.shrinkTo(0);
+ csvNextToken(str,a,',','"');
+ assert(a.data == "");
+ assert(str == ",\"five\nnew line\"\nsix");
+
+ str.popFront();
+ a.shrinkTo(0);
+ csvNextToken(str,a,',','"');
+ assert(a.data == "five\nnew line");
+ assert(str == "\nsix");
+
+ str.popFront();
+ a.shrinkTo(0);
+ csvNextToken(str,a,',','"');
+ assert(a.data == "six");
+ assert(str == "");
+}
+
+// Test empty data is pulled at end of record.
+@safe pure unittest
+{
+ import std.array;
+
+ string str = "one,";
+ auto a = appender!(dchar[])();
+ csvNextToken(str,a,',','"');
+ assert(a.data == "one");
+ assert(str == ",");
+
+ a.shrinkTo(0);
+ csvNextToken(str,a,',','"');
+ assert(a.data == "");
+}
+
+// Test exceptions
+@safe pure unittest
+{
+ import std.array;
+
+ string str = "\"one\nnew line";
+
+ typeof(appender!(dchar[])()) a;
+ try
+ {
+ a = appender!(dchar[])();
+ csvNextToken(str,a,',','"');
+ assert(0);
+ }
+ catch (IncompleteCellException ice)
+ {
+ assert(a.data == "one\nnew line");
+ assert(str == "");
+ }
+
+ str = "Hello world\"";
+
+ try
+ {
+ a = appender!(dchar[])();
+ csvNextToken(str,a,',','"');
+ assert(0);
+ }
+ catch (IncompleteCellException ice)
+ {
+ assert(a.data == "Hello world");
+ assert(str == "\"");
+ }
+
+ str = "one, two \"quoted\" end";
+
+ a = appender!(dchar[])();
+ csvNextToken!(string,Malformed.ignore)(str,a,',','"');
+ assert(a.data == "one");
+ str.popFront();
+ a.shrinkTo(0);
+ csvNextToken!(string,Malformed.ignore)(str,a,',','"');
+ assert(a.data == " two \"quoted\" end");
+}
+
+// Test modifying token delimiter
+@safe pure unittest
+{
+ import std.array;
+
+ string str = `one|two|/three "quoted"/|//`;
+
+ auto a = appender!(dchar[])();
+ csvNextToken(str,a, '|','/');
+ assert(a.data == "one"d);
+ assert(str == `|two|/three "quoted"/|//`);
+
+ str.popFront();
+ a.shrinkTo(0);
+ csvNextToken(str,a, '|','/');
+ assert(a.data == "two"d);
+ assert(str == `|/three "quoted"/|//`);
+
+ str.popFront();
+ a.shrinkTo(0);
+ csvNextToken(str,a, '|','/');
+ assert(a.data == `three "quoted"`);
+ assert(str == `|//`);
+
+ str.popFront();
+ a.shrinkTo(0);
+ csvNextToken(str,a, '|','/');
+ assert(a.data == ""d);
+}
+
+// Bugzilla 8908
+@safe pure unittest
+{
+ string csv = ` 1.0, 2.0, 3.0
+ 4.0, 5.0, 6.0`;
+
+ static struct Data { real a, b, c; }
+ size_t i = 0;
+ foreach (data; csvReader!Data(csv)) with (data)
+ {
+ int[] row = [cast(int) a, cast(int) b, cast(int) c];
+ if (i == 0)
+ assert(row == [1, 2, 3]);
+ else
+ assert(row == [4, 5, 6]);
+ ++i;
+ }
+
+ i = 0;
+ foreach (data; csvReader!real(csv))
+ {
+ 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];
+ if (i == 0)
+ assert(row == [1, 2, 3]);
+ else
+ assert(row == [4, 5, 6]);
+ ++i;
+ }
+}
diff --git a/libphobos/src/std/datetime/date.d b/libphobos/src/std/datetime/date.d
new file mode 100644
index 0000000..38a8766
--- /dev/null
+++ b/libphobos/src/std/datetime/date.d
@@ -0,0 +1,10580 @@
+// 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 @nogc
+ {
+ _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 (January is 1).
+ 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 &lt; rhs) $(TD &lt; 0))
+ $(TR $(TD this == rhs) $(TD 0))
+ $(TR $(TD this &gt; rhs) $(TD &gt; 0))
+ )
+ +/
+ int opCmp(in DateTime rhs) const @safe pure nothrow @nogc
+ {
+ 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() const @safe pure nothrow @nogc
+ {
+ 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 @nogc
+ {
+ _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() const @safe pure nothrow @nogc
+ {
+ 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 @nogc
+ {
+ _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() const @safe pure nothrow @nogc
+ {
+ 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() const @safe 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() const @safe pure nothrow @nogc
+ {
+ 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() const @safe pure nothrow @nogc
+ {
+ 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() const @safe pure nothrow @nogc
+ {
+ 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() const @safe pure nothrow @nogc
+ {
+ 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() const @safe pure nothrow @nogc
+ {
+ 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 @nogc
+ 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 @nogc
+ 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 @nogc
+ 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 @nogc
+ 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) const @safe pure nothrow @nogc
+ 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) const @safe pure nothrow @nogc
+ 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 @nogc
+ 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 @nogc
+ 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) const @safe pure nothrow @nogc
+ 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) const @safe pure nothrow @nogc
+ {
+ 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() const @safe pure nothrow @nogc
+ {
+ 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() const @safe pure nothrow @nogc
+ {
+ 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() const @safe pure nothrow @nogc
+ {
+ 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() const @safe pure nothrow @nogc
+ {
+ 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 @nogc
+ {
+ _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() const @safe 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() const @safe 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() const @safe pure nothrow @nogc
+ {
+ 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() const @safe pure nothrow @nogc
+ {
+ 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() const @safe pure nothrow @nogc
+ {
+ 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() const @safe pure nothrow @nogc
+ {
+ 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() const @safe pure nothrow
+ {
+ import std.format : format;
+ try
+ {
+ return format!("%sT%02d%02d%02d")(
+ _date.toISOString(),
+ _tod._hour,
+ _tod._minute,
+ _tod._second
+ );
+ }
+ 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() const @safe pure nothrow
+ {
+ import std.format : format;
+ try
+ {
+ return format!("%sT%02d:%02d:%02d")(
+ _date.toISOExtString(),
+ _tod._hour,
+ _tod._minute,
+ _tod._second
+ );
+ }
+ 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() const @safe pure nothrow
+ {
+ import std.format : format;
+ try
+ {
+ return format!("%s %02d:%02d:%02d")(
+ _date.toSimpleString(),
+ _tod._hour,
+ _tod._minute,
+ _tod._second
+ );
+ }
+ 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.
+
+ This function exists to make it easy to convert a $(LREF DateTime) to a
+ string for code that does not care what the exact format is - just that
+ it presents the information in a clear manner. It also makes it easy to
+ simply convert a $(LREF DateTime) to a string when using functions such
+ as `to!string`, `format`, or `writeln` which use toString to convert
+ user-defined types. So, it is unlikely that much code will call
+ toString directly.
+
+ The format of the string is purposefully unspecified, and code that
+ cares about the format of the string should use `toISOString`,
+ `toISOExtString`, `toSimpleString`, or some other custom formatting
+ function that explicitly generates the format that the code needs. The
+ reason is that the code is then clear about what format it's using,
+ making it less error-prone to maintain the code and interact with other
+ software that consumes the generated strings. It's for this same reason
+ that $(LREF DateTime) has no `fromString` function, whereas it does have
+ `fromISOString`, `fromISOExtString`, and `fromSimpleString`.
+
+ The format returned by toString may or may not change in the future.
+ +/
+ string toString() const @safe 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.exception : enforce;
+ import std.format : format;
+ import std.string : strip;
+
+ auto str = strip(isoString);
+
+ enforce(str.length >= 15, new DateTimeException(format("Invalid ISO String: %s", isoString)));
+ auto t = str.countUntil('T');
+
+ enforce(t != -1, new DateTimeException(format("Invalid ISO String: %s", isoString)));
+
+ immutable date = Date.fromISOString(str[0 .. t]);
+ immutable tod = TimeOfDay.fromISOString(str[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)));
+ }
+
+ // bug# 17801
+ @safe unittest
+ {
+ import std.conv : to;
+ import std.meta : AliasSeq;
+ foreach (C; AliasSeq!(char, wchar, dchar))
+ {
+ foreach (S; AliasSeq!(C[], const(C)[], immutable(C)[]))
+ assert(DateTime.fromISOString(to!S("20121221T141516")) == DateTime(2012, 12, 21, 14, 15, 16));
+ }
+ }
+
+
+ /++
+ 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.exception : enforce;
+ import std.format : format;
+ import std.string : strip;
+
+ auto str = strip(isoExtString);
+
+ enforce(str.length >= 15, new DateTimeException(format("Invalid ISO Extended String: %s", isoExtString)));
+ auto t = str.countUntil('T');
+
+ enforce(t != -1, new DateTimeException(format("Invalid ISO Extended String: %s", isoExtString)));
+
+ immutable date = Date.fromISOExtString(str[0 .. t]);
+ immutable tod = TimeOfDay.fromISOExtString(str[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)));
+ }
+
+ // bug# 17801
+ @safe unittest
+ {
+ import std.conv : to;
+ import std.meta : AliasSeq;
+ foreach (C; AliasSeq!(char, wchar, dchar))
+ {
+ foreach (S; AliasSeq!(C[], const(C)[], immutable(C)[]))
+ assert(DateTime.fromISOExtString(to!S("2012-12-21T14:15:16")) == DateTime(2012, 12, 21, 14, 15, 16));
+ }
+ }
+
+
+ /++
+ 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.exception : enforce;
+ import std.format : format;
+ import std.string : strip;
+
+ auto str = strip(simpleString);
+
+ enforce(str.length >= 15, new DateTimeException(format("Invalid string format: %s", simpleString)));
+ auto t = str.countUntil(' ');
+
+ enforce(t != -1, new DateTimeException(format("Invalid string format: %s", simpleString)));
+
+ immutable date = Date.fromSimpleString(str[0 .. t]);
+ immutable tod = TimeOfDay.fromISOExtString(str[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)));
+ }
+
+ // bug# 17801
+ @safe unittest
+ {
+ import std.conv : to;
+ import std.meta : AliasSeq;
+ foreach (C; AliasSeq!(char, wchar, dchar))
+ {
+ foreach (S; AliasSeq!(C[], const(C)[], immutable(C)[]))
+ assert(DateTime.fromSimpleString(to!S("2012-Dec-21 14:15:16")) == DateTime(2012, 12, 21, 14, 15, 16));
+ }
+ }
+
+
+ /++
+ Returns the $(LREF DateTime) farthest in the past which is representable
+ by $(LREF DateTime).
+ +/
+ @property static DateTime min() @safe pure nothrow @nogc
+ 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 @nogc
+ 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 @nogc
+ {
+ 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 (January is 1).
+ 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 @nogc
+ {
+ 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;
+
+ setDayOfYear(day);
+ }
+ }
+ else if (day <= 0 && -day < daysInLeapYear)
+ {
+ _year = 0;
+
+ setDayOfYear(daysInLeapYear + day);
+ }
+ 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;
+
+ setDayOfYear(newDoY);
+ }
+ }
+ }
+
+ @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 &lt; rhs) $(TD &lt; 0))
+ $(TR $(TD this == rhs) $(TD 0))
+ $(TR $(TD this &gt; rhs) $(TD &gt; 0))
+ )
+ +/
+ int opCmp(in Date rhs) const @safe pure nothrow @nogc
+ {
+ 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() const @safe pure nothrow @nogc
+ {
+ 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() const @safe 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() const @safe pure nothrow @nogc
+ {
+ 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() const @safe pure nothrow @nogc
+ {
+ 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.
+ +/
+ @safe pure nothrow @nogc
+ ref Date add(string units)(long value, AllowDayOverflow allowOverflow = AllowDayOverflow.yes)
+ 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.
+ @safe pure nothrow @nogc
+ ref Date add(string units)(long months, AllowDayOverflow allowOverflow = AllowDayOverflow.yes)
+ 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.
+ +/
+ @safe pure nothrow @nogc
+ ref Date roll(string units)(long value, AllowDayOverflow allowOverflow = AllowDayOverflow.yes)
+ 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.
+ @safe pure nothrow @nogc
+ ref Date roll(string units)(long months, AllowDayOverflow allowOverflow = AllowDayOverflow.yes)
+ 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 @nogc
+ 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) const @safe pure nothrow @nogc
+ 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) const @safe pure nothrow @nogc
+ 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 @nogc
+ 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 @nogc
+ 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) const @safe pure nothrow @nogc
+ 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) const @safe pure nothrow @nogc
+ {
+ 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() const @safe pure nothrow @nogc
+ {
+ 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() const @safe pure nothrow @nogc
+ {
+ 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() const @safe pure nothrow @nogc
+ {
+ 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
+ {
+ setDayOfYear!true(day);
+ }
+
+ private void setDayOfYear(bool useExceptions = false)(int day)
+ {
+ immutable int[] lastDay = isLeapYear ? lastDayLeap : lastDayNonLeap;
+
+ bool dayOutOfRange = day <= 0 || day > (isLeapYear ? daysInLeapYear : daysInYear);
+ enum errorMsg = "Invalid day of the year.";
+
+ static if (useExceptions)
+ {
+ if (dayOutOfRange) throw new DateTimeException(errorMsg);
+ }
+ else
+ {
+ assert(!dayOutOfRange, errorMsg);
+ }
+
+ 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() const @safe pure nothrow @nogc
+ {
+ 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 @nogc
+ {
+ 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() const @safe 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() const @safe 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() const @safe pure nothrow @nogc
+ {
+ 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() const @safe pure nothrow @nogc
+ {
+ 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() const @safe pure nothrow @nogc
+ {
+ 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() const @safe pure nothrow @nogc
+ {
+ 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() const @safe 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() const @safe 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() const @safe 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.
+
+ This function exists to make it easy to convert a $(LREF Date) to a
+ string for code that does not care what the exact format is - just that
+ it presents the information in a clear manner. It also makes it easy to
+ simply convert a $(LREF Date) to a string when using functions such as
+ `to!string`, `format`, or `writeln` which use toString to convert
+ user-defined types. So, it is unlikely that much code will call
+ toString directly.
+
+ The format of the string is purposefully unspecified, and code that
+ cares about the format of the string should use `toISOString`,
+ `toISOExtString`, `toSimpleString`, or some other custom formatting
+ function that explicitly generates the format that the code needs. The
+ reason is that the code is then clear about what format it's using,
+ making it less error-prone to maintain the code and interact with other
+ software that consumes the generated strings. It's for this same reason
+ $(LREF Date) has no `fromString` function, whereas it does have
+ `fromISOString`, `fromISOExtString`, and `fromSimpleString`.
+
+ The format returned by toString may or may not change in the future.
+ +/
+ string toString() const @safe 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 : startsWith;
+ import std.conv : to, text, ConvException;
+ import std.exception : enforce;
+ import std.string : strip;
+
+ auto str = isoString.strip;
+
+ enforce!DateTimeException(str.length >= 8, text("Invalid ISO String: ", isoString));
+
+ int day, month, year;
+ auto yearStr = str[0 .. $ - 4];
+
+ try
+ {
+ // using conversion to uint plus cast because it checks for +/-
+ // for us quickly while throwing ConvException
+ day = cast(int) to!uint(str[$ - 2 .. $]);
+ month = cast(int) to!uint(str[$ - 4 .. $ - 2]);
+
+ if (yearStr.length > 4)
+ {
+ enforce!DateTimeException(yearStr.startsWith('-', '+'),
+ text("Invalid ISO String: ", isoString));
+ year = to!int(yearStr);
+ }
+ else
+ {
+ year = cast(int) to!uint(yearStr);
+ }
+ }
+ catch (ConvException)
+ {
+ throw new DateTimeException(text("Invalid ISO String: ", isoString));
+ }
+
+ return Date(year, month, 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));
+ }
+
+ // bug# 17801
+ @safe unittest
+ {
+ import std.conv : to;
+ import std.meta : AliasSeq;
+ foreach (C; AliasSeq!(char, wchar, dchar))
+ {
+ foreach (S; AliasSeq!(C[], const(C)[], immutable(C)[]))
+ assert(Date.fromISOString(to!S("20121221")) == Date(2012, 12, 21));
+ }
+ }
+
+
+ /++
+ 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));
+ }
+
+ // bug# 17801
+ @safe unittest
+ {
+ import std.conv : to;
+ import std.meta : AliasSeq;
+ foreach (C; AliasSeq!(char, wchar, dchar))
+ {
+ foreach (S; AliasSeq!(C[], const(C)[], immutable(C)[]))
+ assert(Date.fromISOExtString(to!S("2012-12-21")) == Date(2012, 12, 21));
+ }
+ }
+
+
+ /++
+ 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));
+ }
+
+ // bug# 17801
+ @safe unittest
+ {
+ import std.conv : to;
+ import std.meta : AliasSeq;
+ foreach (C; AliasSeq!(char, wchar, dchar))
+ {
+ foreach (S; AliasSeq!(C[], const(C)[], immutable(C)[]))
+ assert(Date.fromSimpleString(to!S("2012-Dec-21")) == Date(2012, 12, 21));
+ }
+ }
+
+
+ /++
+ Returns the $(LREF Date) farthest in the past which is representable by
+ $(LREF Date).
+ +/
+ @property static Date min() @safe pure nothrow @nogc
+ {
+ 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 @nogc
+ {
+ 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 @nogc
+ {
+ 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 @nogc
+ {
+ 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 &lt; rhs) $(TD &lt; 0))
+ $(TR $(TD this == rhs) $(TD 0))
+ $(TR $(TD this &gt; rhs) $(TD &gt; 0))
+ )
+ +/
+ int opCmp(in TimeOfDay rhs) const @safe pure nothrow @nogc
+ {
+ 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() const @safe pure nothrow @nogc
+ {
+ 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() const @safe pure nothrow @nogc
+ {
+ 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() const @safe pure nothrow @nogc
+ {
+ 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 @nogc
+ 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 @nogc
+ 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) const @safe pure nothrow @nogc
+ 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) const @safe pure nothrow @nogc
+ 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 @nogc
+ 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 @nogc
+ 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) const @safe pure nothrow @nogc
+ 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() const @safe 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() const @safe 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.
+
+ This function exists to make it easy to convert a $(LREF TimeOfDay) to a
+ string for code that does not care what the exact format is - just that
+ it presents the information in a clear manner. It also makes it easy to
+ simply convert a $(LREF TimeOfDay) to a string when using functions such
+ as `to!string`, `format`, or `writeln` which use toString to convert
+ user-defined types. So, it is unlikely that much code will call
+ toString directly.
+
+ The format of the string is purposefully unspecified, and code that
+ cares about the format of the string should use `toISOString`,
+ `toISOExtString`, or some other custom formatting function that
+ explicitly generates the format that the code needs. The reason is that
+ the code is then clear about what format it's using, making it less
+ error-prone to maintain the code and interact with other software that
+ consumes the generated strings. It's for this same reason that
+ $(LREF TimeOfDay) has no `fromString` function, whereas it does have
+ `fromISOString` and `fromISOExtString`.
+
+ The format returned by toString may or may not change in the future.
+ +/
+ string toString() const @safe 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.conv : to, text, ConvException;
+ import std.exception : enforce;
+ import std.string : strip;
+
+ int hours, minutes, seconds;
+ auto str = strip(isoString);
+
+ enforce!DateTimeException(str.length == 6, text("Invalid ISO String: ", isoString));
+
+ try
+ {
+ // cast to int from uint is used because it checks for
+ // non digits without extra loops
+ hours = cast(int) to!uint(str[0 .. 2]);
+ minutes = cast(int) to!uint(str[2 .. 4]);
+ seconds = cast(int) to!uint(str[4 .. $]);
+ }
+ catch (ConvException)
+ {
+ throw new DateTimeException(text("Invalid ISO String: ", isoString));
+ }
+
+ return TimeOfDay(hours, minutes, 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));
+ }
+
+ // bug# 17801
+ @safe unittest
+ {
+ import std.conv : to;
+ import std.meta : AliasSeq;
+ foreach (C; AliasSeq!(char, wchar, dchar))
+ {
+ foreach (S; AliasSeq!(C[], const(C)[], immutable(C)[]))
+ assert(TimeOfDay.fromISOString(to!S("141516")) == TimeOfDay(14, 15, 16));
+ }
+ }
+
+
+ /++
+ 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));
+ }
+
+ // bug# 17801
+ @safe unittest
+ {
+ import std.conv : to;
+ import std.meta : AliasSeq;
+ foreach (C; AliasSeq!(char, wchar, dchar))
+ {
+ foreach (S; AliasSeq!(C[], const(C)[], immutable(C)[]))
+ assert(TimeOfDay.fromISOExtString(to!S("14:15:16")) == TimeOfDay(14, 15, 16));
+ }
+ }
+
+
+ /++
+ Returns midnight.
+ +/
+ @property static TimeOfDay min() @safe pure nothrow @nogc
+ {
+ 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 @nogc
+ {
+ 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 @nogc
+ {
+ 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 @nogc
+ {
+ 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 @nogc
+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 (January is 1).
+ day = The day to validate.
+ +/
+bool valid(string units)(int year, int month, int day) @safe pure nothrow @nogc
+if (units == "days")
+{
+ return day > 0 && day <= maxDay(year, month);
+}
+
+///
+@safe pure nothrow @nogc unittest
+{
+ assert(valid!"days"(2016, 2, 29));
+ assert(!valid!"days"(2016, 2, 30));
+ assert(valid!"days"(2017, 2, 20));
+ assert(!valid!"days"(2017, 2, 29));
+}
+
+
+/++
+ 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 @nogc
+{
+ if (currDoW == dow)
+ return 0;
+ if (currDoW < dow)
+ return dow - currDoW;
+ return DayOfWeek.sat - currDoW + dow + 1;
+}
+
+///
+@safe pure nothrow @nogc unittest
+{
+ assert(daysToDayOfWeek(DayOfWeek.mon, DayOfWeek.mon) == 0);
+ assert(daysToDayOfWeek(DayOfWeek.mon, DayOfWeek.sun) == 6);
+ assert(daysToDayOfWeek(DayOfWeek.mon, DayOfWeek.wed) == 2);
+}
+
+@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 pure unittest
+{
+ assert(monthsToMonth(Month.jan, Month.jan) == 0);
+ assert(monthsToMonth(Month.jan, Month.dec) == 11);
+ assert(monthsToMonth(Month.jul, Month.oct) == 3);
+}
+
+@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 @nogc
+{
+ if (year % 400 == 0)
+ return true;
+ if (year % 100 == 0)
+ return false;
+ return year % 4 == 0;
+}
+
+///
+@safe unittest
+{
+ foreach (year; [1, 2, 100, 2001, 2002, 2003, 2005, 2006, 2007, 2009, 2010])
+ {
+ assert(!yearIsLeapYear(year));
+ assert(!yearIsLeapYear(-year));
+ }
+
+ foreach (year; [0, 4, 8, 400, 800, 1600, 1996, 2000, 2004, 2008, 2012])
+ {
+ assert(yearIsLeapYear(year));
+ assert(yearIsLeapYear(-year));
+ }
+}
+
+@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 $(D Unqual!T).
+
+ 2. $(D T) must define a static property named $(D max) which is the largest
+ value of $(D T) as $(D 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 @nogc
+{
+ import std.algorithm.searching : canFind;
+ foreach (str; units)
+ {
+ if (!canFind(timeStrings[], str))
+ return false;
+ }
+ return true;
+}
+
+///
+@safe @nogc nothrow unittest
+{
+ assert(validTimeUnits("msecs", "seconds", "minutes"));
+ assert(validTimeUnits("days", "weeks", "months"));
+ assert(!validTimeUnits("ms", "seconds", "minutes"));
+}
+
+
+/++
+ Compares two time unit strings. $(D "years") are the largest units and
+ $(D "hnsecs") are the smallest.
+
+ Returns:
+ $(BOOKTABLE,
+ $(TR $(TD this &lt; rhs) $(TD &lt; 0))
+ $(TR $(TD this == rhs) $(TD 0))
+ $(TR $(TD this &gt; rhs) $(TD &gt; 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 pure unittest
+{
+ assert(cmpTimeUnits("hours", "hours") == 0);
+ assert(cmpTimeUnits("hours", "weeks") < 0);
+ assert(cmpTimeUnits("months", "seconds") > 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 &lt; rhs) $(TD &lt; 0))
+ $(TR $(TD this == rhs) $(TD 0))
+ $(TR $(TD this &gt; rhs) $(TD &gt; 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 @nogc
+{
+ 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 @nogc
+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 @nogc
+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 @nogc
+{
+ // 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/libphobos/src/std/datetime/interval.d b/libphobos/src/std/datetime/interval.d
new file mode 100644
index 0000000..d1eec45
--- /dev/null
+++ b/libphobos/src/std/datetime/interval.d
@@ -0,0 +1,9131 @@
+// 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
+ (January is 1).
+ +/
+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/libphobos/src/std/datetime/package.d b/libphobos/src/std/datetime/package.d
new file mode 100644
index 0000000..976d06d
--- /dev/null
+++ b/libphobos/src/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 <a href="core_time.html">$(D core.time)</a>,
+ 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&#46;_datetime)<br>
+ $(HTTP en.wikipedia.org/wiki/ISO_8601, ISO 8601)<br>
+ $(HTTP en.wikipedia.org/wiki/Tz_database,
+ Wikipedia entry on TZ Database)<br>
+ $(HTTP en.wikipedia.org/wiki/List_of_tz_database_time_zones,
+ List of Time Zones)<br>
+
+ 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.functional : unaryFun;
+import std.traits;
+import std.typecons : Flag, Yes, No;
+
+
+// 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/libphobos/src/std/datetime/stopwatch.d b/libphobos/src/std/datetime/stopwatch.d
new file mode 100644
index 0000000..48e025b
--- /dev/null
+++ b/libphobos/src/std/datetime/stopwatch.d
@@ -0,0 +1,428 @@
+// 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);
+ version (GNU)
+ assert(r[0] >= Duration.zero);
+ else
+ 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/libphobos/src/std/datetime/systime.d b/libphobos/src/std/datetime/systime.d
new file mode 100644
index 0000000..a15c125
--- /dev/null
+++ b/libphobos/src/std/datetime/systime.d
@@ -0,0 +1,11151 @@
+// 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.exception : enforce;
+import std.format : format;
+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 &lt; rhs) $(TD &lt; 0))
+ $(TR $(TD this == rhs) $(TD 0))
+ $(TR $(TD this &gt; rhs) $(TD &gt; 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
+ {
+ import std.datetime.date : Date;
+
+ auto st = SysTime(Date(1999, 7, 6));
+ const cst = SysTime(Date(2010, 5, 1));
+ immutable ist = SysTime(Date(2015, 10, 10));
+
+ assert(st.isoWeek == 27);
+ assert(cst.isoWeek == 17);
+ assert(ist.isoWeek == 41);
+ }
+
+
+ /++
+ $(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 8601 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. The current behavior will be kept until July 2019 at which
+ point, fromISOString will be fixed to be standards compliant.)
+ +/
+ 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.
+
+ This function exists to make it easy to convert a $(LREF SysTime) to a
+ string for code that does not care what the exact format is - just that
+ it presents the information in a clear manner. It also makes it easy to
+ simply convert a $(LREF SysTime) to a string when using functions such
+ as `to!string`, `format`, or `writeln` which use toString to convert
+ user-defined types. So, it is unlikely that much code will call
+ toString directly.
+
+ The format of the string is purposefully unspecified, and code that
+ cares about the format of the string should use `toISOString`,
+ `toISOExtString`, `toSimpleString`, or some other custom formatting
+ function that explicitly generates the format that the code needs. The
+ reason is that the code is then clear about what format it's using,
+ making it less error-prone to maintain the code and interact with other
+ software that consumes the generated strings. It's for this same reason
+ that $(LREF SysTime) has no `fromString` function, whereas it does have
+ `fromISOString`, `fromISOExtString`, and `fromSimpleString`.
+
+ The format returned by toString may or may not change in the future.
+ +/
+ 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 8601 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. The current behavior will be kept until July 2019 at which
+ point, fromISOString will be fixed to be standards compliant.)
+
+ 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, hnsecs;
+ 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_2019-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 2019. At that
+ // time, we will update fromISOString so that it is conformant with ISO
+ // 8601, and it will no longer accept ISO extended time zones (it does
+ // currently because of issue #15654 - toISOString used to incorrectly
+ // use the ISO extended time zone format). These tests will then start
+ // failing will need to be updated accordingly. Also, the notes about
+ // this issue in toISOString and fromISOString's documentation will need
+ // to be removed.
+ 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));
+ }
+
+ // bug# 17801
+ @safe unittest
+ {
+ import std.conv : to;
+ import std.meta : AliasSeq;
+ foreach (C; AliasSeq!(char, wchar, dchar))
+ {
+ foreach (S; AliasSeq!(C[], const(C)[], immutable(C)[]))
+ {
+ assert(SysTime.fromISOString(to!S("20121221T141516Z")) ==
+ SysTime(DateTime(2012, 12, 21, 14, 15, 16), UTC()));
+ }
+ }
+ }
+
+
+ /++
+ 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, hnsecs;
+ 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));
+ }
+
+ // bug# 17801
+ @safe unittest
+ {
+ import std.conv : to;
+ import std.meta : AliasSeq;
+ foreach (C; AliasSeq!(char, wchar, dchar))
+ {
+ foreach (S; AliasSeq!(C[], const(C)[], immutable(C)[]))
+ {
+ assert(SysTime.fromISOExtString(to!S("2012-12-21T14:15:16Z")) ==
+ SysTime(DateTime(2012, 12, 21, 14, 15, 16), UTC()));
+ }
+ }
+ }
+
+
+ /++
+ 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, hnsecs;
+ 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));
+ }
+
+ // bug# 17801
+ @safe unittest
+ {
+ import std.conv : to;
+ import std.meta : AliasSeq;
+ foreach (C; AliasSeq!(char, wchar, dchar))
+ {
+ foreach (S; AliasSeq!(C[], const(C)[], immutable(C)[]))
+ {
+ assert(SysTime.fromSimpleString(to!S("2012-Dec-21 14:15:16Z")) ==
+ SysTime(DateTime(2012, 12, 21, 14, 15, 16), UTC()));
+ }
+ }
+ }
+
+
+ /++
+ 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/libphobos/src/std/datetime/timezone.d b/libphobos/src/std/datetime/timezone.d
new file mode 100644
index 0000000..fb06262
--- /dev/null
+++ b/libphobos/src/std/datetime/timezone.d
@@ -0,0 +1,4235 @@
+// 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)<br>
+ $(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);
+ }
+
+ // Explicitly undocumented. It will be removed in June 2018. @@@DEPRECATED_2018-07@@@
+ 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);
+ }
+ }
+ }
+
+
+ // Explicitly undocumented. It will be removed in June 2018. @@@DEPRECATED_2018-07@@@
+ 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)<br>
+ $(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)<br>
+ $(HTTP en.wikipedia.org/wiki/Tz_database, Wikipedia entry on TZ Database)<br>
+ $(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)<br>
+ $(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.conv : to;
+ import std.format : format;
+ import std.path : asNormalizedPath, chainPath;
+ import std.range : retro;
+
+ 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)<br>
+ $(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
+ {
+ 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 = stdName.to!wstring;
+ auto wdstName = dstName.to!wstring;
+ 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:
+ windowsZonesXMLText = 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 = `<mapZone other="`;
+ immutable f2 = `type="`;
+
+ foreach (line; windowsZonesXMLText.lineSplitter())
+ {
+ // Sample line:
+ // <mapZone other="Canada Central Standard Time" territory="CA" type="America/Regina America/Swift_Current"/>
+
+ 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 =
+`<?xml version="1.0" encoding="UTF-8" ?>
+<!DOCTYPE supplementalData SYSTEM "../../common/dtd/ldmlSupplemental.dtd">
+<!--
+Copyright © 1991-2013 Unicode, Inc.
+CLDR data files are interpreted according to the LDML specification (http://unicode.org/reports/tr35/)
+For terms of use, see http://www.unicode.org/copyright.html
+-->
+
+<supplementalData>
+ <version number="$Revision$"/>
+
+ <windowsZones>
+ <mapTimezones otherVersion="7df0005" typeVersion="2015g">
+
+ <!-- (UTC-12:00) International Date Line West -->
+ <mapZone other="Dateline Standard Time" territory="001" type="Etc/GMT+12"/>
+ <mapZone other="Dateline Standard Time" territory="ZZ" type="Etc/GMT+12"/>
+
+ <!-- (UTC-11:00) Coordinated Universal Time-11 -->
+ <mapZone other="UTC-11" territory="001" type="Etc/GMT+11"/>
+ <mapZone other="UTC-11" territory="AS" type="Pacific/Pago_Pago"/>
+ <mapZone other="UTC-11" territory="NU" type="Pacific/Niue"/>
+ <mapZone other="UTC-11" territory="UM" type="Pacific/Midway"/>
+ <mapZone other="UTC-11" territory="ZZ" type="Etc/GMT+11"/>
+
+ <!-- (UTC-10:00) Hawaii -->
+ <mapZone other="Hawaiian Standard Time" territory="001" type="Pacific/Honolulu"/>
+ <mapZone other="Hawaiian Standard Time" territory="CK" type="Pacific/Rarotonga"/>
+ <mapZone other="Hawaiian Standard Time" territory="PF" type="Pacific/Tahiti"/>
+ <mapZone other="Hawaiian Standard Time" territory="UM" type="Pacific/Johnston"/>
+ <mapZone other="Hawaiian Standard Time" territory="US" type="Pacific/Honolulu"/>
+ <mapZone other="Hawaiian Standard Time" territory="ZZ" type="Etc/GMT+10"/>
+
+ <!-- (UTC-09:00) Alaska -->
+ <mapZone other="Alaskan Standard Time" territory="001" type="America/Anchorage"/>
+ <mapZone other="Alaskan Standard Time" territory="US" type="America/Anchorage America/Juneau America/Nome America/Sitka America/Yakutat"/>
+ </mapTimezones>
+ </windowsZones>
+</supplementalData>`;
+
+ 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);
+ }
+}
+
+
+// Explicitly undocumented. It will be removed in June 2018. @@@DEPRECATED_2018-07@@@
+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);
+ }
+}
+
+
+// Explicitly undocumented. It will be removed in June 2018. @@@DEPRECATED_2018-07@@@
+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 <nix2WinFile> <win2NixFile>");
+ return -1;
+ }
+
+ string[][string] win2Nix;
+ string[][string] nix2Win;
+ immutable f1 = `<mapZone other="`;
+ immutable f2 = `type="`;
+
+ auto file = File(args[1]);
+ foreach (line; file.byLine())
+ {
+ line = line.find(f1);
+ if (line.empty)
+ continue;
+ line = line[f1.length .. $];
+ auto next = line.find('"');
+ auto win = to!string(line[0 .. $ - next.length]);
+ line = next.find(f2);
+ line = line[f2.length .. $];
+ next = line.find('"');
+ auto nixes = to!string(line[0 .. $ - next.length]).split();
+
+ if (auto l = win in win2Nix)
+ *l ~= nixes;
+ else
+ win2Nix[win] = nixes;
+ foreach (nix; nixes)
+ {
+ if (auto w = nix in nix2Win)
+ *w ~= win;
+ else
+ nix2Win[nix] = [win];
+ }
+ }
+
+ foreach (nix; nix2Win.byKey())
+ {
+ auto wins = nix2Win[nix];
+ nix2Win[nix] = wins.sort().uniq().array();
+ }
+
+ foreach (win; win2Nix.byKey())
+ {
+ auto nixes = win2Nix[win];
+ win2Nix[win] = nixes.sort().uniq().array();
+ }
+
+ // AFAIK, there should be no cases of a TZ Database time zone converting to
+ // multiple windows time zones.
+ foreach (nix, wins; nix2Win)
+ enforce(wins.length == 1, format("%s -> %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/libphobos/src/std/demangle.d b/libphobos/src/std/demangle.d
new file mode 100644
index 0000000..e49bb9f
--- /dev/null
+++ b/libphobos/src/std/demangle.d
@@ -0,0 +1,89 @@
+// Written in the D programming language.
+
+/**
+ * Demangle D mangled names.
+ *
+ * Copyright: Copyright Digital Mars 2000 - 2009.
+ * 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.
+ * 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.demangle;
+
+/+
+private class MangleException : Exception
+{
+ this()
+ {
+ super("MangleException");
+ }
+}
++/
+
+/*****************************
+ * Demangle D mangled names.
+ *
+ * If it is not a D mangled name, it returns its argument name.
+ * Example:
+ * This program reads standard in and writes it to standard out,
+ * pretty-printing any found D mangled names.
+-------------------
+import core.stdc.stdio : stdin;
+import std.stdio;
+import std.ascii;
+import std.demangle;
+
+void test(int x, float y) { }
+
+int main()
+{
+ string buffer;
+ bool inword;
+ int c;
+
+ writefln("Try typing in: %s", test.mangleof);
+ while ((c = fgetc(stdin)) != EOF)
+ {
+ if (inword)
+ {
+ if (c == '_' || isAlphaNum(c))
+ buffer ~= cast(char) c;
+ else
+ {
+ inword = false;
+ write(demangle(buffer), cast(char) c);
+ }
+ }
+ else
+ { if (c == '_' || isAlpha(c))
+ {
+ inword = true;
+ buffer.length = 0;
+ buffer ~= cast(char) c;
+ }
+ else
+ write(cast(char) c);
+ }
+ }
+ if (inword)
+ write(demangle(buffer));
+ return 0;
+}
+-------------------
+ */
+
+string demangle(string name)
+{
+ import core.demangle : demangle;
+ import std.exception : assumeUnique;
+ auto ret = demangle(name);
+ return assumeUnique(ret);
+}
diff --git a/libphobos/src/std/digest/crc.d b/libphobos/src/std/digest/crc.d
new file mode 100644
index 0000000..c606f4c
--- /dev/null
+++ b/libphobos/src/std/digest/crc.d
@@ -0,0 +1,705 @@
+/**
+Cyclic Redundancy Check (32-bit) implementation.
+
+$(SCRIPT inhibitQuickIndex = 1;)
+
+$(DIVC quickindex,
+$(BOOKTABLE ,
+$(TR $(TH Category) $(TH Functions)
+)
+$(TR $(TDNW Template API) $(TD $(MYREF CRC) $(MYREF CRC32) $(MYREF CRC64ECMA) $(MYREF CRC64ISO)
+)
+)
+$(TR $(TDNW OOP API) $(TD $(MYREF CRC32Digest) $(MYREF CRC64ECMADigest) $(MYREF CRC64ISODigest))
+)
+$(TR $(TDNW Helpers) $(TD $(MYREF crcHexString) $(MYREF crc32Of) $(MYREF crc64ECMAOf) $(MYREF crc64ISOOf))
+)
+)
+)
+
+ *
+ * This module conforms to the APIs defined in $(D std.digest). To understand the
+ * differences between the template and the OOP API, see $(MREF std, digest).
+ *
+ * This module publicly imports $(MREF std, digest) and can be used as a stand-alone
+ * module.
+ *
+ * Note:
+ * CRCs are usually printed with the MSB first. When using
+ * $(REF toHexString, std,digest) the result will be in an unexpected
+ * order. Use $(REF toHexString, std,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: $(HTTP www.boost.org/LICENSE_1_0.txt, Boost License 1.0).
+ *
+ * Authors: Pavel "EvilOne" Minayev, Alex Rønne Petersen, Johannes Pfau
+ *
+ * References:
+ * $(LINK2 http://en.wikipedia.org/wiki/Cyclic_redundancy_check, Wikipedia on CRC)
+ *
+ * Source: $(PHOBOSSRC std/digest/_crc.d)
+ *
+ * Standards:
+ * Implements the 'common' IEEE CRC32 variant
+ * (LSB-first order, Initial value uint.max, complement result)
+ *
+ * CTFE:
+ * Digests do not work in CTFE
+ */
+/*
+ * Copyright (c) 2001 - 2002
+ * Pavel "EvilOne" Minayev
+ * Copyright (c) 2012
+ * Alex Rønne Petersen
+ * 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.digest.crc;
+
+public import std.digest;
+
+version (unittest)
+ import std.exception;
+
+
+///
+@safe unittest
+{
+ //Template API
+ import std.digest.crc;
+
+ ubyte[4] hash = crc32Of("The quick brown fox jumps over the lazy dog");
+ assert(crcHexString(hash) == "414FA339");
+
+ //Feeding data
+ ubyte[1024] data;
+ CRC32 crc;
+ crc.put(data[]);
+ crc.start(); //Start again
+ crc.put(data[]);
+ hash = crc.finish();
+}
+
+///
+@safe unittest
+{
+ //OOP API
+ import std.digest.crc;
+
+ auto crc = new CRC32Digest();
+ ubyte[] hash = crc.digest("The quick brown fox jumps over the lazy dog");
+ assert(crcHexString(hash) == "414FA339"); //352441c2
+
+ //Feeding data
+ ubyte[1024] data;
+ crc.put(data[]);
+ crc.reset(); //Start again
+ crc.put(data[]);
+ hash = crc.finish();
+}
+
+private T[256][8] genTables(T)(T polynomial)
+{
+ T[256][8] res = void;
+
+ foreach (i; 0 .. 0x100)
+ {
+ T 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;
+}
+
+@system unittest
+{
+ auto tables = genTables(0xEDB88320);
+ assert(tables[0][0] == 0x00000000 && tables[0][$ - 1] == 0x2d02ef8d && tables[7][$ - 1] == 0x264b06e6);
+}
+
+/**
+ * Template API CRC32 implementation.
+ * See $(D std.digest) for differences between template and OOP API.
+ */
+alias CRC32 = CRC!(32, 0xEDB88320);
+
+/**
+ * Template API CRC64-ECMA implementation.
+ * See $(D std.digest.digest) for differences between template and OOP API.
+ */
+alias CRC64ECMA = CRC!(64, 0xC96C5795D7870F42);
+
+/**
+ * Template API CRC64-ISO implementation.
+ * See $(D std.digest.digest) for differences between template and OOP API.
+ */
+alias CRC64ISO = CRC!(64, 0xD800000000000000);
+
+/**
+ * Generic Template API used for CRC32 and CRC64 implementations.
+ *
+ * The N parameter indicate the size of the hash in bits.
+ * The parameter P specify the polynomial to be used for reduction.
+ *
+ * You may want to use the CRC32, CRC65ECMA and CRC64ISO aliases
+ * for convenience.
+ *
+ * See $(D std.digest.digest) for differences between template and OOP API.
+ */
+struct CRC(uint N, ulong P) if (N == 32 || N == 64)
+{
+ private:
+ static if (N == 32)
+ {
+ alias T = uint;
+ }
+ else
+ {
+ alias T = ulong;
+ }
+
+ static immutable T[256][8] tables = genTables!T(P);
+
+ /**
+ * Type of the finished CRC hash.
+ * ubyte[4] if N is 32, ubyte[8] if N is 64.
+ */
+ alias R = ubyte[T.sizeof];
+
+ // magic initialization constants
+ T _state = T.max;
+
+ public:
+ /**
+ * Use this to feed the digest with data.
+ * 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
+ {
+ T 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];
+ 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]);
+ uint two = (data.ptr[7] << 24 | data.ptr[6] << 16 | data.ptr[5] << 8 | data.ptr[4]);
+ }
+
+ static if (N == 32)
+ {
+ one ^= crc;
+ }
+ else
+ {
+ one ^= (crc & 0xffffffff);
+ two ^= (crc >> 32);
+ }
+
+ crc =
+ tables[0][two >> 24] ^
+ tables[1][(two >> 16) & 0xFF] ^
+ tables[2][(two >> 8) & 0xFF] ^
+ tables[3][two & 0xFF] ^
+ tables[4][one >> 24] ^
+ tables[5][(one >> 16) & 0xFF] ^
+ tables[6][(one >> 8) & 0xFF] ^
+ tables[7][one & 0xFF];
+
+ data = data[8 .. $];
+ }
+ // remaining 1 to 7 bytes
+ foreach (d; data)
+ crc = (crc >> 8) ^ tables[0][(crc & 0xFF) ^ d];
+ _state = crc;
+ }
+
+ /**
+ * Used to initialize the CRC32 digest.
+ *
+ * Note:
+ * For this CRC32 Digest implementation calling start after default construction
+ * is not necessary. Calling start is only necessary to reset the Digest.
+ *
+ * Generic code which deals with different Digest types should always call start though.
+ */
+ void start() @safe pure nothrow @nogc
+ {
+ this = CRC.init;
+ }
+
+ /**
+ * Returns the finished CRC hash. This also calls $(LREF start) to
+ * reset the internal state.
+ */
+ R finish() @safe pure nothrow @nogc
+ {
+ auto tmp = peek();
+ start();
+ return tmp;
+ }
+
+ /**
+ * Works like $(D finish) but does not reset the internal state, so it's possible
+ * to continue putting data into this CRC after a call to peek.
+ */
+ R peek() const @safe pure nothrow @nogc
+ {
+ import std.bitmanip : nativeToLittleEndian;
+ //Complement, LSB first / Little Endian, see http://rosettacode.org/wiki/CRC-32
+ return nativeToLittleEndian(~_state);
+ }
+}
+
+///
+@safe unittest
+{
+ //Simple example, hashing a string using crc32Of helper function
+ ubyte[4] hash32 = crc32Of("abc");
+ //Let's get a hash string
+ assert(crcHexString(hash32) == "352441C2");
+ // Repeat for CRC64
+ ubyte[8] hash64ecma = crc64ECMAOf("abc");
+ assert(crcHexString(hash64ecma) == "2CD8094A1A277627");
+ ubyte[8] hash64iso = crc64ISOOf("abc");
+ assert(crcHexString(hash64iso) == "3776C42000000000");
+}
+
+///
+@safe unittest
+{
+ ubyte[1024] data;
+ //Using the basic API
+ CRC32 hash32;
+ CRC64ECMA hash64ecma;
+ CRC64ISO hash64iso;
+ //Initialize data here...
+ hash32.put(data);
+ ubyte[4] result32 = hash32.finish();
+ hash64ecma.put(data);
+ ubyte[8] result64ecma = hash64ecma.finish();
+ hash64iso.put(data);
+ ubyte[8] result64iso = hash64iso.finish();
+}
+
+///
+@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)
+ {
+ hash.put(cast(ubyte) 0);
+ }
+ CRC32 crc32;
+ crc32.start();
+ doSomething(crc32);
+ assert(crcHexString(crc32.finish()) == "D202EF8D");
+ // repeat for CRC64
+ CRC64ECMA crc64ecma;
+ crc64ecma.start();
+ doSomething(crc64ecma);
+ assert(crcHexString(crc64ecma.finish()) == "1FADA17364673F59");
+ CRC64ISO crc64iso;
+ crc64iso.start();
+ doSomething(crc64iso);
+ assert(crcHexString(crc64iso.finish()) == "6F90000000000000");
+}
+
+@safe unittest
+{
+ assert(isDigest!CRC32);
+ assert(isDigest!CRC64ECMA);
+ assert(isDigest!CRC64ISO);
+}
+
+@system unittest
+{
+ ubyte[4] digest;
+
+ CRC32 crc;
+ crc.put(cast(ubyte[])"abcdefghijklmnopqrstuvwxyz");
+ assert(crc.peek() == cast(ubyte[]) x"bd50274c");
+ crc.start();
+ crc.put(cast(ubyte[])"");
+ assert(crc.finish() == cast(ubyte[]) x"00000000");
+
+ digest = crc32Of("");
+ 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");
+
+ digest = crc32Of("abc");
+ assert(digest == cast(ubyte[]) x"c2412435");
+
+ digest = crc32Of("abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq");
+ assert(digest == cast(ubyte[]) x"5f3f1a17");
+
+ digest = crc32Of("message digest");
+ assert(digest == cast(ubyte[]) x"7f9d1520");
+
+ digest = crc32Of("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789");
+ assert(digest == cast(ubyte[]) x"d2e6c21f");
+
+ digest = crc32Of("1234567890123456789012345678901234567890"~
+ "1234567890123456789012345678901234567890");
+ assert(digest == cast(ubyte[]) x"724aa97c");
+
+ assert(crcHexString(cast(ubyte[4]) x"c3fcd3d7") == "D7D3FCC3");
+}
+
+@system unittest
+{
+ ubyte[8] digest;
+
+ CRC64ECMA crc;
+ crc.put(cast(ubyte[])"abcdefghijklmnopqrstuvwxyz");
+ assert(crc.peek() == cast(ubyte[]) x"2f121b7575789626");
+ crc.start();
+ crc.put(cast(ubyte[])"");
+ assert(crc.finish() == cast(ubyte[]) x"0000000000000000");
+ digest = crc64ECMAOf("");
+ assert(digest == cast(ubyte[]) x"0000000000000000");
+
+ //Test vector from http://rosettacode.org/wiki/CRC-32
+ assert(crcHexString(crc64ECMAOf("The quick brown fox jumps over the lazy dog")) == "5B5EB8C2E54AA1C4");
+
+ digest = crc64ECMAOf("a");
+ assert(digest == cast(ubyte[]) x"052b652e77840233");
+
+ digest = crc64ECMAOf("abc");
+ assert(digest == cast(ubyte[]) x"2776271a4a09d82c");
+
+ digest = crc64ECMAOf("abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq");
+ assert(digest == cast(ubyte[]) x"4b7cdce3746c449f");
+
+ digest = crc64ECMAOf("message digest");
+ assert(digest == cast(ubyte[]) x"6f9b8a3156c9bc5d");
+
+ digest = crc64ECMAOf("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789");
+ assert(digest == cast(ubyte[]) x"2656b716e1bf0503");
+
+ digest = crc64ECMAOf("1234567890123456789012345678901234567890"~
+ "1234567890123456789012345678901234567890");
+ assert(digest == cast(ubyte[]) x"bd3eb7765d0a22ae");
+
+ assert(crcHexString(cast(ubyte[8]) x"c3fcd3d7efbeadde") == "DEADBEEFD7D3FCC3");
+}
+
+@system unittest
+{
+ ubyte[8] digest;
+
+ CRC64ISO crc;
+ crc.put(cast(ubyte[])"abcdefghijklmnopqrstuvwxyz");
+ assert(crc.peek() == cast(ubyte[]) x"f0494ab780989b42");
+ crc.start();
+ crc.put(cast(ubyte[])"");
+ assert(crc.finish() == cast(ubyte[]) x"0000000000000000");
+ digest = crc64ISOOf("");
+ assert(digest == cast(ubyte[]) x"0000000000000000");
+
+ //Test vector from http://rosettacode.org/wiki/CRC-32
+ assert(crcHexString(crc64ISOOf("The quick brown fox jumps over the lazy dog")) == "4EF14E19F4C6E28E");
+
+ digest = crc64ISOOf("a");
+ assert(digest == cast(ubyte[]) x"0000000000002034");
+
+ digest = crc64ISOOf("abc");
+ assert(digest == cast(ubyte[]) x"0000000020c47637");
+
+ digest = crc64ISOOf("abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq");
+ assert(digest == cast(ubyte[]) x"5173f717971365e5");
+
+ digest = crc64ISOOf("message digest");
+ assert(digest == cast(ubyte[]) x"a2c355bbc0b93f86");
+
+ digest = crc64ISOOf("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789");
+ assert(digest == cast(ubyte[]) x"598B258292E40084");
+
+ digest = crc64ISOOf("1234567890123456789012345678901234567890"~
+ "1234567890123456789012345678901234567890");
+ assert(digest == cast(ubyte[]) x"760cd2d3588bf809");
+
+ assert(crcHexString(cast(ubyte[8]) x"c3fcd3d7efbeadde") == "DEADBEEFD7D3FCC3");
+}
+
+/**
+ * This is a convenience alias for $(REF digest, std,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...
+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 $(REF digest, std,digest) using the
+ * CRC64-ECMA 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:
+ * CRC64-ECMA of data
+ */
+//simple alias doesn't work here, hope this gets inlined...
+ubyte[8] crc64ECMAOf(T...)(T data)
+{
+ return digest!(CRC64ECMA, T)(data);
+}
+
+///
+@system unittest
+{
+ ubyte[] data = [4,5,7,25];
+ assert(data.crc64ECMAOf == [58, 142, 220, 214, 118, 98, 105, 69]);
+
+ import std.utf : byChar;
+ assert("hello"d.byChar.crc64ECMAOf == [177, 55, 185, 219, 229, 218, 30, 155]);
+
+ ubyte[8] hash = "abc".crc64ECMAOf();
+ assert("abc".crc64ECMAOf == [39, 118, 39, 26, 74, 9, 216, 44]);
+ assert(hash == digest!CRC64ECMA("ab", "c"));
+
+ import std.range : iota;
+ enum ubyte S = 5, F = 66;
+ assert(iota(S, F).crc64ECMAOf == [6, 184, 91, 238, 46, 213, 127, 188]);
+}
+
+/**
+ * This is a convenience alias for $(REF digest, std,digest,digest) using the
+ * CRC64-ISO 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:
+ * CRC64-ISO of data
+ */
+//simple alias doesn't work here, hope this gets inlined...
+ubyte[8] crc64ISOOf(T...)(T data)
+{
+ return digest!(CRC64ISO, T)(data);
+}
+
+///
+@system unittest
+{
+ ubyte[] data = [4,5,7,25];
+ assert(data.crc64ISOOf == [0, 0, 0, 80, 137, 232, 203, 120]);
+
+ import std.utf : byChar;
+ assert("hello"d.byChar.crc64ISOOf == [0, 0, 16, 216, 226, 238, 62, 60]);
+
+ ubyte[8] hash = "abc".crc64ISOOf();
+ assert("abc".crc64ISOOf == [0, 0, 0, 0, 32, 196, 118, 55]);
+ assert(hash == digest!CRC64ISO("ab", "c"));
+
+ import std.range : iota;
+ enum ubyte S = 5, F = 66;
+
+ assert(iota(S, F).crc64ISOOf == [21, 185, 116, 95, 219, 11, 54, 7]);
+}
+
+/**
+ * producing the usual CRC32 string output.
+ */
+public alias crcHexString = toHexString!(Order.decreasing);
+///ditto
+public alias crcHexString = toHexString!(Order.decreasing, 16);
+
+/**
+ * OOP API CRC32 implementation.
+ * See $(D std.digest) for differences between template and OOP API.
+ *
+ * This is an alias for $(D $(REF WrapperDigest, std,digest)!CRC32), see
+ * there for more information.
+ */
+alias CRC32Digest = WrapperDigest!CRC32;
+
+/**
+ * OOP API CRC64-ECMA implementation.
+ * See $(D std.digest.digest) for differences between template and OOP API.
+ *
+ * This is an alias for $(D $(REF WrapperDigest, std,digest,digest)!CRC64ECMA),
+ * see there for more information.
+ */
+alias CRC64ECMADigest = WrapperDigest!CRC64ECMA;
+
+/**
+ * OOP API CRC64-ISO implementation.
+ * See $(D std.digest.digest) for differences between template and OOP API.
+ *
+ * This is an alias for $(D $(REF WrapperDigest, std,digest,digest)!CRC64ISO),
+ * see there for more information.
+ */
+alias CRC64ISODigest = WrapperDigest!CRC64ISO;
+
+///
+@safe unittest
+{
+ //Simple example, hashing a string using Digest.digest helper function
+ auto crc = new CRC32Digest();
+ ubyte[] hash = crc.digest("abc");
+ //Let's get a hash string
+ assert(crcHexString(hash) == "352441C2");
+}
+
+///
+@system unittest
+{
+ //Let's use the OOP features:
+ void test(Digest dig)
+ {
+ dig.put(cast(ubyte) 0);
+ }
+ auto crc = new CRC32Digest();
+ test(crc);
+
+ //Let's use a custom buffer:
+ ubyte[4] buf;
+ ubyte[] result = crc.finish(buf[]);
+ assert(crcHexString(result) == "D202EF8D");
+}
+
+///
+@safe unittest
+{
+ //Simple example
+ auto hash = new CRC32Digest();
+ hash.put(cast(ubyte) 0);
+ ubyte[] result = hash.finish();
+}
+
+///
+@system unittest
+{
+ //using a supplied buffer
+ ubyte[4] buf;
+ auto hash = new CRC32Digest();
+ 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)
+}
+
+@system unittest
+{
+ import std.range;
+
+ auto crc = new CRC32Digest();
+
+ crc.put(cast(ubyte[])"abcdefghijklmnopqrstuvwxyz");
+ assert(crc.peek() == cast(ubyte[]) x"bd50274c");
+ crc.reset();
+ crc.put(cast(ubyte[])"");
+ 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");
+
+ debug
+ assertThrown!Error(crc.finish(result[0 .. 3]));
+
+ assert(crc.length == 4);
+
+ assert(crc.digest("") == cast(ubyte[]) x"00000000");
+
+ assert(crc.digest("a") == cast(ubyte[]) x"43beb7e8");
+
+ assert(crc.digest("abc") == cast(ubyte[]) x"c2412435");
+
+ assert(crc.digest("abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq")
+ == cast(ubyte[]) x"5f3f1a17");
+
+ assert(crc.digest("message digest") == cast(ubyte[]) x"7f9d1520");
+
+ assert(crc.digest("abcdefghijklmnopqrstuvwxyz")
+ == cast(ubyte[]) x"bd50274c");
+
+ assert(crc.digest("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789")
+ == cast(ubyte[]) x"d2e6c21f");
+
+ assert(crc.digest("1234567890123456789012345678901234567890",
+ "1234567890123456789012345678901234567890")
+ == cast(ubyte[]) x"724aa97c");
+
+ ubyte[] onemilliona = new ubyte[1000000];
+ onemilliona[] = 'a';
+ auto digest = crc32Of(onemilliona);
+ assert(digest == cast(ubyte[]) x"BCBF25DC");
+
+ auto oneMillionRange = repeat!ubyte(cast(ubyte)'a', 1000000);
+ digest = crc32Of(oneMillionRange);
+ assert(digest == cast(ubyte[]) x"BCBF25DC");
+}
diff --git a/libphobos/src/std/digest/digest.d b/libphobos/src/std/digest/digest.d
new file mode 100644
index 0000000..325afd4
--- /dev/null
+++ b/libphobos/src/std/digest/digest.d
@@ -0,0 +1,21 @@
+module std.digest.digest;
+
+import _newDigest = std.digest;
+
+// scheduled for deprecation in 2.077
+// See also: https://github.com/dlang/phobos/pull/5013#issuecomment-313987845
+alias isDigest = _newDigest.isDigest;
+alias DigestType = _newDigest.DigestType;
+alias hasPeek = _newDigest.hasPeek;
+alias hasBlockSize = _newDigest.hasBlockSize;
+alias digest = _newDigest.digest;
+alias hexDigest = _newDigest.hexDigest;
+alias makeDigest = _newDigest.makeDigest;
+alias Digest = _newDigest.Digest;
+alias Order = _newDigest.Order;
+alias toHexString = _newDigest.toHexString;
+alias asArray = _newDigest.asArray;
+alias digestLength = _newDigest.digestLength;
+alias WrapperDigest = _newDigest.WrapperDigest;
+alias secureEqual = _newDigest.secureEqual;
+alias LetterCase = _newDigest.LetterCase;
diff --git a/libphobos/src/std/digest/hmac.d b/libphobos/src/std/digest/hmac.d
new file mode 100644
index 0000000..bf0cbf3
--- /dev/null
+++ b/libphobos/src/std/digest/hmac.d
@@ -0,0 +1,336 @@
+// 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 : isDigest, hasBlockSize, isDigestibleRange, DigestType;
+import std.meta : allSatisfy;
+
+@safe:
+
+/**
+ * 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).
+ */
+
+/// Compute HMAC over an input string
+@safe unittest
+{
+ import std.ascii : LetterCase;
+ import std.digest : toHexString;
+ import std.digest.sha : SHA1;
+ import std.string : representation;
+
+ auto secret = "secret".representation;
+ assert("The quick brown fox jumps over the lazy dog"
+ .representation
+ .hmac!SHA1(secret)
+ .toHexString!(LetterCase.lower) == "198ea1ea04c435c1246b586a06d5cf11c3ffcda6");
+}
+
+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.hmac, std.digest.sha;
+ 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.hmac, std.digest.sha;
+ 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.hmac, std.digest.sha;
+ 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.hmac, std.digest.sha;
+ 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.hmac, std.digest.sha;
+ 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.range.primitives : put;
+ auto hash = HMAC!(H, blockSize)(secret);
+ foreach (datum; data)
+ put(hash, datum);
+ return hash.finish();
+ }
+
+ ///
+ @safe pure nothrow @nogc unittest
+ {
+ import std.algorithm.iteration : map;
+ import std.digest.hmac, std.digest.sha;
+ import std.string : representation;
+ 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 : 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);
+}
+
+@safe 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/libphobos/src/std/digest/md.d b/libphobos/src/std/digest/md.d
new file mode 100644
index 0000000..1b621cf
--- /dev/null
+++ b/libphobos/src/std/digest/md.d
@@ -0,0 +1,590 @@
+/**
+ * Computes MD5 hashes of arbitrary data. MD5 hashes are 16 byte quantities that are like a
+ * checksum or CRC, but are more robust.
+ *
+$(SCRIPT inhibitQuickIndex = 1;)
+
+$(DIVC quickindex,
+$(BOOKTABLE ,
+$(TR $(TH Category) $(TH Functions)
+)
+$(TR $(TDNW Template API) $(TD $(MYREF MD5)
+)
+)
+$(TR $(TDNW OOP API) $(TD $(MYREF MD5Digest))
+)
+$(TR $(TDNW Helpers) $(TD $(MYREF md5Of))
+)
+)
+)
+
+ * This module conforms to the APIs defined in $(D std.digest). To understand the
+ * differences between the template and the OOP API, see $(MREF std, digest).
+ *
+ * This module publicly imports $(MREF std, digest) and can be used as a stand-alone
+ * module.
+ *
+ * License: $(HTTP www.boost.org/LICENSE_1_0.txt, Boost License 1.0).
+ *
+ * CTFE:
+ * Digests do not work in CTFE
+ *
+ * Authors:
+ * Piotr Szturmaj, Kai Nacke, Johannes Pfau $(BR)
+ * The routines and algorithms are derived from the $(I RSA Data Security, Inc. MD5 Message-Digest Algorithm).
+ *
+ * References:
+ * $(LINK2 http://en.wikipedia.org/wiki/Md5, Wikipedia on MD5)
+ *
+ * Source: $(PHOBOSSRC std/digest/_md.d)
+ *
+ */
+
+/* md5.d - RSA Data Security, Inc., MD5 message-digest algorithm
+ * Derived from the RSA Data Security, Inc. MD5 Message-Digest Algorithm.
+ */
+module std.digest.md;
+
+public import std.digest;
+
+///
+@safe unittest
+{
+ //Template API
+ import std.digest.md;
+
+ //Feeding data
+ ubyte[1024] data;
+ MD5 md5;
+ md5.start();
+ md5.put(data[]);
+ md5.start(); //Start again
+ md5.put(data[]);
+ auto hash = md5.finish();
+}
+
+///
+@safe unittest
+{
+ //OOP API
+ import std.digest.md;
+
+ auto md5 = new MD5Digest();
+ ubyte[] hash = md5.digest("abc");
+ assert(toHexString(hash) == "900150983CD24FB0D6963F7D28E17F72");
+
+ //Feeding data
+ ubyte[1024] data;
+ md5.put(data[]);
+ md5.reset(); //Start again
+ md5.put(data[]);
+ hash = md5.finish();
+}
+
+//rotateLeft rotates x left n bits
+private uint rotateLeft(uint x, uint n) @safe pure nothrow @nogc
+{
+ // With recently added optimization to DMD (commit 32ea0206 at 07/28/11), this is translated to rol.
+ // No assembler required.
+ return (x << n) | (x >> (32-n));
+}
+
+/**
+ * Template API MD5 implementation.
+ * See $(D std.digest) for differences between template and OOP API.
+ */
+struct MD5
+{
+ private:
+ // magic initialization constants
+ uint[4] _state = [0x67452301,0xefcdab89,0x98badcfe,0x10325476]; // state (ABCD)
+ ulong _count; //number of bits, modulo 2^64
+ ubyte[64] _buffer; // input buffer
+
+ static immutable ubyte[64] _padding =
+ [
+ 0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
+ ];
+
+ // F, G, H and I are basic MD5 functions
+ static @safe pure nothrow @nogc
+ {
+ uint F(uint x, uint y, uint z) { return (x & y) | (~x & z); }
+ uint G(uint x, uint y, uint z) { return (x & z) | (y & ~z); }
+ uint H(uint x, uint y, uint z) { return x ^ y ^ z; }
+ uint I(uint x, uint y, uint z) { return y ^ (x | ~z); }
+ }
+
+
+ /*
+ * FF, GG, HH, and II transformations for rounds 1, 2, 3, and 4.
+ * Rotation is separate from addition to prevent recomputation.
+ */
+ static void FF(ref uint a, uint b, uint c, uint d, uint x, uint s, uint ac)
+ @safe pure nothrow @nogc
+ {
+ a += F (b, c, d) + x + ac;
+ a = rotateLeft(a, s);
+ a += b;
+ }
+
+ static void GG(ref uint a, uint b, uint c, uint d, uint x, uint s, uint ac)
+ @safe pure nothrow @nogc
+ {
+ a += G (b, c, d) + x + ac;
+ a = rotateLeft(a, s);
+ a += b;
+ }
+
+ static void HH(ref uint a, uint b, uint c, uint d, uint x, uint s, uint ac)
+ @safe pure nothrow @nogc
+ {
+ a += H (b, c, d) + x + ac;
+ a = rotateLeft(a, s);
+ a += b;
+ }
+
+ static void II(ref uint a, uint b, uint c, uint d, uint x, uint s, uint ac)
+ @safe pure nothrow @nogc
+ {
+ a += I (b, c, d) + x + ac;
+ a = rotateLeft(a, s);
+ a += b;
+ }
+
+ /*
+ * MD5 basic transformation. Transforms state based on block.
+ */
+
+ //Constants for MD5Transform routine.
+ enum
+ {
+ S11 = 7,
+ S12 = 12,
+ S13 = 17,
+ S14 = 22,
+ S21 = 5,
+ S22 = 9,
+ S23 = 14,
+ S24 = 20,
+ S31 = 4,
+ S32 = 11,
+ S33 = 16,
+ S34 = 23,
+ S41 = 6,
+ S42 = 10,
+ S43 = 15,
+ S44 = 21,
+ }
+
+ private void transform(const(ubyte[64])* block) pure nothrow @nogc
+ {
+ uint a = _state[0],
+ b = _state[1],
+ c = _state[2],
+ d = _state[3];
+
+ uint[16] x = void;
+
+ version (BigEndian)
+ {
+ 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];
+ }
+
+ //Round 1
+ FF (a, b, c, d, x[ 0], S11, 0xd76aa478); /* 1 */
+ FF (d, a, b, c, x[ 1], S12, 0xe8c7b756); /* 2 */
+ FF (c, d, a, b, x[ 2], S13, 0x242070db); /* 3 */
+ FF (b, c, d, a, x[ 3], S14, 0xc1bdceee); /* 4 */
+ FF (a, b, c, d, x[ 4], S11, 0xf57c0faf); /* 5 */
+ FF (d, a, b, c, x[ 5], S12, 0x4787c62a); /* 6 */
+ FF (c, d, a, b, x[ 6], S13, 0xa8304613); /* 7 */
+ FF (b, c, d, a, x[ 7], S14, 0xfd469501); /* 8 */
+ FF (a, b, c, d, x[ 8], S11, 0x698098d8); /* 9 */
+ FF (d, a, b, c, x[ 9], S12, 0x8b44f7af); /* 10 */
+ FF (c, d, a, b, x[10], S13, 0xffff5bb1); /* 11 */
+ FF (b, c, d, a, x[11], S14, 0x895cd7be); /* 12 */
+ FF (a, b, c, d, x[12], S11, 0x6b901122); /* 13 */
+ FF (d, a, b, c, x[13], S12, 0xfd987193); /* 14 */
+ FF (c, d, a, b, x[14], S13, 0xa679438e); /* 15 */
+ FF (b, c, d, a, x[15], S14, 0x49b40821); /* 16 */
+
+ //Round 2
+ GG (a, b, c, d, x[ 1], S21, 0xf61e2562); /* 17 */
+ GG (d, a, b, c, x[ 6], S22, 0xc040b340); /* 18 */
+ GG (c, d, a, b, x[11], S23, 0x265e5a51); /* 19 */
+ GG (b, c, d, a, x[ 0], S24, 0xe9b6c7aa); /* 20 */
+ GG (a, b, c, d, x[ 5], S21, 0xd62f105d); /* 21 */
+ GG (d, a, b, c, x[10], S22, 0x2441453); /* 22 */
+ GG (c, d, a, b, x[15], S23, 0xd8a1e681); /* 23 */
+ GG (b, c, d, a, x[ 4], S24, 0xe7d3fbc8); /* 24 */
+ GG (a, b, c, d, x[ 9], S21, 0x21e1cde6); /* 25 */
+ GG (d, a, b, c, x[14], S22, 0xc33707d6); /* 26 */
+ GG (c, d, a, b, x[ 3], S23, 0xf4d50d87); /* 27 */
+ GG (b, c, d, a, x[ 8], S24, 0x455a14ed); /* 28 */
+ GG (a, b, c, d, x[13], S21, 0xa9e3e905); /* 29 */
+ GG (d, a, b, c, x[ 2], S22, 0xfcefa3f8); /* 30 */
+ GG (c, d, a, b, x[ 7], S23, 0x676f02d9); /* 31 */
+ GG (b, c, d, a, x[12], S24, 0x8d2a4c8a); /* 32 */
+
+ //Round 3
+ HH (a, b, c, d, x[ 5], S31, 0xfffa3942); /* 33 */
+ HH (d, a, b, c, x[ 8], S32, 0x8771f681); /* 34 */
+ HH (c, d, a, b, x[11], S33, 0x6d9d6122); /* 35 */
+ HH (b, c, d, a, x[14], S34, 0xfde5380c); /* 36 */
+ HH (a, b, c, d, x[ 1], S31, 0xa4beea44); /* 37 */
+ HH (d, a, b, c, x[ 4], S32, 0x4bdecfa9); /* 38 */
+ HH (c, d, a, b, x[ 7], S33, 0xf6bb4b60); /* 39 */
+ HH (b, c, d, a, x[10], S34, 0xbebfbc70); /* 40 */
+ HH (a, b, c, d, x[13], S31, 0x289b7ec6); /* 41 */
+ HH (d, a, b, c, x[ 0], S32, 0xeaa127fa); /* 42 */
+ HH (c, d, a, b, x[ 3], S33, 0xd4ef3085); /* 43 */
+ HH (b, c, d, a, x[ 6], S34, 0x4881d05); /* 44 */
+ HH (a, b, c, d, x[ 9], S31, 0xd9d4d039); /* 45 */
+ HH (d, a, b, c, x[12], S32, 0xe6db99e5); /* 46 */
+ HH (c, d, a, b, x[15], S33, 0x1fa27cf8); /* 47 */
+ HH (b, c, d, a, x[ 2], S34, 0xc4ac5665); /* 48 */
+
+ //Round 4
+ II (a, b, c, d, x[ 0], S41, 0xf4292244); /* 49 */
+ II (d, a, b, c, x[ 7], S42, 0x432aff97); /* 50 */
+ II (c, d, a, b, x[14], S43, 0xab9423a7); /* 51 */
+ II (b, c, d, a, x[ 5], S44, 0xfc93a039); /* 52 */
+ II (a, b, c, d, x[12], S41, 0x655b59c3); /* 53 */
+ II (d, a, b, c, x[ 3], S42, 0x8f0ccc92); /* 54 */
+ II (c, d, a, b, x[10], S43, 0xffeff47d); /* 55 */
+ II (b, c, d, a, x[ 1], S44, 0x85845dd1); /* 56 */
+ II (a, b, c, d, x[ 8], S41, 0x6fa87e4f); /* 57 */
+ II (d, a, b, c, x[15], S42, 0xfe2ce6e0); /* 58 */
+ II (c, d, a, b, x[ 6], S43, 0xa3014314); /* 59 */
+ II (b, c, d, a, x[13], S44, 0x4e0811a1); /* 60 */
+ II (a, b, c, d, x[ 4], S41, 0xf7537e82); /* 61 */
+ II (d, a, b, c, x[11], S42, 0xbd3af235); /* 62 */
+ II (c, d, a, b, x[ 2], S43, 0x2ad7d2bb); /* 63 */
+ II (b, c, d, a, x[ 9], S44, 0xeb86d391); /* 64 */
+
+ _state[0] += a;
+ _state[1] += b;
+ _state[2] += c;
+ _state[3] += d;
+
+ //Zeroize sensitive information.
+ x[] = 0;
+ }
+
+ public:
+ enum blockSize = 512;
+
+ /**
+ * Use this to feed the digest with data.
+ * Also implements the $(REF isOutputRange, std,range,primitives)
+ * interface for $(D ubyte) and $(D const(ubyte)[]).
+ *
+ * Example:
+ * ----
+ * MD5 dig;
+ * dig.put(cast(ubyte) 0); //single ubyte
+ * dig.put(cast(ubyte) 0, cast(ubyte) 0); //variadic
+ * ubyte[10] buf;
+ * dig.put(buf); //buffer
+ * ----
+ */
+ void put(scope const(ubyte)[] data...) @trusted pure nothrow @nogc
+ {
+ uint i, index, partLen;
+ auto inputLen = data.length;
+
+ //Compute number of bytes mod 64
+ index = (cast(uint)_count >> 3) & (64 - 1);
+
+ //Update number of bits
+ _count += inputLen * 8;
+
+ partLen = 64 - index;
+
+ //Transform as many times as possible
+ if (inputLen >= partLen)
+ {
+ (&_buffer[index])[0 .. partLen] = data.ptr[0 .. partLen];
+ transform(&_buffer);
+
+ for (i = partLen; i + 63 < inputLen; i += 64)
+ {
+ transform(cast(const(ubyte[64])*)(data[i .. i + 64].ptr));
+ }
+
+ index = 0;
+ }
+ else
+ {
+ i = 0;
+ }
+
+ /* Buffer remaining input */
+ if (inputLen - i)
+ (&_buffer[index])[0 .. inputLen-i] = (&data[i])[0 .. inputLen-i];
+ }
+
+ /**
+ * Used to (re)initialize the MD5 digest.
+ *
+ * Note:
+ * For this MD5 Digest implementation calling start after default construction
+ * is not necessary. Calling start is only necessary to reset the Digest.
+ *
+ * Generic code which deals with different Digest types should always call start though.
+ *
+ * Example:
+ * --------
+ * MD5 digest;
+ * //digest.start(); //Not necessary
+ * digest.put(0);
+ * --------
+ */
+ void start() @safe pure nothrow @nogc
+ {
+ this = MD5.init;
+ }
+
+ /**
+ * Returns the finished MD5 hash. This also calls $(LREF start) to
+ * reset the internal state.
+ */
+ ubyte[16] finish() @trusted pure nothrow @nogc
+ {
+ import std.bitmanip : nativeToLittleEndian;
+
+ ubyte[16] data = void;
+ ubyte[8] bits = void;
+ uint index, padLen;
+
+ //Save number of bits
+ bits[0 .. 8] = nativeToLittleEndian(_count)[];
+
+ //Pad out to 56 mod 64
+ index = (cast(uint)_count >> 3) & (64 - 1);
+ padLen = (index < 56) ? (56 - index) : (120 - index);
+ put(_padding[0 .. padLen]);
+
+ //Append length (before padding)
+ put(bits);
+
+ //Store state in digest
+ data[0 .. 4] = nativeToLittleEndian(_state[0])[];
+ data[4 .. 8] = nativeToLittleEndian(_state[1])[];
+ data[8 .. 12] = nativeToLittleEndian(_state[2])[];
+ data[12 .. 16] = nativeToLittleEndian(_state[3])[];
+
+ /* Zeroize sensitive information. */
+ start();
+ return data;
+ }
+ ///
+ @safe unittest
+ {
+ //Simple example
+ MD5 hash;
+ hash.start();
+ hash.put(cast(ubyte) 0);
+ ubyte[16] result = hash.finish();
+ }
+}
+
+///
+@safe unittest
+{
+ //Simple example, hashing a string using md5Of helper function
+ ubyte[16] hash = md5Of("abc");
+ //Let's get a hash string
+ assert(toHexString(hash) == "900150983CD24FB0D6963F7D28E17F72");
+}
+
+///
+@safe unittest
+{
+ //Using the basic API
+ MD5 hash;
+ hash.start();
+ ubyte[1024] data;
+ //Initialize data here...
+ hash.put(data);
+ ubyte[16] result = hash.finish();
+}
+
+///
+@safe unittest
+{
+ //Let's use the template features:
+ void doSomething(T)(ref T hash)
+ if (isDigest!T)
+ {
+ hash.put(cast(ubyte) 0);
+ }
+ MD5 md5;
+ md5.start();
+ doSomething(md5);
+ assert(toHexString(md5.finish()) == "93B885ADFE0DA089CDF634904FD59F71");
+}
+
+@safe unittest
+{
+ assert(isDigest!MD5);
+}
+
+@system unittest
+{
+ import std.range;
+
+ ubyte[16] digest;
+
+ MD5 md5;
+ md5.put(cast(ubyte[])"abcdef");
+ md5.start();
+ md5.put(cast(ubyte[])"");
+ assert(md5.finish() == cast(ubyte[]) x"d41d8cd98f00b204e9800998ecf8427e");
+
+ digest = md5Of("");
+ assert(digest == cast(ubyte[]) x"d41d8cd98f00b204e9800998ecf8427e");
+
+ digest = md5Of("a");
+ assert(digest == cast(ubyte[]) x"0cc175b9c0f1b6a831c399e269772661");
+
+ digest = md5Of("abc");
+ assert(digest == cast(ubyte[]) x"900150983cd24fb0d6963f7d28e17f72");
+
+ digest = md5Of("abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq");
+ assert(digest == cast(ubyte[]) x"8215ef0796a20bcaaae116d3876c664a");
+
+ digest = md5Of("message digest");
+ assert(digest == cast(ubyte[]) x"f96b697d7cb7938d525a2f31aaf161d0");
+
+ digest = md5Of("abcdefghijklmnopqrstuvwxyz");
+ assert(digest == cast(ubyte[]) x"c3fcd3d76192e4007dfb496cca67e13b");
+
+ digest = md5Of("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789");
+ assert(digest == cast(ubyte[]) x"d174ab98d277d9f5a5611c2c9f419d9f");
+
+ digest = md5Of("1234567890123456789012345678901234567890"~
+ "1234567890123456789012345678901234567890");
+ assert(digest == cast(ubyte[]) x"57edf4a22be3c955ac49da2e2107b67a");
+
+ assert(toHexString(cast(ubyte[16]) x"c3fcd3d76192e4007dfb496cca67e13b")
+ == "C3FCD3D76192E4007DFB496CCA67E13B");
+
+ ubyte[] onemilliona = new ubyte[1000000];
+ onemilliona[] = 'a';
+ digest = md5Of(onemilliona);
+ assert(digest == cast(ubyte[]) x"7707D6AE4E027C70EEA2A935C2296F21");
+
+ auto oneMillionRange = repeat!ubyte(cast(ubyte)'a', 1000000);
+ digest = md5Of(oneMillionRange);
+ assert(digest == cast(ubyte[]) x"7707D6AE4E027C70EEA2A935C2296F21");
+}
+
+/**
+ * This is a convenience alias for $(REF digest, std,digest) using the
+ * MD5 implementation.
+ */
+//simple alias doesn't work here, hope this gets inlined...
+auto md5Of(T...)(T data)
+{
+ return digest!(MD5, T)(data);
+}
+
+///
+@safe unittest
+{
+ ubyte[16] hash = md5Of("abc");
+ assert(hash == digest!MD5("abc"));
+}
+
+/**
+ * OOP API MD5 implementation.
+ * See $(D std.digest) for differences between template and OOP API.
+ *
+ * This is an alias for $(D $(REF WrapperDigest, std,digest)!MD5), see
+ * there for more information.
+ */
+alias MD5Digest = WrapperDigest!MD5;
+
+///
+@safe unittest
+{
+ //Simple example, hashing a string using Digest.digest helper function
+ auto md5 = new MD5Digest();
+ ubyte[] hash = md5.digest("abc");
+ //Let's get a hash string
+ assert(toHexString(hash) == "900150983CD24FB0D6963F7D28E17F72");
+}
+
+///
+@system unittest
+{
+ //Let's use the OOP features:
+ void test(Digest dig)
+ {
+ dig.put(cast(ubyte) 0);
+ }
+ auto md5 = new MD5Digest();
+ test(md5);
+
+ //Let's use a custom buffer:
+ ubyte[16] buf;
+ ubyte[] result = md5.finish(buf[]);
+ assert(toHexString(result) == "93B885ADFE0DA089CDF634904FD59F71");
+}
+
+@system unittest
+{
+ auto md5 = new MD5Digest();
+
+ md5.put(cast(ubyte[])"abcdef");
+ md5.reset();
+ md5.put(cast(ubyte[])"");
+ 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");
+
+ debug
+ {
+ import std.exception;
+ assertThrown!Error(md5.finish(result[0 .. 15]));
+ }
+
+ assert(md5.length == 16);
+
+ assert(md5.digest("") == cast(ubyte[]) x"d41d8cd98f00b204e9800998ecf8427e");
+
+ assert(md5.digest("a") == cast(ubyte[]) x"0cc175b9c0f1b6a831c399e269772661");
+
+ assert(md5.digest("abc") == cast(ubyte[]) x"900150983cd24fb0d6963f7d28e17f72");
+
+ assert(md5.digest("abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq")
+ == cast(ubyte[]) x"8215ef0796a20bcaaae116d3876c664a");
+
+ assert(md5.digest("message digest") == cast(ubyte[]) x"f96b697d7cb7938d525a2f31aaf161d0");
+
+ assert(md5.digest("abcdefghijklmnopqrstuvwxyz")
+ == cast(ubyte[]) x"c3fcd3d76192e4007dfb496cca67e13b");
+
+ assert(md5.digest("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789")
+ == cast(ubyte[]) x"d174ab98d277d9f5a5611c2c9f419d9f");
+
+ assert(md5.digest("1234567890123456789012345678901234567890",
+ "1234567890123456789012345678901234567890")
+ == cast(ubyte[]) x"57edf4a22be3c955ac49da2e2107b67a");
+}
diff --git a/libphobos/src/std/digest/murmurhash.d b/libphobos/src/std/digest/murmurhash.d
new file mode 100644
index 0000000..74efed5
--- /dev/null
+++ b/libphobos/src/std/digest/murmurhash.d
@@ -0,0 +1,755 @@
+/**
+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 $(MREF std, digest).
+
+This module publicly imports $(MREF std, digest) and can be used as a stand-alone module.
+
+Source: $(PHOBOSSRC std/digest/_murmurhash.d)
+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 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]);
+ assert(hashed == [0, 173, 69, 68]);
+}
+
+///
+@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();
+ assert(hashed == [181, 151, 88, 252]);
+}
+
+///
+@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();
+ assert(hashed == [188, 165, 108, 2]);
+}
+
+public import std.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/libphobos/src/std/digest/package.d b/libphobos/src/std/digest/package.d
new file mode 100644
index 0000000..f4646ae
--- /dev/null
+++ b/libphobos/src/std/digest/package.d
@@ -0,0 +1,1171 @@
+/**
+ * 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;)
+
+$(DIVC quickindex,
+$(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)
+)
+)
+$(TR $(TDNW OOP API) $(TD $(MYREF Digest)
+)
+)
+$(TR $(TDNW Helper functions) $(TD $(MYREF toHexString))
+)
+$(TR $(TDNW Implementation helpers) $(TD $(MYREF digestLength) $(MYREF WrapperDigest))
+)
+)
+)
+
+ * APIs:
+ * There are two APIs for digests: The template API and the OOP API. The template API uses structs
+ * and template helpers like $(LREF isDigest). The OOP API implements digests as classes inheriting
+ * the $(LREF Digest) interface. All digests are named so that the template API struct is called "$(B x)"
+ * and the OOP API class is called "$(B x)Digest". For example we have $(D MD5) <--> $(D MD5Digest),
+ * $(D CRC32) <--> $(D CRC32Digest), etc.
+ *
+ * The template API is slightly more efficient. It does not have to allocate memory dynamically,
+ * all memory is allocated on the stack. The OOP API has to allocate in the finish method if no
+ * buffer was provided. If you provide a buffer to the OOP APIs finish function, it doesn't allocate,
+ * but the $(LREF Digest) classes still have to be created using $(D new) which allocates them using the GC.
+ *
+ * The OOP API is useful to change the _digest function and/or _digest backend at 'runtime'. The benefit here
+ * is that switching e.g. Phobos MD5Digest and an OpenSSLMD5Digest implementation is ABI compatible.
+ *
+ * If just one specific _digest type and backend is needed, the template API is usually a good fit.
+ * In this simplest case, the template API can even be used without templates: Just use the "$(B x)" structs
+ * directly.
+ *
+ * License: $(HTTP www.boost.org/LICENSE_1_0.txt, Boost License 1.0).
+ * Authors:
+ * Johannes Pfau
+ *
+ * Source: $(PHOBOSSRC std/_digest/_package.d)
+ *
+ * CTFE:
+ * Digests do not work in CTFE
+ *
+ * TODO:
+ * Digesting single bits (as opposed to bytes) is not implemented. This will be done as another
+ * template constraint helper (hasBitDigesting!T) and an additional interface (BitDigest)
+ */
+/* Copyright Johannes Pfau 2012.
+ * 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.digest;
+
+public import std.ascii : LetterCase;
+import std.meta : allSatisfy;
+import std.range.primitives;
+import std.traits;
+
+
+///
+@system unittest
+{
+ import std.digest.crc;
+
+ //Simple example
+ char[8] hexHash = hexDigest!CRC32("The quick brown fox jumps over the lazy dog");
+ assert(hexHash == "39A34F41");
+
+ //Simple example, using the API manually
+ CRC32 context = makeDigest!CRC32();
+ context.put(cast(ubyte[])"The quick brown fox jumps over the lazy dog");
+ ubyte[4] hash = context.finish();
+ assert(toHexString(hash) == "39A34F41");
+}
+
+///
+@system unittest
+{
+ //Generating the hashes of a file, idiomatic D way
+ import std.digest.crc, std.digest.md, std.digest.sha;
+ import std.stdio;
+
+ // Digests a file and prints the result.
+ void digestFile(Hash)(string filename)
+ if (isDigest!Hash)
+ {
+ auto file = File(filename);
+ auto result = digest!Hash(file.byChunk(4096 * 1024));
+ writefln("%s (%s) = %s", Hash.stringof, filename, toHexString(result));
+ }
+
+ void main(string[] args)
+ {
+ foreach (name; args[1 .. $])
+ {
+ digestFile!MD5(name);
+ digestFile!SHA1(name);
+ digestFile!CRC32(name);
+ }
+ }
+}
+///
+@system unittest
+{
+ //Generating the hashes of a file using the template API
+ import std.digest.crc, std.digest.md, std.digest.sha;
+ import std.stdio;
+ // Digests a file and prints the result.
+ void digestFile(Hash)(ref Hash hash, string filename)
+ if (isDigest!Hash)
+ {
+ File file = File(filename);
+
+ //As digests imlement OutputRange, we could use std.algorithm.copy
+ //Let's do it manually for now
+ foreach (buffer; file.byChunk(4096 * 1024))
+ hash.put(buffer);
+
+ auto result = hash.finish();
+ writefln("%s (%s) = %s", Hash.stringof, filename, toHexString(result));
+ }
+
+ void uMain(string[] args)
+ {
+ MD5 md5;
+ SHA1 sha1;
+ CRC32 crc32;
+
+ md5.start();
+ sha1.start();
+ crc32.start();
+
+ foreach (arg; args[1 .. $])
+ {
+ digestFile(md5, arg);
+ digestFile(sha1, arg);
+ digestFile(crc32, arg);
+ }
+ }
+}
+
+///
+@system unittest
+{
+ import std.digest.crc, std.digest.md, std.digest.sha;
+ import std.stdio;
+
+ // Digests a file and prints the result.
+ void digestFile(Digest hash, string filename)
+ {
+ File file = File(filename);
+
+ //As digests implement OutputRange, we could use std.algorithm.copy
+ //Let's do it manually for now
+ foreach (buffer; file.byChunk(4096 * 1024))
+ hash.put(buffer);
+
+ ubyte[] result = hash.finish();
+ writefln("%s (%s) = %s", typeid(hash).toString(), filename, toHexString(result));
+ }
+
+ void umain(string[] args)
+ {
+ auto md5 = new MD5Digest();
+ auto sha1 = new SHA1Digest();
+ auto crc32 = new CRC32Digest();
+
+ foreach (arg; args[1 .. $])
+ {
+ digestFile(md5, arg);
+ digestFile(sha1, arg);
+ digestFile(crc32, arg);
+ }
+ }
+}
+
+version (StdDdoc)
+ version = ExampleDigest;
+
+version (ExampleDigest)
+{
+ /**
+ * This documents the general structure of a Digest in the template API.
+ * All digest implementations should implement the following members and therefore pass
+ * the $(LREF isDigest) test.
+ *
+ * Note:
+ * $(UL
+ * $(LI A digest must be a struct (value type) to pass the $(LREF isDigest) test.)
+ * $(LI A digest passing the $(LREF isDigest) test is always an $(D OutputRange))
+ * )
+ */
+ struct ExampleDigest
+ {
+ public:
+ /**
+ * Use this to feed the digest with data.
+ * 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
+ * ubyte[10] buf;
+ * dig.put(buf); //buffer
+ * ----
+ */
+ @trusted void put(scope const(ubyte)[] data...)
+ {
+
+ }
+
+ /**
+ * This function is used to (re)initialize the digest.
+ * It must be called before using the digest and it also works as a 'reset' function
+ * if the digest has already processed data.
+ */
+ @trusted void start()
+ {
+
+ }
+
+ /**
+ * The finish function returns the final hash sum and resets the Digest.
+ *
+ * Note:
+ * The actual type returned by finish depends on the digest implementation.
+ * $(D ubyte[16]) is just used as an example. It is guaranteed that the type is a
+ * static array of ubytes.
+ *
+ * $(UL
+ * $(LI Use $(LREF DigestType) to obtain the actual return type.)
+ * $(LI Use $(LREF digestLength) to obtain the length of the ubyte array.)
+ * )
+ */
+ @trusted ubyte[16] finish()
+ {
+ return (ubyte[16]).init;
+ }
+ }
+}
+
+///
+@system unittest
+{
+ //Using the OutputRange feature
+ import std.algorithm.mutation : copy;
+ import std.digest.md;
+ import std.range : repeat;
+
+ auto oneMillionRange = repeat!ubyte(cast(ubyte)'a', 1000000);
+ auto ctx = makeDigest!MD5();
+ copy(oneMillionRange, &ctx); //Note: You must pass a pointer to copy!
+ assert(ctx.finish().toHexString() == "7707D6AE4E027C70EEA2A935C2296F21");
+}
+
+/**
+ * Use this to check if a type is a digest. See $(LREF ExampleDigest) to see what
+ * a type must provide to pass this check.
+ *
+ * Note:
+ * This is very useful as a template constraint (see examples)
+ *
+ * BUGS:
+ * $(UL
+ * $(LI Does not yet verify that put takes scope parameters.)
+ * $(LI Should check that finish() returns a ubyte[num] array)
+ * )
+ */
+template isDigest(T)
+{
+ import std.range : isOutputRange;
+ enum bool isDigest = isOutputRange!(T, const(ubyte)[]) && isOutputRange!(T, ubyte) &&
+ is(T == struct) &&
+ is(typeof(
+ {
+ T dig = void; //Can define
+ dig.put(cast(ubyte) 0, cast(ubyte) 0); //varags
+ dig.start(); //has start
+ auto value = dig.finish(); //has finish
+ }));
+}
+
+///
+@system unittest
+{
+ import std.digest.crc;
+ static assert(isDigest!CRC32);
+}
+///
+@system unittest
+{
+ import std.digest.crc;
+ void myFunction(T)()
+ if (isDigest!T)
+ {
+ T dig;
+ dig.start();
+ auto result = dig.finish();
+ }
+ myFunction!CRC32();
+}
+
+/**
+ * Use this template to get the type which is returned by a digest's $(LREF finish) method.
+ */
+template DigestType(T)
+{
+ static if (isDigest!T)
+ {
+ alias DigestType =
+ ReturnType!(typeof(
+ {
+ T dig = void;
+ return dig.finish();
+ }));
+ }
+ else
+ static assert(false, T.stringof ~ " is not a digest! (fails isDigest!T)");
+}
+
+///
+@system unittest
+{
+ import std.digest.crc;
+ assert(is(DigestType!(CRC32) == ubyte[4]));
+}
+///
+@system unittest
+{
+ import std.digest.crc;
+ CRC32 dig;
+ dig.start();
+ DigestType!CRC32 result = dig.finish();
+}
+
+/**
+ * Used to check if a digest supports the $(D peek) method.
+ * Peek has exactly the same function signatures as finish, but it doesn't reset
+ * the digest's internal state.
+ *
+ * Note:
+ * $(UL
+ * $(LI This is very useful as a template constraint (see examples))
+ * $(LI This also checks if T passes $(LREF isDigest))
+ * )
+ */
+template hasPeek(T)
+{
+ enum bool hasPeek = isDigest!T &&
+ is(typeof(
+ {
+ T dig = void; //Can define
+ DigestType!T val = dig.peek();
+ }));
+}
+
+///
+@system unittest
+{
+ import std.digest.crc, std.digest.md;
+ assert(!hasPeek!(MD5));
+ assert(hasPeek!CRC32);
+}
+///
+@system unittest
+{
+ import std.digest.crc;
+ void myFunction(T)()
+ if (hasPeek!T)
+ {
+ T dig;
+ dig.start();
+ auto result = dig.peek();
+ }
+ myFunction!CRC32();
+}
+
+/**
+ * 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.hmac, std.digest.md;
+ 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;
+ enum bool isDigestibleRange = isInputRange!Range && is(typeof(
+ {
+ MD5 ha; //Could use any conformant hash
+ ElementType!Range val;
+ ha.put(val);
+ }));
+}
+
+/**
+ * This is a convenience function to calculate a hash using the template API.
+ * Every digest passing the $(LREF isDigest) test can be used with this function.
+ *
+ * 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
+ && isDigestibleRange!Range)
+{
+ import std.algorithm.mutation : copy;
+ Hash hash;
+ hash.start();
+ copy(range, &hash);
+ return hash.finish();
+}
+
+///
+@system unittest
+{
+ import std.digest.md;
+ import std.range : repeat;
+ auto testRange = repeat!ubyte(cast(ubyte)'a', 100);
+ auto md5 = digest!MD5(testRange);
+}
+
+/**
+ * This overload of the digest function handles arrays.
+ *
+ * Params:
+ * data= one or more arrays of any type
+ */
+DigestType!Hash digest(Hash, T...)(scope const T data)
+if (allSatisfy!(isArray, typeof(data)))
+{
+ Hash hash;
+ hash.start();
+ foreach (datum; data)
+ hash.put(cast(const(ubyte[]))datum);
+ return hash.finish();
+}
+
+///
+@system unittest
+{
+ import std.digest.crc, std.digest.md, std.digest.sha;
+ auto md5 = digest!MD5( "The quick brown fox jumps over the lazy dog");
+ auto sha1 = digest!SHA1( "The quick brown fox jumps over the lazy dog");
+ auto crc32 = digest!CRC32("The quick brown fox jumps over the lazy dog");
+ assert(toHexString(crc32) == "39A34F41");
+}
+
+///
+@system unittest
+{
+ import std.digest.crc;
+ auto crc32 = digest!CRC32("The quick ", "brown ", "fox jumps over the lazy dog");
+ assert(toHexString(crc32) == "39A34F41");
+}
+
+/**
+ * This is a convenience function similar to $(LREF digest), but it returns the string
+ * representation of the hash. Every digest passing the $(LREF isDigest) test can be used with this
+ * function.
+ *
+ * Params:
+ * order= the order in which the bytes are processed (see $(LREF toHexString))
+ * 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)
+{
+ return toHexString!order(digest!Hash(range));
+}
+
+///
+@system unittest
+{
+ import std.digest.md;
+ import std.range : repeat;
+ auto testRange = repeat!ubyte(cast(ubyte)'a', 100);
+ assert(hexDigest!MD5(testRange) == "36A92CC94A9E0FA21F625F8BFB007ADF");
+}
+
+/**
+ * This overload of the hexDigest function handles arrays.
+ *
+ * Params:
+ * order= the order in which the bytes are processed (see $(LREF toHexString))
+ * 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)))
+{
+ return toHexString!order(digest!Hash(data));
+}
+
+///
+@system unittest
+{
+ import std.digest.crc;
+ assert(hexDigest!(CRC32, Order.decreasing)("The quick brown fox jumps over the lazy dog") == "414FA339");
+}
+///
+@system unittest
+{
+ import std.digest.crc;
+ assert(hexDigest!(CRC32, Order.decreasing)("The quick ", "brown ", "fox jumps over the lazy dog") == "414FA339");
+}
+
+/**
+ * This is a convenience function which returns an initialized digest, so it's not necessary to call
+ * start manually.
+ */
+Hash makeDigest(Hash)()
+{
+ Hash hash;
+ hash.start();
+ return hash;
+}
+
+///
+@system unittest
+{
+ import std.digest.md;
+ auto md5 = makeDigest!MD5();
+ md5.put(0);
+ assert(toHexString(md5.finish()) == "93B885ADFE0DA089CDF634904FD59F71");
+}
+
+/*+*************************** End of template part, welcome to OOP land **************************/
+
+/**
+ * This describes the OOP API. To understand when to use the template API and when to use the OOP API,
+ * see the module documentation at the top of this page.
+ *
+ * The Digest interface is the base interface which is implemented by all digests.
+ *
+ * Note:
+ * A Digest implementation is always an $(D OutputRange)
+ */
+interface Digest
+{
+ public:
+ /**
+ * Use this to feed the digest with data.
+ * Also implements the $(REF isOutputRange, std,range,primitives)
+ * interface for $(D ubyte) and $(D const(ubyte)[]).
+ *
+ * Example:
+ * ----
+ * void test(Digest dig)
+ * {
+ * dig.put(cast(ubyte) 0); //single ubyte
+ * dig.put(cast(ubyte) 0, cast(ubyte) 0); //variadic
+ * ubyte[10] buf;
+ * dig.put(buf); //buffer
+ * }
+ * ----
+ */
+ @trusted nothrow void put(scope const(ubyte)[] data...);
+
+ /**
+ * Resets the internal state of the digest.
+ * Note:
+ * $(LREF finish) calls this internally, so it's not necessary to call
+ * $(D reset) manually after a call to $(LREF finish).
+ */
+ @trusted nothrow void reset();
+
+ /**
+ * This is the length in bytes of the hash value which is returned by $(LREF finish).
+ * It's also the required size of a buffer passed to $(LREF finish).
+ */
+ @trusted nothrow @property size_t length() const;
+
+ /**
+ * The finish function returns the hash value. It takes an optional buffer to copy the data
+ * into. If a buffer is passed, it must be at least $(LREF length) bytes big.
+ */
+ @trusted nothrow ubyte[] finish();
+ ///ditto
+ nothrow ubyte[] finish(ubyte[] buf);
+ //@@@BUG@@@ http://d.puremagic.com/issues/show_bug.cgi?id=6549
+ /*in
+ {
+ assert(buf.length >= this.length);
+ }*/
+
+ /**
+ * This is a convenience function to calculate the hash of a value using the OOP API.
+ */
+ final @trusted nothrow ubyte[] digest(scope const(void[])[] data...)
+ {
+ this.reset();
+ foreach (datum; data)
+ this.put(cast(ubyte[]) datum);
+ return this.finish();
+ }
+}
+
+///
+@system unittest
+{
+ //Using the OutputRange feature
+ import std.algorithm.mutation : copy;
+ import std.digest.md;
+ import std.range : repeat;
+
+ auto oneMillionRange = repeat!ubyte(cast(ubyte)'a', 1000000);
+ auto ctx = new MD5Digest();
+ copy(oneMillionRange, ctx);
+ assert(ctx.finish().toHexString() == "7707D6AE4E027C70EEA2A935C2296F21");
+}
+
+///
+@system unittest
+{
+ import std.digest.crc, std.digest.md, std.digest.sha;
+ ubyte[] md5 = (new MD5Digest()).digest("The quick brown fox jumps over the lazy dog");
+ ubyte[] sha1 = (new SHA1Digest()).digest("The quick brown fox jumps over the lazy dog");
+ ubyte[] crc32 = (new CRC32Digest()).digest("The quick brown fox jumps over the lazy dog");
+ assert(crcHexString(crc32) == "414FA339");
+}
+
+///
+@system unittest
+{
+ import std.digest.crc;
+ ubyte[] crc32 = (new CRC32Digest()).digest("The quick ", "brown ", "fox jumps over the lazy dog");
+ assert(crcHexString(crc32) == "414FA339");
+}
+
+@system unittest
+{
+ import std.range : isOutputRange;
+ assert(!isDigest!(Digest));
+ assert(isOutputRange!(Digest, ubyte));
+}
+
+///
+@system unittest
+{
+ void test(Digest dig)
+ {
+ dig.put(cast(ubyte) 0); //single ubyte
+ dig.put(cast(ubyte) 0, cast(ubyte) 0); //variadic
+ ubyte[10] buf;
+ dig.put(buf); //buffer
+ }
+}
+
+/*+*************************** End of OOP part, helper functions follow ***************************/
+
+/**
+ * See $(LREF toHexString)
+ */
+enum Order : bool
+{
+ increasing, ///
+ decreasing ///
+}
+
+
+/**
+ * Used to convert a hash value (a static or dynamic array of ubytes) to a string.
+ * Can be used with the OOP and with the template API.
+ *
+ * The additional order parameter can be used to specify the order of the input data.
+ * By default the data is processed in increasing order, starting at index 0. To process it in the
+ * opposite order, pass Order.decreasing as a parameter.
+ *
+ * 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)
+{
+ static if (letterCase == LetterCase.upper)
+ {
+ import std.ascii : hexDigits = hexDigits;
+ }
+ else
+ {
+ import std.ascii : hexDigits = lowerHexDigits;
+ }
+
+
+ char[num*2] result;
+ size_t i;
+
+ static if (order == Order.increasing)
+ {
+ foreach (u; digest)
+ {
+ result[i++] = hexDigits[u >> 4];
+ result[i++] = hexDigits[u & 15];
+ }
+ }
+ else
+ {
+ size_t j = num - 1;
+ while (i < num*2)
+ {
+ result[i++] = hexDigits[digest[j] >> 4];
+ result[i++] = hexDigits[digest[j] & 15];
+ j--;
+ }
+ }
+ return result;
+}
+
+///ditto
+char[num*2] toHexString(LetterCase letterCase, Order order = Order.increasing, size_t num)(in ubyte[num] digest)
+{
+ return toHexString!(order, num, letterCase)(digest);
+}
+
+///ditto
+string toHexString(Order order = Order.increasing, LetterCase letterCase = LetterCase.upper)
+(in ubyte[] digest)
+{
+ static if (letterCase == LetterCase.upper)
+ {
+ import std.ascii : hexDigits = hexDigits;
+ }
+ else
+ {
+ import std.ascii : hexDigits = lowerHexDigits;
+ }
+
+ auto result = new char[digest.length*2];
+ size_t i;
+
+ static if (order == Order.increasing)
+ {
+ foreach (u; digest)
+ {
+ result[i++] = hexDigits[u >> 4];
+ result[i++] = hexDigits[u & 15];
+ }
+ }
+ else
+ {
+ import std.range : retro;
+ foreach (u; retro(digest))
+ {
+ result[i++] = hexDigits[u >> 4];
+ result[i++] = hexDigits[u & 15];
+ }
+ }
+ import std.exception : assumeUnique;
+ // memory was just created, so casting to immutable is safe
+ return () @trusted { return assumeUnique(result); }();
+}
+
+///ditto
+string toHexString(LetterCase letterCase, Order order = Order.increasing)(in ubyte[] digest)
+{
+ return toHexString!(order, letterCase)(digest);
+}
+
+//For more example unittests, see Digest.digest, digest
+
+///
+@safe unittest
+{
+ import std.digest.crc;
+ //Test with template API:
+ auto crc32 = digest!CRC32("The quick ", "brown ", "fox jumps over the lazy dog");
+ //Lower case variant:
+ assert(toHexString!(LetterCase.lower)(crc32) == "39a34f41");
+ //Usually CRCs are printed in this order, though:
+ assert(toHexString!(Order.decreasing)(crc32) == "414FA339");
+ assert(toHexString!(LetterCase.lower, Order.decreasing)(crc32) == "414fa339");
+}
+
+///
+@safe unittest
+{
+ import std.digest.crc;
+ // With OOP API
+ auto crc32 = (new CRC32Digest()).digest("The quick ", "brown ", "fox jumps over the lazy dog");
+ //Usually CRCs are printed in this order, though:
+ assert(toHexString!(Order.decreasing)(crc32) == "414FA339");
+}
+
+@safe unittest
+{
+ ubyte[16] data;
+ assert(toHexString(data) == "00000000000000000000000000000000");
+
+ assert(toHexString(cast(ubyte[4])[42, 43, 44, 45]) == "2A2B2C2D");
+ assert(toHexString(cast(ubyte[])[42, 43, 44, 45]) == "2A2B2C2D");
+ assert(toHexString!(Order.decreasing)(cast(ubyte[4])[42, 43, 44, 45]) == "2D2C2B2A");
+ assert(toHexString!(Order.decreasing, LetterCase.lower)(cast(ubyte[4])[42, 43, 44, 45]) == "2d2c2b2a");
+ assert(toHexString!(Order.decreasing)(cast(ubyte[])[42, 43, 44, 45]) == "2D2C2B2A");
+}
+
+/*+*********************** End of public helper part, private helpers follow ***********************/
+
+/*
+ * Used to convert from a ubyte[] slice to a ref ubyte[N].
+ * This helper is used internally in the WrapperDigest template to wrap the template API's
+ * finish function.
+ */
+ref T[N] asArray(size_t N, T)(ref T[] source, string errorMsg = "")
+{
+ assert(source.length >= N, errorMsg);
+ return *cast(T[N]*) source.ptr;
+}
+
+/*
+ * Returns the length (in bytes) of the hash value produced by 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
+{
+ protected:
+ T _digest;
+
+ public final:
+ /**
+ * Initializes the digest.
+ */
+ this()
+ {
+ _digest.start();
+ }
+
+ /**
+ * Use this to feed the digest with data.
+ * Also implements the $(REF isOutputRange, std,range,primitives)
+ * interface for $(D ubyte) and $(D const(ubyte)[]).
+ */
+ @trusted nothrow void put(scope const(ubyte)[] data...)
+ {
+ _digest.put(data);
+ }
+
+ /**
+ * Resets the internal state of the digest.
+ * Note:
+ * $(LREF finish) calls this internally, so it's not necessary to call
+ * $(D reset) manually after a call to $(LREF finish).
+ */
+ @trusted nothrow void reset()
+ {
+ _digest.start();
+ }
+
+ /**
+ * This is the length in bytes of the hash value which is returned by $(LREF finish).
+ * It's also the required size of a buffer passed to $(LREF finish).
+ */
+ @trusted nothrow @property size_t length() const pure
+ {
+ return digestLength!T;
+ }
+
+ /**
+ * 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.
+ *
+ * Example:
+ * --------
+ *
+ * import std.digest.md;
+ * ubyte[16] buf;
+ * auto hash = new WrapperDigest!MD5();
+ * 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(ubyte[] buf)
+ in
+ {
+ assert(buf.length >= this.length);
+ }
+ body
+ {
+ enum string msg = "Buffer needs to be at least " ~ digestLength!(T).stringof ~ " bytes " ~
+ "big, check " ~ typeof(this).stringof ~ ".length!";
+ asArray!(digestLength!T)(buf, msg) = _digest.finish();
+ return buf[0 .. digestLength!T];
+ }
+
+ ///ditto
+ @trusted nothrow ubyte[] finish()
+ {
+ enum len = digestLength!T;
+ auto buf = new ubyte[len];
+ asArray!(digestLength!T)(buf) = _digest.finish();
+ return buf;
+ }
+
+ version (StdDdoc)
+ {
+ /**
+ * Works like $(D finish) but does not reset the internal state, so it's possible
+ * to continue putting data into this WrapperDigest after a call to peek.
+ *
+ * These functions are only available if $(D hasPeek!T) is true.
+ */
+ @trusted ubyte[] peek(ubyte[] buf) const;
+ ///ditto
+ @trusted ubyte[] peek() const;
+ }
+ else static if (hasPeek!T)
+ {
+ @trusted ubyte[] peek(ubyte[] buf) const
+ in
+ {
+ assert(buf.length >= this.length);
+ }
+ body
+ {
+ enum string msg = "Buffer needs to be at least " ~ digestLength!(T).stringof ~ " bytes " ~
+ "big, check " ~ typeof(this).stringof ~ ".length!";
+ asArray!(digestLength!T)(buf, msg) = _digest.peek();
+ return buf[0 .. digestLength!T];
+ }
+
+ @trusted ubyte[] peek() const
+ {
+ enum len = digestLength!T;
+ auto buf = new ubyte[len];
+ asArray!(digestLength!T)(buf) = _digest.peek();
+ return buf;
+ }
+ }
+}
+
+///
+@system unittest
+{
+ import std.digest.md;
+ //Simple example
+ auto hash = new WrapperDigest!MD5();
+ hash.put(cast(ubyte) 0);
+ auto result = hash.finish();
+}
+
+///
+@system unittest
+{
+ //using a supplied buffer
+ import std.digest.md;
+ ubyte[16] buf;
+ auto hash = new WrapperDigest!MD5();
+ 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/libphobos/src/std/digest/ripemd.d b/libphobos/src/std/digest/ripemd.d
new file mode 100644
index 0000000..c47a741
--- /dev/null
+++ b/libphobos/src/std/digest/ripemd.d
@@ -0,0 +1,762 @@
+/**
+ * Computes RIPEMD-160 hashes of arbitrary data. RIPEMD-160 hashes are 20 byte quantities
+ * that are like a checksum or CRC, but are more robust.
+ *
+$(SCRIPT inhibitQuickIndex = 1;)
+
+$(DIVC quickindex,
+$(BOOKTABLE ,
+$(TR $(TH Category) $(TH Functions)
+)
+$(TR $(TDNW Template API) $(TD $(MYREF RIPEMD160)
+)
+)
+$(TR $(TDNW OOP API) $(TD $(MYREF RIPEMD160Digest))
+)
+$(TR $(TDNW Helpers) $(TD $(MYREF ripemd160Of))
+)
+)
+)
+
+ * This module conforms to the APIs defined in $(MREF std, digest). To understand the
+ * differences between the template and the OOP API, see $(MREF std, digest).
+ *
+ * This module publicly imports $(D std.digest) and can be used as a stand-alone
+ * module.
+ *
+ * License: $(HTTP www.boost.org/LICENSE_1_0.txt, Boost License 1.0).
+ *
+ * CTFE:
+ * Digests do not work in CTFE
+ *
+ * Authors:
+ * Kai Nacke $(BR)
+ * The algorithm was designed by Hans Dobbertin, Antoon Bosselaers, and Bart Preneel. $(BR)
+ * The D implementation is a direct translation of the ANSI C implementation by Antoon Bosselaers.
+ *
+ * References:
+ * $(UL
+ * $(LI $(LINK2 http://homes.esat.kuleuven.be/~bosselae/ripemd160.html, The hash function RIPEMD-160))
+ * $(LI $(LINK2 http://en.wikipedia.org/wiki/RIPEMD-160, Wikipedia on RIPEMD-160))
+ * )
+ *
+ * Source: $(PHOBOSSRC std/digest/_ripemd.d)
+ *
+ */
+
+module std.digest.ripemd;
+
+public import std.digest;
+
+///
+@safe unittest
+{
+ //Template API
+ import std.digest.md;
+
+ ubyte[20] hash = ripemd160Of("abc");
+ assert(toHexString(hash) == "8EB208F7E05D987A9B044A8E98C6B087F15A0BFC");
+
+ //Feeding data
+ ubyte[1024] data;
+ RIPEMD160 md;
+ md.start();
+ md.put(data[]);
+ md.start(); //Start again
+ md.put(data[]);
+ hash = md.finish();
+}
+
+///
+@safe unittest
+{
+ //OOP API
+ import std.digest.md;
+
+ auto md = new RIPEMD160Digest();
+ ubyte[] hash = md.digest("abc");
+ assert(toHexString(hash) == "8EB208F7E05D987A9B044A8E98C6B087F15A0BFC");
+
+ //Feeding data
+ ubyte[1024] data;
+ md.put(data[]);
+ md.reset(); //Start again
+ md.put(data[]);
+ hash = md.finish();
+}
+
+//rotateLeft rotates x left n bits
+private uint rotateLeft(uint x, uint n) @safe pure nothrow @nogc
+{
+ // With recently added optimization to DMD (commit 32ea0206 at 07/28/11), this is translated to rol.
+ // No assembler required.
+ return (x << n) | (x >> (32-n));
+}
+
+/**
+ * Template API RIPEMD160 implementation.
+ * See $(D std.digest) for differences between template and OOP API.
+ */
+struct RIPEMD160
+{
+ private:
+ // magic initialization constants
+ uint[5] _state = [0x67452301,0xefcdab89,0x98badcfe,0x10325476,0xc3d2e1f0]; // state (ABCDE)
+ ulong _count; //number of bits, modulo 2^64
+ ubyte[64] _buffer; // input buffer
+
+ static immutable ubyte[64] _padding =
+ [
+ 0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
+ ];
+
+ // F, G, H, I and J are basic RIPEMD160 functions
+ static @safe pure nothrow @nogc
+ {
+ uint F(uint x, uint y, uint z) { return x ^ y ^ z; }
+ uint G(uint x, uint y, uint z) { return (x & y) | (~x & z); }
+ uint H(uint x, uint y, uint z) { return (x | ~y) ^ z; }
+ uint I(uint x, uint y, uint z) { return (x & z) | (y & ~z); }
+ uint J(uint x, uint y, uint z) { return x ^ (y | ~z); }
+ }
+
+ /*
+ * FF, GG, HH, and II transformations for rounds 1, 2, 3, and 4.
+ * Rotation is separate from addition to prevent recomputation.
+ */
+
+ /* the ten basic operations FF() through III() */
+ static void FF(ref uint a, uint b, ref uint c, uint d, uint e, uint x, uint s)
+ @safe pure nothrow @nogc
+ {
+ a += F(b, c, d) + x;
+ a = rotateLeft(a, s) + e;
+ c = rotateLeft(c, 10);
+ }
+
+ static void GG(ref uint a, uint b, ref uint c, uint d, uint e, uint x, uint s)
+ @safe pure nothrow @nogc
+ {
+ a += G(b, c, d) + x + 0x5a827999UL;
+ a = rotateLeft(a, s) + e;
+ c = rotateLeft(c, 10);
+ }
+
+ static void HH(ref uint a, uint b, ref uint c, uint d, uint e, uint x, uint s)
+ @safe pure nothrow @nogc
+ {
+ a += H(b, c, d) + x + 0x6ed9eba1UL;
+ a = rotateLeft(a, s) + e;
+ c = rotateLeft(c, 10);
+ }
+
+ static void II(ref uint a, uint b, ref uint c, uint d, uint e, uint x, uint s)
+ @safe pure nothrow @nogc
+ {
+ a += I(b, c, d) + x + 0x8f1bbcdcUL;
+ a = rotateLeft(a, s) + e;
+ c = rotateLeft(c, 10);
+ }
+
+ static void JJ(ref uint a, uint b, ref uint c, uint d, uint e, uint x, uint s)
+ @safe pure nothrow @nogc
+ {
+ a += J(b, c, d) + x + 0xa953fd4eUL;
+ a = rotateLeft(a, s) + e;
+ c = rotateLeft(c, 10);
+ }
+
+ /*
+ * FFF, GGG, HHH, and III transformations for parallel rounds 1, 2, 3, and 4.
+ * Rotation is separate from addition to prevent recomputation.
+ */
+
+ static void FFF(ref uint a, uint b, ref uint c, uint d, uint e, uint x, uint s)
+ @safe pure nothrow @nogc
+ {
+ a += F(b, c, d) + x;
+ a = rotateLeft(a, s) + e;
+ c = rotateLeft(c, 10);
+ }
+
+ static void GGG(ref uint a, uint b, ref uint c, uint d, uint e, uint x, uint s)
+ @safe pure nothrow @nogc
+ {
+ a += G(b, c, d) + x + 0x7a6d76e9UL;
+ a = rotateLeft(a, s) + e;
+ c = rotateLeft(c, 10);
+ }
+
+ static void HHH(ref uint a, uint b, ref uint c, uint d, uint e, uint x, uint s)
+ @safe pure nothrow @nogc
+ {
+ a += H(b, c, d) + x + 0x6d703ef3UL;
+ a = rotateLeft(a, s) + e;
+ c = rotateLeft(c, 10);
+ }
+
+ static void III(ref uint a, uint b, ref uint c, uint d, uint e, uint x, uint s)
+ @safe pure nothrow @nogc
+ {
+ a += I(b, c, d) + x + 0x5c4dd124UL;
+ a = rotateLeft(a, s) + e;
+ c = rotateLeft(c, 10);
+ }
+
+ static void JJJ(ref uint a, uint b, ref uint c, uint d, uint e, uint x, uint s)
+ @safe pure nothrow @nogc
+ {
+ a += J(b, c, d) + x + 0x50a28be6UL;
+ a = rotateLeft(a, s) + e;
+ c = rotateLeft(c, 10);
+ }
+
+ /*
+ * RIPEMD160 basic transformation. Transforms state based on block.
+ */
+
+ private void transform(const(ubyte[64])* block)
+ pure nothrow @nogc
+ {
+ uint aa = _state[0],
+ bb = _state[1],
+ cc = _state[2],
+ dd = _state[3],
+ ee = _state[4];
+ uint aaa = _state[0],
+ bbb = _state[1],
+ ccc = _state[2],
+ ddd = _state[3],
+ eee = _state[4];
+
+ uint[16] x = void;
+
+ version (BigEndian)
+ {
+ 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];
+ }
+
+ /* round 1 */
+ FF(aa, bb, cc, dd, ee, x[ 0], 11);
+ FF(ee, aa, bb, cc, dd, x[ 1], 14);
+ FF(dd, ee, aa, bb, cc, x[ 2], 15);
+ FF(cc, dd, ee, aa, bb, x[ 3], 12);
+ FF(bb, cc, dd, ee, aa, x[ 4], 5);
+ FF(aa, bb, cc, dd, ee, x[ 5], 8);
+ FF(ee, aa, bb, cc, dd, x[ 6], 7);
+ FF(dd, ee, aa, bb, cc, x[ 7], 9);
+ FF(cc, dd, ee, aa, bb, x[ 8], 11);
+ FF(bb, cc, dd, ee, aa, x[ 9], 13);
+ FF(aa, bb, cc, dd, ee, x[10], 14);
+ FF(ee, aa, bb, cc, dd, x[11], 15);
+ FF(dd, ee, aa, bb, cc, x[12], 6);
+ FF(cc, dd, ee, aa, bb, x[13], 7);
+ FF(bb, cc, dd, ee, aa, x[14], 9);
+ FF(aa, bb, cc, dd, ee, x[15], 8);
+
+ /* round 2 */
+ GG(ee, aa, bb, cc, dd, x[ 7], 7);
+ GG(dd, ee, aa, bb, cc, x[ 4], 6);
+ GG(cc, dd, ee, aa, bb, x[13], 8);
+ GG(bb, cc, dd, ee, aa, x[ 1], 13);
+ GG(aa, bb, cc, dd, ee, x[10], 11);
+ GG(ee, aa, bb, cc, dd, x[ 6], 9);
+ GG(dd, ee, aa, bb, cc, x[15], 7);
+ GG(cc, dd, ee, aa, bb, x[ 3], 15);
+ GG(bb, cc, dd, ee, aa, x[12], 7);
+ GG(aa, bb, cc, dd, ee, x[ 0], 12);
+ GG(ee, aa, bb, cc, dd, x[ 9], 15);
+ GG(dd, ee, aa, bb, cc, x[ 5], 9);
+ GG(cc, dd, ee, aa, bb, x[ 2], 11);
+ GG(bb, cc, dd, ee, aa, x[14], 7);
+ GG(aa, bb, cc, dd, ee, x[11], 13);
+ GG(ee, aa, bb, cc, dd, x[ 8], 12);
+
+ /* round 3 */
+ HH(dd, ee, aa, bb, cc, x[ 3], 11);
+ HH(cc, dd, ee, aa, bb, x[10], 13);
+ HH(bb, cc, dd, ee, aa, x[14], 6);
+ HH(aa, bb, cc, dd, ee, x[ 4], 7);
+ HH(ee, aa, bb, cc, dd, x[ 9], 14);
+ HH(dd, ee, aa, bb, cc, x[15], 9);
+ HH(cc, dd, ee, aa, bb, x[ 8], 13);
+ HH(bb, cc, dd, ee, aa, x[ 1], 15);
+ HH(aa, bb, cc, dd, ee, x[ 2], 14);
+ HH(ee, aa, bb, cc, dd, x[ 7], 8);
+ HH(dd, ee, aa, bb, cc, x[ 0], 13);
+ HH(cc, dd, ee, aa, bb, x[ 6], 6);
+ HH(bb, cc, dd, ee, aa, x[13], 5);
+ HH(aa, bb, cc, dd, ee, x[11], 12);
+ HH(ee, aa, bb, cc, dd, x[ 5], 7);
+ HH(dd, ee, aa, bb, cc, x[12], 5);
+
+ /* round 4 */
+ II(cc, dd, ee, aa, bb, x[ 1], 11);
+ II(bb, cc, dd, ee, aa, x[ 9], 12);
+ II(aa, bb, cc, dd, ee, x[11], 14);
+ II(ee, aa, bb, cc, dd, x[10], 15);
+ II(dd, ee, aa, bb, cc, x[ 0], 14);
+ II(cc, dd, ee, aa, bb, x[ 8], 15);
+ II(bb, cc, dd, ee, aa, x[12], 9);
+ II(aa, bb, cc, dd, ee, x[ 4], 8);
+ II(ee, aa, bb, cc, dd, x[13], 9);
+ II(dd, ee, aa, bb, cc, x[ 3], 14);
+ II(cc, dd, ee, aa, bb, x[ 7], 5);
+ II(bb, cc, dd, ee, aa, x[15], 6);
+ II(aa, bb, cc, dd, ee, x[14], 8);
+ II(ee, aa, bb, cc, dd, x[ 5], 6);
+ II(dd, ee, aa, bb, cc, x[ 6], 5);
+ II(cc, dd, ee, aa, bb, x[ 2], 12);
+
+ /* round 5 */
+ JJ(bb, cc, dd, ee, aa, x[ 4], 9);
+ JJ(aa, bb, cc, dd, ee, x[ 0], 15);
+ JJ(ee, aa, bb, cc, dd, x[ 5], 5);
+ JJ(dd, ee, aa, bb, cc, x[ 9], 11);
+ JJ(cc, dd, ee, aa, bb, x[ 7], 6);
+ JJ(bb, cc, dd, ee, aa, x[12], 8);
+ JJ(aa, bb, cc, dd, ee, x[ 2], 13);
+ JJ(ee, aa, bb, cc, dd, x[10], 12);
+ JJ(dd, ee, aa, bb, cc, x[14], 5);
+ JJ(cc, dd, ee, aa, bb, x[ 1], 12);
+ JJ(bb, cc, dd, ee, aa, x[ 3], 13);
+ JJ(aa, bb, cc, dd, ee, x[ 8], 14);
+ JJ(ee, aa, bb, cc, dd, x[11], 11);
+ JJ(dd, ee, aa, bb, cc, x[ 6], 8);
+ JJ(cc, dd, ee, aa, bb, x[15], 5);
+ JJ(bb, cc, dd, ee, aa, x[13], 6);
+
+ /* parallel round 1 */
+ JJJ(aaa, bbb, ccc, ddd, eee, x[ 5], 8);
+ JJJ(eee, aaa, bbb, ccc, ddd, x[14], 9);
+ JJJ(ddd, eee, aaa, bbb, ccc, x[ 7], 9);
+ JJJ(ccc, ddd, eee, aaa, bbb, x[ 0], 11);
+ JJJ(bbb, ccc, ddd, eee, aaa, x[ 9], 13);
+ JJJ(aaa, bbb, ccc, ddd, eee, x[ 2], 15);
+ JJJ(eee, aaa, bbb, ccc, ddd, x[11], 15);
+ JJJ(ddd, eee, aaa, bbb, ccc, x[ 4], 5);
+ JJJ(ccc, ddd, eee, aaa, bbb, x[13], 7);
+ JJJ(bbb, ccc, ddd, eee, aaa, x[ 6], 7);
+ JJJ(aaa, bbb, ccc, ddd, eee, x[15], 8);
+ JJJ(eee, aaa, bbb, ccc, ddd, x[ 8], 11);
+ JJJ(ddd, eee, aaa, bbb, ccc, x[ 1], 14);
+ JJJ(ccc, ddd, eee, aaa, bbb, x[10], 14);
+ JJJ(bbb, ccc, ddd, eee, aaa, x[ 3], 12);
+ JJJ(aaa, bbb, ccc, ddd, eee, x[12], 6);
+
+ /* parallel round 2 */
+ III(eee, aaa, bbb, ccc, ddd, x[ 6], 9);
+ III(ddd, eee, aaa, bbb, ccc, x[11], 13);
+ III(ccc, ddd, eee, aaa, bbb, x[ 3], 15);
+ III(bbb, ccc, ddd, eee, aaa, x[ 7], 7);
+ III(aaa, bbb, ccc, ddd, eee, x[ 0], 12);
+ III(eee, aaa, bbb, ccc, ddd, x[13], 8);
+ III(ddd, eee, aaa, bbb, ccc, x[ 5], 9);
+ III(ccc, ddd, eee, aaa, bbb, x[10], 11);
+ III(bbb, ccc, ddd, eee, aaa, x[14], 7);
+ III(aaa, bbb, ccc, ddd, eee, x[15], 7);
+ III(eee, aaa, bbb, ccc, ddd, x[ 8], 12);
+ III(ddd, eee, aaa, bbb, ccc, x[12], 7);
+ III(ccc, ddd, eee, aaa, bbb, x[ 4], 6);
+ III(bbb, ccc, ddd, eee, aaa, x[ 9], 15);
+ III(aaa, bbb, ccc, ddd, eee, x[ 1], 13);
+ III(eee, aaa, bbb, ccc, ddd, x[ 2], 11);
+
+ /* parallel round 3 */
+ HHH(ddd, eee, aaa, bbb, ccc, x[15], 9);
+ HHH(ccc, ddd, eee, aaa, bbb, x[ 5], 7);
+ HHH(bbb, ccc, ddd, eee, aaa, x[ 1], 15);
+ HHH(aaa, bbb, ccc, ddd, eee, x[ 3], 11);
+ HHH(eee, aaa, bbb, ccc, ddd, x[ 7], 8);
+ HHH(ddd, eee, aaa, bbb, ccc, x[14], 6);
+ HHH(ccc, ddd, eee, aaa, bbb, x[ 6], 6);
+ HHH(bbb, ccc, ddd, eee, aaa, x[ 9], 14);
+ HHH(aaa, bbb, ccc, ddd, eee, x[11], 12);
+ HHH(eee, aaa, bbb, ccc, ddd, x[ 8], 13);
+ HHH(ddd, eee, aaa, bbb, ccc, x[12], 5);
+ HHH(ccc, ddd, eee, aaa, bbb, x[ 2], 14);
+ HHH(bbb, ccc, ddd, eee, aaa, x[10], 13);
+ HHH(aaa, bbb, ccc, ddd, eee, x[ 0], 13);
+ HHH(eee, aaa, bbb, ccc, ddd, x[ 4], 7);
+ HHH(ddd, eee, aaa, bbb, ccc, x[13], 5);
+
+ /* parallel round 4 */
+ GGG(ccc, ddd, eee, aaa, bbb, x[ 8], 15);
+ GGG(bbb, ccc, ddd, eee, aaa, x[ 6], 5);
+ GGG(aaa, bbb, ccc, ddd, eee, x[ 4], 8);
+ GGG(eee, aaa, bbb, ccc, ddd, x[ 1], 11);
+ GGG(ddd, eee, aaa, bbb, ccc, x[ 3], 14);
+ GGG(ccc, ddd, eee, aaa, bbb, x[11], 14);
+ GGG(bbb, ccc, ddd, eee, aaa, x[15], 6);
+ GGG(aaa, bbb, ccc, ddd, eee, x[ 0], 14);
+ GGG(eee, aaa, bbb, ccc, ddd, x[ 5], 6);
+ GGG(ddd, eee, aaa, bbb, ccc, x[12], 9);
+ GGG(ccc, ddd, eee, aaa, bbb, x[ 2], 12);
+ GGG(bbb, ccc, ddd, eee, aaa, x[13], 9);
+ GGG(aaa, bbb, ccc, ddd, eee, x[ 9], 12);
+ GGG(eee, aaa, bbb, ccc, ddd, x[ 7], 5);
+ GGG(ddd, eee, aaa, bbb, ccc, x[10], 15);
+ GGG(ccc, ddd, eee, aaa, bbb, x[14], 8);
+
+ /* parallel round 5 */
+ FFF(bbb, ccc, ddd, eee, aaa, x[12] , 8);
+ FFF(aaa, bbb, ccc, ddd, eee, x[15] , 5);
+ FFF(eee, aaa, bbb, ccc, ddd, x[10] , 12);
+ FFF(ddd, eee, aaa, bbb, ccc, x[ 4] , 9);
+ FFF(ccc, ddd, eee, aaa, bbb, x[ 1] , 12);
+ FFF(bbb, ccc, ddd, eee, aaa, x[ 5] , 5);
+ FFF(aaa, bbb, ccc, ddd, eee, x[ 8] , 14);
+ FFF(eee, aaa, bbb, ccc, ddd, x[ 7] , 6);
+ FFF(ddd, eee, aaa, bbb, ccc, x[ 6] , 8);
+ FFF(ccc, ddd, eee, aaa, bbb, x[ 2] , 13);
+ FFF(bbb, ccc, ddd, eee, aaa, x[13] , 6);
+ FFF(aaa, bbb, ccc, ddd, eee, x[14] , 5);
+ FFF(eee, aaa, bbb, ccc, ddd, x[ 0] , 15);
+ FFF(ddd, eee, aaa, bbb, ccc, x[ 3] , 13);
+ FFF(ccc, ddd, eee, aaa, bbb, x[ 9] , 11);
+ FFF(bbb, ccc, ddd, eee, aaa, x[11] , 11);
+
+ /* combine results */
+ ddd += cc + _state[1]; /* final result for _state[0] */
+ _state[1] = _state[2] + dd + eee;
+ _state[2] = _state[3] + ee + aaa;
+ _state[3] = _state[4] + aa + bbb;
+ _state[4] = _state[0] + bb + ccc;
+ _state[0] = ddd;
+
+ //Zeroize sensitive information.
+ x[] = 0;
+ }
+
+ public:
+ enum blockSize = 512;
+
+ /**
+ * Use this to feed the digest with data.
+ * Also implements the $(REF isOutputRange, std,range,primitives)
+ * interface for $(D ubyte) and $(D const(ubyte)[]).
+ *
+ * Example:
+ * ----
+ * RIPEMD160 dig;
+ * dig.put(cast(ubyte) 0); //single ubyte
+ * dig.put(cast(ubyte) 0, cast(ubyte) 0); //variadic
+ * ubyte[10] buf;
+ * dig.put(buf); //buffer
+ * ----
+ */
+ void put(scope const(ubyte)[] data...) @trusted pure nothrow @nogc
+ {
+ uint i, index, partLen;
+ auto inputLen = data.length;
+
+ //Compute number of bytes mod 64
+ index = (cast(uint)_count >> 3) & (64 - 1);
+
+ //Update number of bits
+ _count += inputLen * 8;
+
+ partLen = 64 - index;
+
+ //Transform as many times as possible
+ if (inputLen >= partLen)
+ {
+ (&_buffer[index])[0 .. partLen] = data.ptr[0 .. partLen];
+ transform(&_buffer);
+
+ for (i = partLen; i + 63 < inputLen; i += 64)
+ {
+ transform(cast(const(ubyte[64])*)(data[i .. i + 64].ptr));
+ }
+
+ index = 0;
+ }
+ else
+ {
+ i = 0;
+ }
+
+ /* Buffer remaining input */
+ if (inputLen - i)
+ (&_buffer[index])[0 .. inputLen-i] = (&data[i])[0 .. inputLen-i];
+ }
+
+ /**
+ * Used to (re)initialize the RIPEMD160 digest.
+ *
+ * Note:
+ * For this RIPEMD160 Digest implementation calling start after default construction
+ * is not necessary. Calling start is only necessary to reset the Digest.
+ *
+ * Generic code which deals with different Digest types should always call start though.
+ *
+ * Example:
+ * --------
+ * RIPEMD160 digest;
+ * //digest.start(); //Not necessary
+ * digest.put(0);
+ * --------
+ */
+ void start() @safe pure nothrow @nogc
+ {
+ this = RIPEMD160.init;
+ }
+
+ /**
+ * Returns the finished RIPEMD160 hash. This also calls $(LREF start) to
+ * reset the internal state.
+ *
+ * Example:
+ * --------
+ * //Simple example
+ * RIPEMD160 hash;
+ * hash.start();
+ * hash.put(cast(ubyte) 0);
+ * ubyte[20] result = hash.finish();
+ * assert(toHexString(result) == "C81B94933420221A7AC004A90242D8B1D3E5070D");
+ * --------
+ */
+ ubyte[20] finish() @trusted pure nothrow @nogc
+ {
+ import std.bitmanip : nativeToLittleEndian;
+
+ ubyte[20] data = void;
+ ubyte[8] bits = void;
+ uint index, padLen;
+
+ //Save number of bits
+ bits[0 .. 8] = nativeToLittleEndian(_count)[];
+
+ //Pad out to 56 mod 64
+ index = (cast(uint)_count >> 3) & (64 - 1);
+ padLen = (index < 56) ? (56 - index) : (120 - index);
+ put(_padding[0 .. padLen]);
+
+ //Append length (before padding)
+ put(bits);
+
+ //Store state in digest
+ data[0 .. 4] = nativeToLittleEndian(_state[0])[];
+ data[4 .. 8] = nativeToLittleEndian(_state[1])[];
+ data[8 .. 12] = nativeToLittleEndian(_state[2])[];
+ data[12 .. 16] = nativeToLittleEndian(_state[3])[];
+ data[16 .. 20] = nativeToLittleEndian(_state[4])[];
+
+ /* Zeroize sensitive information. */
+ start();
+ return data;
+ }
+}
+
+///
+@safe unittest
+{
+ //Simple example, hashing a string using ripemd160Of helper function
+ ubyte[20] hash = ripemd160Of("abc");
+ //Let's get a hash string
+ assert(toHexString(hash) == "8EB208F7E05D987A9B044A8E98C6B087F15A0BFC");
+}
+
+///
+@safe unittest
+{
+ //Using the basic API
+ RIPEMD160 hash;
+ hash.start();
+ ubyte[1024] data;
+ //Initialize data here...
+ hash.put(data);
+ ubyte[20] result = hash.finish();
+}
+
+///
+@safe unittest
+{
+ //Let's use the template features:
+ void doSomething(T)(ref T hash)
+ if (isDigest!T)
+ {
+ hash.put(cast(ubyte) 0);
+ }
+ RIPEMD160 md;
+ md.start();
+ doSomething(md);
+ assert(toHexString(md.finish()) == "C81B94933420221A7AC004A90242D8B1D3E5070D");
+}
+
+///
+@safe unittest
+{
+ //Simple example
+ RIPEMD160 hash;
+ hash.start();
+ hash.put(cast(ubyte) 0);
+ ubyte[20] result = hash.finish();
+ assert(toHexString(result) == "C81B94933420221A7AC004A90242D8B1D3E5070D");
+}
+
+@safe unittest
+{
+ assert(isDigest!RIPEMD160);
+}
+
+@system unittest
+{
+ import std.range;
+
+ ubyte[20] digest;
+
+ RIPEMD160 md;
+ md.put(cast(ubyte[])"abcdef");
+ md.start();
+ md.put(cast(ubyte[])"");
+ assert(md.finish() == cast(ubyte[]) x"9c1185a5c5e9fc54612808977ee8f548b2258d31");
+
+ digest = ripemd160Of("");
+ assert(digest == cast(ubyte[]) x"9c1185a5c5e9fc54612808977ee8f548b2258d31");
+
+ digest = ripemd160Of("a");
+ assert(digest == cast(ubyte[]) x"0bdc9d2d256b3ee9daae347be6f4dc835a467ffe");
+
+ digest = ripemd160Of("abc");
+ assert(digest == cast(ubyte[]) x"8eb208f7e05d987a9b044a8e98c6b087f15a0bfc");
+
+ digest = ripemd160Of("abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq");
+ assert(digest == cast(ubyte[]) x"12a053384a9c0c88e405a06c27dcf49ada62eb2b");
+
+ digest = ripemd160Of("message digest");
+ assert(digest == cast(ubyte[]) x"5d0689ef49d2fae572b881b123a85ffa21595f36");
+
+ digest = ripemd160Of("abcdefghijklmnopqrstuvwxyz");
+ assert(digest == cast(ubyte[]) x"f71c27109c692c1b56bbdceb5b9d2865b3708dbc");
+
+ digest = ripemd160Of("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789");
+ assert(digest == cast(ubyte[]) x"b0e20b6e3116640286ed3a87a5713079b21f5189");
+
+ digest = ripemd160Of("1234567890123456789012345678901234567890"~
+ "1234567890123456789012345678901234567890");
+ assert(digest == cast(ubyte[]) x"9b752e45573d4b39f4dbd3323cab82bf63326bfb");
+
+ assert(toHexString(cast(ubyte[20]) x"f71c27109c692c1b56bbdceb5b9d2865b3708dbc")
+ == "F71C27109C692C1B56BBDCEB5B9D2865B3708DBC");
+
+ ubyte[] onemilliona = new ubyte[1000000];
+ onemilliona[] = 'a';
+ digest = ripemd160Of(onemilliona);
+ assert(digest == cast(ubyte[]) x"52783243c1697bdbe16d37f97f68f08325dc1528");
+
+ auto oneMillionRange = repeat!ubyte(cast(ubyte)'a', 1000000);
+ digest = ripemd160Of(oneMillionRange);
+ assert(digest == cast(ubyte[]) x"52783243c1697bdbe16d37f97f68f08325dc1528");
+}
+
+/**
+ * This is a convenience alias for $(REF digest, std,digest) using the
+ * RIPEMD160 implementation.
+ */
+//simple alias doesn't work here, hope this gets inlined...
+auto ripemd160Of(T...)(T data)
+{
+ return digest!(RIPEMD160, T)(data);
+}
+
+///
+@safe unittest
+{
+ ubyte[20] hash = ripemd160Of("abc");
+ assert(hash == digest!RIPEMD160("abc"));
+}
+
+/**
+ * OOP API RIPEMD160 implementation.
+ * See $(D std.digest) for differences between template and OOP API.
+ *
+ * This is an alias for $(D $(REF WrapperDigest, std,digest)!RIPEMD160),
+ * see there for more information.
+ */
+alias RIPEMD160Digest = WrapperDigest!RIPEMD160;
+
+///
+@safe unittest
+{
+ //Simple example, hashing a string using Digest.digest helper function
+ auto md = new RIPEMD160Digest();
+ ubyte[] hash = md.digest("abc");
+ //Let's get a hash string
+ assert(toHexString(hash) == "8EB208F7E05D987A9B044A8E98C6B087F15A0BFC");
+}
+
+///
+@system unittest
+{
+ //Let's use the OOP features:
+ void test(Digest dig)
+ {
+ dig.put(cast(ubyte) 0);
+ }
+ auto md = new RIPEMD160Digest();
+ test(md);
+
+ //Let's use a custom buffer:
+ ubyte[20] buf;
+ ubyte[] result = md.finish(buf[]);
+ assert(toHexString(result) == "C81B94933420221A7AC004A90242D8B1D3E5070D");
+}
+
+@system unittest
+{
+ auto md = new RIPEMD160Digest();
+
+ md.put(cast(ubyte[])"abcdef");
+ md.reset();
+ md.put(cast(ubyte[])"");
+ 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");
+
+ debug
+ {
+ import std.exception;
+ assertThrown!Error(md.finish(result[0 .. 19]));
+ }
+
+ assert(md.length == 20);
+
+ assert(md.digest("") == cast(ubyte[]) x"9c1185a5c5e9fc54612808977ee8f548b2258d31");
+
+ assert(md.digest("a") == cast(ubyte[]) x"0bdc9d2d256b3ee9daae347be6f4dc835a467ffe");
+
+ assert(md.digest("abc") == cast(ubyte[]) x"8eb208f7e05d987a9b044a8e98c6b087f15a0bfc");
+
+ assert(md.digest("abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq")
+ == cast(ubyte[]) x"12a053384a9c0c88e405a06c27dcf49ada62eb2b");
+
+ assert(md.digest("message digest") == cast(ubyte[]) x"5d0689ef49d2fae572b881b123a85ffa21595f36");
+
+ assert(md.digest("abcdefghijklmnopqrstuvwxyz")
+ == cast(ubyte[]) x"f71c27109c692c1b56bbdceb5b9d2865b3708dbc");
+
+ assert(md.digest("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789")
+ == cast(ubyte[]) x"b0e20b6e3116640286ed3a87a5713079b21f5189");
+
+ assert(md.digest("1234567890123456789012345678901234567890",
+ "1234567890123456789012345678901234567890")
+ == cast(ubyte[]) x"9b752e45573d4b39f4dbd3323cab82bf63326bfb");
+
+ assert(md.digest(new ubyte[160/8]) // 160 zero bits
+ == cast(ubyte[]) x"5c00bd4aca04a9057c09b20b05f723f2e23deb65");
+}
diff --git a/libphobos/src/std/digest/sha.d b/libphobos/src/std/digest/sha.d
new file mode 100644
index 0000000..671e07b
--- /dev/null
+++ b/libphobos/src/std/digest/sha.d
@@ -0,0 +1,1291 @@
+// Written in the D programming language.
+/**
+ * Computes SHA1 and SHA2 hashes of arbitrary data. SHA hashes are 20 to 64 byte
+ * quantities (depending on the SHA algorithm) that are like a checksum or CRC,
+ * but are more robust.
+ *
+$(SCRIPT inhibitQuickIndex = 1;)
+
+$(DIVC quickindex,
+$(BOOKTABLE ,
+$(TR $(TH Category) $(TH Functions)
+)
+$(TR $(TDNW Template API) $(TD $(MYREF SHA1)
+)
+)
+$(TR $(TDNW OOP API) $(TD $(MYREF SHA1Digest))
+)
+$(TR $(TDNW Helpers) $(TD $(MYREF sha1Of))
+)
+)
+)
+
+ * SHA2 comes in several different versions, all supported by this module:
+ * SHA-224, SHA-256, SHA-384, SHA-512, SHA-512/224 and SHA-512/256.
+ *
+ * This module conforms to the APIs defined in $(MREF std, digest). To understand the
+ * differences between the template and the OOP API, see $(MREF std, digest).
+ *
+ * This module publicly imports $(D std.digest) and can be used as a stand-alone
+ * module.
+ *
+ * License: $(HTTP www.boost.org/LICENSE_1_0.txt, Boost License 1.0).
+ *
+ * CTFE:
+ * Digests do not work in CTFE
+ *
+ * Authors:
+ * The routines and algorithms are derived from the
+ * $(I Secure Hash Signature Standard (SHS) (FIPS PUB 180-2)). $(BR )
+ * Kai Nacke, Johannes Pfau, Nick Sabalausky
+ *
+ * References:
+ * $(UL
+ * $(LI $(LINK2 http://csrc.nist.gov/publications/fips/fips180-2/fips180-2withchangenotice.pdf, FIPS PUB180-2))
+ * $(LI $(LINK2 http://software.intel.com/en-us/articles/improving-the-performance-of-the-secure-hash-algorithm-1/, Fast implementation of SHA1))
+ * $(LI $(LINK2 http://en.wikipedia.org/wiki/Secure_Hash_Algorithm, Wikipedia article about SHA))
+ * )
+ *
+ * Source: $(PHOBOSSRC std/digest/_sha.d)
+ *
+ */
+
+/* Copyright Kai Nacke 2012.
+ * 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.digest.sha;
+
+///
+@safe unittest
+{
+ //Template API
+ import std.digest.sha;
+
+ ubyte[20] hash1 = sha1Of("abc");
+ assert(toHexString(hash1) == "A9993E364706816ABA3E25717850C26C9CD0D89D");
+
+ ubyte[28] hash224 = sha224Of("abc");
+ assert(toHexString(hash224) == "23097D223405D8228642A477BDA255B32AADBCE4BDA0B3F7E36C9DA7");
+
+ //Feeding data
+ ubyte[1024] data;
+ SHA1 sha1;
+ sha1.start();
+ sha1.put(data[]);
+ sha1.start(); //Start again
+ sha1.put(data[]);
+ hash1 = sha1.finish();
+}
+
+///
+@safe unittest
+{
+ //OOP API
+ import std.digest.sha;
+
+ auto sha1 = new SHA1Digest();
+ ubyte[] hash1 = sha1.digest("abc");
+ assert(toHexString(hash1) == "A9993E364706816ABA3E25717850C26C9CD0D89D");
+
+ auto sha224 = new SHA224Digest();
+ ubyte[] hash224 = sha224.digest("abc");
+ assert(toHexString(hash224) == "23097D223405D8228642A477BDA255B32AADBCE4BDA0B3F7E36C9DA7");
+
+ //Feeding data
+ ubyte[1024] data;
+ sha1.put(data[]);
+ sha1.reset(); //Start again
+ sha1.put(data[]);
+ hash1 = sha1.finish();
+}
+
+version (Win64)
+{
+ // wrong calling convention
+}
+else version (D_InlineAsm_X86)
+{
+ version (D_PIC) {} // Bugzilla 9378
+ else private version = USE_SSSE3;
+}
+else version (D_InlineAsm_X86_64)
+{
+ private version = USE_SSSE3;
+}
+
+version (LittleEndian) import core.bitop : bswap;
+
+
+version (unittest)
+{
+ import std.exception;
+}
+
+
+public import std.digest;
+
+/*
+ * Helper methods for encoding the buffer.
+ * Can be removed if the optimizer can inline the methods from std.bitmanip.
+ */
+private ubyte[8] nativeToBigEndian(ulong val) @trusted pure nothrow @nogc
+{
+ version (LittleEndian)
+ immutable ulong res = (cast(ulong) bswap(cast(uint) val)) << 32 | bswap(cast(uint) (val >> 32));
+ else
+ immutable ulong res = val;
+ return *cast(ubyte[8]*) &res;
+}
+
+private ubyte[4] nativeToBigEndian(uint val) @trusted pure nothrow @nogc
+{
+ version (LittleEndian)
+ immutable uint res = bswap(val);
+ else
+ immutable uint res = val;
+ return *cast(ubyte[4]*) &res;
+}
+
+private ulong bigEndianToNative(ubyte[8] val) @trusted pure nothrow @nogc
+{
+ version (LittleEndian)
+ {
+ import std.bitmanip : bigEndianToNative;
+ return bigEndianToNative!ulong(val);
+ }
+ else
+ return *cast(ulong*) &val;
+}
+
+private uint bigEndianToNative(ubyte[4] val) @trusted pure nothrow @nogc
+{
+ version (LittleEndian)
+ return bswap(*cast(uint*) &val);
+ else
+ return *cast(uint*) &val;
+}
+
+//rotateLeft rotates x left n bits
+private uint rotateLeft(uint x, uint n) @safe pure nothrow @nogc
+{
+ // With recently added optimization to DMD (commit 32ea0206 at 07/28/11), this is translated to rol.
+ // No assembler required.
+ return (x << n) | (x >> (32-n));
+}
+
+//rotateRight rotates x right n bits
+private uint rotateRight(uint x, uint n) @safe pure nothrow @nogc
+{
+ return (x >> n) | (x << (32-n));
+}
+private ulong rotateRight(ulong x, uint n) @safe pure nothrow @nogc
+{
+ return (x >> n) | (x << (64-n));
+}
+
+/**
+ * Template API SHA1/SHA2 implementation. Supports: SHA-1, SHA-224, SHA-256,
+ * SHA-384, SHA-512, SHA-512/224 and SHA-512/256.
+ *
+ * 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) for differences between template and OOP API.
+ */
+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,
+ "Invalid SHA digestSize, must be 224, 256, 384 or 512");
+ static assert(!(blockSize == 512 && digestSize > 256),
+ "Invalid SHA digestSize for a blockSize of 512. The digestSize must be 160, 224 or 256.");
+ 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 */
+ {
+ version (USE_SSSE3)
+ {
+ import core.cpuid : ssse3;
+ import std.internal.digest.sha_SSSE3 : sse3_constants=constants, transformSSSE3;
+
+ static void transform(uint[5]* state, const(ubyte[64])* block) pure nothrow @nogc
+ {
+ 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
+ {
+ alias transform = transformX86;
+ }
+ }
+ 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 */
+ alias transform = transformSHA2!ulong;
+ else
+ static assert(0);
+
+ private:
+ /* magic initialization constants - state (ABCDEFGH) */
+ 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 */
+ {
+ uint[8] state = [
+ 0xc1059ed8, 0x367cd507, 0x3070dd17, 0xf70e5939,
+ 0xffc00b31, 0x68581511, 0x64f98fa7, 0xbefa4fa4,
+ ];
+ }
+ 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 */
+ {
+ ulong[8] state = [
+ 0x8C3D37C8_19544DA2, 0x73E19966_89DCD4D6,
+ 0x1DFAB7AE_32FF9C82, 0x679DD514_582F9FCF,
+ 0x0F6D2B69_7BD44DA8, 0x77E36F73_04C48942,
+ 0x3F9D85A8_6A1D36C8, 0x1112E6AD_91D692A1,
+ ];
+ }
+ else static if (blockSize == 1024 && digestSize == 256) /* SHA-512/256 */
+ {
+ ulong[8] state = [
+ 0x22312194_FC2BF72C, 0x9F555FA3_C84C64C2,
+ 0x2393B86B_6F53B151, 0x96387719_5940EABD,
+ 0x96283EE2_A88EFFE3, 0xBE5E1E25_53863992,
+ 0x2B0199FC_2C85B8AA, 0x0EB72DDC_81C52CA2,
+ ];
+ }
+ else static if (blockSize == 1024 && digestSize == 384) /* SHA-384 */
+ {
+ ulong[8] state = [
+ 0xcbbb9d5d_c1059ed8, 0x629a292a_367cd507,
+ 0x9159015a_3070dd17, 0x152fecd8_f70e5939,
+ 0x67332667_ffc00b31, 0x8eb44a87_68581511,
+ 0xdb0c2e0d_64f98fa7, 0x47b5481d_befa4fa4,
+ ];
+ }
+ else static if (blockSize == 1024 && digestSize == 512) /* SHA-512 */
+ {
+ ulong[8] state = [
+ 0x6a09e667_f3bcc908, 0xbb67ae85_84caa73b,
+ 0x3c6ef372_fe94f82b, 0xa54ff53a_5f1d36f1,
+ 0x510e527f_ade682d1, 0x9b05688c_2b3e6c1f,
+ 0x1f83d9ab_fb41bd6b, 0x5be0cd19_137e2179,
+ ];
+ }
+ else
+ static assert(0);
+
+ /* constants */
+ static if (blockSize == 512)
+ {
+ static immutable uint[64] constants = [
+ 0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5, 0x3956c25b, 0x59f111f1, 0x923f82a4, 0xab1c5ed5,
+ 0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3, 0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174,
+ 0xe49b69c1, 0xefbe4786, 0x0fc19dc6, 0x240ca1cc, 0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da,
+ 0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7, 0xc6e00bf3, 0xd5a79147, 0x06ca6351, 0x14292967,
+ 0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13, 0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85,
+ 0xa2bfe8a1, 0xa81a664b, 0xc24b8b70, 0xc76c51a3, 0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070,
+ 0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5, 0x391c0cb3, 0x4ed8aa4a, 0x5b9cca4f, 0x682e6ff3,
+ 0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208, 0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2,
+ ];
+ }
+ else static if (blockSize == 1024)
+ {
+ static immutable ulong[80] constants = [
+ 0x428a2f98_d728ae22, 0x71374491_23ef65cd, 0xb5c0fbcf_ec4d3b2f, 0xe9b5dba5_8189dbbc,
+ 0x3956c25b_f348b538, 0x59f111f1_b605d019, 0x923f82a4_af194f9b, 0xab1c5ed5_da6d8118,
+ 0xd807aa98_a3030242, 0x12835b01_45706fbe, 0x243185be_4ee4b28c, 0x550c7dc3_d5ffb4e2,
+ 0x72be5d74_f27b896f, 0x80deb1fe_3b1696b1, 0x9bdc06a7_25c71235, 0xc19bf174_cf692694,
+ 0xe49b69c1_9ef14ad2, 0xefbe4786_384f25e3, 0x0fc19dc6_8b8cd5b5, 0x240ca1cc_77ac9c65,
+ 0x2de92c6f_592b0275, 0x4a7484aa_6ea6e483, 0x5cb0a9dc_bd41fbd4, 0x76f988da_831153b5,
+ 0x983e5152_ee66dfab, 0xa831c66d_2db43210, 0xb00327c8_98fb213f, 0xbf597fc7_beef0ee4,
+ 0xc6e00bf3_3da88fc2, 0xd5a79147_930aa725, 0x06ca6351_e003826f, 0x14292967_0a0e6e70,
+ 0x27b70a85_46d22ffc, 0x2e1b2138_5c26c926, 0x4d2c6dfc_5ac42aed, 0x53380d13_9d95b3df,
+ 0x650a7354_8baf63de, 0x766a0abb_3c77b2a8, 0x81c2c92e_47edaee6, 0x92722c85_1482353b,
+ 0xa2bfe8a1_4cf10364, 0xa81a664b_bc423001, 0xc24b8b70_d0f89791, 0xc76c51a3_0654be30,
+ 0xd192e819_d6ef5218, 0xd6990624_5565a910, 0xf40e3585_5771202a, 0x106aa070_32bbd1b8,
+ 0x19a4c116_b8d2d0c8, 0x1e376c08_5141ab53, 0x2748774c_df8eeb99, 0x34b0bcb5_e19b48a8,
+ 0x391c0cb3_c5c95a63, 0x4ed8aa4a_e3418acb, 0x5b9cca4f_7763e373, 0x682e6ff3_d6b2b8a3,
+ 0x748f82ee_5defb2fc, 0x78a5636f_43172f60, 0x84c87814_a1f0ab72, 0x8cc70208_1a6439ec,
+ 0x90befffa_23631e28, 0xa4506ceb_de82bde9, 0xbef9a3f7_b2c67915, 0xc67178f2_e372532b,
+ 0xca273ece_ea26619c, 0xd186b8c7_21c0c207, 0xeada7dd6_cde0eb1e, 0xf57d4f7f_ee6ed178,
+ 0x06f067aa_72176fba, 0x0a637dc5_a2c898a6, 0x113f9804_bef90dae, 0x1b710b35_131c471b,
+ 0x28db77f5_23047d84, 0x32caab7b_40c72493, 0x3c9ebe0a_15c9bebc, 0x431d67c4_9c100d4c,
+ 0x4cc5d4be_cb3e42b6, 0x597f299c_fc657e2a, 0x5fcb6fab_3ad6faec, 0x6c44198c_4a475817,
+ ];
+ }
+ else
+ static assert(0);
+
+ /*
+ * number of bits, modulo 2^64 (ulong[1]) or 2^128 (ulong[2]),
+ * should just use ucent instead of ulong[2] once it's available
+ */
+ ulong[blockSize/512] count;
+ ubyte[blockSize/8] buffer; /* input buffer */
+
+ static immutable ubyte[128] padding =
+ [
+ 0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+
+ 0x00, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
+ ];
+
+ /*
+ * Basic SHA1/SHA2 functions.
+ */
+ static @safe pure nothrow @nogc
+ {
+ /* All SHA1/SHA2 */
+ T Ch(T)(T x, T y, T z) { return z ^ (x & (y ^ z)); }
+ T Maj(T)(T x, T y, T z) { return (x & y) | (z & (x ^ y)); }
+
+ /* SHA-1 */
+ uint Parity(uint x, uint y, uint z) { return x ^ y ^ z; }
+
+ /* SHA-224, SHA-256 */
+ uint BigSigma0(uint x) { return rotateRight(x, 2) ^ rotateRight(x, 13) ^ rotateRight(x, 22); }
+ uint BigSigma1(uint x) { return rotateRight(x, 6) ^ rotateRight(x, 11) ^ rotateRight(x, 25); }
+ uint SmSigma0(uint x) { return rotateRight(x, 7) ^ rotateRight(x, 18) ^ x >> 3; }
+ uint SmSigma1(uint x) { return rotateRight(x, 17) ^ rotateRight(x, 19) ^ x >> 10; }
+
+ /* SHA-384, SHA-512, SHA-512/224, SHA-512/256 */
+ ulong BigSigma0(ulong x) { return rotateRight(x, 28) ^ rotateRight(x, 34) ^ rotateRight(x, 39); }
+ ulong BigSigma1(ulong x) { return rotateRight(x, 14) ^ rotateRight(x, 18) ^ rotateRight(x, 41); }
+ ulong SmSigma0(ulong x) { return rotateRight(x, 1) ^ rotateRight(x, 8) ^ x >> 7; }
+ ulong SmSigma1(ulong x) { return rotateRight(x, 19) ^ rotateRight(x, 61) ^ x >> 6; }
+ }
+
+ /*
+ * SHA1 basic transformation. Transforms state based on block.
+ */
+ static void T_0_15(int i, const(ubyte[64])* input, ref uint[16] W, uint A, ref uint B, uint C, uint D,
+ uint E, ref uint T) pure nothrow @nogc
+ {
+ uint Wi = W[i] = bigEndianToNative(*cast(ubyte[4]*)&((*input)[i*4]));
+ T = Ch(B, C, D) + E + rotateLeft(A, 5) + Wi + 0x5a827999;
+ B = rotateLeft(B, 30);
+ }
+
+ static void T_16_19(int i, ref uint[16] W, uint A, ref uint B, uint C, uint D, uint E, ref uint T)
+ pure nothrow @nogc
+ {
+ W[i&15] = rotateLeft(W[(i-3)&15] ^ W[(i-8)&15] ^ W[(i-14)&15] ^ W[(i-16)&15], 1);
+ T = Ch(B, C, D) + E + rotateLeft(A, 5) + W[i&15] + 0x5a827999;
+ B = rotateLeft(B, 30);
+ }
+
+ static void T_20_39(int i, ref uint[16] W, uint A, ref uint B, uint C, uint D, uint E,
+ ref uint T) pure nothrow @nogc
+ {
+ W[i&15] = rotateLeft(W[(i-3)&15] ^ W[(i-8)&15] ^ W[(i-14)&15] ^ W[(i-16)&15], 1);
+ T = Parity(B, C, D) + E + rotateLeft(A, 5) + W[i&15] + 0x6ed9eba1;
+ B = rotateLeft(B, 30);
+ }
+
+ static void T_40_59(int i, ref uint[16] W, uint A, ref uint B, uint C, uint D, uint E,
+ ref uint T) pure nothrow @nogc
+ {
+ W[i&15] = rotateLeft(W[(i-3)&15] ^ W[(i-8)&15] ^ W[(i-14)&15] ^ W[(i-16)&15], 1);
+ T = Maj(B, C, D) + E + rotateLeft(A, 5) + W[i&15] + 0x8f1bbcdc;
+ B = rotateLeft(B, 30);
+ }
+
+ static void T_60_79(int i, ref uint[16] W, uint A, ref uint B, uint C, uint D, uint E,
+ ref uint T) pure nothrow @nogc
+ {
+ W[i&15] = rotateLeft(W[(i-3)&15] ^ W[(i-8)&15] ^ W[(i-14)&15] ^ W[(i-16)&15], 1);
+ T = Parity(B, C, D) + E + rotateLeft(A, 5) + W[i&15] + 0xca62c1d6;
+ B = rotateLeft(B, 30);
+ }
+
+ private static void transformX86(uint[5]* state, const(ubyte[64])* block) pure nothrow @nogc
+ {
+ uint A, B, C, D, E, T;
+ uint[16] W = void;
+
+ A = (*state)[0];
+ B = (*state)[1];
+ C = (*state)[2];
+ D = (*state)[3];
+ E = (*state)[4];
+
+ T_0_15 ( 0, block, W, A, B, C, D, E, T);
+ T_0_15 ( 1, block, W, T, A, B, C, D, E);
+ T_0_15 ( 2, block, W, E, T, A, B, C, D);
+ T_0_15 ( 3, block, W, D, E, T, A, B, C);
+ T_0_15 ( 4, block, W, C, D, E, T, A, B);
+ T_0_15 ( 5, block, W, B, C, D, E, T, A);
+ T_0_15 ( 6, block, W, A, B, C, D, E, T);
+ T_0_15 ( 7, block, W, T, A, B, C, D, E);
+ T_0_15 ( 8, block, W, E, T, A, B, C, D);
+ T_0_15 ( 9, block, W, D, E, T, A, B, C);
+ T_0_15 (10, block, W, C, D, E, T, A, B);
+ T_0_15 (11, block, W, B, C, D, E, T, A);
+ T_0_15 (12, block, W, A, B, C, D, E, T);
+ T_0_15 (13, block, W, T, A, B, C, D, E);
+ T_0_15 (14, block, W, E, T, A, B, C, D);
+ T_0_15 (15, block, W, D, E, T, A, B, C);
+ T_16_19(16, W, C, D, E, T, A, B);
+ T_16_19(17, W, B, C, D, E, T, A);
+ T_16_19(18, W, A, B, C, D, E, T);
+ T_16_19(19, W, T, A, B, C, D, E);
+ T_20_39(20, W, E, T, A, B, C, D);
+ T_20_39(21, W, D, E, T, A, B, C);
+ T_20_39(22, W, C, D, E, T, A, B);
+ T_20_39(23, W, B, C, D, E, T, A);
+ T_20_39(24, W, A, B, C, D, E, T);
+ T_20_39(25, W, T, A, B, C, D, E);
+ T_20_39(26, W, E, T, A, B, C, D);
+ T_20_39(27, W, D, E, T, A, B, C);
+ T_20_39(28, W, C, D, E, T, A, B);
+ T_20_39(29, W, B, C, D, E, T, A);
+ T_20_39(30, W, A, B, C, D, E, T);
+ T_20_39(31, W, T, A, B, C, D, E);
+ T_20_39(32, W, E, T, A, B, C, D);
+ T_20_39(33, W, D, E, T, A, B, C);
+ T_20_39(34, W, C, D, E, T, A, B);
+ T_20_39(35, W, B, C, D, E, T, A);
+ T_20_39(36, W, A, B, C, D, E, T);
+ T_20_39(37, W, T, A, B, C, D, E);
+ T_20_39(38, W, E, T, A, B, C, D);
+ T_20_39(39, W, D, E, T, A, B, C);
+ T_40_59(40, W, C, D, E, T, A, B);
+ T_40_59(41, W, B, C, D, E, T, A);
+ T_40_59(42, W, A, B, C, D, E, T);
+ T_40_59(43, W, T, A, B, C, D, E);
+ T_40_59(44, W, E, T, A, B, C, D);
+ T_40_59(45, W, D, E, T, A, B, C);
+ T_40_59(46, W, C, D, E, T, A, B);
+ T_40_59(47, W, B, C, D, E, T, A);
+ T_40_59(48, W, A, B, C, D, E, T);
+ T_40_59(49, W, T, A, B, C, D, E);
+ T_40_59(50, W, E, T, A, B, C, D);
+ T_40_59(51, W, D, E, T, A, B, C);
+ T_40_59(52, W, C, D, E, T, A, B);
+ T_40_59(53, W, B, C, D, E, T, A);
+ T_40_59(54, W, A, B, C, D, E, T);
+ T_40_59(55, W, T, A, B, C, D, E);
+ T_40_59(56, W, E, T, A, B, C, D);
+ T_40_59(57, W, D, E, T, A, B, C);
+ T_40_59(58, W, C, D, E, T, A, B);
+ T_40_59(59, W, B, C, D, E, T, A);
+ T_60_79(60, W, A, B, C, D, E, T);
+ T_60_79(61, W, T, A, B, C, D, E);
+ T_60_79(62, W, E, T, A, B, C, D);
+ T_60_79(63, W, D, E, T, A, B, C);
+ T_60_79(64, W, C, D, E, T, A, B);
+ T_60_79(65, W, B, C, D, E, T, A);
+ T_60_79(66, W, A, B, C, D, E, T);
+ T_60_79(67, W, T, A, B, C, D, E);
+ T_60_79(68, W, E, T, A, B, C, D);
+ T_60_79(69, W, D, E, T, A, B, C);
+ T_60_79(70, W, C, D, E, T, A, B);
+ T_60_79(71, W, B, C, D, E, T, A);
+ T_60_79(72, W, A, B, C, D, E, T);
+ T_60_79(73, W, T, A, B, C, D, E);
+ T_60_79(74, W, E, T, A, B, C, D);
+ T_60_79(75, W, D, E, T, A, B, C);
+ T_60_79(76, W, C, D, E, T, A, B);
+ T_60_79(77, W, B, C, D, E, T, A);
+ T_60_79(78, W, A, B, C, D, E, T);
+ T_60_79(79, W, T, A, B, C, D, E);
+
+ (*state)[0] += E;
+ (*state)[1] += T;
+ (*state)[2] += A;
+ (*state)[3] += B;
+ (*state)[4] += C;
+
+ /* Zeroize sensitive information. */
+ W[] = 0;
+ }
+
+ /*
+ * SHA2 basic transformation. Transforms state based on block.
+ */
+ static void T_SHA2_0_15(Word)(int i, const(ubyte[blockSize/8])* input, ref Word[16] W,
+ Word A, Word B, Word C, ref Word D, Word E, Word F, Word G, ref Word H, Word K)
+ pure nothrow @nogc
+ {
+ Word Wi = W[i] = bigEndianToNative(*cast(ubyte[Word.sizeof]*)&((*input)[i*Word.sizeof]));
+ Word T1 = H + BigSigma1(E) + Ch(E, F, G) + K + Wi;
+ Word T2 = BigSigma0(A) + Maj(A, B, C);
+ D += T1;
+ H = T1 + T2;
+ }
+
+ static void T_SHA2_16_79(Word)(int i, ref Word[16] W,
+ Word A, Word B, Word C, ref Word D, Word E, Word F, Word G, ref Word H, Word K)
+ pure nothrow @nogc
+ {
+ W[i&15] = SmSigma1(W[(i-2)&15]) + W[(i-7)&15] + SmSigma0(W[(i-15)&15]) + W[i&15];
+ Word T1 = H + BigSigma1(E) + Ch(E, F, G) + K + W[i&15];
+ Word T2 = BigSigma0(A) + Maj(A, B, C);
+ D += T1;
+ H = T1 + T2;
+ }
+
+ private static void transformSHA2(Word)(Word[8]* state, const(ubyte[blockSize/8])* block)
+ pure nothrow @nogc
+ {
+ Word A, B, C, D, E, F, G, H;
+ Word[16] W = void;
+
+ A = (*state)[0];
+ B = (*state)[1];
+ C = (*state)[2];
+ D = (*state)[3];
+ E = (*state)[4];
+ F = (*state)[5];
+ G = (*state)[6];
+ H = (*state)[7];
+
+ T_SHA2_0_15!Word ( 0, block, W, A, B, C, D, E, F, G, H, constants[ 0]);
+ T_SHA2_0_15!Word ( 1, block, W, H, A, B, C, D, E, F, G, constants[ 1]);
+ T_SHA2_0_15!Word ( 2, block, W, G, H, A, B, C, D, E, F, constants[ 2]);
+ T_SHA2_0_15!Word ( 3, block, W, F, G, H, A, B, C, D, E, constants[ 3]);
+ T_SHA2_0_15!Word ( 4, block, W, E, F, G, H, A, B, C, D, constants[ 4]);
+ T_SHA2_0_15!Word ( 5, block, W, D, E, F, G, H, A, B, C, constants[ 5]);
+ T_SHA2_0_15!Word ( 6, block, W, C, D, E, F, G, H, A, B, constants[ 6]);
+ T_SHA2_0_15!Word ( 7, block, W, B, C, D, E, F, G, H, A, constants[ 7]);
+ T_SHA2_0_15!Word ( 8, block, W, A, B, C, D, E, F, G, H, constants[ 8]);
+ T_SHA2_0_15!Word ( 9, block, W, H, A, B, C, D, E, F, G, constants[ 9]);
+ T_SHA2_0_15!Word (10, block, W, G, H, A, B, C, D, E, F, constants[10]);
+ T_SHA2_0_15!Word (11, block, W, F, G, H, A, B, C, D, E, constants[11]);
+ T_SHA2_0_15!Word (12, block, W, E, F, G, H, A, B, C, D, constants[12]);
+ T_SHA2_0_15!Word (13, block, W, D, E, F, G, H, A, B, C, constants[13]);
+ T_SHA2_0_15!Word (14, block, W, C, D, E, F, G, H, A, B, constants[14]);
+ T_SHA2_0_15!Word (15, block, W, B, C, D, E, F, G, H, A, constants[15]);
+ T_SHA2_16_79!Word(16, W, A, B, C, D, E, F, G, H, constants[16]);
+ T_SHA2_16_79!Word(17, W, H, A, B, C, D, E, F, G, constants[17]);
+ T_SHA2_16_79!Word(18, W, G, H, A, B, C, D, E, F, constants[18]);
+ T_SHA2_16_79!Word(19, W, F, G, H, A, B, C, D, E, constants[19]);
+ T_SHA2_16_79!Word(20, W, E, F, G, H, A, B, C, D, constants[20]);
+ T_SHA2_16_79!Word(21, W, D, E, F, G, H, A, B, C, constants[21]);
+ T_SHA2_16_79!Word(22, W, C, D, E, F, G, H, A, B, constants[22]);
+ T_SHA2_16_79!Word(23, W, B, C, D, E, F, G, H, A, constants[23]);
+ T_SHA2_16_79!Word(24, W, A, B, C, D, E, F, G, H, constants[24]);
+ T_SHA2_16_79!Word(25, W, H, A, B, C, D, E, F, G, constants[25]);
+ T_SHA2_16_79!Word(26, W, G, H, A, B, C, D, E, F, constants[26]);
+ T_SHA2_16_79!Word(27, W, F, G, H, A, B, C, D, E, constants[27]);
+ T_SHA2_16_79!Word(28, W, E, F, G, H, A, B, C, D, constants[28]);
+ T_SHA2_16_79!Word(29, W, D, E, F, G, H, A, B, C, constants[29]);
+ T_SHA2_16_79!Word(30, W, C, D, E, F, G, H, A, B, constants[30]);
+ T_SHA2_16_79!Word(31, W, B, C, D, E, F, G, H, A, constants[31]);
+ T_SHA2_16_79!Word(32, W, A, B, C, D, E, F, G, H, constants[32]);
+ T_SHA2_16_79!Word(33, W, H, A, B, C, D, E, F, G, constants[33]);
+ T_SHA2_16_79!Word(34, W, G, H, A, B, C, D, E, F, constants[34]);
+ T_SHA2_16_79!Word(35, W, F, G, H, A, B, C, D, E, constants[35]);
+ T_SHA2_16_79!Word(36, W, E, F, G, H, A, B, C, D, constants[36]);
+ T_SHA2_16_79!Word(37, W, D, E, F, G, H, A, B, C, constants[37]);
+ T_SHA2_16_79!Word(38, W, C, D, E, F, G, H, A, B, constants[38]);
+ T_SHA2_16_79!Word(39, W, B, C, D, E, F, G, H, A, constants[39]);
+ T_SHA2_16_79!Word(40, W, A, B, C, D, E, F, G, H, constants[40]);
+ T_SHA2_16_79!Word(41, W, H, A, B, C, D, E, F, G, constants[41]);
+ T_SHA2_16_79!Word(42, W, G, H, A, B, C, D, E, F, constants[42]);
+ T_SHA2_16_79!Word(43, W, F, G, H, A, B, C, D, E, constants[43]);
+ T_SHA2_16_79!Word(44, W, E, F, G, H, A, B, C, D, constants[44]);
+ T_SHA2_16_79!Word(45, W, D, E, F, G, H, A, B, C, constants[45]);
+ T_SHA2_16_79!Word(46, W, C, D, E, F, G, H, A, B, constants[46]);
+ T_SHA2_16_79!Word(47, W, B, C, D, E, F, G, H, A, constants[47]);
+ T_SHA2_16_79!Word(48, W, A, B, C, D, E, F, G, H, constants[48]);
+ T_SHA2_16_79!Word(49, W, H, A, B, C, D, E, F, G, constants[49]);
+ T_SHA2_16_79!Word(50, W, G, H, A, B, C, D, E, F, constants[50]);
+ T_SHA2_16_79!Word(51, W, F, G, H, A, B, C, D, E, constants[51]);
+ T_SHA2_16_79!Word(52, W, E, F, G, H, A, B, C, D, constants[52]);
+ T_SHA2_16_79!Word(53, W, D, E, F, G, H, A, B, C, constants[53]);
+ T_SHA2_16_79!Word(54, W, C, D, E, F, G, H, A, B, constants[54]);
+ T_SHA2_16_79!Word(55, W, B, C, D, E, F, G, H, A, constants[55]);
+ T_SHA2_16_79!Word(56, W, A, B, C, D, E, F, G, H, constants[56]);
+ T_SHA2_16_79!Word(57, W, H, A, B, C, D, E, F, G, constants[57]);
+ T_SHA2_16_79!Word(58, W, G, H, A, B, C, D, E, F, constants[58]);
+ T_SHA2_16_79!Word(59, W, F, G, H, A, B, C, D, E, constants[59]);
+ T_SHA2_16_79!Word(60, W, E, F, G, H, A, B, C, D, constants[60]);
+ T_SHA2_16_79!Word(61, W, D, E, F, G, H, A, B, C, constants[61]);
+ 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))
+ {
+ 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]);
+ T_SHA2_16_79!Word(66, W, G, H, A, B, C, D, E, F, constants[66]);
+ T_SHA2_16_79!Word(67, W, F, G, H, A, B, C, D, E, constants[67]);
+ T_SHA2_16_79!Word(68, W, E, F, G, H, A, B, C, D, constants[68]);
+ T_SHA2_16_79!Word(69, W, D, E, F, G, H, A, B, C, constants[69]);
+ T_SHA2_16_79!Word(70, W, C, D, E, F, G, H, A, B, constants[70]);
+ T_SHA2_16_79!Word(71, W, B, C, D, E, F, G, H, A, constants[71]);
+ T_SHA2_16_79!Word(72, W, A, B, C, D, E, F, G, H, constants[72]);
+ T_SHA2_16_79!Word(73, W, H, A, B, C, D, E, F, G, constants[73]);
+ T_SHA2_16_79!Word(74, W, G, H, A, B, C, D, E, F, constants[74]);
+ T_SHA2_16_79!Word(75, W, F, G, H, A, B, C, D, E, constants[75]);
+ T_SHA2_16_79!Word(76, W, E, F, G, H, A, B, C, D, constants[76]);
+ T_SHA2_16_79!Word(77, W, D, E, F, G, H, A, B, C, constants[77]);
+ T_SHA2_16_79!Word(78, W, C, D, E, F, G, H, A, B, constants[78]);
+ T_SHA2_16_79!Word(79, W, B, C, D, E, F, G, H, A, constants[79]);
+ }
+
+ (*state)[0] += A;
+ (*state)[1] += B;
+ (*state)[2] += C;
+ (*state)[3] += D;
+ (*state)[4] += E;
+ (*state)[5] += F;
+ (*state)[6] += G;
+ (*state)[7] += H;
+
+ /* Zeroize sensitive information. */
+ W[] = 0;
+ }
+
+ public:
+ /**
+ * SHA initialization. Begins an SHA1/SHA2 operation.
+ *
+ * Note:
+ * For this SHA Digest implementation calling start after default construction
+ * is not necessary. Calling start is only necessary to reset the Digest.
+ *
+ * Generic code which deals with different Digest types should always call start though.
+ *
+ * Example:
+ * --------
+ * SHA1 digest;
+ * //digest.start(); //Not necessary
+ * digest.put(0);
+ * --------
+ */
+ void start() @safe pure nothrow @nogc
+ {
+ this = typeof(this).init;
+ }
+
+ /**
+ * Use this to feed the digest with data.
+ * 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
+ {
+ enum blockSizeInBytes = blockSize/8;
+ uint i, index, partLen;
+ auto inputLen = input.length;
+
+ /* Compute number of bytes mod block size (64 or 128 bytes) */
+ index = (cast(uint) count[0] >> 3) & (blockSizeInBytes - 1);
+
+ /* Update number of bits */
+ static if (blockSize == 512)
+ count[0] += inputLen * 8;
+ 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)
+ count[1]++;
+ }
+ else
+ static assert(0);
+
+ partLen = blockSizeInBytes - index;
+
+ /* Transform as many times as possible. */
+ if (inputLen >= partLen)
+ {
+ (&buffer[index])[0 .. partLen] = input.ptr[0 .. partLen];
+ transform (&state, &buffer);
+
+ for (i = partLen; i + blockSizeInBytes-1 < inputLen; i += blockSizeInBytes)
+ transform(&state, cast(ubyte[blockSizeInBytes]*)(input.ptr + i));
+
+ index = 0;
+ }
+ else
+ i = 0;
+
+ /* Buffer remaining input */
+ if (inputLen - i)
+ (&buffer[index])[0 .. inputLen-i] = (&input[i])[0 .. inputLen-i];
+ }
+
+ @safe unittest
+ {
+ typeof(this) dig;
+ dig.put(cast(ubyte) 0); //single ubyte
+ dig.put(cast(ubyte) 0, cast(ubyte) 0); //variadic
+ ubyte[10] buf;
+ dig.put(buf); //buffer
+ }
+
+
+ /**
+ * Returns the finished SHA hash. This also calls $(LREF start) to
+ * reset the internal state.
+ */
+ ubyte[digestSize/8] finish() @trusted pure nothrow @nogc
+ {
+ static if (blockSize == 512)
+ {
+ ubyte[32] data = void;
+ uint index, padLen;
+
+ /* Save number of bits */
+ ubyte[8] bits = nativeToBigEndian(count[0]);
+
+ /* Pad out to 56 mod 64. */
+ index = (cast(uint) count[0] >> 3) & (64 - 1);
+ padLen = (index < 56) ? (56 - index) : (120 - index);
+ put(padding[0 .. padLen]);
+
+ /* Append length (before padding) */
+ put(bits);
+
+ /* Store state in digest */
+ for (auto i = 0; i < ((digestSize == 160)? 5 : 8); i++)
+ data[i*4..(i+1)*4] = nativeToBigEndian(state[i])[];
+
+ /* Zeroize sensitive information. */
+ start();
+ return data[0 .. digestSize/8];
+ }
+ 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]);
+
+ /* Pad out to 112 mod 128. */
+ index = (cast(uint) count[0] >> 3) & (128 - 1);
+ padLen = (index < 112) ? (112 - index) : (240 - index);
+ put(padding[0 .. padLen]);
+
+ /* Append length (before padding) */
+ put(bits);
+
+ /* Store state in digest */
+ for (auto i = 0; i < 8; i++)
+ data[i*8..(i+1)*8] = nativeToBigEndian(state[i])[];
+
+ /* Zeroize sensitive information. */
+ start();
+ return data[0 .. digestSize/8];
+ }
+ else
+ static assert(0);
+ }
+ ///
+ @safe unittest
+ {
+ //Simple example
+ SHA1 hash;
+ hash.start();
+ hash.put(cast(ubyte) 0);
+ ubyte[20] result = hash.finish();
+ }
+}
+
+alias SHA1 = SHA!(512, 160); /// SHA alias for SHA-1, hash is ubyte[20]
+alias SHA224 = SHA!(512, 224); /// SHA alias for SHA-224, hash is ubyte[28]
+alias SHA256 = SHA!(512, 256); /// SHA alias for SHA-256, hash is ubyte[32]
+alias SHA384 = SHA!(1024, 384); /// SHA alias for SHA-384, hash is ubyte[48]
+alias SHA512 = SHA!(1024, 512); /// SHA alias for SHA-512, hash is ubyte[64]
+alias SHA512_224 = SHA!(1024, 224); /// SHA alias for SHA-512/224, hash is ubyte[28]
+alias SHA512_256 = SHA!(1024, 256); /// SHA alias for SHA-512/256, hash is ubyte[32]
+
+///
+@safe unittest
+{
+ //Simple example, hashing a string using sha1Of helper function
+ ubyte[20] hash = sha1Of("abc");
+ //Let's get a hash string
+ assert(toHexString(hash) == "A9993E364706816ABA3E25717850C26C9CD0D89D");
+
+ //The same, but using SHA-224
+ ubyte[28] hash224 = sha224Of("abc");
+ assert(toHexString(hash224) == "23097D223405D8228642A477BDA255B32AADBCE4BDA0B3F7E36C9DA7");
+}
+
+///
+@safe unittest
+{
+ //Using the basic API
+ SHA1 hash;
+ hash.start();
+ ubyte[1024] data;
+ //Initialize data here...
+ hash.put(data);
+ ubyte[20] result = hash.finish();
+}
+
+///
+@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)
+ {
+ hash.put(cast(ubyte) 0);
+ }
+ SHA1 sha;
+ sha.start();
+ doSomething(sha);
+ assert(toHexString(sha.finish()) == "5BA93C9DB0CFF93F52B521D7420E43F6EDA2784F");
+}
+
+@safe unittest
+{
+ assert(isDigest!SHA1);
+ assert(isDigest!SHA224);
+ assert(isDigest!SHA256);
+ assert(isDigest!SHA384);
+ assert(isDigest!SHA512);
+ assert(isDigest!SHA512_224);
+ assert(isDigest!SHA512_256);
+}
+
+@system unittest
+{
+ import std.conv : hexString;
+ import std.range;
+
+ ubyte[20] digest;
+ ubyte[28] digest224;
+ ubyte[32] digest256;
+ ubyte[48] digest384;
+ ubyte[64] digest512;
+ ubyte[28] digest512_224;
+ ubyte[32] digest512_256;
+
+ SHA1 sha;
+ sha.put(cast(ubyte[])"abcdef");
+ sha.start();
+ sha.put(cast(ubyte[])"");
+ 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");
+
+ SHA256 sha256;
+ sha256.put(cast(ubyte[])"abcdef");
+ sha256.start();
+ sha256.put(cast(ubyte[])"");
+ 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[]) hexString!("38b060a751ac96384cd9327eb1b1e36a21fdb71114be07434c"
+ ~"0cc7bf63f6e1da274edebfe76f65fbd51ad2f14898b95b"));
+
+ SHA512 sha512;
+ sha512.put(cast(ubyte[])"abcdef");
+ sha512.start();
+ sha512.put(cast(ubyte[])"");
+ 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");
+
+ 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");
+
+ digest = sha1Of ("");
+ digest224 = sha224Of ("");
+ digest256 = sha256Of ("");
+ digest384 = sha384Of ("");
+ 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[]) 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");
+ digest256 = sha256Of ("a");
+ digest384 = sha384Of ("a");
+ 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[]) 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");
+ digest256 = sha256Of ("abc");
+ digest384 = sha384Of ("abc");
+ 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[]) 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");
+ digest256 = sha256Of ("abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq");
+ digest384 = sha384Of ("abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq");
+ 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[]) 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");
+ digest256 = sha256Of ("message digest");
+ digest384 = sha384Of ("message digest");
+ 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[]) 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");
+ digest256 = sha256Of ("abcdefghijklmnopqrstuvwxyz");
+ digest384 = sha384Of ("abcdefghijklmnopqrstuvwxyz");
+ 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[]) 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");
+ digest256 = sha256Of ("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789");
+ digest384 = sha384Of ("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789");
+ 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[]) 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");
+ digest224 = sha224Of ("1234567890123456789012345678901234567890"~
+ "1234567890123456789012345678901234567890");
+ digest256 = sha256Of ("1234567890123456789012345678901234567890"~
+ "1234567890123456789012345678901234567890");
+ digest384 = sha384Of ("1234567890123456789012345678901234567890"~
+ "1234567890123456789012345678901234567890");
+ digest512 = sha512Of ("1234567890123456789012345678901234567890"~
+ "1234567890123456789012345678901234567890");
+ digest512_224 = sha512_224Of("1234567890123456789012345678901234567890"~
+ "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[]) 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';
+ digest = sha1Of(onemilliona);
+ digest224 = sha224Of(onemilliona);
+ digest256 = sha256Of(onemilliona);
+ digest384 = sha384Of(onemilliona);
+ 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[]) 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);
+ digest224 = sha224Of(oneMillionRange);
+ digest256 = sha256Of(oneMillionRange);
+ digest384 = sha384Of(oneMillionRange);
+ 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[]) 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 $(REF digest, std,digest) using the
+ * SHA implementation.
+ */
+//simple alias doesn't work here, hope this gets inlined...
+auto sha1Of(T...)(T data)
+{
+ return digest!(SHA1, T)(data);
+}
+///ditto
+auto sha224Of(T...)(T data)
+{
+ return digest!(SHA224, T)(data);
+}
+///ditto
+auto sha256Of(T...)(T data)
+{
+ return digest!(SHA256, T)(data);
+}
+///ditto
+auto sha384Of(T...)(T data)
+{
+ return digest!(SHA384, T)(data);
+}
+///ditto
+auto sha512Of(T...)(T data)
+{
+ return digest!(SHA512, T)(data);
+}
+///ditto
+auto sha512_224Of(T...)(T data)
+{
+ return digest!(SHA512_224, T)(data);
+}
+///ditto
+auto sha512_256Of(T...)(T data)
+{
+ return digest!(SHA512_256, T)(data);
+}
+
+///
+@safe unittest
+{
+ ubyte[20] hash = sha1Of("abc");
+ assert(hash == digest!SHA1("abc"));
+
+ ubyte[28] hash224 = sha224Of("abc");
+ assert(hash224 == digest!SHA224("abc"));
+
+ ubyte[32] hash256 = sha256Of("abc");
+ assert(hash256 == digest!SHA256("abc"));
+
+ ubyte[48] hash384 = sha384Of("abc");
+ assert(hash384 == digest!SHA384("abc"));
+
+ ubyte[64] hash512 = sha512Of("abc");
+ assert(hash512 == digest!SHA512("abc"));
+
+ ubyte[28] hash512_224 = sha512_224Of("abc");
+ assert(hash512_224 == digest!SHA512_224("abc"));
+
+ ubyte[32] hash512_256 = sha512_256Of("abc");
+ assert(hash512_256 == digest!SHA512_256("abc"));
+}
+
+@safe unittest
+{
+ string a = "Mary has ", b = "a little lamb";
+ int[] c = [ 1, 2, 3, 4, 5 ];
+ string d = toHexString(sha1Of(a, b, c));
+ version (LittleEndian)
+ assert(d == "CDBB611D00AC2387B642D3D7BDF4C3B342237110", d);
+ else
+ assert(d == "A0F1196C7A379C09390476D9CA4AA11B71FD11C8", d);
+}
+
+/**
+ * OOP API SHA1 and SHA2 implementations.
+ * See $(D std.digest) for differences between template and OOP API.
+ *
+ * This is an alias for $(D $(REF WrapperDigest, std,digest)!SHA1), see
+ * there for more information.
+ */
+alias SHA1Digest = WrapperDigest!SHA1;
+alias SHA224Digest = WrapperDigest!SHA224; ///ditto
+alias SHA256Digest = WrapperDigest!SHA256; ///ditto
+alias SHA384Digest = WrapperDigest!SHA384; ///ditto
+alias SHA512Digest = WrapperDigest!SHA512; ///ditto
+alias SHA512_224Digest = WrapperDigest!SHA512_224; ///ditto
+alias SHA512_256Digest = WrapperDigest!SHA512_256; ///ditto
+
+///
+@safe unittest
+{
+ //Simple example, hashing a string using Digest.digest helper function
+ auto sha = new SHA1Digest();
+ ubyte[] hash = sha.digest("abc");
+ //Let's get a hash string
+ assert(toHexString(hash) == "A9993E364706816ABA3E25717850C26C9CD0D89D");
+
+ //The same, but using SHA-224
+ auto sha224 = new SHA224Digest();
+ ubyte[] hash224 = sha224.digest("abc");
+ //Let's get a hash string
+ assert(toHexString(hash224) == "23097D223405D8228642A477BDA255B32AADBCE4BDA0B3F7E36C9DA7");
+}
+
+///
+@system unittest
+{
+ //Let's use the OOP features:
+ void test(Digest dig)
+ {
+ dig.put(cast(ubyte) 0);
+ }
+ auto sha = new SHA1Digest();
+ test(sha);
+
+ //Let's use a custom buffer:
+ ubyte[20] buf;
+ ubyte[] result = sha.finish(buf[]);
+ assert(toHexString(result) == "5BA93C9DB0CFF93F52B521D7420E43F6EDA2784F");
+}
+
+@system unittest
+{
+ auto sha = new SHA1Digest();
+
+ sha.put(cast(ubyte[])"abcdef");
+ sha.reset();
+ sha.put(cast(ubyte[])"");
+ 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");
+
+ debug
+ assertThrown!Error(sha.finish(result[0 .. 15]));
+
+ assert(sha.length == 20);
+
+ assert(sha.digest("") == cast(ubyte[]) x"da39a3ee5e6b4b0d3255bfef95601890afd80709");
+
+ assert(sha.digest("a") == cast(ubyte[]) x"86f7e437faa5a7fce15d1ddcb9eaeaea377667b8");
+
+ assert(sha.digest("abc") == cast(ubyte[]) x"a9993e364706816aba3e25717850c26c9cd0d89d");
+
+ assert(sha.digest("abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq")
+ == cast(ubyte[]) x"84983e441c3bd26ebaae4aa1f95129e5e54670f1");
+
+ assert(sha.digest("message digest") == cast(ubyte[]) x"c12252ceda8be8994d5fa0290a47231c1d16aae3");
+
+ assert(sha.digest("abcdefghijklmnopqrstuvwxyz")
+ == cast(ubyte[]) x"32d10c7b8cf96570ca04ce37f2a19d84240d3a89");
+
+ assert(sha.digest("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789")
+ == cast(ubyte[]) x"761c457bf73b14d27e9e9265c46f4b4dda11f940");
+
+ assert(sha.digest("1234567890123456789012345678901234567890",
+ "1234567890123456789012345678901234567890")
+ == cast(ubyte[]) x"50abf5706a150990a08b2c5ea40fa0e585554732");
+
+ ubyte[] onemilliona = new ubyte[1000000];
+ onemilliona[] = 'a';
+ assert(sha.digest(onemilliona) == cast(ubyte[]) x"34aa973cd4c4daa4f61eeb2bdbad27316534016f");
+}
diff --git a/libphobos/src/std/encoding.d b/libphobos/src/std/encoding.d
new file mode 100644
index 0000000..5bbd0d4
--- /dev/null
+++ b/libphobos/src/std/encoding.d
@@ -0,0 +1,3662 @@
+// Written in the D programming language.
+
+/**
+Classes and functions for handling and transcoding between various encodings.
+
+For cases where the _encoding is known at compile-time, functions are provided
+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), 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 &amp; 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, 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");
+----------------------------------------------------
+
+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 $(LREF
+EncodingScheme) subclasses for any other _encoding.
+
+Copyright: Copyright Janice Caron 2008 - 2009.
+License: $(HTTP www.boost.org/LICENSE_1_0.txt, Boost License 1.0).
+Authors: Janice Caron
+Source: $(PHOBOSSRC std/_encoding.d)
+*/
+/*
+ Copyright Janice Caron 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.encoding;
+
+import std.range.primitives;
+import std.traits;
+import std.typecons;
+
+@system unittest
+{
+ static ubyte[][] validStrings =
+ [
+ // Plain ASCII
+ cast(ubyte[])"hello",
+
+ // First possible sequence of a certain length
+ [ 0x00 ], // U+00000000 one byte
+ [ 0xC2, 0x80 ], // U+00000080 two bytes
+ [ 0xE0, 0xA0, 0x80 ], // U+00000800 three bytes
+ [ 0xF0, 0x90, 0x80, 0x80 ], // U+00010000 three bytes
+
+ // Last possible sequence of a certain length
+ [ 0x7F ], // U+0000007F one byte
+ [ 0xDF, 0xBF ], // U+000007FF two bytes
+ [ 0xEF, 0xBF, 0xBF ], // U+0000FFFF three bytes
+
+ // Other boundary conditions
+ [ 0xED, 0x9F, 0xBF ],
+ // U+0000D7FF Last character before surrogates
+ [ 0xEE, 0x80, 0x80 ],
+ // U+0000E000 First character after surrogates
+ [ 0xEF, 0xBF, 0xBD ],
+ // U+0000FFFD Unicode replacement character
+ [ 0xF4, 0x8F, 0xBF, 0xBF ],
+ // U+0010FFFF Very last character
+
+ // Non-character code points
+ /* NOTE: These are legal in UTF, and may be converted from
+ one UTF to another, however they do not represent Unicode
+ characters. These code points have been reserved by
+ Unicode as non-character code points. They are permissible
+ for data exchange within an application, but they are are
+ not permitted to be used as characters. Since this module
+ deals with UTF, and not with Unicode per se, we choose to
+ accept them here. */
+ [ 0xDF, 0xBE ], // U+0000FFFE
+ [ 0xDF, 0xBF ], // U+0000FFFF
+ ];
+
+ static ubyte[][] invalidStrings =
+ [
+ // First possible sequence of a certain length, but greater
+ // than U+10FFFF
+ [ 0xF8, 0x88, 0x80, 0x80, 0x80 ], // U+00200000 five bytes
+ [ 0xFC, 0x84, 0x80, 0x80, 0x80, 0x80 ], // U+04000000 six bytes
+
+ // Last possible sequence of a certain length, but greater than U+10FFFF
+ [ 0xF7, 0xBF, 0xBF, 0xBF ], // U+001FFFFF four bytes
+ [ 0xFB, 0xBF, 0xBF, 0xBF, 0xBF ], // U+03FFFFFF five bytes
+ [ 0xFD, 0xBF, 0xBF, 0xBF, 0xBF, 0xBF ], // U+7FFFFFFF six bytes
+
+ // Other boundary conditions
+ [ 0xF4, 0x90, 0x80, 0x80 ], // U+00110000
+ // First code
+ // point after
+ // last character
+
+ // Unexpected continuation bytes
+ [ 0x80 ],
+ [ 0xBF ],
+ [ 0x20, 0x80, 0x20 ],
+ [ 0x20, 0xBF, 0x20 ],
+ [ 0x80, 0x9F, 0xA0 ],
+
+ // Lonely start bytes
+ [ 0xC0 ],
+ [ 0xCF ],
+ [ 0x20, 0xC0, 0x20 ],
+ [ 0x20, 0xCF, 0x20 ],
+ [ 0xD0 ],
+ [ 0xDF ],
+ [ 0x20, 0xD0, 0x20 ],
+ [ 0x20, 0xDF, 0x20 ],
+ [ 0xE0 ],
+ [ 0xEF ],
+ [ 0x20, 0xE0, 0x20 ],
+ [ 0x20, 0xEF, 0x20 ],
+ [ 0xF0 ],
+ [ 0xF1 ],
+ [ 0xF2 ],
+ [ 0xF3 ],
+ [ 0xF4 ],
+ [ 0xF5 ], // If this were legal it would start a character > U+10FFFF
+ [ 0xF6 ], // If this were legal it would start a character > U+10FFFF
+ [ 0xF7 ], // If this were legal it would start a character > U+10FFFF
+
+ [ 0xEF, 0xBF ], // Three byte sequence with third byte missing
+ [ 0xF7, 0xBF, 0xBF ], // Four byte sequence with fourth byte missing
+ [ 0xEF, 0xBF, 0xF7, 0xBF, 0xBF ], // Concatenation of the above
+
+ // Impossible bytes
+ [ 0xF8 ],
+ [ 0xF9 ],
+ [ 0xFA ],
+ [ 0xFB ],
+ [ 0xFC ],
+ [ 0xFD ],
+ [ 0xFE ],
+ [ 0xFF ],
+ [ 0x20, 0xF8, 0x20 ],
+ [ 0x20, 0xF9, 0x20 ],
+ [ 0x20, 0xFA, 0x20 ],
+ [ 0x20, 0xFB, 0x20 ],
+ [ 0x20, 0xFC, 0x20 ],
+ [ 0x20, 0xFD, 0x20 ],
+ [ 0x20, 0xFE, 0x20 ],
+ [ 0x20, 0xFF, 0x20 ],
+
+ // Overlong sequences, all representing U+002F
+ /* With a safe UTF-8 decoder, all of the following five overlong
+ representations of the ASCII character slash ("/") should be
+ rejected like a malformed UTF-8 sequence */
+ [ 0xC0, 0xAF ],
+ [ 0xE0, 0x80, 0xAF ],
+ [ 0xF0, 0x80, 0x80, 0xAF ],
+ [ 0xF8, 0x80, 0x80, 0x80, 0xAF ],
+ [ 0xFC, 0x80, 0x80, 0x80, 0x80, 0xAF ],
+
+ // Maximum overlong sequences
+ /* Below you see the highest Unicode value that is still resulting in
+ an overlong sequence if represented with the given number of bytes.
+ This is a boundary test for safe UTF-8 decoders. All five
+ characters should be rejected like malformed UTF-8 sequences. */
+ [ 0xC1, 0xBF ], // U+0000007F
+ [ 0xE0, 0x9F, 0xBF ], // U+000007FF
+ [ 0xF0, 0x8F, 0xBF, 0xBF ], // U+0000FFFF
+ [ 0xF8, 0x87, 0xBF, 0xBF, 0xBF ], // U+001FFFFF
+ [ 0xFC, 0x83, 0xBF, 0xBF, 0xBF, 0xBF ], // U+03FFFFFF
+
+ // Overlong representation of the NUL character
+ /* The following five sequences should also be rejected like malformed
+ UTF-8 sequences and should not be treated like the ASCII NUL
+ character. */
+ [ 0xC0, 0x80 ],
+ [ 0xE0, 0x80, 0x80 ],
+ [ 0xF0, 0x80, 0x80, 0x80 ],
+ [ 0xF8, 0x80, 0x80, 0x80, 0x80 ],
+ [ 0xFC, 0x80, 0x80, 0x80, 0x80, 0x80 ],
+
+ // Illegal code positions
+ /* The following UTF-8 sequences should be rejected like malformed
+ sequences, because they never represent valid ISO 10646 characters
+ and a UTF-8 decoder that accepts them might introduce security
+ problems comparable to overlong UTF-8 sequences. */
+ [ 0xED, 0xA0, 0x80 ], // U+D800
+ [ 0xED, 0xAD, 0xBF ], // U+DB7F
+ [ 0xED, 0xAE, 0x80 ], // U+DB80
+ [ 0xED, 0xAF, 0xBF ], // U+DBFF
+ [ 0xED, 0xB0, 0x80 ], // U+DC00
+ [ 0xED, 0xBE, 0x80 ], // U+DF80
+ [ 0xED, 0xBF, 0xBF ], // U+DFFF
+ ];
+
+ static string[] sanitizedStrings =
+ [
+ "\uFFFD","\uFFFD",
+ "\uFFFD","\uFFFD","\uFFFD","\uFFFD","\uFFFD","\uFFFD"," \uFFFD ",
+ " \uFFFD ","\uFFFD\uFFFD\uFFFD","\uFFFD","\uFFFD"," \uFFFD "," \uFFFD ",
+ "\uFFFD","\uFFFD"," \uFFFD "," \uFFFD ","\uFFFD","\uFFFD"," \uFFFD ",
+ " \uFFFD ","\uFFFD","\uFFFD","\uFFFD","\uFFFD","\uFFFD","\uFFFD",
+ "\uFFFD","\uFFFD","\uFFFD","\uFFFD","\uFFFD\uFFFD","\uFFFD","\uFFFD",
+ "\uFFFD","\uFFFD","\uFFFD","\uFFFD","\uFFFD","\uFFFD"," \uFFFD ",
+ " \uFFFD "," \uFFFD "," \uFFFD "," \uFFFD "," \uFFFD "," \uFFFD ",
+ " \uFFFD ","\uFFFD","\uFFFD","\uFFFD","\uFFFD","\uFFFD","\uFFFD",
+ "\uFFFD","\uFFFD","\uFFFD","\uFFFD","\uFFFD","\uFFFD","\uFFFD","\uFFFD",
+ "\uFFFD","\uFFFD","\uFFFD","\uFFFD","\uFFFD","\uFFFD","\uFFFD","\uFFFD",
+ ];
+
+ // Make sure everything that should be valid, is
+ foreach (a;validStrings)
+ {
+ 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)
+ {
+ 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<invalidStrings.length; ++i)
+ {
+ string s = cast(string) invalidStrings[i];
+ string t = sanitize(s);
+ assert(isValid(t));
+ assert(t == sanitizedStrings[i]);
+ ubyte[] u = cast(ubyte[]) t;
+ validStrings ~= u;
+ }
+
+ // Make sure all transcodings work in both directions, using both forward
+ // and reverse iteration
+ foreach (a; validStrings)
+ {
+ string s = cast(string) a;
+ string s2;
+ wstring ws, ws2;
+ dstring ds, ds2;
+
+ transcode(s,ws);
+ assert(isValid(ws));
+ transcode(ws,s2);
+ assert(s == s2);
+
+ transcode(s,ds);
+ assert(isValid(ds));
+ transcode(ds,s2);
+ assert(s == s2);
+
+ transcode(ws,s);
+ assert(isValid(s));
+ transcode(s,ws2);
+ assert(ws == ws2);
+
+ transcode(ws,ds);
+ assert(isValid(ds));
+ transcode(ds,ws2);
+ assert(ws == ws2);
+
+ transcode(ds,s);
+ assert(isValid(s));
+ transcode(s,ds2);
+ assert(ds == ds2);
+
+ transcode(ds,ws);
+ assert(isValid(ws));
+ transcode(ws,ds2);
+ assert(ds == ds2);
+
+ transcodeReverse(s,ws);
+ assert(isValid(ws));
+ transcodeReverse(ws,s2);
+ assert(s == s2);
+
+ transcodeReverse(s,ds);
+ assert(isValid(ds));
+ transcodeReverse(ds,s2);
+ assert(s == s2);
+
+ transcodeReverse(ws,s);
+ assert(isValid(s));
+ transcodeReverse(s,ws2);
+ assert(ws == ws2);
+
+ transcodeReverse(ws,ds);
+ assert(isValid(ds));
+ transcodeReverse(ds,ws2);
+ assert(ws == ws2);
+
+ transcodeReverse(ds,s);
+ assert(isValid(s));
+ transcodeReverse(s,ds2);
+ assert(ds == ds2);
+
+ transcodeReverse(ds,ws);
+ assert(isValid(ws));
+ transcodeReverse(ws,ds2);
+ assert(ds == ds2);
+ }
+
+ // Make sure the non-UTF encodings work too
+ {
+ auto s = "\u20AC100";
+ Windows1252String t;
+ transcode(s,t);
+ assert(t == cast(Windows1252Char[])[0x80, '1', '0', '0']);
+ string u;
+ transcode(s,u);
+ assert(s == u);
+ Latin1String v;
+ transcode(s,v);
+ assert(cast(string) v == "?100");
+ AsciiString w;
+ transcode(v,w);
+ assert(cast(string) w == "?100");
+ s = "\u017Dlu\u0165ou\u010Dk\u00FD k\u016F\u0148";
+ Latin2String x;
+ transcode(s,x);
+ assert(x == cast(Latin2Char[])[0xae, 'l', 'u', 0xbb, 'o', 'u', 0xe8, 'k', 0xfd, ' ', 'k', 0xf9, 0xf2]);
+ Windows1250String y;
+ transcode(s,y);
+ assert(y == cast(Windows1250Char[])[0x8e, 'l', 'u', 0x9d, 'o', 'u', 0xe8, 'k', 0xfd, ' ', 'k', 0xf9, 0xf2]);
+ }
+
+ // Make sure we can count properly
+ {
+ assert(encodedLength!(char)('A') == 1);
+ assert(encodedLength!(char)('\u00E3') == 2);
+ assert(encodedLength!(char)('\u2028') == 3);
+ assert(encodedLength!(char)('\U0010FFF0') == 4);
+ assert(encodedLength!(wchar)('A') == 1);
+ assert(encodedLength!(wchar)('\U0010FFF0') == 2);
+ }
+
+ // Make sure we can write into mutable arrays
+ {
+ char[4] buffer;
+ auto n = encode(cast(dchar)'\u00E3',buffer);
+ assert(n == 2);
+ assert(buffer[0] == 0xC3);
+ assert(buffer[1] == 0xA3);
+ }
+}
+
+//=============================================================================
+
+/** Special value returned by $(D safeDecode) */
+enum dchar INVALID_SEQUENCE = cast(dchar) 0xFFFFFFFF;
+
+template EncoderFunctions()
+{
+ // Various forms of read
+
+ template ReadFromString()
+ {
+ @property bool canRead() { return s.length != 0; }
+ E peek() @safe pure @nogc nothrow { return s[0]; }
+ E read() @safe pure @nogc nothrow { E t = s[0]; s = s[1..$]; return t; }
+ }
+
+ template ReverseReadFromString()
+ {
+ @property bool canRead() { return s.length != 0; }
+ E peek() @safe pure @nogc nothrow { return s[$-1]; }
+ E read() @safe pure @nogc nothrow { E t = s[$-1]; s = s[0..$-1]; return t; }
+ }
+
+ // Various forms of Write
+
+ template WriteToString()
+ {
+ E[] s;
+ void write(E c) @safe pure nothrow { s ~= c; }
+ }
+
+ template WriteToArray()
+ {
+ void write(E c) @safe pure @nogc nothrow { array[0] = c; array = array[1..$]; }
+ }
+
+ template WriteToDelegate()
+ {
+ void write(E c) { dg(c); }
+ }
+
+ // Functions we will export
+
+ template EncodeViaWrite()
+ {
+ mixin encodeViaWrite;
+ void encode(dchar c) { encodeViaWrite(c); }
+ }
+
+ template SkipViaRead()
+ {
+ mixin skipViaRead;
+ void skip() @safe pure @nogc nothrow { skipViaRead(); }
+ }
+
+ template DecodeViaRead()
+ {
+ mixin decodeViaRead;
+ dchar decode() @safe pure @nogc nothrow { return decodeViaRead(); }
+ }
+
+ template SafeDecodeViaRead()
+ {
+ mixin safeDecodeViaRead;
+ dchar safeDecode() @safe pure @nogc nothrow { return safeDecodeViaRead(); }
+ }
+
+ template DecodeReverseViaRead()
+ {
+ mixin decodeReverseViaRead;
+ dchar decodeReverse() @safe pure @nogc nothrow { return decodeReverseViaRead(); }
+ }
+
+ // Encoding to different destinations
+
+ template EncodeToString()
+ {
+ mixin WriteToString;
+ mixin EncodeViaWrite;
+ }
+
+ template EncodeToArray()
+ {
+ mixin WriteToArray;
+ mixin EncodeViaWrite;
+ }
+
+ template EncodeToDelegate()
+ {
+ mixin WriteToDelegate;
+ mixin EncodeViaWrite;
+ }
+
+ // Decoding functions
+
+ template SkipFromString()
+ {
+ mixin ReadFromString;
+ mixin SkipViaRead;
+ }
+
+ template DecodeFromString()
+ {
+ mixin ReadFromString;
+ mixin DecodeViaRead;
+ }
+
+ template SafeDecodeFromString()
+ {
+ mixin ReadFromString;
+ mixin SafeDecodeViaRead;
+ }
+
+ template DecodeReverseFromString()
+ {
+ mixin ReverseReadFromString;
+ mixin DecodeReverseViaRead;
+ }
+
+ //=========================================================================
+
+ // Below are the functions we will ultimately expose to the user
+
+ E[] encode(dchar c) @safe pure nothrow
+ {
+ mixin EncodeToString e;
+ e.encode(c);
+ return e.s;
+ }
+
+ void encode(dchar c, ref E[] array) @safe pure nothrow
+ {
+ mixin EncodeToArray e;
+ e.encode(c);
+ }
+
+ void encode(dchar c, void delegate(E) dg)
+ {
+ mixin EncodeToDelegate e;
+ e.encode(c);
+ }
+
+ void skip(ref const(E)[] s) @safe pure nothrow
+ {
+ mixin SkipFromString e;
+ e.skip();
+ }
+
+ dchar decode(S)(ref S s)
+ {
+ mixin DecodeFromString e;
+ return e.decode();
+ }
+
+ dchar safeDecode(S)(ref S s)
+ {
+ mixin SafeDecodeFromString e;
+ return e.safeDecode();
+ }
+
+ dchar decodeReverse(ref const(E)[] s) @safe pure nothrow
+ {
+ mixin DecodeReverseFromString e;
+ return e.decodeReverse();
+ }
+}
+
+//=========================================================================
+
+struct CodePoints(E)
+{
+ const(E)[] s;
+
+ this(const(E)[] s)
+ in
+ {
+ assert(isValid(s));
+ }
+ body
+ {
+ this.s = s;
+ }
+
+ int opApply(scope int delegate(ref dchar) dg)
+ {
+ int result = 0;
+ while (s.length != 0)
+ {
+ dchar c = decode(s);
+ result = dg(c);
+ if (result != 0) break;
+ }
+ return result;
+ }
+
+ int opApply(scope int delegate(ref size_t, ref dchar) dg)
+ {
+ size_t i = 0;
+ int result = 0;
+ while (s.length != 0)
+ {
+ immutable len = s.length;
+ dchar c = decode(s);
+ size_t j = i; // We don't want the delegate corrupting i
+ result = dg(j,c);
+ if (result != 0) break;
+ i += len - s.length;
+ }
+ return result;
+ }
+
+ int opApplyReverse(scope int delegate(ref dchar) dg)
+ {
+ int result = 0;
+ while (s.length != 0)
+ {
+ dchar c = decodeReverse(s);
+ result = dg(c);
+ if (result != 0) break;
+ }
+ return result;
+ }
+
+ int opApplyReverse(scope int delegate(ref size_t, ref dchar) dg)
+ {
+ int result = 0;
+ while (s.length != 0)
+ {
+ dchar c = decodeReverse(s);
+ size_t i = s.length;
+ result = dg(i,c);
+ if (result != 0) break;
+ }
+ return result;
+ }
+}
+
+struct CodeUnits(E)
+{
+ E[] s;
+
+ this(dchar d)
+ in
+ {
+ assert(isValidCodePoint(d));
+ }
+ body
+ {
+ s = encode!(E)(d);
+ }
+
+ int opApply(scope int delegate(ref E) dg)
+ {
+ int result = 0;
+ foreach (E c;s)
+ {
+ result = dg(c);
+ if (result != 0) break;
+ }
+ return result;
+ }
+
+ int opApplyReverse(scope int delegate(ref E) dg)
+ {
+ int result = 0;
+ foreach_reverse (E c;s)
+ {
+ result = dg(c);
+ if (result != 0) break;
+ }
+ return result;
+ }
+}
+
+//=============================================================================
+
+template EncoderInstance(E)
+{
+ static assert(false,"Cannot instantiate EncoderInstance for type "
+ ~ E.stringof);
+}
+
+private template GenericEncoder()
+{
+ bool canEncode(dchar c) @safe pure @nogc nothrow
+ {
+ if (c < m_charMapStart || (c > 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
+//=============================================================================
+
+/** Defines various character sets. */
+enum AsciiChar : ubyte { init }
+/// Ditto
+alias AsciiString = immutable(AsciiChar)[];
+
+template EncoderInstance(CharType : AsciiChar)
+{
+ alias E = AsciiChar;
+ alias EString = AsciiString;
+
+ @property string encodingName() @safe pure nothrow @nogc
+ {
+ return "ASCII";
+ }
+
+ bool canEncode(dchar c) @safe pure nothrow @nogc
+ {
+ return c < 0x80;
+ }
+
+ bool isValidCodeUnit(AsciiChar c) @safe pure nothrow @nogc
+ {
+ return c < 0x80;
+ }
+
+ size_t encodedLength(dchar c) @safe pure nothrow @nogc
+ in
+ {
+ assert(canEncode(c));
+ }
+ body
+ {
+ return 1;
+ }
+
+ void encodeX(Range)(dchar c, Range r)
+ {
+ if (!canEncode(c)) c = '?';
+ r.write(cast(AsciiChar) c);
+ }
+
+ void encodeViaWrite()(dchar c)
+ {
+ if (!canEncode(c)) c = '?';
+ write(cast(AsciiChar) c);
+ }
+
+ void skipViaRead()()
+ {
+ read();
+ }
+
+ dchar decodeViaRead()()
+ {
+ return read();
+ }
+
+ dchar safeDecodeViaRead()()
+ {
+ immutable c = read();
+ return canEncode(c) ? c : INVALID_SEQUENCE;
+ }
+
+ dchar decodeReverseViaRead()()
+ {
+ return read();
+ }
+
+ @property EString replacementSequence() @safe pure nothrow @nogc
+ {
+ return cast(EString)("?");
+ }
+
+ mixin EncoderFunctions;
+}
+
+//=============================================================================
+// ISO-8859-1
+//=============================================================================
+
+/** Defines an Latin1-encoded character. */
+enum Latin1Char : ubyte { init }
+/**
+Defines an Latin1-encoded string (as an array of $(D
+immutable(Latin1Char))).
+ */
+alias Latin1String = immutable(Latin1Char)[];
+
+template EncoderInstance(CharType : Latin1Char)
+{
+ alias E = Latin1Char;
+ alias EString = Latin1String;
+
+ @property string encodingName() @safe pure nothrow @nogc
+ {
+ return "ISO-8859-1";
+ }
+
+ bool canEncode(dchar c) @safe pure nothrow @nogc
+ {
+ return c < 0x100;
+ }
+
+ bool isValidCodeUnit(Latin1Char c) @safe pure nothrow @nogc
+ {
+ return true;
+ }
+
+ size_t encodedLength(dchar c) @safe pure nothrow @nogc
+ in
+ {
+ assert(canEncode(c));
+ }
+ body
+ {
+ return 1;
+ }
+
+ void encodeViaWrite()(dchar c)
+ {
+ if (!canEncode(c)) c = '?';
+ write(cast(Latin1Char) c);
+ }
+
+ void skipViaRead()()
+ {
+ read();
+ }
+
+ dchar decodeViaRead()()
+ {
+ return read();
+ }
+
+ dchar safeDecodeViaRead()()
+ {
+ return read();
+ }
+
+ dchar decodeReverseViaRead()()
+ {
+ return read();
+ }
+
+ @property EString replacementSequence() @safe pure nothrow @nogc
+ {
+ return cast(EString)("?");
+ }
+
+ mixin EncoderFunctions;
+}
+
+//=============================================================================
+// ISO-8859-2
+//=============================================================================
+
+/// Defines a Latin2-encoded character.
+enum Latin2Char : ubyte { init }
+
+/**
+ * Defines an Latin2-encoded string (as an array of $(D
+ * immutable(Latin2Char))).
+ */
+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')
+ ];
+
+ mixin GenericEncoder!();
+}
+
+//=============================================================================
+// WINDOWS-1250
+//=============================================================================
+
+/// Defines a Windows1250-encoded character.
+enum Windows1250Char : ubyte { init }
+
+/**
+ * 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')
+ ];
+
+ mixin GenericEncoder!();
+}
+
+//=============================================================================
+// WINDOWS-1252
+//=============================================================================
+
+/// Defines a Windows1252-encoded character.
+enum Windows1252Char : ubyte { init }
+
+/**
+ * Defines an Windows1252-encoded string (as an array of $(D
+ * immutable(Windows1252Char))).
+ */
+alias Windows1252String = immutable(Windows1252Char)[];
+
+template EncoderInstance(CharType : Windows1252Char)
+{
+ import std.typecons : Tuple, tuple;
+
+ alias E = Windows1252Char;
+ alias EString = Windows1252String;
+
+ @property string encodingName() @safe pure nothrow @nogc
+ {
+ return "windows-1252";
+ }
+
+ private static immutable dchar m_charMapStart = 0x80;
+ private static immutable dchar m_charMapEnd = 0x9f;
+
+ 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!();
+}
+
+//=============================================================================
+// UTF-8
+//=============================================================================
+
+template EncoderInstance(CharType : char)
+{
+ alias E = char;
+ alias EString = immutable(char)[];
+
+ @property string encodingName() @safe pure nothrow @nogc
+ {
+ return "UTF-8";
+ }
+
+ bool canEncode(dchar c) @safe pure nothrow @nogc
+ {
+ return isValidCodePoint(c);
+ }
+
+ bool isValidCodeUnit(char c) @safe pure nothrow @nogc
+ {
+ return (c < 0xC0 || (c >= 0xC2 && c < 0xF5));
+ }
+
+ immutable ubyte[128] tailTable =
+ [
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
+ 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
+ 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,
+ 3,3,3,3,3,3,3,3,4,4,4,4,5,5,6,0,
+ ];
+
+ private int tails(char c) @safe pure nothrow @nogc
+ in
+ {
+ assert(c >= 0x80);
+ }
+ body
+ {
+ return tailTable[c-0x80];
+ }
+
+ size_t encodedLength(dchar c) @safe pure nothrow @nogc
+ in
+ {
+ assert(canEncode(c));
+ }
+ body
+ {
+ if (c < 0x80) return 1;
+ if (c < 0x800) return 2;
+ if (c < 0x10000) return 3;
+ return 4;
+ }
+
+ void encodeViaWrite()(dchar c)
+ {
+ if (c < 0x80)
+ {
+ write(cast(char) c);
+ }
+ else if (c < 0x800)
+ {
+ write(cast(char)((c >> 6) + 0xC0));
+ write(cast(char)((c & 0x3F) + 0x80));
+ }
+ else if (c < 0x10000)
+ {
+ write(cast(char)((c >> 12) + 0xE0));
+ write(cast(char)(((c >> 6) & 0x3F) + 0x80));
+ write(cast(char)((c & 0x3F) + 0x80));
+ }
+ else
+ {
+ write(cast(char)((c >> 18) + 0xF0));
+ write(cast(char)(((c >> 12) & 0x3F) + 0x80));
+ write(cast(char)(((c >> 6) & 0x3F) + 0x80));
+ write(cast(char)((c & 0x3F) + 0x80));
+ }
+ }
+
+ void skipViaRead()()
+ {
+ auto c = read();
+ if (c < 0xC0) return;
+ int n = tails(cast(char) c);
+ for (size_t i=0; i<n; ++i)
+ {
+ read();
+ }
+ }
+
+ dchar decodeViaRead()()
+ {
+ dchar c = read();
+ if (c < 0xC0) return c;
+ int n = tails(cast(char) c);
+ c &= (1 << (6 - n)) - 1;
+ for (size_t i=0; i<n; ++i)
+ {
+ c = (c << 6) + (read() & 0x3F);
+ }
+ return c;
+ }
+
+ dchar safeDecodeViaRead()()
+ {
+ dchar c = read();
+ if (c < 0x80) return c;
+ int n = tails(cast(char) c);
+ if (n == 0) return INVALID_SEQUENCE;
+
+ if (!canRead) return INVALID_SEQUENCE;
+ size_t d = peek();
+ immutable err =
+ (
+ (c < 0xC2) // fail overlong 2-byte sequences
+ || (c > 0xF4) // fail overlong 4-6-byte sequences
+ || (c == 0xE0 && ((d & 0xE0) == 0x80)) // fail overlong 3-byte sequences
+ || (c == 0xED && ((d & 0xE0) == 0xA0)) // fail surrogates
+ || (c == 0xF0 && ((d & 0xF0) == 0x80)) // fail overlong 4-byte sequences
+ || (c == 0xF4 && ((d & 0xF0) >= 0x90)) // fail code points > 0x10FFFF
+ );
+
+ c &= (1 << (6 - n)) - 1;
+ for (size_t i=0; i<n; ++i)
+ {
+ if (!canRead) return INVALID_SEQUENCE;
+ d = peek();
+ if ((d & 0xC0) != 0x80) return INVALID_SEQUENCE;
+ c = (c << 6) + (read() & 0x3F);
+ }
+
+ return err ? INVALID_SEQUENCE : c;
+ }
+
+ dchar decodeReverseViaRead()()
+ {
+ dchar c = read();
+ if (c < 0x80) return c;
+ size_t shift = 0;
+ c &= 0x3F;
+ for (size_t i=0; i<4; ++i)
+ {
+ shift += 6;
+ auto d = read();
+ size_t n = tails(cast(char) d);
+ immutable mask = n == 0 ? 0x3F : (1 << (6 - n)) - 1;
+ c += ((d & mask) << shift);
+ if (n != 0) break;
+ }
+ return c;
+ }
+
+ @property EString replacementSequence() @safe pure nothrow @nogc
+ {
+ return "\uFFFD";
+ }
+
+ mixin EncoderFunctions;
+}
+
+//=============================================================================
+// UTF-16
+//=============================================================================
+
+template EncoderInstance(CharType : wchar)
+{
+ alias E = wchar;
+ alias EString = immutable(wchar)[];
+
+ @property string encodingName() @safe pure nothrow @nogc
+ {
+ return "UTF-16";
+ }
+
+ bool canEncode(dchar c) @safe pure nothrow @nogc
+ {
+ return isValidCodePoint(c);
+ }
+
+ bool isValidCodeUnit(wchar c) @safe pure nothrow @nogc
+ {
+ return true;
+ }
+
+ size_t encodedLength(dchar c) @safe pure nothrow @nogc
+ in
+ {
+ assert(canEncode(c));
+ }
+ body
+ {
+ return (c < 0x10000) ? 1 : 2;
+ }
+
+ void encodeViaWrite()(dchar c)
+ {
+ if (c < 0x10000)
+ {
+ write(cast(wchar) c);
+ }
+ else
+ {
+ size_t n = c - 0x10000;
+ write(cast(wchar)(0xD800 + (n >> 10)));
+ write(cast(wchar)(0xDC00 + (n & 0x3FF)));
+ }
+ }
+
+ void skipViaRead()()
+ {
+ immutable c = read();
+ if (c < 0xD800 || c >= 0xE000) return;
+ read();
+ }
+
+ dchar decodeViaRead()()
+ {
+ wchar c = read();
+ if (c < 0xD800 || c >= 0xE000) return cast(dchar) c;
+ wchar d = read();
+ c &= 0x3FF;
+ d &= 0x3FF;
+ return 0x10000 + (c << 10) + d;
+ }
+
+ dchar safeDecodeViaRead()()
+ {
+ wchar c = read();
+ if (c < 0xD800 || c >= 0xE000) return cast(dchar) c;
+ if (c >= 0xDC00) return INVALID_SEQUENCE;
+ if (!canRead) return INVALID_SEQUENCE;
+ wchar d = peek();
+ if (d < 0xDC00 || d >= 0xE000) return INVALID_SEQUENCE;
+ d = read();
+ c &= 0x3FF;
+ d &= 0x3FF;
+ return 0x10000 + (c << 10) + d;
+ }
+
+ dchar decodeReverseViaRead()()
+ {
+ wchar c = read();
+ if (c < 0xD800 || c >= 0xE000) return cast(dchar) c;
+ wchar d = read();
+ c &= 0x3FF;
+ d &= 0x3FF;
+ return 0x10000 + (d << 10) + c;
+ }
+
+ @property EString replacementSequence() @safe pure nothrow @nogc
+ {
+ return "\uFFFD"w;
+ }
+
+ mixin EncoderFunctions;
+}
+
+//=============================================================================
+// UTF-32
+//=============================================================================
+
+template EncoderInstance(CharType : dchar)
+{
+ alias E = dchar;
+ alias EString = immutable(dchar)[];
+
+ @property string encodingName() @safe pure nothrow @nogc
+ {
+ return "UTF-32";
+ }
+
+ bool canEncode(dchar c) @safe pure @nogc nothrow
+ {
+ return isValidCodePoint(c);
+ }
+
+ bool isValidCodeUnit(dchar c) @safe pure @nogc nothrow
+ {
+ return isValidCodePoint(c);
+ }
+
+ size_t encodedLength(dchar c) @safe pure @nogc nothrow
+ in
+ {
+ assert(canEncode(c));
+ }
+ body
+ {
+ return 1;
+ }
+
+ void encodeViaWrite()(dchar c)
+ {
+ write(c);
+ }
+
+ void skipViaRead()()
+ {
+ read();
+ }
+
+ dchar decodeViaRead()()
+ {
+ return cast(dchar) read();
+ }
+
+ dchar safeDecodeViaRead()()
+ {
+ immutable c = read();
+ return isValidCodePoint(c) ? c : INVALID_SEQUENCE;
+ }
+
+ dchar decodeReverseViaRead()()
+ {
+ return cast(dchar) read();
+ }
+
+ @property EString replacementSequence() @safe pure nothrow @nogc
+ {
+ return "\uFFFD"d;
+ }
+
+ mixin EncoderFunctions;
+}
+
+//=============================================================================
+// Below are forwarding functions which expose the function to the user
+
+/**
+Returns true if c is a valid code point
+
+ Note that this includes the non-character code points U+FFFE and U+FFFF,
+ since these are valid code points (even though they are not valid
+ characters).
+
+ Supersedes:
+ This function supersedes $(D std.utf.startsValidDchar()).
+
+ 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) @safe pure nothrow @nogc
+{
+ return c < 0xD800 || (c >= 0xE000 && c < 0x110000);
+}
+
+/**
+ Returns the name of an encoding.
+
+ 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, ISO-8859-2, WINDOWS-1250,
+ WINDOWS-1252
+ */
+@property string encodingName(T)()
+{
+ return EncoderInstance!(T).encodingName;
+}
+
+///
+@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");
+}
+
+/**
+ Returns true iff it is possible to represent the specified codepoint
+ in the encoding.
+
+ 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, ISO-8859-2, WINDOWS-1250,
+ WINDOWS-1252
+ */
+bool canEncode(E)(dchar c)
+{
+ return EncoderInstance!(E).canEncode(c);
+}
+
+///
+@safe pure unittest
+{
+ 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));
+}
+
+/// 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);
+}
+
+/**
+ Returns true if the code unit is legal. For example, the byte 0x80 would
+ 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, ISO-8859-2, WINDOWS-1250,
+ WINDOWS-1252
+
+ Params:
+ c = the code unit to be tested
+ */
+bool isValidCodeUnit(E)(E c)
+{
+ return EncoderInstance!(E).isValidCodeUnit(c);
+}
+
+///
+@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(Windows1250Char) 0x80));
+ assert(!isValidCodeUnit(cast(Windows1250Char) 0x81));
+ assert( isValidCodeUnit(cast(Windows1252Char) 0x80));
+ assert(!isValidCodeUnit(cast(Windows1252Char) 0x81));
+}
+
+/**
+ Returns true if the string is encoded correctly
+
+ Supersedes:
+ This function supersedes std.utf.validate(), however note that this
+ 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, ISO-8859-2, WINDOWS-1250,
+ WINDOWS-1252
+
+ Params:
+ s = the string to be tested
+ */
+bool isValid(E)(const(E)[] s)
+{
+ return s.length == validLength(s);
+}
+
+///
+@system pure unittest
+{
+ assert( isValid("\u20AC100"));
+ assert(!isValid(cast(char[3])[167, 133, 175]));
+}
+
+/**
+ 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, ISO-8859-2, WINDOWS-1250,
+ WINDOWS-1252
+
+ Params:
+ s = the string to be tested
+ */
+size_t validLength(E)(const(E)[] s)
+{
+ size_t result, before = void;
+ while ((before = s.length) > 0)
+ {
+ if (EncoderInstance!(E).safeDecode(s) == INVALID_SEQUENCE)
+ break;
+ result += before - s.length;
+ }
+ return result;
+}
+
+/**
+ Sanitizes a string by replacing malformed code unit sequences with valid
+ code unit sequences. The result is guaranteed to be valid for this encoding.
+
+ If the input string is already valid, this function returns the original,
+ otherwise it constructs a new string by replacing all illegal code unit
+ sequences with the encoding's replacement character, Invalid sequences will
+ be replaced with the Unicode replacement character (U+FFFD) if the
+ character repertoire contains it, otherwise invalid sequences will be
+ replaced with '?'.
+
+ Standards: Unicode 5.0, ASCII, ISO-8859-1, ISO-8859-2, WINDOWS-1250,
+ WINDOWS-1252
+
+ Params:
+ s = the string to be sanitized
+ */
+immutable(E)[] sanitize(E)(immutable(E)[] s)
+{
+ size_t n = validLength(s);
+ if (n == s.length) return s;
+
+ auto repSeq = EncoderInstance!(E).replacementSequence;
+
+ // Count how long the string needs to be.
+ // Overestimating is not a problem
+ size_t len = s.length;
+ const(E)[] t = s[n..$];
+ while (t.length != 0)
+ {
+ immutable c = EncoderInstance!(E).safeDecode(t);
+ assert(c == INVALID_SEQUENCE);
+ len += repSeq.length;
+ t = t[validLength(t)..$];
+ }
+
+ // Now do the write
+ E[] array = new E[len];
+ array[0 .. n] = s[0 .. n];
+ size_t offset = n;
+
+ t = s[n..$];
+ while (t.length != 0)
+ {
+ immutable c = EncoderInstance!(E).safeDecode(t);
+ assert(c == INVALID_SEQUENCE);
+ array[offset .. offset+repSeq.length] = repSeq[];
+ offset += repSeq.length;
+ n = validLength(t);
+ array[offset .. offset+n] = t[0 .. n];
+ offset += n;
+ t = t[n..$];
+ }
+ return cast(immutable(E)[])array[0 .. offset];
+}
+
+///
+@system pure unittest
+{
+ assert(sanitize("hello \xF0\x80world") == "hello \xEF\xBF\xBDworld");
+}
+
+/**
+ 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.
+
+ Standards: Unicode 5.0, ASCII, ISO-8859-1, ISO-8859-2, WINDOWS-1250,
+ WINDOWS-1252
+
+ Params:
+ s = the string to be sliced
+ */
+size_t firstSequence(E)(const(E)[] s)
+in
+{
+ assert(s.length != 0);
+ const(E)[] u = s;
+ assert(safeDecode(u) != INVALID_SEQUENCE);
+}
+body
+{
+ auto before = s.length;
+ EncoderInstance!(E).skip(s);
+ return before - s.length;
+}
+
+///
+@system pure unittest
+{
+ assert(firstSequence("\u20AC1000") == "\u20AC".length);
+ assert(firstSequence("hel") == "h".length);
+}
+
+/**
+ Returns the length of the last encoded sequence.
+
+ 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, ISO-8859-2, WINDOWS-1250,
+ WINDOWS-1252
+
+ Params:
+ s = the string to be sliced
+ */
+size_t lastSequence(E)(const(E)[] s)
+in
+{
+ assert(s.length != 0);
+ assert(isValid(s));
+}
+body
+{
+ const(E)[] t = s;
+ EncoderInstance!(E).decodeReverse(s);
+ return t.length - s.length;
+}
+
+///
+@system pure unittest
+{
+ assert(lastSequence("1000\u20AC") == "\u20AC".length);
+ assert(lastSequence("hellö") == "ö".length);
+}
+
+/**
+ Returns the array index at which the (n+1)th code point begins.
+
+ The input to this function MUST be validly encoded.
+ This is enforced by the function's in-contract.
+
+ Supersedes:
+ This function supersedes std.utf.toUTFindex().
+
+ Standards: Unicode 5.0, ASCII, ISO-8859-1, ISO-8859-2, WINDOWS-1250,
+ WINDOWS-1252
+
+ Params:
+ s = the string to be counted
+ n = the current code point index
+ */
+ptrdiff_t index(E)(const(E)[] s,int n)
+in
+{
+ assert(isValid(s));
+ assert(n >= 0);
+}
+body
+{
+ const(E)[] t = s;
+ for (size_t i=0; i<n; ++i) EncoderInstance!(E).skip(s);
+ return t.length - s.length;
+}
+
+///
+@system pure unittest
+{
+ assert(index("\u20AC100",1) == 3);
+ assert(index("hällo",2) == 3);
+}
+
+/**
+ Decodes a single code point.
+
+ This function removes one or more code units from the start of a string,
+ and returns the decoded code point which those code units represent.
+
+ The input to this function MUST be validly encoded.
+ This is enforced by the function's in-contract.
+
+ Supersedes:
+ This function supersedes std.utf.decode(), however, note that the
+ function codePoints() supersedes it more conveniently.
+
+ 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
+ */
+dchar decode(S)(ref S s)
+in
+{
+ assert(s.length != 0);
+ auto u = s;
+ assert(safeDecode(u) != INVALID_SEQUENCE);
+}
+body
+{
+ return EncoderInstance!(typeof(s[0])).decode(s);
+}
+
+/**
+ Decodes a single code point from the end of a string.
+
+ This function removes one or more code units from the end of a string,
+ and returns the decoded code point which those code units represent.
+
+ 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, ISO-8859-2, WINDOWS-1250,
+ WINDOWS-1252
+
+ Params:
+ s = the string whose first code point is to be decoded
+ */
+dchar decodeReverse(E)(ref const(E)[] s)
+in
+{
+ assert(s.length != 0);
+ assert(isValid(s));
+}
+body
+{
+ return EncoderInstance!(E).decodeReverse(s);
+}
+
+/**
+ Decodes a single code point. The input does not have to be valid.
+
+ This function removes one or more code units from the start of a string,
+ and returns the decoded code point which those code units represent.
+
+ This function will accept an invalidly encoded string as input.
+ 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, ISO-8859-2, WINDOWS-1250,
+ WINDOWS-1252
+
+ Params:
+ s = the string whose first code point is to be decoded
+ */
+dchar safeDecode(S)(ref S s)
+in
+{
+ assert(s.length != 0);
+}
+body
+{
+ return EncoderInstance!(typeof(s[0])).safeDecode(s);
+}
+
+/**
+ Returns the number of code units required to encode a single code point.
+
+ 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.
+
+ Standards: Unicode 5.0, ASCII, ISO-8859-1, ISO-8859-2, WINDOWS-1250,
+ WINDOWS-1252
+
+ Params:
+ c = the code point to be encoded
+ */
+size_t encodedLength(E)(dchar c)
+in
+{
+ assert(isValidCodePoint(c));
+}
+body
+{
+ return EncoderInstance!(E).encodedLength(c);
+}
+
+/**
+ Encodes a single code point.
+
+ This function encodes a single code point into one or more code units.
+ It returns a string containing those code units.
+
+ 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, ISO-8859-2, WINDOWS-1250,
+ WINDOWS-1252
+
+ Params:
+ c = the code point to be encoded
+ */
+E[] encode(E)(dchar c)
+in
+{
+ assert(isValidCodePoint(c));
+}
+body
+{
+ return EncoderInstance!(E).encode(c);
+}
+
+/**
+ Encodes a single code point into an array.
+
+ This function encodes a single code point into one or more code units
+ The code units are stored in a user-supplied fixed-size array,
+ which must be passed by reference.
+
+ 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, ISO-8859-2, WINDOWS-1250,
+ WINDOWS-1252
+
+ Params:
+ c = the code point to be encoded
+ array = the destination array
+
+ Returns:
+ the number of code units written to the array
+ */
+size_t encode(E)(dchar c, E[] array)
+in
+{
+ assert(isValidCodePoint(c));
+}
+body
+{
+ E[] t = array;
+ EncoderInstance!(E).encode(c,t);
+ return array.length - t.length;
+}
+
+/*
+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.
+ */
+size_t encode(E, R)(dchar c, auto ref R range)
+if (isNativeOutputRange!(R, E))
+{
+ static if (is(Unqual!E == char))
+ {
+ if (c <= 0x7F)
+ {
+ put(range, cast(char) c);
+ return 1;
+ }
+ if (c <= 0x7FF)
+ {
+ put(range, cast(char)(0xC0 | (c >> 6)));
+ put(range, cast(char)(0x80 | (c & 0x3F)));
+ return 2;
+ }
+ if (c <= 0xFFFF)
+ {
+ put(range, cast(char)(0xE0 | (c >> 12)));
+ put(range, cast(char)(0x80 | ((c >> 6) & 0x3F)));
+ put(range, cast(char)(0x80 | (c & 0x3F)));
+ return 3;
+ }
+ if (c <= 0x10FFFF)
+ {
+ put(range, cast(char)(0xF0 | (c >> 18)));
+ put(range, cast(char)(0x80 | ((c >> 12) & 0x3F)));
+ put(range, cast(char)(0x80 | ((c >> 6) & 0x3F)));
+ put(range, cast(char)(0x80 | (c & 0x3F)));
+ return 4;
+ }
+ else
+ {
+ assert(0);
+ }
+ }
+ else static if (is(Unqual!E == wchar))
+ {
+ if (c <= 0xFFFF)
+ {
+ range.put(cast(wchar) c);
+ return 1;
+ }
+ range.put(cast(wchar) ((((c - 0x10000) >> 10) & 0x3FF) + 0xD800));
+ range.put(cast(wchar) (((c - 0x10000) & 0x3FF) + 0xDC00));
+ return 2;
+ }
+ else static if (is(Unqual!E == dchar))
+ {
+ range.put(c);
+ return 1;
+ }
+ else
+ {
+ static assert(0);
+ }
+}
+
+@safe pure unittest
+{
+ import std.array;
+ Appender!(char[]) r;
+ assert(encode!(char)('T', r) == 1);
+ assert(encode!(wchar)('T', r) == 1);
+ assert(encode!(dchar)('T', r) == 1);
+}
+
+/**
+ Encodes a single code point to a delegate.
+
+ This function encodes a single code point into one or more code units.
+ The code units are passed one at a time to the supplied delegate.
+
+ 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, ISO-8859-2, WINDOWS-1250,
+ WINDOWS-1252
+
+ Params:
+ c = the code point to be encoded
+ dg = the delegate to invoke for each code unit
+ */
+void encode(E)(dchar c, void delegate(E) dg)
+in
+{
+ assert(isValidCodePoint(c));
+}
+body
+{
+ EncoderInstance!(E).encode(c,dg);
+}
+
+/**
+Encodes the contents of $(D s) in units of type $(D Tgt), writing the result to an
+output range.
+
+Returns: The number of $(D Tgt) elements written.
+Params:
+Tgt = Element type of $(D range).
+s = Input array.
+range = Output range.
+ */
+size_t encode(Tgt, Src, R)(in Src[] s, R range)
+{
+ size_t result;
+ foreach (c; s)
+ {
+ result += encode!(Tgt)(c, range);
+ }
+ return result;
+}
+
+/**
+ Returns a foreachable struct which can bidirectionally iterate over all
+ code points in a string.
+
+ The input to this function MUST be validly encoded.
+ This is enforced by the function's in-contract.
+
+ You can foreach either
+ with or without an index. If an index is specified, it will be initialized
+ at each iteration with the offset into the string at which the code point
+ begins.
+
+ Supersedes:
+ This function supersedes std.utf.decode().
+
+ Standards: Unicode 5.0, ASCII, ISO-8859-1, ISO-8859-2, WINDOWS-1250,
+ WINDOWS-1252
+
+ Params:
+ s = the string to be decoded
+
+ Example:
+ --------------------------------------------------------
+ string s = "hello world";
+ 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)
+ in that the latter will fall over on encountering U+FFFF.
+ */
+CodePoints!(E) codePoints(E)(immutable(E)[] s)
+in
+{
+ assert(isValid(s));
+}
+body
+{
+ return CodePoints!(E)(s);
+}
+
+///
+@system unittest
+{
+ string s = "hello";
+ string t;
+ foreach (c;codePoints(s))
+ {
+ t ~= cast(char) c;
+ }
+ assert(s == t);
+}
+
+/**
+ Returns a foreachable struct which can bidirectionally iterate over all
+ code units in a code point.
+
+ 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 type in the template parameter.
+
+ Supersedes:
+ This function supersedes std.utf.encode().
+
+ Standards: Unicode 5.0, ASCII, ISO-8859-1, ISO-8859-2, WINDOWS-1250,
+ WINDOWS-1252
+
+ Params:
+ c = the code point to be encoded
+ */
+CodeUnits!(E) codeUnits(E)(dchar c)
+in
+{
+ assert(isValidCodePoint(c));
+}
+body
+{
+ return CodeUnits!(E)(c);
+}
+
+///
+@system unittest
+{
+ char[] a;
+ foreach (c;codeUnits!(char)(cast(dchar)'\u20AC'))
+ {
+ a ~= c;
+ }
+ assert(a.length == 3);
+ assert(a[0] == 0xE2);
+ assert(a[1] == 0x82);
+ assert(a[2] == 0xAC);
+}
+
+/**
+ Convert a string from one encoding to another.
+
+ Supersedes:
+ This function supersedes std.utf.toUTF8(), std.utf.toUTF16() and
+ std.utf.toUTF32()
+ (but note that to!() supersedes it more conveniently).
+
+ Standards: Unicode 5.0, ASCII, ISO-8859-1, ISO-8859-2, WINDOWS-1250,
+ WINDOWS-1252
+
+ Params:
+ s = Source string. $(B Must) be validly encoded.
+ This is enforced by the function's in-contract.
+ r = Destination string
+
+ See_Also:
+ $(REF to, std,conv)
+ */
+void transcode(Src, Dst)(Src[] s, out Dst[] r)
+in
+{
+ assert(isValid(s));
+}
+body
+{
+ static if (is(Src == Dst) && is(Src == immutable))
+ {
+ r = s;
+ }
+ else static if (is(Unqual!Src == AsciiChar))
+ {
+ transcode(cast(const(char)[])s, r);
+ }
+ else
+ {
+ static if (is(Unqual!Dst == wchar))
+ {
+ immutable minReservePlace = 2;
+ }
+ else static if (is(Unqual!Dst == dchar))
+ {
+ immutable minReservePlace = 1;
+ }
+ else
+ {
+ immutable minReservePlace = 6;
+ }
+
+ auto buffer = new Unqual!Dst[s.length];
+ auto tmpBuffer = buffer;
+
+ while (s.length != 0)
+ {
+ if (tmpBuffer.length < minReservePlace)
+ {
+ size_t prevLength = buffer.length;
+ buffer.length += s.length + minReservePlace;
+ tmpBuffer = buffer[prevLength - tmpBuffer.length .. $];
+ }
+ EncoderInstance!(Unqual!Dst).encode(decode(s), tmpBuffer);
+ }
+
+ r = cast(Dst[]) buffer[0 .. buffer.length - tmpBuffer.length];
+ }
+}
+
+///
+@system pure unittest
+{
+ wstring ws;
+ // transcode from UTF-8 to UTF-16
+ transcode("hello world",ws);
+ assert(ws == "hello world"w);
+
+ Latin1String ls;
+ // transcode from UTF-16 to ISO-8859-1
+ transcode(ws, ls);
+ assert(ws == "hello world");
+}
+
+@system pure unittest
+{
+ import std.meta;
+ import std.range;
+ {
+ import std.conv : to;
+
+ string asciiCharString = to!string(iota(0, 128, 1));
+
+ alias Types = AliasSeq!(string, Latin1String, Latin2String, AsciiString,
+ Windows1250String, Windows1252String, dstring, wstring);
+ foreach (S; Types)
+ foreach (D; Types)
+ {
+ string str;
+ S sStr;
+ D dStr;
+ transcode(asciiCharString, sStr);
+ transcode(sStr, dStr);
+ transcode(dStr, str);
+ assert(asciiCharString == str);
+ }
+ }
+ {
+ string czechChars = "Příliš žluťoučký kůň úpěl ďábelské ódy.";
+ alias Types = AliasSeq!(string, dstring, wstring);
+ foreach (S; Types)
+ foreach (D; Types)
+ {
+ string str;
+ S sStr;
+ D dStr;
+ transcode(czechChars, sStr);
+ transcode(sStr, dStr);
+ transcode(dStr, str);
+ assert(czechChars == str);
+ }
+ }
+}
+
+@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) @safe pure { super(msg); } }
+
+class UnrecognizedEncodingException : EncodingException
+{
+ private this(string msg) @safe pure { super(msg); }
+}
+
+/** Abstract base class of all encoding schemes */
+abstract class EncodingScheme
+{
+ import std.uni : toLower;
+
+ /**
+ * Registers a subclass of EncodingScheme.
+ *
+ * This function allows user-defined subclasses of EncodingScheme to
+ * be declared in other modules.
+ *
+ * Params:
+ * Klass = The subclass of EncodingScheme to register.
+ *
+ * Example:
+ * ----------------------------------------------
+ * class Amiga1251 : EncodingScheme
+ * {
+ * shared static this()
+ * {
+ * 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();
+ if (scheme is null)
+ throw new EncodingException("Unable to create class "~className);
+ foreach (encodingName;scheme.names())
+ {
+ supportedFactories[toLower(encodingName)] = className;
+ }
+ }
+
+ /**
+ * Obtains a subclass of EncodingScheme which is capable of encoding
+ * and decoding the named encoding scheme.
+ *
+ * This function is only aware of EncodingSchemes which have been
+ * registered with the register() function.
+ *
+ * Example:
+ * ---------------------------------------------------
+ * auto scheme = EncodingScheme.create("Amiga-1251");
+ * ---------------------------------------------------
+ */
+ static EncodingScheme create(string encodingName)
+ {
+ static bool registerDefaultEncodings()
+ {
+ EncodingScheme.register!EncodingSchemeASCII;
+ EncodingScheme.register!EncodingSchemeLatin1;
+ EncodingScheme.register!EncodingSchemeLatin2;
+ EncodingScheme.register!EncodingSchemeWindows1250;
+ EncodingScheme.register!EncodingSchemeWindows1252;
+ EncodingScheme.register!EncodingSchemeUtf8;
+ EncodingScheme.register!EncodingSchemeUtf16Native;
+ EncodingScheme.register!EncodingSchemeUtf32Native;
+ return true;
+ }
+
+ static shared bool initialized;
+ import std.concurrency : initOnce;
+ initOnce!initialized(registerDefaultEncodings());
+ 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();
+ if (scheme is null) throw new EncodingException("Unable to create class "~className);
+ return scheme;
+ }
+
+ const
+ {
+ /**
+ * Returns the standard name of the encoding scheme
+ */
+ abstract override string toString();
+
+ /**
+ * Returns an array of all known names for this encoding scheme
+ */
+ abstract string[] names();
+
+ /**
+ * Returns true if the character c can be represented
+ * in this encoding scheme.
+ */
+ abstract bool canEncode(dchar c);
+
+ /**
+ * Returns the number of ubytes required to encode this code point.
+ *
+ * The input to this function MUST be a valid code point.
+ *
+ * Params:
+ * c = the code point to be encoded
+ *
+ * Returns:
+ * the number of ubytes required.
+ */
+ abstract size_t encodedLength(dchar c);
+
+ /**
+ * Encodes a single code point into a user-supplied, fixed-size buffer.
+ *
+ * This function encodes a single code point into one or more ubytes.
+ * The supplied buffer must be code unit aligned.
+ * (For example, UTF-16LE or UTF-16BE must be wchar-aligned,
+ * UTF-32LE or UTF-32BE must be dchar-aligned, etc.)
+ *
+ * The input to this function MUST be a valid code point.
+ *
+ * Params:
+ * c = the code point to be encoded
+ * buffer = the destination array
+ *
+ * Returns:
+ * the number of ubytes written.
+ */
+ abstract size_t encode(dchar c, ubyte[] buffer);
+
+ /**
+ * Decodes a single code point.
+ *
+ * This function removes one or more ubytes from the start of an array,
+ * and returns the decoded code point which those ubytes represent.
+ *
+ * The input to this function MUST be validly encoded.
+ *
+ * Params:
+ * s = the array whose first code point is to be decoded
+ */
+ abstract dchar decode(ref const(ubyte)[] s);
+
+ /**
+ * Decodes a single code point. The input does not have to be valid.
+ *
+ * This function removes one or more ubytes from the start of an array,
+ * and returns the decoded code point which those ubytes represent.
+ *
+ * This function will accept an invalidly encoded array as input.
+ * If an invalid sequence is found at the start of the string, this
+ * function will remove it, and return the value INVALID_SEQUENCE.
+ *
+ * Params:
+ * s = the array whose first code point is to be decoded
+ */
+ abstract dchar safeDecode(ref const(ubyte)[] s);
+
+ /**
+ * Returns the sequence of ubytes to be used to represent
+ * any character which cannot be represented in the encoding scheme.
+ *
+ * Normally this will be a representation of some substitution
+ * character, such as U+FFFD or '?'.
+ */
+ abstract @property immutable(ubyte)[] replacementSequence();
+ }
+
+ /**
+ * Returns true if the array is encoded correctly
+ *
+ * Params:
+ * s = the array to be tested
+ */
+ bool isValid(const(ubyte)[] s)
+ {
+ while (s.length != 0)
+ {
+ if (safeDecode(s) == INVALID_SEQUENCE)
+ return false;
+ }
+ return true;
+ }
+
+ /**
+ * Returns the length of the longest possible substring, starting from
+ * the first element, which is validly encoded.
+ *
+ * Params:
+ * s = the array to be tested
+ */
+ size_t validLength()(const(ubyte)[] s)
+ {
+ const(ubyte)[] r = s;
+ const(ubyte)[] t = s;
+ while (s.length != 0)
+ {
+ if (safeDecode(s) == INVALID_SEQUENCE) break;
+ t = s;
+ }
+ return r.length - t.length;
+ }
+
+ /**
+ * Sanitizes an array by replacing malformed ubyte sequences with valid
+ * ubyte sequences. The result is guaranteed to be valid for this
+ * encoding scheme.
+ *
+ * If the input array is already valid, this function returns the
+ * original, otherwise it constructs a new array by replacing all illegal
+ * sequences with the encoding scheme's replacement sequence.
+ *
+ * Params:
+ * s = the string to be sanitized
+ */
+ immutable(ubyte)[] sanitize()(immutable(ubyte)[] s)
+ {
+ auto n = validLength(s);
+ if (n == s.length) return s;
+
+ auto repSeq = replacementSequence;
+
+ // Count how long the string needs to be.
+ // Overestimating is not a problem
+ auto len = s.length;
+ const(ubyte)[] t = s[n..$];
+ while (t.length != 0)
+ {
+ immutable c = safeDecode(t);
+ assert(c == INVALID_SEQUENCE);
+ len += repSeq.length;
+ t = t[validLength(t)..$];
+ }
+
+ // Now do the write
+ ubyte[] array = new ubyte[len];
+ array[0 .. n] = s[0 .. n];
+ auto offset = n;
+
+ t = s[n..$];
+ while (t.length != 0)
+ {
+ immutable c = safeDecode(t);
+ assert(c == INVALID_SEQUENCE);
+ array[offset .. offset+repSeq.length] = repSeq[];
+ offset += repSeq.length;
+ n = validLength(t);
+ array[offset .. offset+n] = t[0 .. n];
+ offset += n;
+ t = t[n..$];
+ }
+ return cast(immutable(ubyte)[])array[0 .. offset];
+ }
+
+ /**
+ * 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.
+ *
+ * Params:
+ * s = the array to be sliced
+ */
+ size_t firstSequence()(const(ubyte)[] s)
+ in
+ {
+ assert(s.length != 0);
+ const(ubyte)[] u = s;
+ assert(safeDecode(u) != INVALID_SEQUENCE);
+ }
+ body
+ {
+ const(ubyte)[] t = s;
+ decode(s);
+ return t.length - s.length;
+ }
+
+ /**
+ * Returns the total number of code points encoded in a ubyte array.
+ *
+ * The input to this function MUST be validly encoded.
+ * This is enforced by the function's in-contract.
+ *
+ * Params:
+ * s = the string to be counted
+ */
+ size_t count()(const(ubyte)[] s)
+ in
+ {
+ assert(isValid(s));
+ }
+ body
+ {
+ size_t n = 0;
+ while (s.length != 0)
+ {
+ decode(s);
+ ++n;
+ }
+ return n;
+ }
+
+ /**
+ * Returns the array index at which the (n+1)th code point begins.
+ *
+ * The input to this function MUST be validly encoded.
+ * This is enforced by the function's in-contract.
+ *
+ * Params:
+ * s = the string to be counted
+ * n = the current code point index
+ */
+ ptrdiff_t index()(const(ubyte)[] s, size_t n)
+ in
+ {
+ assert(isValid(s));
+ assert(n >= 0);
+ }
+ body
+ {
+ const(ubyte)[] t = s;
+ for (size_t i=0; i<n; ++i) decode(s);
+ return t.length - s.length;
+ }
+
+ __gshared EncodingScheme function()[string] supported;
+ __gshared string[string] supportedFactories;
+}
+
+/**
+ EncodingScheme to handle ASCII
+
+ This scheme recognises the following names:
+ "ANSI_X3.4-1968",
+ "ANSI_X3.4-1986",
+ "ASCII",
+ "IBM367",
+ "ISO646-US",
+ "ISO_646.irv:1991",
+ "US-ASCII",
+ "cp367",
+ "csASCII"
+ "iso-ir-6",
+ "us"
+ */
+class EncodingSchemeASCII : EncodingScheme
+{
+ /* // moved to std.internal.phobosinit
+ shared static this()
+ {
+ EncodingScheme.register("std.encoding.EncodingSchemeASCII");
+ }*/
+
+ const
+ {
+ override string[] names() @safe pure nothrow
+ {
+ return
+ [
+ "ANSI_X3.4-1968",
+ "ANSI_X3.4-1986",
+ "ASCII",
+ "IBM367",
+ "ISO646-US",
+ "ISO_646.irv:1991",
+ "US-ASCII",
+ "cp367",
+ "csASCII",
+ "iso-ir-6",
+ "us"
+ ];
+ }
+
+ override string toString() @safe pure nothrow @nogc
+ {
+ return "ASCII";
+ }
+
+ override bool canEncode(dchar c) @safe pure nothrow @nogc
+ {
+ return std.encoding.canEncode!(AsciiChar)(c);
+ }
+
+ override size_t encodedLength(dchar c) @safe pure nothrow @nogc
+ {
+ return std.encoding.encodedLength!(AsciiChar)(c);
+ }
+
+ override size_t encode(dchar c, ubyte[] buffer) @safe pure nothrow @nogc
+ {
+ auto r = cast(AsciiChar[]) buffer;
+ return std.encoding.encode(c,r);
+ }
+
+ override dchar decode(ref const(ubyte)[] s) @safe pure nothrow @nogc
+ {
+ auto t = cast(const(AsciiChar)[]) 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(AsciiChar)[]) 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 Latin-1
+
+ This scheme recognises the following names:
+ "CP819",
+ "IBM819",
+ "ISO-8859-1",
+ "ISO_8859-1",
+ "ISO_8859-1:1987",
+ "csISOLatin1",
+ "iso-ir-100",
+ "l1",
+ "latin1"
+ */
+class EncodingSchemeLatin1 : EncodingScheme
+{
+ /* // moved to std.internal.phobosinit
+ shared static this()
+ {
+ EncodingScheme.register("std.encoding.EncodingSchemeLatin1");
+ }*/
+
+ const
+ {
+ override string[] names() @safe pure nothrow
+ {
+ return
+ [
+ "CP819",
+ "IBM819",
+ "ISO-8859-1",
+ "ISO_8859-1",
+ "ISO_8859-1:1987",
+ "csISOLatin1",
+ "iso-ir-100",
+ "l1",
+ "latin1"
+ ];
+ }
+
+ override string toString() @safe pure nothrow @nogc
+ {
+ return "ISO-8859-1";
+ }
+
+ override bool canEncode(dchar c) @safe pure nothrow @nogc
+ {
+ return std.encoding.canEncode!(Latin1Char)(c);
+ }
+
+ override size_t encodedLength(dchar c) @safe pure nothrow @nogc
+ {
+ return std.encoding.encodedLength!(Latin1Char)(c);
+ }
+
+ override size_t encode(dchar c, ubyte[] buffer) @safe pure nothrow @nogc
+ {
+ auto r = cast(Latin1Char[]) buffer;
+ return std.encoding.encode(c,r);
+ }
+
+ override dchar decode(ref const(ubyte)[] s) @safe pure nothrow @nogc
+ {
+ auto t = cast(const(Latin1Char)[]) 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(Latin1Char)[]) 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 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)[])"?";
+ }
+ }
+}
+
+/**
+ EncodingScheme to handle Windows-1252
+
+ This scheme recognises the following names:
+ "windows-1252"
+ */
+class EncodingSchemeWindows1252 : EncodingScheme
+{
+ /* // moved to std.internal.phobosinit
+ shared static this()
+ {
+ EncodingScheme.register("std.encoding.EncodingSchemeWindows1252");
+ }*/
+
+ const
+ {
+ override string[] names() @safe pure nothrow
+ {
+ return
+ [
+ "windows-1252"
+ ];
+ }
+
+ override string toString() @safe pure nothrow @nogc
+ {
+ return "windows-1252";
+ }
+
+ override bool canEncode(dchar c) @safe pure nothrow @nogc
+ {
+ return std.encoding.canEncode!(Windows1252Char)(c);
+ }
+
+ override size_t encodedLength(dchar c) @safe pure nothrow @nogc
+ {
+ return std.encoding.encodedLength!(Windows1252Char)(c);
+ }
+
+ override size_t encode(dchar c, ubyte[] buffer) @safe pure nothrow @nogc
+ {
+ auto r = cast(Windows1252Char[]) buffer;
+ return std.encoding.encode(c,r);
+ }
+
+ override dchar decode(ref const(ubyte)[] s) @safe pure nothrow @nogc
+ {
+ auto t = cast(const(Windows1252Char)[]) 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(Windows1252Char)[]) 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 UTF-8
+
+ This scheme recognises the following names:
+ "UTF-8"
+ */
+class EncodingSchemeUtf8 : EncodingScheme
+{
+ /* // moved to std.internal.phobosinit
+ shared static this()
+ {
+ EncodingScheme.register("std.encoding.EncodingSchemeUtf8");
+ }*/
+
+ const
+ {
+ override string[] names() @safe pure nothrow
+ {
+ return
+ [
+ "UTF-8"
+ ];
+ }
+
+ override string toString() @safe pure nothrow @nogc
+ {
+ return "UTF-8";
+ }
+
+ override bool canEncode(dchar c) @safe pure nothrow @nogc
+ {
+ return std.encoding.canEncode!(char)(c);
+ }
+
+ override size_t encodedLength(dchar c) @safe pure nothrow @nogc
+ {
+ return std.encoding.encodedLength!(char)(c);
+ }
+
+ override size_t encode(dchar c, ubyte[] buffer) @safe pure nothrow @nogc
+ {
+ auto r = cast(char[]) buffer;
+ return std.encoding.encode(c,r);
+ }
+
+ override dchar decode(ref const(ubyte)[] s) @safe pure nothrow @nogc
+ {
+ auto t = cast(const(char)[]) 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(char)[]) 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)[])"\uFFFD";
+ }
+ }
+}
+
+/**
+ EncodingScheme to handle UTF-16 in native byte order
+
+ This scheme recognises the following names:
+ "UTF-16LE" (little-endian architecture only)
+ "UTF-16BE" (big-endian architecture only)
+ */
+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() @safe pure nothrow
+ {
+ return [ NAME ];
+ }
+
+ override string toString() @safe pure nothrow @nogc
+ {
+ return NAME;
+ }
+
+ override bool canEncode(dchar c) @safe pure nothrow @nogc
+ {
+ return std.encoding.canEncode!(wchar)(c);
+ }
+
+ override size_t encodedLength(dchar c) @safe pure nothrow @nogc
+ {
+ return std.encoding.encodedLength!(wchar)(c);
+ }
+
+ override size_t encode(dchar c, ubyte[] buffer) @safe pure nothrow @nogc
+ {
+ auto r = cast(wchar[]) buffer;
+ return wchar.sizeof * std.encoding.encode(c,r);
+ }
+
+ override dchar decode(ref const(ubyte)[] s) @safe pure nothrow @nogc
+ in
+ {
+ assert((s.length & 1) == 0);
+ }
+ body
+ {
+ auto t = cast(const(wchar)[]) s;
+ dchar c = std.encoding.decode(t);
+ s = s[$-t.length * wchar.sizeof..$];
+ return c;
+ }
+
+ override dchar safeDecode(ref const(ubyte)[] s) @safe pure nothrow @nogc
+ in
+ {
+ assert((s.length & 1) == 0);
+ }
+ body
+ {
+ auto t = cast(const(wchar)[]) s;
+ dchar c = std.encoding.safeDecode(t);
+ s = s[$-t.length * wchar.sizeof..$];
+ return c;
+ }
+
+ override @property immutable(ubyte)[] replacementSequence() @safe pure nothrow @nogc
+ {
+ return cast(immutable(ubyte)[])"\uFFFD"w;
+ }
+ }
+}
+@system unittest
+{
+ version (LittleEndian)
+ {
+ auto efrom = EncodingScheme.create("utf-16le");
+ ubyte[6] sample = [154,1, 155,1, 156,1];
+ }
+ version (BigEndian)
+ {
+ auto efrom = EncodingScheme.create("utf-16be");
+ ubyte[6] sample = [1,154, 1,155, 1,156];
+ }
+ const(ubyte)[] ub = cast(const(ubyte)[])sample;
+ dchar dc = efrom.safeDecode(ub);
+ assert(dc == 410);
+ assert(ub.length == 4);
+}
+
+/**
+ EncodingScheme to handle UTF-32 in native byte order
+
+ This scheme recognises the following names:
+ "UTF-32LE" (little-endian architecture only)
+ "UTF-32BE" (big-endian architecture only)
+ */
+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() @safe pure nothrow
+ {
+ return [ NAME ];
+ }
+
+ override string toString() @safe pure nothrow @nogc
+ {
+ return NAME;
+ }
+
+ override bool canEncode(dchar c) @safe pure nothrow @nogc
+ {
+ return std.encoding.canEncode!(dchar)(c);
+ }
+
+ override size_t encodedLength(dchar c) @safe pure nothrow @nogc
+ {
+ return std.encoding.encodedLength!(dchar)(c);
+ }
+
+ override size_t encode(dchar c, ubyte[] buffer) @safe pure nothrow @nogc
+ {
+ auto r = cast(dchar[]) buffer;
+ return dchar.sizeof * std.encoding.encode(c,r);
+ }
+
+ override dchar decode(ref const(ubyte)[] s) @safe pure nothrow @nogc
+ in
+ {
+ assert((s.length & 3) == 0);
+ }
+ body
+ {
+ auto t = cast(const(dchar)[]) s;
+ dchar c = std.encoding.decode(t);
+ s = s[$-t.length * dchar.sizeof..$];
+ return c;
+ }
+
+ override dchar safeDecode(ref const(ubyte)[] s) @safe pure nothrow @nogc
+ in
+ {
+ assert((s.length & 3) == 0);
+ }
+ body
+ {
+ auto t = cast(const(dchar)[]) s;
+ dchar c = std.encoding.safeDecode(t);
+ s = s[$-t.length * dchar.sizeof..$];
+ return c;
+ }
+
+ override @property immutable(ubyte)[] replacementSequence() @safe pure nothrow @nogc
+ {
+ return cast(immutable(ubyte)[])"\uFFFD"d;
+ }
+ }
+}
+@system unittest
+{
+ version (LittleEndian)
+ {
+ auto efrom = EncodingScheme.create("utf-32le");
+ ubyte[12] sample = [154,1,0,0, 155,1,0,0, 156,1,0,0];
+ }
+ version (BigEndian)
+ {
+ auto efrom = EncodingScheme.create("utf-32be");
+ ubyte[12] sample = [0,0,1,154, 0,0,1,155, 0,0,1,156];
+ }
+ const(ubyte)[] ub = cast(const(ubyte)[])sample;
+ dchar dc = efrom.safeDecode(ub);
+ assert(dc == 410);
+ assert(ub.length == 8);
+}
+
+//=============================================================================
+
+
+// Helper functions
+version (unittest)
+{
+ void transcodeReverse(Src,Dst)(immutable(Src)[] s, out immutable(Dst)[] r)
+ {
+ static if (is(Src == Dst))
+ {
+ return s;
+ }
+ else static if (is(Src == AsciiChar))
+ {
+ transcodeReverse!(char,Dst)(cast(string) s,r);
+ }
+ else
+ {
+ foreach_reverse (d;codePoints(s))
+ {
+ foreach_reverse (c;codeUnits!(Dst)(d))
+ {
+ r = c ~ r;
+ }
+ }
+ }
+ }
+
+ string makeReadable(string s)
+ {
+ string r = "\"";
+ foreach (char c;s)
+ {
+ if (c >= 0x20 && c < 0x80)
+ {
+ r ~= c;
+ }
+ else
+ {
+ r ~= "\\x";
+ r ~= toHexDigit(c >> 4);
+ r ~= toHexDigit(c);
+ }
+ }
+ r ~= "\"";
+ return r;
+ }
+
+ string makeReadable(wstring s)
+ {
+ string r = "\"";
+ foreach (wchar c;s)
+ {
+ if (c >= 0x20 && c < 0x80)
+ {
+ r ~= cast(char) c;
+ }
+ else
+ {
+ r ~= "\\u";
+ r ~= toHexDigit(c >> 12);
+ r ~= toHexDigit(c >> 8);
+ r ~= toHexDigit(c >> 4);
+ r ~= toHexDigit(c);
+ }
+ }
+ r ~= "\"w";
+ return r;
+ }
+
+ string makeReadable(dstring s)
+ {
+ string r = "\"";
+ foreach (dchar c; s)
+ {
+ if (c >= 0x20 && c < 0x80)
+ {
+ r ~= cast(char) c;
+ }
+ else if (c < 0x10000)
+ {
+ r ~= "\\u";
+ r ~= toHexDigit(c >> 12);
+ r ~= toHexDigit(c >> 8);
+ r ~= toHexDigit(c >> 4);
+ r ~= toHexDigit(c);
+ }
+ else
+ {
+ r ~= "\\U00";
+ r ~= toHexDigit(c >> 20);
+ r ~= toHexDigit(c >> 16);
+ r ~= toHexDigit(c >> 12);
+ r ~= toHexDigit(c >> 8);
+ r ~= toHexDigit(c >> 4);
+ r ~= toHexDigit(c);
+ }
+ }
+ r ~= "\"d";
+ return r;
+ }
+
+ char toHexDigit(int n)
+ {
+ 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/libphobos/src/std/exception.d b/libphobos/src/std/exception.d
new file mode 100644
index 0000000..73afadc
--- /dev/null
+++ b/libphobos/src/std/exception.d
@@ -0,0 +1,2316 @@
+// Written in the D programming language.
+
+/++
+ 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()
+ {
+ FILE* f = enforce(fopen("some/file"));
+ // f is not null from here on
+ FILE* g = enforce!WriteException(fopen("some/other/file", "w"));
+ // g is not null from here on
+
+ Exception e = collectException(write(g, readln(f)));
+ if (e)
+ {
+ ... an exception occurred...
+ ... We have the exception to play around with...
+ }
+
+ string msg = collectExceptionMsg(write(g, readln(f)));
+ if (msg)
+ {
+ ... an exception occurred...
+ ... We have the message from the exception but not the exception...
+ }
+
+ char[] line;
+ enforce(readln(f, line));
+ return assumeUnique(line);
+ }
+ --------------------
+
+ Copyright: Copyright Andrei Alexandrescu 2008-, Jonathan M Davis 2011-.
+ 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.primitives;
+import std.traits;
+
+/++
+ 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,
+ it is caught and does not escape assertNotThrown. Rather, an
+ $(D AssertError) is thrown. However, any other $(D Throwable)s will escape.
+
+ Params:
+ T = The $(D Throwable) to test for.
+ expression = The expression to test.
+ msg = Optional message to output on test failure.
+ If msg is empty, and the thrown exception has a
+ non-empty msg field, the exception's msg field
+ will be output on test failure.
+ file = The file where the error occurred.
+ Defaults to $(D __FILE__).
+ line = The line where the error occurred.
+ Defaults to $(D __LINE__).
+
+ Throws:
+ $(D AssertError) if the given $(D Throwable) is thrown.
+
+ Returns:
+ the result of `expression`.
+ +/
+auto assertNotThrown(T : Throwable = Exception, E)
+ (lazy E expression,
+ string msg = null,
+ string file = __FILE__,
+ size_t line = __LINE__)
+{
+ import core.exception : AssertError;
+ try
+ {
+ return expression();
+ }
+ catch (T t)
+ {
+ immutable message = msg.length == 0 ? t.msg : msg;
+ immutable tail = message.length == 0 ? "." : ": " ~ message;
+ throw new AssertError("assertNotThrown failed: " ~ T.stringof ~ " was thrown" ~ tail, file, line, t);
+ }
+}
+///
+@system unittest
+{
+ import core.exception : AssertError;
+
+ import std.string;
+ assertNotThrown!StringException(enforce!StringException(true, "Error!"));
+
+ //Exception is the default.
+ assertNotThrown(enforce!StringException(true, "Error!"));
+
+ assert(collectExceptionMsg!AssertError(assertNotThrown!StringException(
+ enforce!StringException(false, "Error!"))) ==
+ `assertNotThrown failed: StringException was thrown: Error!`);
+}
+@system unittest
+{
+ import core.exception : AssertError;
+ import std.string;
+ assert(collectExceptionMsg!AssertError(assertNotThrown!StringException(
+ enforce!StringException(false, ""), "Error!")) ==
+ `assertNotThrown failed: StringException was thrown: Error!`);
+
+ assert(collectExceptionMsg!AssertError(assertNotThrown!StringException(
+ enforce!StringException(false, ""))) ==
+ `assertNotThrown failed: StringException was thrown.`);
+
+ assert(collectExceptionMsg!AssertError(assertNotThrown!StringException(
+ enforce!StringException(false, ""), "")) ==
+ `assertNotThrown failed: StringException was thrown.`);
+}
+
+@system unittest
+{
+ import core.exception : AssertError;
+
+ void throwEx(Throwable t) { throw t; }
+ bool nothrowEx() { return true; }
+
+ try
+ {
+ assert(assertNotThrown!Exception(nothrowEx()));
+ }
+ catch (AssertError) assert(0);
+
+ try
+ {
+ assert(assertNotThrown!Exception(nothrowEx(), "It's a message"));
+ }
+ catch (AssertError) assert(0);
+
+ try
+ {
+ assert(assertNotThrown!AssertError(nothrowEx()));
+ }
+ catch (AssertError) assert(0);
+
+ try
+ {
+ assert(assertNotThrown!AssertError(nothrowEx(), "It's a message"));
+ }
+ catch (AssertError) assert(0);
+
+ {
+ bool thrown = false;
+ try
+ {
+ assertNotThrown!Exception(
+ throwEx(new Exception("It's an Exception")));
+ }
+ catch (AssertError) thrown = true;
+ assert(thrown);
+ }
+
+ {
+ bool thrown = false;
+ try
+ {
+ assertNotThrown!Exception(
+ throwEx(new Exception("It's an Exception")), "It's a message");
+ }
+ catch (AssertError) thrown = true;
+ assert(thrown);
+ }
+
+ {
+ bool thrown = false;
+ try
+ {
+ assertNotThrown!AssertError(
+ throwEx(new AssertError("It's an AssertError", __FILE__, __LINE__)));
+ }
+ catch (AssertError) thrown = true;
+ assert(thrown);
+ }
+
+ {
+ bool thrown = false;
+ try
+ {
+ assertNotThrown!AssertError(
+ throwEx(new AssertError("It's an AssertError", __FILE__, __LINE__)),
+ "It's a message");
+ }
+ catch (AssertError) thrown = true;
+ assert(thrown);
+ }
+}
+
+/++
+ Asserts that the given expression throws the given type of $(D Throwable).
+ The $(D Throwable) is caught and does not escape assertThrown. However,
+ any other $(D Throwable)s $(I will) escape, and if no $(D Throwable)
+ of the given type is thrown, then an $(D AssertError) is thrown.
+
+ Params:
+ T = The $(D Throwable) to test for.
+ expression = The expression to test.
+ msg = Optional message to output on test failure.
+ file = The file where the error occurred.
+ Defaults to $(D __FILE__).
+ line = The line where the error occurred.
+ Defaults to $(D __LINE__).
+
+ Throws:
+ $(D AssertError) if the given $(D Throwable) is not thrown.
+ +/
+void assertThrown(T : Throwable = Exception, E)
+ (lazy E expression,
+ string msg = null,
+ string file = __FILE__,
+ size_t line = __LINE__)
+{
+ import core.exception : AssertError;
+
+ try
+ expression();
+ catch (T)
+ return;
+ throw new AssertError("assertThrown failed: No " ~ T.stringof ~ " was thrown"
+ ~ (msg.length == 0 ? "." : ": ") ~ msg,
+ file, line);
+}
+///
+@system unittest
+{
+ import core.exception : AssertError;
+ import std.string;
+
+ assertThrown!StringException(enforce!StringException(false, "Error!"));
+
+ //Exception is the default.
+ assertThrown(enforce!StringException(false, "Error!"));
+
+ assert(collectExceptionMsg!AssertError(assertThrown!StringException(
+ enforce!StringException(true, "Error!"))) ==
+ `assertThrown failed: No StringException was thrown.`);
+}
+
+@system unittest
+{
+ import core.exception : AssertError;
+
+ void throwEx(Throwable t) { throw t; }
+ void nothrowEx() { }
+
+ try
+ {
+ assertThrown!Exception(throwEx(new Exception("It's an Exception")));
+ }
+ catch (AssertError) assert(0);
+
+ try
+ {
+ assertThrown!Exception(throwEx(new Exception("It's an Exception")),
+ "It's a message");
+ }
+ catch (AssertError) assert(0);
+
+ try
+ {
+ assertThrown!AssertError(throwEx(new AssertError("It's an AssertError",
+ __FILE__, __LINE__)));
+ }
+ catch (AssertError) assert(0);
+
+ try
+ {
+ assertThrown!AssertError(throwEx(new AssertError("It's an AssertError",
+ __FILE__, __LINE__)),
+ "It's a message");
+ }
+ catch (AssertError) assert(0);
+
+
+ {
+ bool thrown = false;
+ try
+ assertThrown!Exception(nothrowEx());
+ catch (AssertError)
+ thrown = true;
+
+ assert(thrown);
+ }
+
+ {
+ bool thrown = false;
+ try
+ assertThrown!Exception(nothrowEx(), "It's a message");
+ catch (AssertError)
+ thrown = true;
+
+ assert(thrown);
+ }
+
+ {
+ bool thrown = false;
+ try
+ assertThrown!AssertError(nothrowEx());
+ catch (AssertError)
+ thrown = true;
+
+ assert(thrown);
+ }
+
+ {
+ bool thrown = false;
+ try
+ assertThrown!AssertError(nothrowEx(), "It's a message");
+ catch (AssertError)
+ thrown = true;
+
+ assert(thrown);
+ }
+}
+
+
+/++
+ 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:
+ $(D enforce) is used to throw exceptions and is therefore intended to
+ aid in error handling. It is $(I not) intended for verifying the logic
+ of your program. That is what $(D assert) is for. Also, do not use
+ $(D enforce) inside of contracts (i.e. inside of $(D in) and $(D out)
+ blocks and $(D invariant)s), because they will be compiled out when
+ compiling with $(I -release). Use $(D assert) in contracts.
+
+ Example:
+ --------------------
+ auto f = enforce(fopen("data.txt"));
+ auto line = readln(f);
+ 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) {} })))
+{
+ if (!value) bailOut!E(file, line, msg);
+ return value;
+}
+
+/++
+ Enforces that the given value is true.
+
+ 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 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 (!value) dg();
+ return value;
+}
+
+private void bailOut(E : Throwable = Exception)(string file, size_t line, in char[] msg)
+{
+ static if (is(typeof(new E(string.init, string.init, size_t.init))))
+ {
+ throw new E(msg ? msg.idup : "Enforcement failed", file, line);
+ }
+ else static if (is(typeof(new E(string.init, size_t.init))))
+ {
+ throw new E(file, line);
+ }
+ else
+ {
+ static assert(0, "Expected this(string, string, size_t) or this(string, size_t)" ~
+ " constructor for " ~ __traits(identifier, E));
+ }
+}
+
+@safe unittest
+{
+ assert(enforce(123) == 123);
+
+ try
+ {
+ enforce(false, "error");
+ assert(false);
+ }
+ catch (Exception e)
+ {
+ assert(e.msg == "error");
+ assert(e.file == __FILE__);
+ assert(e.line == __LINE__-7);
+ }
+}
+
+@safe unittest
+{
+ // Issue 10510
+ extern(C) void cFoo() { }
+ enforce(false, &cFoo);
+}
+
+// purity and safety inference test
+@system unittest
+{
+ import std.meta : AliasSeq;
+
+ foreach (EncloseSafe; AliasSeq!(false, true))
+ foreach (EnclosePure; AliasSeq!(false, true))
+ {
+ foreach (BodySafe; AliasSeq!(false, true))
+ foreach (BodyPure; AliasSeq!(false, true))
+ {
+ enum code =
+ "delegate void() " ~
+ (EncloseSafe ? "@safe " : "") ~
+ (EnclosePure ? "pure " : "") ~
+ "{ enforce(true, { " ~
+ "int n; " ~
+ (BodySafe ? "" : "auto p = &n + 10; " ) ~ // unsafe code
+ (BodyPure ? "" : "static int g; g = 10; ") ~ // impure code
+ "}); " ~
+ "}";
+ enum expect =
+ (BodySafe || !EncloseSafe) && (!EnclosePure || BodyPure);
+
+ version (none)
+ pragma(msg, "safe = ", EncloseSafe?1:0, "/", BodySafe?1:0, ", ",
+ "pure = ", EnclosePure?1:0, "/", BodyPure?1:0, ", ",
+ "expect = ", expect?"OK":"NG", ", ",
+ "code = ", code);
+
+ static assert(__traits(compiles, mixin(code)()) == expect);
+ }
+ }
+}
+
+// Test for bugzilla 8637
+@system unittest
+{
+ struct S
+ {
+ 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;
+ }
+ }
+ S s;
+
+ enforce(s);
+ enforce(s, {});
+ enforce(s, new Exception(""));
+
+ errnoEnforce(s);
+
+ alias E1 = Exception;
+ static class E2 : Exception
+ {
+ this(string fn, size_t ln) { super("", fn, ln); }
+ }
+ static class E3 : Exception
+ {
+ this(string msg) { super(msg, __FILE__, __LINE__); }
+ }
+ enforce!E1(s);
+ enforce!E2(s);
+}
+
+@safe unittest
+{
+ // Issue 14685
+
+ class E : Exception
+ {
+ this() { super("Not found"); }
+ }
+ static assert(!__traits(compiles, { enforce!E(false); }));
+}
+
+/++
+ 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:
+ --------------------
+ auto f = enforce(fopen("data.txt"));
+ auto line = readln(f);
+ enforce(line.length, new IOException); // expect a non-empty line
+ --------------------
+ +/
+T enforce(T)(T value, lazy Throwable ex)
+{
+ if (!value) throw ex();
+ return value;
+}
+
+@safe unittest
+{
+ assertNotThrown(enforce(true, new Exception("this should not be thrown")));
+ assertThrown(enforce(false, new Exception("this should be thrown")));
+}
+
+/++
+ 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:
+ --------------------
+ auto f = errnoEnforce(fopen("data.txt"));
+ auto line = readln(f);
+ enforce(line.length); // expect a non-empty line
+ --------------------
+ +/
+T errnoEnforce(T, string file = __FILE__, size_t line = __LINE__)
+ (T value, lazy string msg = null)
+{
+ if (!value) throw new ErrnoException(msg, file, line);
+ return value;
+}
+
+
+/++
+ 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.
+
+ This is legacy name, it is recommended to use $(D enforce!E) instead.
+
+ Example:
+ --------------------
+ auto f = enforceEx!FileMissingException(fopen("data.txt"));
+ auto line = readln(f);
+ enforceEx!DataCorruptionException(line.length);
+ --------------------
+ +/
+template enforceEx(E : Throwable)
+if (is(typeof(new E("", __FILE__, __LINE__))))
+{
+ /++ Ditto +/
+ T enforceEx(T)(T value, lazy string msg = "", string file = __FILE__, size_t line = __LINE__)
+ {
+ if (!value) throw new E(msg, file, line);
+ return value;
+ }
+}
+
+/++ Ditto +/
+template enforceEx(E : Throwable)
+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__)
+ {
+ if (!value) throw new E(file, line);
+ return value;
+ }
+}
+
+@system unittest
+{
+ import core.exception : OutOfMemoryError;
+ import std.array : empty;
+ assertNotThrown(enforceEx!Exception(true));
+ assertNotThrown(enforceEx!Exception(true, "blah"));
+ assertNotThrown(enforceEx!OutOfMemoryError(true));
+
+ {
+ auto e = collectException(enforceEx!Exception(false));
+ assert(e !is null);
+ assert(e.msg.empty);
+ assert(e.file == __FILE__);
+ assert(e.line == __LINE__ - 4);
+ }
+
+ {
+ auto e = collectException(enforceEx!Exception(false, "hello", "file", 42));
+ assert(e !is null);
+ assert(e.msg == "hello");
+ assert(e.file == "file");
+ assert(e.line == 42);
+ }
+
+ {
+ auto e = collectException!Error(enforceEx!OutOfMemoryError(false));
+ assert(e !is null);
+ assert(e.msg == "Memory allocation failed");
+ assert(e.file == __FILE__);
+ assert(e.line == __LINE__ - 4);
+ }
+
+ {
+ auto e = collectException!Error(enforceEx!OutOfMemoryError(false, "file", 42));
+ assert(e !is null);
+ assert(e.msg == "Memory allocation failed");
+ assert(e.file == "file");
+ assert(e.line == 42);
+ }
+
+ static assert(!is(typeof(enforceEx!int(true))));
+}
+
+@safe unittest
+{
+ alias enf = enforceEx!Exception;
+ assertNotThrown(enf(true));
+ assertThrown(enf(false, "blah"));
+}
+
+
+/++
+ Catches and returns the exception thrown from the given expression.
+ If no exception is thrown, then null is returned and $(D result) is
+ set to the result of the expression.
+
+ Note that while $(D collectException) $(I can) be used to collect any
+ $(D Throwable) and not just $(D Exception)s, it is generally ill-advised to
+ catch anything that is neither an $(D Exception) nor a type derived from
+ $(D Exception). So, do not use $(D collectException) to collect
+ non-$(D Exception)s unless you're sure that that's what you really want to
+ do.
+
+ Params:
+ T = The type of exception to catch.
+ expression = The expression which may throw an exception.
+ result = The result of the expression if no exception is thrown.
++/
+T collectException(T = Exception, E)(lazy E expression, ref E result)
+{
+ try
+ {
+ result = expression();
+ }
+ catch (T e)
+ {
+ return e;
+ }
+ return null;
+}
+///
+@system unittest
+{
+ int b;
+ int foo() { throw new Exception("blah"); }
+ assert(collectException(foo(), b));
+
+ int[] a = new int[3];
+ import core.exception : RangeError;
+ assert(collectException!RangeError(a[4], b));
+}
+
+/++
+ Catches and returns the exception thrown from the given expression.
+ If no exception is thrown, then null is returned. $(D E) can be
+ $(D void).
+
+ Note that while $(D collectException) $(I can) be used to collect any
+ $(D Throwable) and not just $(D Exception)s, it is generally ill-advised to
+ catch anything that is neither an $(D Exception) nor a type derived from
+ $(D Exception). So, do not use $(D collectException) to collect
+ non-$(D Exception)s unless you're sure that that's what you really want to
+ do.
+
+ Params:
+ T = The type of exception to catch.
+ expression = The expression which may throw an exception.
++/
+T collectException(T : Throwable = Exception, E)(lazy E expression)
+{
+ try
+ {
+ expression();
+ }
+ catch (T t)
+ {
+ return t;
+ }
+ return null;
+}
+
+@safe unittest
+{
+ int foo() { throw new Exception("blah"); }
+ assert(collectException(foo()));
+}
+
+/++
+ Catches the exception thrown from the given expression and returns the
+ msg property of that exception. If no exception is thrown, then null is
+ returned. $(D E) can be $(D void).
+
+ If an exception is thrown but it has an empty message, then
+ $(D emptyExceptionMsg) is returned.
+
+ Note that while $(D collectExceptionMsg) $(I can) be used to collect any
+ $(D Throwable) and not just $(D Exception)s, it is generally ill-advised to
+ catch anything that is neither an $(D Exception) nor a type derived from
+ $(D Exception). So, do not use $(D collectExceptionMsg) to collect
+ non-$(D Exception)s unless you're sure that that's what you really want to
+ do.
+
+ Params:
+ T = The type of exception to catch.
+ expression = The expression which may throw an exception.
++/
+string collectExceptionMsg(T = Exception, E)(lazy E expression)
+{
+ import std.array : empty;
+ try
+ {
+ expression();
+
+ return cast(string) null;
+ }
+ catch (T e)
+ return e.msg.empty ? emptyExceptionMsg : e.msg;
+}
+///
+@safe unittest
+{
+ void throwFunc() { throw new Exception("My Message."); }
+ assert(collectExceptionMsg(throwFunc()) == "My Message.");
+
+ void nothrowFunc() {}
+ assert(collectExceptionMsg(nothrowFunc()) is null);
+
+ void throwEmptyFunc() { throw new Exception(""); }
+ assert(collectExceptionMsg(throwEmptyFunc()) == emptyExceptionMsg);
+}
+
+/++
+ Value that collectExceptionMsg returns when it catches an exception
+ with an empty exception message.
+ +/
+enum emptyExceptionMsg = "<Empty Exception Message>";
+
+/**
+ * Casts a mutable array to an immutable array in an idiomatic
+ * manner. Technically, $(D assumeUnique) just inserts a cast,
+ * but its name documents assumptions on the part of the
+ * caller. $(D assumeUnique(arr)) should only be called when
+ * there are no more active mutable aliases to elements of $(D
+ * arr). To strengthen this assumption, $(D assumeUnique(arr))
+ * also clears $(D arr) before returning. Essentially $(D
+ * assumeUnique(arr)) indicates commitment from the caller that there
+ * is no more mutable access to any of $(D arr)'s elements
+ * (transitively), and that all future accesses will be done through
+ * the immutable array returned by $(D assumeUnique).
+ *
+ * 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:
+ *
+ * ----
+ * string letters()
+ * {
+ * char[] result = new char['z' - 'a' + 1];
+ * foreach (i, ref e; result)
+ * {
+ * e = cast(char)('a' + i);
+ * }
+ * return assumeUnique(result);
+ * }
+ * ----
+ *
+ * The use in the example above is correct because $(D result)
+ * was private to $(D letters) and is inaccessible in writing
+ * after the function returns. The following example shows an
+ * incorrect use of $(D assumeUnique).
+ *
+ * Bad:
+ *
+ * ----
+ * private char[] buffer;
+ * string letters(char first, char last)
+ * {
+ * if (first >= last) return null; // fine
+ * auto sneaky = buffer;
+ * sneaky.length = last - first + 1;
+ * foreach (i, ref e; sneaky)
+ * {
+ * e = cast(char)('a' + i);
+ * }
+ * return assumeUnique(sneaky); // BAD
+ * }
+ * ----
+ *
+ * The example above wreaks havoc on client code because it is
+ * modifying arrays that callers considered immutable. To obtain an
+ * immutable array from the writable array $(D buffer), replace
+ * the last line with:
+ * ----
+ * return to!(string)(sneaky); // not that sneaky anymore
+ * ----
+ *
+ * The call will duplicate the array appropriately.
+ *
+ * Note that checking for uniqueness during compilation is
+ * possible in certain cases, especially when a function is
+ * marked as a pure function. The following example does not
+ * need to call assumeUnique because the compiler can infer the
+ * uniqueness of the array in the pure function:
+ * ----
+ * string letters() pure
+ * {
+ * char[] result = new char['z' - 'a' + 1];
+ * foreach (i, ref e; result)
+ * {
+ * e = cast(char)('a' + i);
+ * }
+ * return result;
+ * }
+ * ----
+ *
+ * For more on infering uniqueness see the $(B unique) and
+ * $(B lent) keywords in the
+ * $(HTTP archjava.fluid.cs.cmu.edu/papers/oopsla02.pdf, ArchJava)
+ * language.
+ *
+ * The downside of using $(D assumeUnique)'s
+ * convention-based usage is that at this time there is no
+ * formal checking of the correctness of the assumption;
+ * on the upside, the idiomatic use of $(D assumeUnique) is
+ * simple and rare enough to be tolerable.
+ *
+ */
+immutable(T)[] assumeUnique(T)(T[] array) pure nothrow
+{
+ return .assumeUnique(array); // call ref version
+}
+/// ditto
+immutable(T)[] assumeUnique(T)(ref T[] array) pure nothrow
+{
+ auto result = cast(immutable(T)[]) array;
+ array = null;
+ return result;
+}
+/// ditto
+immutable(T[U]) assumeUnique(T, U)(ref T[U] array) pure nothrow
+{
+ auto result = cast(immutable(T[U])) array;
+ array = null;
+ 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) @system unittest
+{
+ int[string] arr = ["a":1];
+ auto arr1 = assumeUnique(arr);
+ assert(is(typeof(arr1) == immutable(int[string])) && arr == null);
+}
+
+/**
+ * Wraps a possibly-throwing expression in a $(D nothrow) wrapper so that it
+ * can be called by a $(D nothrow) function.
+ *
+ * This wrapper function documents commitment on the part of the caller that
+ * the appropriate steps have been taken to avoid whatever conditions may
+ * trigger an exception during the evaluation of $(D expr). If it turns out
+ * that the expression $(I does) throw at runtime, the wrapper will throw an
+ * $(D AssertError).
+ *
+ * (Note that $(D Throwable) objects such as $(D AssertError) that do not
+ * 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,
+ string file = __FILE__,
+ size_t line = __LINE__) nothrow
+{
+ import core.exception : AssertError;
+ try
+ {
+ return expr;
+ }
+ catch (Exception e)
+ {
+ import std.range.primitives : empty;
+ immutable tail = msg.empty ? "." : ": " ~ msg;
+ throw new AssertError("assumeWontThrow failed: Expression did throw" ~
+ tail, file, line);
+ }
+}
+
+///
+@safe unittest
+{
+ import std.math : sqrt;
+
+ // This function may throw.
+ int squareRoot(int x)
+ {
+ if (x < 0)
+ throw new Exception("Tried to take root of negative number");
+ return cast(int) sqrt(cast(double) x);
+ }
+
+ // This function never throws.
+ int computeLength(int x, int y) nothrow
+ {
+ // Since x*x + y*y is always positive, we can safely assume squareRoot
+ // won't throw, and use it to implement this nothrow function. If it
+ // does throw (e.g., if x*x + y*y overflows a 32-bit value), then the
+ // program will terminate.
+ return assumeWontThrow(squareRoot(x*x + y*y));
+ }
+
+ assert(computeLength(3, 4) == 5);
+}
+
+@system unittest
+{
+ import core.exception : AssertError;
+
+ void alwaysThrows()
+ {
+ throw new Exception("I threw up");
+ }
+ void bad() nothrow
+ {
+ assumeWontThrow(alwaysThrows());
+ }
+ assertThrown!AssertError(bad());
+}
+
+/**
+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
+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 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.
+
+If $(D source) is or contains a union, then there may be either false positives or
+false negatives:
+
+$(D doesPointTo) will return $(D true) if it is absolutely certain
+$(D source) points to $(D target). It may produce false negatives, but never
+false positives. This function should be prefered when trying to validate
+input data.
+
+$(D mayPointTo) will return $(D false) if it is absolutely certain
+$(D source) does not point to $(D target). It may produce false positives, but never
+false negatives. This function should be prefered for defensively choosing a
+code path.
+
+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))
+{
+ static if (isPointer!S || is(S == class) || is(S == interface))
+ {
+ const m = *cast(void**) &source;
+ const b = cast(void*) &target;
+ const e = b + target.sizeof;
+ return b <= m && m < e;
+ }
+ else static if (is(S == struct) || is(S == union))
+ {
+ foreach (i, Subobj; typeof(source.tupleof))
+ static if (!isUnionAliased!(S, i))
+ if (doesPointTo(source.tupleof[i], target)) return true;
+ return false;
+ }
+ else static if (isStaticArray!S)
+ {
+ foreach (size_t i; 0 .. S.length)
+ if (doesPointTo(source[i], target)) return true;
+ return false;
+ }
+ else static if (isDynamicArray!S)
+ {
+ import std.array : overlap;
+ return overlap(cast(void[]) source, cast(void[])(&target)[0 .. 1]).length != 0;
+ }
+ else
+ {
+ return false;
+ }
+}
+
+// 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))
+{
+ static if (isPointer!S || is(S == class) || is(S == interface))
+ {
+ const m = *cast(void**) &source;
+ const b = cast(void*) &target;
+ const e = b + target.sizeof;
+ return b <= m && m < e;
+ }
+ else static if (is(S == struct) || is(S == union))
+ {
+ foreach (i, Subobj; typeof(source.tupleof))
+ if (mayPointTo(source.tupleof[i], target)) return true;
+ return false;
+ }
+ else static if (isStaticArray!S)
+ {
+ foreach (size_t i; 0 .. S.length)
+ if (mayPointTo(source[i], target)) return true;
+ return false;
+ }
+ else static if (isDynamicArray!S)
+ {
+ import std.array : overlap;
+ return overlap(cast(void[]) source, cast(void[])(&target)[0 .. 1]).length != 0;
+ }
+ else
+ {
+ return false;
+ }
+}
+
+// for shared objects
+/// 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);
+}
+
+/// Pointers
+@system unittest
+{
+ int i = 0;
+ int* p = null;
+ assert(!p.doesPointTo(i));
+ p = &i;
+ assert( p.doesPointTo(i));
+}
+
+/// Structs and Unions
+@system unittest
+{
+ struct S
+ {
+ int v;
+ int* p;
+ }
+ int i;
+ auto s = S(0, &i);
+
+ // 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
+}
+
+/// Arrays (dynamic and static)
+@system unittest
+{
+ int i;
+ int[] slice = [0, 1, 2, 3, 4];
+ int[5] arr = [0, 1, 2, 3, 4];
+ int*[] slicep = [&i];
+ int*[1] arrp = [&i];
+
+ // 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]
+
+ // 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
+ // itself.
+
+ // Notice the difference between static and dynamic arrays:
+ assert(!arr .doesPointTo(arr[0]));
+ assert( arr[].doesPointTo(arr[0]));
+ assert( arrp .doesPointTo(i));
+ assert(!arrp[].doesPointTo(i));
+}
+
+/// Classes
+@system unittest
+{
+ class C
+ {
+ this(int* p){this.p = p;}
+ int* p;
+ }
+ 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.
+
+ //To check the class payload itself, iterate on its members:
+ ()
+ {
+ 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:
+ auto aLoc = cast(ubyte[__traits(classInstanceSize, C)]*) a;
+ assert(b.doesPointTo(*aLoc)); // b points to where a is pointing
+}
+
+@system unittest
+{
+ struct S1 { int a; S1 * b; }
+ S1 a1;
+ S1 * p = &a1;
+ assert(doesPointTo(p, a1));
+
+ S1 a2;
+ a2.b = &a1;
+ assert(doesPointTo(a2, a1));
+
+ struct S3 { int[10] a; }
+ S3 a3;
+ auto a4 = a3.a[2 .. 3];
+ assert(doesPointTo(a4, a3));
+
+ auto a5 = new double[4];
+ auto a6 = a5[1 .. 2];
+ assert(!doesPointTo(a5, a6));
+
+ auto a7 = new double[3];
+ auto a8 = new double[][1];
+ a8[0] = a7;
+ assert(!doesPointTo(a8[0], a8[0]));
+
+ // don't invoke postblit on subobjects
+ {
+ static struct NoCopy { this(this) { assert(0); } }
+ static struct Holder { NoCopy a, b, c; }
+ Holder h;
+ cast(void) doesPointTo(h, h);
+ }
+
+ shared S3 sh3;
+ shared sh3sub = sh3.a[];
+ assert(doesPointTo(sh3sub, sh3));
+
+ int[] darr = [1, 2, 3, 4];
+
+ //dynamic arrays don't point to each other, or slices of themselves
+ assert(!doesPointTo(darr, darr));
+ assert(!doesPointTo(darr[0 .. 1], darr));
+
+ //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]));
+}
+
+@system unittest
+{
+ //tests with static arrays
+ //Static arrays themselves are just objects, and don't really *point* to anything.
+ //They aggregate their contents, much the same way a structure aggregates its attributes.
+ //*However* The elements inside the static array may themselves point to stuff.
+
+ //Standard array
+ int[2] k;
+ assert(!doesPointTo(k, k)); //an array doesn't point to itself
+ //Technically, k doesn't point its elements, although it does alias them
+ assert(!doesPointTo(k, k[0]));
+ assert(!doesPointTo(k, k[1]));
+ //But an extracted slice will point to the same array.
+ assert(doesPointTo(k[], k));
+ assert(doesPointTo(k[], k[1]));
+
+ //An array of pointers
+ int*[2] pp;
+ int a;
+ int b;
+ pp[0] = &a;
+ assert( doesPointTo(pp, a)); //The array contains a pointer to a
+ assert(!doesPointTo(pp, b)); //The array does NOT contain a pointer to b
+ assert(!doesPointTo(pp, pp)); //The array does not point itslef
+
+ //A struct containing a static array of pointers
+ static struct S
+ {
+ int*[2] p;
+ }
+ S s;
+ s.p[0] = &a;
+ assert( doesPointTo(s, a)); //The struct contains an array that points a
+ assert(!doesPointTo(s, b)); //But doesn't point b
+ assert(!doesPointTo(s, s)); //The struct doesn't actually point itslef.
+
+ //An array containing structs that have pointers
+ static struct SS
+ {
+ int* p;
+ }
+ SS[2] ss = [SS(&a), SS(null)];
+ assert( doesPointTo(ss, a)); //The array contains a struct that points to a
+ assert(!doesPointTo(ss, b)); //The array doesn't contains a struct that points to b
+ assert(!doesPointTo(ss, ss)); //The array doesn't point itself.
+}
+
+
+@system unittest //Unions
+{
+ int i;
+ union U //Named union
+ {
+ size_t asInt = 0;
+ int* asPointer;
+ }
+ struct S
+ {
+ union //Anonymous union
+ {
+ size_t asInt = 0;
+ int* asPointer;
+ }
+ }
+
+ U u;
+ S s;
+ assert(!doesPointTo(u, i));
+ assert(!doesPointTo(s, i));
+ assert(!mayPointTo(u, i));
+ assert(!mayPointTo(s, i));
+
+ u.asPointer = &i;
+ s.asPointer = &i;
+ assert(!doesPointTo(u, i));
+ assert(!doesPointTo(s, i));
+ assert( mayPointTo(u, i));
+ assert( mayPointTo(s, i));
+
+ u.asInt = cast(size_t)&i;
+ s.asInt = cast(size_t)&i;
+ assert(!doesPointTo(u, i));
+ assert(!doesPointTo(s, i));
+ assert( mayPointTo(u, i));
+ assert( mayPointTo(s, i));
+}
+
+@system unittest //Classes
+{
+ int i;
+ static class A
+ {
+ int* p;
+ }
+ A a = new A, b = a;
+ assert(!doesPointTo(a, b)); //a does not point to b
+ a.p = &i;
+ assert(!doesPointTo(a, i)); //a does not point to i
+}
+@safe unittest //alias this test
+{
+ static int i;
+ static int j;
+ struct S
+ {
+ int* p;
+ @property int* foo(){return &i;}
+ alias foo this;
+ }
+ assert(is(S : int*));
+ S s = S(&j);
+ assert(!doesPointTo(s, i));
+ assert( doesPointTo(s, j));
+ assert( doesPointTo(cast(int*) s, i));
+ assert(!doesPointTo(cast(int*) s, j));
+}
+@safe unittest //more alias this opCast
+{
+ void* p;
+ class A
+ {
+ void* opCast(T)() if (is(T == void*))
+ {
+ return p;
+ }
+ alias foo = opCast!(void*);
+ alias foo this;
+ }
+ assert(!doesPointTo(A.init, p));
+ 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;
+}
+
+package string errnoString(int errno) nothrow @trusted
+{
+ import core.stdc.string : strlen;
+ version (CRuntime_Glibc)
+ {
+ import core.stdc.string : strerror_r;
+ char[1024] buf = void;
+ auto s = strerror_r(errno, buf.ptr, buf.length);
+ }
+ else version (Posix)
+ {
+ // XSI-compliant
+ import core.stdc.string : strerror_r;
+ char[1024] buf = void;
+ const(char)* s;
+ if (strerror_r(errno, buf.ptr, buf.length) == 0)
+ s = buf.ptr;
+ else
+ return "Unknown error";
+ }
+ else
+ {
+ import core.stdc.string : strerror;
+ auto s = strerror(errno);
+ }
+ return s[0 .. s.strlen].idup;
+}
+
+/*********************
+ * Thrown if errors that set $(D errno) occur.
+ */
+class ErrnoException : Exception
+{
+ final @property uint errno() { return _errno; } /// Operating system error code.
+ private uint _errno;
+ /// Constructor which takes an error message. The current global $(REF errno, core,stdc,errno) value is used as error code.
+ this(string msg, string file = null, size_t line = 0) @trusted
+ {
+ import core.stdc.errno : errno;
+ this(msg, errno, file, line);
+ }
+ /// Constructor which takes an error message and error code.
+ this(string msg, int errno, string file = null, size_t line = 0) @trusted
+ {
+ _errno = errno;
+ super(msg ~ " (" ~ errnoString(errno) ~ ")", 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);
+ }
+}
+
+/++
+ ML-style functional exception handling. Runs the supplied expression and
+ returns its result. If the expression throws a $(D Throwable), runs the
+ supplied error handler instead and return its result. The error handler's
+ type must be the same as the expression's type.
+
+ Params:
+ E = The type of $(D Throwable)s to catch. Defaults to $(D Exception)
+ T1 = The type of the expression.
+ T2 = The return type of the error handler.
+ expression = The expression to run and return its result.
+ errorHandler = The handler to run if the expression throwed.
+
+ 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);
+ --------------------
+
+ You can also chain multiple calls to ifThrown, each capturing errors from the
+ entire preceding expression.
+
+ Example:
+ --------------------
+ //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())
+ == 1);
+
+ //Respond differently to different types of errors
+ assert(enforce("x".to!int() < 1).to!string()
+ .ifThrown!ConvException("not a number")
+ .ifThrown!Exception("number too small")
+ == "not a number");
+ --------------------
+
+ The expression and the errorHandler must have a common type they can both
+ be implicitly casted to, and that type will be the type of the compound
+ expression.
+
+ Example:
+ --------------------
+ //null and new Object have a common type(Object).
+ static assert(is(typeof(null.ifThrown(new Object())) == Object));
+ static assert(is(typeof((new Object()).ifThrown(null)) == Object));
+
+ //1 and new Object do not have a common type.
+ static assert(!__traits(compiles, 1.ifThrown(new Object())));
+ static assert(!__traits(compiles, (new Object()).ifThrown(1)));
+ --------------------
+
+ If you need to use the actual thrown exception, you can use a delegate.
+ Example:
+ --------------------
+ //Use a lambda to get the thrown object.
+ assert("%s".format().ifThrown!Exception(e => e.classinfo.name) == "std.format.FormatException");
+ --------------------
+ +/
+//lazy version
+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 ~
+ ")."
+ );
+ try
+ {
+ return expression();
+ }
+ catch (E)
+ {
+ return errorHandler();
+ }
+}
+
+///ditto
+//delegate version
+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 ~
+ ")."
+ );
+ try
+ {
+ return expression();
+ }
+ catch (E e)
+ {
+ return errorHandler(e);
+ }
+}
+
+///ditto
+//delegate version, general overload to catch any Exception
+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 ~
+ ")."
+ );
+ try
+ {
+ return expression();
+ }
+ catch (Exception e)
+ {
+ return errorHandler(e);
+ }
+}
+
+//Verify Examples
+@system unittest
+{
+ import std.conv;
+ import std.string;
+ //Revert to a default value upon an error:
+ assert("x".to!int().ifThrown(0) == 0);
+
+ //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())
+ == 1);
+
+ //Respond differently to different types of errors
+ assert(enforce("x".to!int() < 1).to!string()
+ .ifThrown!ConvException("not a number")
+ .ifThrown!Exception("number too small")
+ == "not a number");
+
+ //null and new Object have a common type(Object).
+ static assert(is(typeof(null.ifThrown(new Object())) == Object));
+ static assert(is(typeof((new Object()).ifThrown(null)) == Object));
+
+ //1 and new Object do not have a common type.
+ static assert(!__traits(compiles, 1.ifThrown(new Object())));
+ static assert(!__traits(compiles, (new Object()).ifThrown(1)));
+
+ //Use a lambda to get the thrown object.
+ assert("%s".format().ifThrown(e => e.classinfo.name) == "std.format.FormatException");
+}
+
+@system unittest
+{
+ import core.exception;
+ import std.conv;
+ import std.string;
+ //Basic behaviour - all versions.
+ assert("1".to!int().ifThrown(0) == 1);
+ assert("x".to!int().ifThrown(0) == 0);
+ assert("1".to!int().ifThrown!ConvException(0) == 1);
+ assert("x".to!int().ifThrown!ConvException(0) == 0);
+ assert("1".to!int().ifThrown(e=>0) == 1);
+ assert("x".to!int().ifThrown(e=>0) == 0);
+ static if (__traits(compiles, 0.ifThrown!Exception(e => 0))) //This will only work with a fix that was not yet pulled
+ {
+ assert("1".to!int().ifThrown!ConvException(e=>0) == 1);
+ assert("x".to!int().ifThrown!ConvException(e=>0) == 0);
+ }
+
+ //Exceptions other than stated not caught.
+ assert("x".to!int().ifThrown!StringException(0).collectException!ConvException() !is null);
+ static if (__traits(compiles, 0.ifThrown!Exception(e => 0))) //This will only work with a fix that was not yet pulled
+ {
+ assert("x".to!int().ifThrown!StringException(e=>0).collectException!ConvException() !is null);
+ }
+
+ //Default does not include errors.
+ int throwRangeError() { throw new RangeError; }
+ assert(throwRangeError().ifThrown(0).collectException!RangeError() !is null);
+ assert(throwRangeError().ifThrown(e=>0).collectException!RangeError() !is null);
+
+ //Incompatible types are not accepted.
+ static assert(!__traits(compiles, 1.ifThrown(new Object())));
+ static assert(!__traits(compiles, (new Object()).ifThrown(1)));
+ static assert(!__traits(compiles, 1.ifThrown(e=>new Object())));
+ static assert(!__traits(compiles, (new Object()).ifThrown(e=>1)));
+}
+
+version (unittest) package
+@property void assertCTFEable(alias 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
+ $(LREF handle) range wrapper. The values of the $(D enum) can be $(D OR)'d to
+ select multiple primitives to be handled.
+
+ $(D RangePrimitive.access) is a shortcut for the access primitives; $(D front),
+ $(D back) and $(D opIndex).
+
+ $(D RangePrimitive.pop) is a shortcut for the mutating primitives;
+ $(D popFront) and $(D popBack).
+ */
+enum RangePrimitive
+{
+ front = 0b00_0000_0001, ///
+ back = 0b00_0000_0010, /// Ditto
+ popFront = 0b00_0000_0100, /// Ditto
+ popBack = 0b00_0000_1000, /// Ditto
+ empty = 0b00_0001_0000, /// Ditto
+ save = 0b00_0010_0000, /// Ditto
+ length = 0b00_0100_0000, /// Ditto
+ opDollar = 0b00_1000_0000, /// Ditto
+ opIndex = 0b01_0000_0000, /// Ditto
+ opSlice = 0b10_0000_0000, /// Ditto
+ access = front | back | opIndex, /// Ditto
+ pop = popFront | popBack, /// Ditto
+}
+
+/** Handle exceptions thrown from range primitives.
+
+Use the $(LREF RangePrimitive) enum to specify which primitives to _handle.
+Multiple range primitives can be handled at once by using the $(D OR) operator
+or the pseudo-primitives $(D RangePrimitive.access) and $(D RangePrimitive.pop).
+All handled primitives must have return types or values compatible with the
+user-supplied handler.
+
+Params:
+ E = The type of $(D Throwable) to _handle.
+ primitivesToHandle = Set of range primitives to _handle.
+ handler = The callable that is called when a handled primitive throws a
+ $(D Throwable) of type $(D E). The handler must accept arguments of
+ the form $(D E, ref IRange) and its return value is used as the primitive's
+ return value whenever $(D E) is thrown. For $(D opIndex), the handler can
+ optionally recieve a third argument; the index that caused the exception.
+ input = The range to _handle.
+
+Returns: A wrapper $(D struct) that preserves the range interface of $(D input).
+
+Note:
+Infinite ranges with slicing support must return an instance of
+$(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)
+{
+ static struct Handler
+ {
+ private Range range;
+
+ static if (isForwardRange!Range)
+ {
+ @property typeof(this) save()
+ {
+ static if (primitivesToHandle & RangePrimitive.save)
+ {
+ try
+ {
+ return typeof(this)(range.save);
+ }
+ catch (E exception)
+ {
+ return typeof(this)(handler(exception, this.range));
+ }
+ }
+ else
+ return typeof(this)(range.save);
+ }
+ }
+
+ static if (isInfinite!Range)
+ {
+ enum bool empty = false;
+ }
+ else
+ {
+ @property bool empty()
+ {
+ static if (primitivesToHandle & RangePrimitive.empty)
+ {
+ try
+ {
+ return this.range.empty;
+ }
+ catch (E exception)
+ {
+ return handler(exception, this.range);
+ }
+ }
+ else
+ return this.range.empty;
+ }
+ }
+
+ @property auto ref front()
+ {
+ static if (primitivesToHandle & RangePrimitive.front)
+ {
+ try
+ {
+ return this.range.front;
+ }
+ catch (E exception)
+ {
+ return handler(exception, this.range);
+ }
+ }
+ else
+ return this.range.front;
+ }
+
+ void popFront()
+ {
+ static if (primitivesToHandle & RangePrimitive.popFront)
+ {
+ try
+ {
+ this.range.popFront();
+ }
+ catch (E exception)
+ {
+ handler(exception, this.range);
+ }
+ }
+ else
+ this.range.popFront();
+ }
+
+ static if (isBidirectionalRange!Range)
+ {
+ @property auto ref back()
+ {
+ static if (primitivesToHandle & RangePrimitive.back)
+ {
+ try
+ {
+ return this.range.back;
+ }
+ catch (E exception)
+ {
+ return handler(exception, this.range);
+ }
+ }
+ else
+ return this.range.back;
+ }
+
+ void popBack()
+ {
+ static if (primitivesToHandle & RangePrimitive.popBack)
+ {
+ try
+ {
+ this.range.popBack();
+ }
+ catch (E exception)
+ {
+ handler(exception, this.range);
+ }
+ }
+ else
+ this.range.popBack();
+ }
+ }
+
+ static if (isRandomAccessRange!Range)
+ {
+ auto ref opIndex(size_t index)
+ {
+ static if (primitivesToHandle & RangePrimitive.opIndex)
+ {
+ try
+ {
+ return this.range[index];
+ }
+ catch (E exception)
+ {
+ static if (__traits(compiles, handler(exception, this.range, index)))
+ return handler(exception, this.range, index);
+ else
+ return handler(exception, this.range);
+ }
+ }
+ else
+ return this.range[index];
+ }
+ }
+
+ static if (hasLength!Range)
+ {
+ @property auto length()
+ {
+ static if (primitivesToHandle & RangePrimitive.length)
+ {
+ try
+ {
+ return this.range.length;
+ }
+ catch (E exception)
+ {
+ return handler(exception, this.range);
+ }
+ }
+ else
+ return this.range.length;
+ }
+ }
+
+ static if (hasSlicing!Range)
+ {
+ static if (hasLength!Range)
+ {
+ typeof(this) opSlice(size_t lower, size_t upper)
+ {
+ static if (primitivesToHandle & RangePrimitive.opSlice)
+ {
+ try
+ {
+ return typeof(this)(this.range[lower .. upper]);
+ }
+ catch (E exception)
+ {
+ return typeof(this)(handler(exception, this.range));
+ }
+ }
+ else
+ return typeof(this)(this.range[lower .. upper]);
+ }
+ }
+ else static if (is(typeof(Range.init[size_t.init .. $])))
+ {
+ import std.range : Take, takeExactly;
+ static struct DollarToken {}
+ enum opDollar = DollarToken.init;
+
+ typeof(this) opSlice(size_t lower, DollarToken)
+ {
+ static if (primitivesToHandle & RangePrimitive.opSlice)
+ {
+ try
+ {
+ return typeof(this)(this.range[lower .. $]);
+ }
+ catch (E exception)
+ {
+ return typeof(this)(handler(exception, this.range));
+ }
+ }
+ else
+ return typeof(this)(this.range[lower .. $]);
+ }
+
+ Take!Handler opSlice(size_t lower, size_t upper)
+ {
+ static if (primitivesToHandle & RangePrimitive.opSlice)
+ {
+ try
+ {
+ return takeExactly(typeof(this)(this.range[lower .. $]), upper - 1);
+ }
+ catch (E exception)
+ {
+ return takeExactly(typeof(this)(handler(exception, this.range)), 0);
+ }
+ }
+ else
+ return takeExactly(typeof(this)(this.range[lower .. $]), upper - 1);
+ }
+ }
+ }
+ }
+
+ return Handler(input);
+}
+
+///
+pure @safe unittest
+{
+ 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";
+
+ // The next line composition will throw when iterated
+ // as some elements of the input do not convert to integer
+ auto r = s.splitter(',').map!(a => to!int(a));
+
+ // Substitute 0 for cases of ConvException
+ auto h = r.handle!(ConvException, RangePrimitive.front, (e, r) => 0);
+ assert(h.equal([12, 0, 54, 2, 7, 9, 0, 6, 8]));
+}
+
+///
+pure @safe unittest
+{
+ import std.algorithm.comparison : equal;
+ import std.range : retro;
+ import std.utf : UTFException;
+
+ auto str = "hello\xFFworld"; // 0xFF is an invalid UTF-8 code unit
+
+ auto handled = str.handle!(UTFException, RangePrimitive.access,
+ (e, r) => ' '); // Replace invalid code points with spaces
+
+ assert(handled.equal("hello world")); // `front` is handled,
+ assert(handled.retro.equal("dlrow olleh")); // as well as `back`
+}
+
+pure nothrow @safe unittest
+{
+ static struct ThrowingRange
+ {
+ pure @safe:
+ @property bool empty()
+ {
+ throw new Exception("empty has thrown");
+ }
+
+ @property int front()
+ {
+ throw new Exception("front has thrown");
+ }
+
+ @property int back()
+ {
+ throw new Exception("back has thrown");
+ }
+
+ void popFront()
+ {
+ throw new Exception("popFront has thrown");
+ }
+
+ void popBack()
+ {
+ throw new Exception("popBack has thrown");
+ }
+
+ int opIndex(size_t)
+ {
+ throw new Exception("opIndex has thrown");
+ }
+
+ ThrowingRange opSlice(size_t, size_t)
+ {
+ throw new Exception("opSlice has thrown");
+ }
+
+ @property size_t length()
+ {
+ throw new Exception("length has thrown");
+ }
+
+ alias opDollar = length;
+
+ @property ThrowingRange save()
+ {
+ throw new Exception("save has thrown");
+ }
+ }
+
+ static assert(isInputRange!ThrowingRange);
+ static assert(isForwardRange!ThrowingRange);
+ static assert(isBidirectionalRange!ThrowingRange);
+ static assert(hasSlicing!ThrowingRange);
+ static assert(hasLength!ThrowingRange);
+
+ auto f = ThrowingRange();
+ auto fb = f.handle!(Exception, RangePrimitive.front | RangePrimitive.back,
+ (e, r) => -1)();
+ assert(fb.front == -1);
+ assert(fb.back == -1);
+ assertThrown(fb.popFront());
+ assertThrown(fb.popBack());
+ assertThrown(fb.empty);
+ assertThrown(fb.save);
+ assertThrown(fb[0]);
+
+ auto accessRange = f.handle!(Exception, RangePrimitive.access,
+ (e, r) => -1);
+ assert(accessRange.front == -1);
+ assert(accessRange.back == -1);
+ assert(accessRange[0] == -1);
+ assertThrown(accessRange.popFront());
+ assertThrown(accessRange.popBack());
+
+ auto pfb = f.handle!(Exception, RangePrimitive.pop, (e, r) => -1)();
+
+ pfb.popFront(); // this would throw otherwise
+ pfb.popBack(); // this would throw otherwise
+
+ auto em = f.handle!(Exception,
+ RangePrimitive.empty, (e, r) => false)();
+
+ assert(!em.empty);
+
+ auto arr = f.handle!(Exception,
+ RangePrimitive.opIndex, (e, r) => 1337)();
+
+ assert(arr[0] == 1337);
+
+ auto arr2 = f.handle!(Exception,
+ RangePrimitive.opIndex, (e, r, i) => i)();
+
+ assert(arr2[0] == 0);
+ assert(arr2[1337] == 1337);
+
+ auto save = f.handle!(Exception,
+ RangePrimitive.save,
+ function(Exception e, ref ThrowingRange r) {
+ return ThrowingRange();
+ })();
+
+ save.save;
+
+ auto slice = f.handle!(Exception,
+ RangePrimitive.opSlice, (e, r) => ThrowingRange())();
+
+ auto sliced = slice[0 .. 1337]; // this would throw otherwise
+
+ static struct Infinite
+ {
+ import std.range : Take;
+ pure @safe:
+ enum bool empty = false;
+ int front() { assert(false); }
+ void popFront() { assert(false); }
+ Infinite save() @property { assert(false); }
+ static struct DollarToken {}
+ enum opDollar = DollarToken.init;
+ Take!Infinite opSlice(size_t, size_t) { assert(false); }
+ Infinite opSlice(size_t, DollarToken)
+ {
+ throw new Exception("opSlice has thrown");
+ }
+ }
+
+ static assert(isInputRange!Infinite);
+ static assert(isInfinite!Infinite);
+ static assert(hasSlicing!Infinite);
+
+ assertThrown(Infinite()[0 .. $]);
+
+ auto infinite = Infinite.init.handle!(Exception,
+ RangePrimitive.opSlice, (e, r) => Infinite())();
+
+ 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/libphobos/src/std/experimental/allocator/building_blocks/affix_allocator.d b/libphobos/src/std/experimental/allocator/building_blocks/affix_allocator.d
new file mode 100644
index 0000000..cfeae0c
--- /dev/null
+++ b/libphobos/src/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.algorithm.comparison : min;
+ import std.conv : emplace;
+ import std.experimental.allocator : IAllocator, theAllocator;
+ import std.experimental.allocator.common : stateSize, forwardToMember,
+ roundUpToMultipleOf, alignedAt, alignDownTo, roundUpToMultipleOf,
+ hasStaticallyKnownAlignment;
+ import std.math : isPowerOf2;
+ import std.traits : hasMember;
+ import std.typecons : Ternary;
+
+ 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;
+ import std.experimental.allocator.gc_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/libphobos/src/std/experimental/allocator/building_blocks/allocator_list.d b/libphobos/src/std/experimental/allocator/building_blocks/allocator_list.d
new file mode 100644
index 0000000..2d0e670
--- /dev/null
+++ b/libphobos/src/std/experimental/allocator/building_blocks/allocator_list.d
@@ -0,0 +1,640 @@
+///
+module std.experimental.allocator.building_blocks.allocator_list;
+
+import std.experimental.allocator.building_blocks.null_allocator;
+import std.experimental.allocator.common;
+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.conv : emplace;
+ import std.experimental.allocator.building_blocks.stats_collector
+ : StatsCollector, Options;
+ import std.traits : hasMember;
+ import std.typecons : Ternary;
+
+ 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 core.stdc.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.free_list : ContiguousFreeList;
+ import std.experimental.allocator.building_blocks.null_allocator : NullAllocator;
+ import std.experimental.allocator.building_blocks.region : Region;
+ import std.experimental.allocator.building_blocks.segregator : Segregator;
+ 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/libphobos/src/std/experimental/allocator/building_blocks/bitmapped_block.d b/libphobos/src/std/experimental/allocator/building_blocks/bitmapped_block.d
new file mode 100644
index 0000000..24e27a9
--- /dev/null
+++ b/libphobos/src/std/experimental/allocator/building_blocks/bitmapped_block.d
@@ -0,0 +1,1423 @@
+///
+module std.experimental.allocator.building_blocks.bitmapped_block;
+
+import std.experimental.allocator.building_blocks.null_allocator;
+import std.experimental.allocator.common;
+
+/**
+
+$(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.conv : text;
+ import std.traits : hasMember;
+ import std.typecons : Ternary;
+ import std.typecons : tuple, Tuple;
+
+ @system unittest
+ {
+ import std.algorithm.comparison : max;
+ import std.experimental.allocator.mallocator : AlignedMallocator;
+ 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/libphobos/src/std/experimental/allocator/building_blocks/bucketizer.d b/libphobos/src/std/experimental/allocator/building_blocks/bucketizer.d
new file mode 100644
index 0000000..64067dd
--- /dev/null
+++ b/libphobos/src/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 common = std.experimental.allocator.common : roundUpToMultipleOf;
+ import std.traits : hasMember;
+ 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.algorithm.comparison : max;
+ 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.common : unbounded;
+ import std.experimental.allocator.mallocator : Mallocator;
+ import std.typecons : Ternary;
+ 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/libphobos/src/std/experimental/allocator/building_blocks/fallback_allocator.d b/libphobos/src/std/experimental/allocator/building_blocks/fallback_allocator.d
new file mode 100644
index 0000000..ca7961b
--- /dev/null
+++ b/libphobos/src/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.conv : text;
+ import std.experimental.allocator.building_blocks.region : InSituRegion;
+ import std.experimental.allocator.gc_allocator : GCAllocator;
+ 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/libphobos/src/std/experimental/allocator/building_blocks/free_list.d b/libphobos/src/std/experimental/allocator/building_blocks/free_list.d
new file mode 100644
index 0000000..8860806
--- /dev/null
+++ b/libphobos/src/std/experimental/allocator/building_blocks/free_list.d
@@ -0,0 +1,1205 @@
+///
+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.common : chooseAtRuntime;
+ import std.experimental.allocator.mallocator : Mallocator;
+
+ 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.building_blocks.allocator_list
+ : AllocatorList;
+ import std.experimental.allocator.gc_allocator : GCAllocator;
+
+ 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 $(D 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;
+
+ private enum unchecked = minSize == 0 && maxSize == unbounded;
+
+ 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.common : chooseAtRuntime;
+ import std.experimental.allocator.mallocator : Mallocator;
+
+ 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.common : chooseAtRuntime;
+ import std.experimental.allocator.mallocator : Mallocator;
+
+ 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;
+ }
+
+ /**
+ 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() shared
+ {
+ lock.lock();
+ scope(exit) lock.unlock();
+
+ for (auto n = _root; n;)
+ {
+ auto tmp = n.next;
+ parent.deallocate((cast(ubyte*) n)[0 .. max]);
+ n = tmp;
+ }
+
+ _root = null;
+ resetNodes();
+ }
+}
+
+@system unittest
+{
+ import core.thread : ThreadGroup;
+ import std.algorithm.comparison : equal;
+ import std.experimental.allocator.mallocator : Mallocator;
+ import std.range : repeat;
+
+ 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;
+ static shared SharedFreeList!(Mallocator, 64, 128, 10) a;
+ auto b = a.allocate(100);
+ auto c = a.allocate(100);
+ a.deallocate(c);
+ assert(a.nodes == 1);
+ c = [];
+ a.minimize();
+ assert(a.nodes == 0);
+ a.deallocate(b);
+ assert(a.nodes == 1);
+ b = [];
+ a.minimize();
+ assert(a.nodes == 0);
+}
+
+@system unittest
+{
+ import std.experimental.allocator.mallocator : Mallocator;
+ static shared SharedFreeList!(Mallocator, 64, 128, 10) a;
+ auto b = a.allocate(100);
+ auto c = a.allocate(100);
+ assert(a.nodes == 0);
+ a.deallocate(b);
+ a.deallocate(c);
+ assert(a.nodes == 2);
+ b = [];
+ c = [];
+ a.minimize();
+ assert(a.nodes == 0);
+}
+
+@system unittest
+{
+ import std.experimental.allocator.mallocator : Mallocator;
+ shared SharedFreeList!(Mallocator, chooseAtRuntime, chooseAtRuntime) a;
+ scope(exit) a.deallocateAll();
+ 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;
+ scope(exit) a.deallocateAll;
+ a.allocate(64);
+}
+
+@system unittest
+{
+ import std.experimental.allocator.mallocator : Mallocator;
+ shared SharedFreeList!(Mallocator, 30, 40) a;
+ scope(exit) a.deallocateAll;
+ a.allocate(64);
+}
+
+@system unittest
+{
+ import std.experimental.allocator.mallocator : Mallocator;
+ shared SharedFreeList!(Mallocator, 30, 40, chooseAtRuntime) a;
+ scope(exit) a.deallocateAll;
+ a.allocate(64);
+}
+
+@system unittest
+{
+ // Pull request #5556
+ import std.experimental.allocator.mallocator : Mallocator;
+ shared SharedFreeList!(Mallocator, 0, chooseAtRuntime) a;
+ scope(exit) a.deallocateAll;
+ a.max = 64;
+ a.allocate(64);
+}
+
+@system unittest
+{
+ // Pull request #5556
+ import std.experimental.allocator.mallocator : Mallocator;
+ shared SharedFreeList!(Mallocator, chooseAtRuntime, 64) a;
+ scope(exit) a.deallocateAll;
+ a.min = 32;
+ a.allocate(64);
+}
diff --git a/libphobos/src/std/experimental/allocator/building_blocks/free_tree.d b/libphobos/src/std/experimental/allocator/building_blocks/free_tree.d
new file mode 100644
index 0000000..6b64659
--- /dev/null
+++ b/libphobos/src/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/libphobos/src/std/experimental/allocator/building_blocks/kernighan_ritchie.d b/libphobos/src/std/experimental/allocator/building_blocks/kernighan_ritchie.d
new file mode 100644
index 0000000..555daba
--- /dev/null
+++ b/libphobos/src/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;
+version (unittest) import std.conv : text;
+debug(KRRegion) import std.stdio;
+
+// 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.building_blocks.fallback_allocator
+ : fallbackAllocator;
+ import std.experimental.allocator.gc_allocator : GCAllocator;
+ 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.building_blocks.allocator_list
+ : AllocatorList;
+ import std.experimental.allocator.gc_allocator : GCAllocator;
+ import std.experimental.allocator.mmap_allocator : MmapAllocator;
+ AllocatorList!(n => KRRegion!MmapAllocator(max(n * 16, 1024 * 1024))) alloc;
+}
+
+@system unittest
+{
+ import std.algorithm.comparison : max;
+ import std.experimental.allocator.building_blocks.allocator_list
+ : AllocatorList;
+ import std.experimental.allocator.gc_allocator : GCAllocator;
+ import std.experimental.allocator.mallocator : Mallocator;
+ import std.typecons : Ternary;
+ /*
+ 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.building_blocks.allocator_list
+ : AllocatorList;
+ import std.experimental.allocator.gc_allocator : GCAllocator;
+ import std.experimental.allocator.mmap_allocator : MmapAllocator;
+ import std.typecons : Ternary;
+ /*
+ 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.algorithm.comparison : max;
+ import std.experimental.allocator.building_blocks.allocator_list
+ : AllocatorList;
+ import std.experimental.allocator.common : testAllocator;
+ import std.experimental.allocator.gc_allocator : GCAllocator;
+ 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 core.stdc.string : memcpy;
+ import std.algorithm.mutation : move;
+ import std.conv : emplace;
+
+ 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)
+ {
+ align(size_t.sizeof) 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)
+ {
+ align(size_t.sizeof) 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/libphobos/src/std/experimental/allocator/building_blocks/null_allocator.d b/libphobos/src/std/experimental/allocator/building_blocks/null_allocator.d
new file mode 100644
index 0000000..68bab70
--- /dev/null
+++ b/libphobos/src/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/libphobos/src/std/experimental/allocator/building_blocks/package.d b/libphobos/src/std/experimental/allocator/building_blocks/package.d
new file mode 100644
index 0000000..d55a16b
--- /dev/null
+++ b/libphobos/src/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 GCAllocator).
+
+----
+ 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/libphobos/src/std/experimental/allocator/building_blocks/quantizer.d b/libphobos/src/std/experimental/allocator/building_blocks/quantizer.d
new file mode 100644
index 0000000..b3f205d
--- /dev/null
+++ b/libphobos/src/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.common : roundUpToMultipleOf;
+ import std.experimental.allocator.gc_allocator : GCAllocator;
+
+ // 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/libphobos/src/std/experimental/allocator/building_blocks/region.d b/libphobos/src/std/experimental/allocator/building_blocks/region.d
new file mode 100644
index 0000000..80157ae
--- /dev/null
+++ b/libphobos/src/std/experimental/allocator/building_blocks/region.d
@@ -0,0 +1,784 @@
+///
+module std.experimental.allocator.building_blocks.region;
+
+import std.experimental.allocator.building_blocks.null_allocator;
+import std.experimental.allocator.common;
+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.algorithm.comparison : max;
+ import std.experimental.allocator.building_blocks.allocator_list
+ : AllocatorList;
+ import std.experimental.allocator.mallocator : Mallocator;
+ // 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 (MIPS32) 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.building_blocks.bitmapped_block
+ : BitmappedBlock;
+ import std.experimental.allocator.gc_allocator : GCAllocator;
+ 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)
+{
+ 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/libphobos/src/std/experimental/allocator/building_blocks/scoped_allocator.d b/libphobos/src/std/experimental/allocator/building_blocks/scoped_allocator.d
new file mode 100644
index 0000000..ff3261f
--- /dev/null
+++ b/libphobos/src/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());
+ }
+
+ import std.experimental.allocator.building_blocks.affix_allocator
+ : AffixAllocator;
+ 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/libphobos/src/std/experimental/allocator/building_blocks/segregator.d b/libphobos/src/std/experimental/allocator/building_blocks/segregator.d
new file mode 100644
index 0000000..76ca6f4
--- /dev/null
+++ b/libphobos/src/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 $(D
+ 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.gc_allocator : GCAllocator;
+ import std.experimental.allocator.mallocator : Mallocator;
+ 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.gc_allocator : GCAllocator;
+ import std.experimental.allocator.mallocator : Mallocator;
+ 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/libphobos/src/std/experimental/allocator/building_blocks/stats_collector.d b/libphobos/src/std/experimental/allocator/building_blocks/stats_collector.d
new file mode 100644
index 0000000..aba084e
--- /dev/null
+++ b/libphobos/src/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.conv : to;
+ import std.traits : EnumMembers;
+ 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.array : array;
+ import std.range : repeat;
+
+ 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.building_blocks.free_list : FreeList;
+ import std.experimental.allocator.gc_allocator : GCAllocator;
+ 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.range : walkLength;
+ import std.stdio : File;
+
+ 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.building_blocks.free_list : FreeList;
+ import std.experimental.allocator.gc_allocator : GCAllocator;
+ 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.building_blocks.free_list : FreeList;
+ import std.experimental.allocator.gc_allocator : GCAllocator;
+ test!(StatsCollector!(GCAllocator, 0, 0));
+}
diff --git a/libphobos/src/std/experimental/allocator/common.d b/libphobos/src/std/experimental/allocator/common.d
new file mode 100644
index 0000000..0eec0d3
--- /dev/null
+++ b/libphobos/src/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.math : isPowerOf2;
+ import std.stdio : writeln, stderr;
+ 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.math : isPowerOf2;
+ import std.stdio : writeln, stderr;
+ 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/libphobos/src/std/experimental/allocator/gc_allocator.d b/libphobos/src/std/experimental/allocator/gc_allocator.d
new file mode 100644
index 0000000..4189456
--- /dev/null
+++ b/libphobos/src/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/libphobos/src/std/experimental/allocator/mallocator.d b/libphobos/src/std/experimental/allocator/mallocator.d
new file mode 100644
index 0000000..146c974
--- /dev/null
+++ b/libphobos/src/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 core.stdc.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 core.stdc.stdlib : free;
+ import core.stdc.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 core.stdc.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/libphobos/src/std/experimental/allocator/mmap_allocator.d b/libphobos/src/std/experimental/allocator/mmap_allocator.d
new file mode 100644
index 0000000..945859b
--- /dev/null
+++ b/libphobos/src/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/libphobos/src/std/experimental/allocator/package.d b/libphobos/src/std/experimental/allocator/package.d
new file mode 100644
index 0000000..11c8547
--- /dev/null
+++ b/libphobos/src/std/experimental/allocator/package.d
@@ -0,0 +1,3028 @@
+// 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.allocator_list
+ : AllocatorList;
+ import std.experimental.allocator.building_blocks.bitmapped_block
+ : BitmappedBlock;
+ import std.experimental.allocator.building_blocks.bucketizer : Bucketizer;
+ import std.experimental.allocator.building_blocks.free_list : FreeList;
+ import std.experimental.allocator.building_blocks.segregator : Segregator;
+ import std.experimental.allocator.gc_allocator : GCAllocator;
+
+ 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;
+}
+
+private shared ISharedAllocator _processAllocator;
+private IAllocator _threadAllocator;
+
+private IAllocator setupThreadAllocator() nothrow @nogc @safe
+{
+ /*
+ 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 = () @trusted { return emplace!(ThreadAllocator)(_threadAllocatorState[]); } ();
+ return _threadAllocator;
+}
+
+/**
+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()
+{
+ auto p = _threadAllocator;
+ return p !is null ? p : setupThreadAllocator();
+}
+
+/// 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()
+{
+ import std.experimental.allocator.gc_allocator : GCAllocator;
+ import std.concurrency : initOnce;
+ return initOnce!_processAllocator(
+ sharedAllocatorObject(GCAllocator.instance));
+}
+
+/// Ditto
+@property void processAllocator(shared ISharedAllocator a)
+{
+ assert(a);
+ _processAllocator = a;
+}
+
+@system unittest
+{
+ import core.exception : AssertError;
+ import std.exception : assertThrown;
+ import std.experimental.allocator.building_blocks.free_list : SharedFreeList;
+ import std.experimental.allocator.mallocator : Mallocator;
+
+ 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.experimental.allocator.gc_allocator : GCAllocator;
+ import std.internal.test.dummyrange;
+ 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/libphobos/src/std/experimental/allocator/showcase.d b/libphobos/src/std/experimental/allocator/showcase.d
new file mode 100644
index 0000000..6985e5d
--- /dev/null
+++ b/libphobos/src/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;
+ import std.algorithm.comparison : max;
+ import std.experimental.allocator.building_blocks.region
+ : Region;
+ 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/libphobos/src/std/experimental/allocator/typed.d b/libphobos/src/std/experimental/allocator/typed.d
new file mode 100644
index 0000000..92828f3
--- /dev/null
+++ b/libphobos/src/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 <td style="text-align:left">$(D $1)</td> $(TD $(ARGS $+)))
+*/
+
+module std.experimental.allocator.typed;
+
+import std.experimental.allocator;
+import std.experimental.allocator.common;
+import std.range : isInputRange, isForwardRange, walkLength, save, empty,
+ front, popFront;
+import std.traits : isPointer, hasElaborateDestructor;
+import std.typecons : Flag, Yes, No;
+
+/**
+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.algorithm.sorting : isSorted;
+ import std.meta : AliasSeq;
+ import std.typecons : Tuple;
+
+ 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/libphobos/src/std/experimental/checkedint.d b/libphobos/src/std/experimental/checkedint.d
new file mode 100644
index 0000000..48ed2f7
--- /dev/null
+++ b/libphobos/src/std/experimental/checkedint.d
@@ -0,0 +1,3063 @@
+/**
+$(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 Throw)) $(TD
+ fails every incorrect operation by throwing an exception.
+ ))
+ $(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.experimental.allocator.common : stateSize;
+ import std.traits : hasMember;
+
+ /**
+ 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
+overflow = The overflow 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 overflow)
+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;
+ }
+ overflow = 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)
+ {
+ overflow = true;
+ return -1;
+ }
+ if (!isUnsigned!R && rhs < 0)
+ {
+ overflow = 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, overflow);
+ }
+ 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), overflow);
+ }
+ else static if (x == "-")
+ {
+ static if (isUnsigned!Result) alias impl = subu;
+ else alias impl = subs;
+ return impl(Result(lhs), Result(rhs), overflow);
+ }
+ 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), overflow);
+ }
+ 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), overflow);
+ }
+ else static if (!isUnsigned!R)
+ {
+ if (rhs < 0)
+ return subu(Result(lhs), Result(-rhs), overflow);
+ }
+ return addu(Result(lhs), Result(rhs), overflow);
+ }
+ 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), overflow);
+ }
+ return subu(Result(lhs), Result(rhs), overflow);
+ }
+ 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), overflow);
+ }
+ 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:
+ overflow = 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/libphobos/src/std/experimental/logger/core.d b/libphobos/src/std/experimental/logger/core.d
new file mode 100644
index 0000000..2f857c6
--- /dev/null
+++ b/libphobos/src/std/experimental/logger/core.d
@@ -0,0 +1,3187 @@
+///
+module std.experimental.logger.core;
+
+import core.sync.mutex : Mutex;
+import std.datetime.date : DateTime;
+import std.datetime.systime : Clock, SysTime;
+import std.range.primitives;
+import std.traits;
+
+import std.experimental.logger.filelogger;
+
+/** This template evaluates if the passed $(D LogLevel) is active.
+The previously described version statements are used to decide if the
+$(D LogLevel) is active. The version statements only influence the compile
+unit they are used with, therefore this function can only disable logging this
+specific compile unit.
+*/
+template isLoggingActiveAt(LogLevel ll)
+{
+ version (StdLoggerDisableLogging)
+ {
+ enum isLoggingActiveAt = false;
+ }
+ else
+ {
+ static if (ll == LogLevel.trace)
+ {
+ version (StdLoggerDisableTrace) enum isLoggingActiveAt = false;
+ }
+ else static if (ll == LogLevel.info)
+ {
+ version (StdLoggerDisableInfo) enum isLoggingActiveAt = false;
+ }
+ else static if (ll == LogLevel.warning)
+ {
+ version (StdLoggerDisableWarning) enum isLoggingActiveAt = false;
+ }
+ else static if (ll == LogLevel.error)
+ {
+ version (StdLoggerDisableError) enum isLoggingActiveAt = false;
+ }
+ else static if (ll == LogLevel.critical)
+ {
+ version (StdLoggerDisableCritical) enum isLoggingActiveAt = false;
+ }
+ else static if (ll == LogLevel.fatal)
+ {
+ version (StdLoggerDisableFatal) enum isLoggingActiveAt = false;
+ }
+ // If `isLoggingActiveAt` didn't get defined above to false,
+ // we default it to true.
+ static if (!is(typeof(isLoggingActiveAt) == bool))
+ {
+ enum isLoggingActiveAt = true;
+ }
+ }
+}
+
+/// This compile-time flag is $(D true) if logging is not statically disabled.
+enum isLoggingActive = isLoggingActiveAt!(LogLevel.all);
+
+/** This functions is used at runtime to determine if a $(D LogLevel) is
+active. The same previously defined version statements are used to disable
+certain levels. Again the version statements are associated with a compile
+unit and can therefore not disable logging in other compile units.
+pure bool isLoggingEnabled()(LogLevel ll) @safe nothrow @nogc
+*/
+bool isLoggingEnabled()(LogLevel ll, LogLevel loggerLL,
+ LogLevel globalLL, lazy bool condition = true) @safe
+{
+ switch (ll)
+ {
+ case LogLevel.trace:
+ version (StdLoggerDisableTrace) return false;
+ else break;
+ case LogLevel.info:
+ version (StdLoggerDisableInfo) return false;
+ else break;
+ case LogLevel.warning:
+ version (StdLoggerDisableWarning) return false;
+ else break;
+ case LogLevel.critical:
+ version (StdLoggerDisableCritical) return false;
+ else break;
+ case LogLevel.fatal:
+ version (StdLoggerDisableFatal) return false;
+ else break;
+ default: break;
+ }
+
+ return ll >= globalLL
+ && ll >= loggerLL
+ && ll != LogLevel.off
+ && globalLL != LogLevel.off
+ && loggerLL != LogLevel.off
+ && condition;
+}
+
+/** This template returns the $(D LogLevel) named "logLevel" of type $(D
+LogLevel) defined in a user defined module where the filename has the
+suffix "_loggerconfig.d". This $(D LogLevel) sets the minimal $(D LogLevel)
+of the module.
+
+A minimal $(D LogLevel) can be defined on a per module basis.
+In order to define a module $(D LogLevel) a file with a modulename
+"MODULENAME_loggerconfig" must be found. If no such module exists and the
+module is a nested module, it is checked if there exists a
+"PARENT_MODULE_loggerconfig" module with such a symbol.
+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)
+{
+ // default
+ enum moduleLogLevel = LogLevel.all;
+}
+
+///
+@system unittest
+{
+ static assert(moduleLogLevel!"" == LogLevel.all);
+}
+
+/// ditto
+template moduleLogLevel(string moduleName)
+if (moduleName.length)
+{
+ import std.string : format;
+ mixin(q{
+ static if (__traits(compiles, {import %1$s : logLevel;}))
+ {
+ import %1$s : logLevel;
+ static assert(is(typeof(logLevel) : LogLevel),
+ "Expect 'logLevel' to be of Type 'LogLevel'.");
+ // don't enforce enum here
+ alias moduleLogLevel = logLevel;
+ }
+ else
+ // use logLevel of package or default
+ alias moduleLogLevel = moduleLogLevel!(parentOf(moduleName));
+ }.format(moduleName ~ "_loggerconfig"));
+}
+
+///
+@system unittest
+{
+ static assert(moduleLogLevel!"not.amodule.path" == LogLevel.all);
+}
+
+private string parentOf(string mod)
+{
+ foreach_reverse (i, c; mod)
+ if (c == '.') return mod[0 .. i];
+ return null;
+}
+
+/* This function formates a $(D SysTime) into an $(D OutputRange).
+
+The $(D SysTime) is formatted similar to
+$(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))
+{
+ 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",
+ dt.year, dt.month, dt.day, dt.hour, dt.minute, dt.second,
+ fsec);
+}
+
+/** This function logs data.
+
+In order for the data to be processed, the $(D LogLevel) of the log call must
+be greater or equal to the $(D LogLevel) of the $(D sharedLog) and the
+$(D defaultLogLevel); additionally the condition passed must be $(D true).
+
+Params:
+ ll = The $(D LogLevel) used by this log call.
+ condition = The condition must be $(D true) for the data to be logged.
+ args = The data that should be logged.
+
+Example:
+--------------------
+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)
+if (args.length != 1)
+{
+ static if (isLoggingActive)
+ {
+ if (ll >= moduleLogLevel!moduleName)
+ {
+ stdThreadLocalLog.log!(line, file, funcName, prettyFuncName, moduleName)
+ (ll, condition, args);
+ }
+ }
+}
+
+/// Ditto
+void log(T, string moduleName = __MODULE__)(const LogLevel ll,
+ lazy bool condition, lazy T arg, int line = __LINE__, string file = __FILE__,
+ string funcName = __FUNCTION__, string prettyFuncName = __PRETTY_FUNCTION__)
+{
+ static if (isLoggingActive)
+ {
+ if (ll >= moduleLogLevel!moduleName)
+ {
+ stdThreadLocalLog.log!(T,moduleName)(ll, condition, arg, line, file, funcName,
+ prettyFuncName);
+ }
+ }
+}
+
+/** This function logs data.
+
+In order for the data to be processed the $(D LogLevel) of the log call must
+be greater or equal to the $(D LogLevel) of the $(D sharedLog).
+
+Params:
+ ll = The $(D LogLevel) used by this log call.
+ args = The data that should be logged.
+
+Example:
+--------------------
+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))
+{
+ static if (isLoggingActive)
+ {
+ if (ll >= moduleLogLevel!moduleName)
+ {
+ stdThreadLocalLog.log!(line, file, funcName, prettyFuncName, moduleName)
+ (ll, args);
+ }
+ }
+}
+
+/// Ditto
+void log(T, string moduleName = __MODULE__)(const LogLevel ll, lazy T arg,
+ int line = __LINE__, string file = __FILE__, string funcName = __FUNCTION__,
+ string prettyFuncName = __PRETTY_FUNCTION__)
+{
+ static if (isLoggingActive)
+ {
+ if (ll >= moduleLogLevel!moduleName)
+ {
+ stdThreadLocalLog.log!T(ll, arg, line, file, funcName, prettyFuncName,
+ moduleName);
+ }
+ }
+}
+
+/** This function logs data.
+
+In order for the data to be processed the $(D LogLevel) of the
+$(D sharedLog) must be greater or equal to the $(D defaultLogLevel)
+add the condition passed must be $(D true).
+
+Params:
+ condition = The condition must be $(D true) for the data to be logged.
+ args = The data that should be logged.
+
+Example:
+--------------------
+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)
+{
+ static if (isLoggingActive)
+ {
+ stdThreadLocalLog.log!(line, file, funcName, prettyFuncName, moduleName)
+ (stdThreadLocalLog.logLevel, condition, args);
+ }
+}
+
+/// Ditto
+void log(T, string moduleName = __MODULE__)(lazy bool condition, lazy T arg,
+ int line = __LINE__, string file = __FILE__, string funcName = __FUNCTION__,
+ string prettyFuncName = __PRETTY_FUNCTION__)
+{
+ static if (isLoggingActive)
+ {
+ stdThreadLocalLog.log!(T,moduleName)(stdThreadLocalLog.logLevel,
+ condition, arg, line, file, funcName, prettyFuncName);
+ }
+}
+
+/** This function logs data.
+
+In order for the data to be processed the $(D LogLevel) of the
+$(D sharedLog) must be greater or equal to the $(D defaultLogLevel).
+
+Params:
+ args = The data that should be logged.
+
+Example:
+--------------------
+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)
+{
+ static if (isLoggingActive)
+ {
+ stdThreadLocalLog.log!(line, file, funcName,
+ prettyFuncName, moduleName)(stdThreadLocalLog.logLevel, args);
+ }
+}
+
+void log(T)(lazy T arg, int line = __LINE__, string file = __FILE__,
+ string funcName = __FUNCTION__, string prettyFuncName = __PRETTY_FUNCTION__,
+ string moduleName = __MODULE__)
+{
+ static if (isLoggingActive)
+ {
+ stdThreadLocalLog.log!T(stdThreadLocalLog.logLevel, arg, line, file,
+ funcName, prettyFuncName, moduleName);
+ }
+}
+
+/** This function logs data in a $(D printf)-style manner.
+
+In order for the data to be processed the $(D LogLevel) of the log call must
+be greater or equal to the $(D LogLevel) of the $(D sharedLog) and the
+$(D defaultLogLevel) additionally the condition passed must be $(D true).
+
+Params:
+ ll = The $(D LogLevel) used by this log call.
+ condition = The condition must be $(D true) for the data to be logged.
+ msg = The $(D printf)-style string.
+ args = The data that should be logged.
+
+Example:
+--------------------
+logf(LogLevel.warning, true, "Hello World %f", 3.1415);
+--------------------
+*/
+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)
+{
+ static if (isLoggingActive)
+ {
+ if (ll >= moduleLogLevel!moduleName)
+ {
+ stdThreadLocalLog.logf!(line, file, funcName, prettyFuncName, moduleName)
+ (ll, condition, msg, args);
+ }
+ }
+}
+
+/** This function logs data in a $(D printf)-style manner.
+
+In order for the data to be processed the $(D LogLevel) of the log call must
+be greater or equal to the $(D LogLevel) of the $(D sharedLog) and the
+$(D defaultLogLevel).
+
+Params:
+ ll = The $(D LogLevel) used by this log call.
+ msg = The $(D printf)-style string.
+ args = The data that should be logged.
+
+Example:
+--------------------
+logf(LogLevel.warning, "Hello World %f", 3.1415);
+--------------------
+*/
+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)
+{
+ static if (isLoggingActive)
+ {
+ if (ll >= moduleLogLevel!moduleName)
+ {
+ stdThreadLocalLog.logf!(line, file, funcName, prettyFuncName, moduleName)
+ (ll, msg, args);
+ }
+ }
+}
+
+/** This function logs data in a $(D printf)-style manner.
+
+In order for the data to be processed the $(D LogLevel) of the log call must
+be greater or equal to the $(D defaultLogLevel) additionally the condition
+passed must be $(D true).
+
+Params:
+ condition = The condition must be $(D true) for the data to be logged.
+ msg = The $(D printf)-style string.
+ args = The data that should be logged.
+
+Example:
+--------------------
+logf(true, "Hello World %f", 3.1415);
+--------------------
+*/
+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)
+{
+ static if (isLoggingActive)
+ {
+ stdThreadLocalLog.logf!(line, file, funcName, prettyFuncName, moduleName)
+ (stdThreadLocalLog.logLevel, condition, msg, args);
+ }
+}
+
+/** This function logs data in a $(D printf)-style manner.
+
+In order for the data to be processed the $(D LogLevel) of the log call must
+be greater or equal to the $(D defaultLogLevel).
+
+Params:
+ msg = The $(D printf)-style string.
+ args = The data that should be logged.
+
+Example:
+--------------------
+logf("Hello World %f", 3.1415);
+--------------------
+*/
+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)
+{
+ static if (isLoggingActive)
+ {
+ stdThreadLocalLog.logf!(line, file, funcName,prettyFuncName, moduleName)
+ (stdThreadLocalLog.logLevel, msg, args);
+ }
+}
+
+/** This template provides the global log functions with the $(D LogLevel)
+is encoded in the function name.
+
+The aliases following this template create the public names of these log
+functions.
+*/
+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)
+ if ((args.length > 0 && !is(Unqual!(A[0]) : bool)) || args.length == 0)
+ {
+ static if (isLoggingActiveAt!ll && ll >= moduleLogLevel!moduleName)
+ {
+ stdThreadLocalLog.memLogFunctions!(ll).logImpl!(line, file, funcName,
+ prettyFuncName, moduleName)(args);
+ }
+ }
+
+ void defaultLogFunction(int line = __LINE__, string file = __FILE__,
+ string funcName = __FUNCTION__,
+ string prettyFuncName = __PRETTY_FUNCTION__,
+ string moduleName = __MODULE__, A...)(lazy bool condition, lazy A args)
+ {
+ static if (isLoggingActiveAt!ll && ll >= moduleLogLevel!moduleName)
+ {
+ stdThreadLocalLog.memLogFunctions!(ll).logImpl!(line, file, funcName,
+ prettyFuncName, moduleName)(condition, args);
+ }
+ }
+}
+
+/** 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.
+
+Example:
+--------------------
+trace(1337, "is number");
+info(1337, "is number");
+error(1337, "is number");
+critical(1337, "is number");
+fatal(1337, "is number");
+trace(true, 1337, "is number");
+info(false, 1337, "is number");
+error(true, 1337, "is number");
+critical(false, 1337, "is number");
+fatal(true, 1337, "is number");
+--------------------
+*/
+alias trace = defaultLogFunction!(LogLevel.trace);
+/// Ditto
+alias info = defaultLogFunction!(LogLevel.info);
+/// Ditto
+alias warning = defaultLogFunction!(LogLevel.warning);
+/// Ditto
+alias error = defaultLogFunction!(LogLevel.error);
+/// Ditto
+alias critical = defaultLogFunction!(LogLevel.critical);
+/// Ditto
+alias fatal = defaultLogFunction!(LogLevel.fatal);
+
+/** This template provides the global $(D printf)-style log functions with
+the $(D LogLevel) is encoded in the function name.
+
+The aliases following this template create the public names of the log
+functions.
+*/
+template defaultLogFunctionf(LogLevel ll)
+{
+ void defaultLogFunctionf(int line = __LINE__, string file = __FILE__,
+ string funcName = __FUNCTION__,
+ string prettyFuncName = __PRETTY_FUNCTION__,
+ string moduleName = __MODULE__, A...)(lazy string msg, lazy A args)
+ {
+ static if (isLoggingActiveAt!ll && ll >= moduleLogLevel!moduleName)
+ {
+ stdThreadLocalLog.memLogFunctions!(ll).logImplf!(line, file, funcName,
+ prettyFuncName, moduleName)(msg, args);
+ }
+ }
+
+ void defaultLogFunctionf(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)
+ {
+ static if (isLoggingActiveAt!ll && ll >= moduleLogLevel!moduleName)
+ {
+ stdThreadLocalLog.memLogFunctions!(ll).logImplf!(line, file, funcName,
+ prettyFuncName, moduleName)(condition, msg, args);
+ }
+ }
+}
+
+/** This function logs data to the $(D sharedLog) in a $(D printf)-style
+manner.
+
+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 sharedLog) 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).
+
+Params:
+ msg = The $(D printf)-style string.
+ args = The data that should be logged.
+
+Example:
+--------------------
+tracef("is number %d", 1);
+infof("is number %d", 2);
+errorf("is number %d", 3);
+criticalf("is number %d", 4);
+fatalf("is number %d", 5);
+--------------------
+
+The second version of the function logs data to the $(D sharedLog) in a $(D
+printf)-style manner.
+
+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 sharedLog) 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).
+
+Params:
+ condition = The condition must be $(D true) for the data to be logged.
+ msg = The $(D printf)-style string.
+ args = The data that should be logged.
+
+Example:
+--------------------
+tracef(false, "is number %d", 1);
+infof(false, "is number %d", 2);
+errorf(true, "is number %d", 3);
+criticalf(true, "is number %d", 4);
+fatalf(someFunct(), "is number %d", 5);
+--------------------
+*/
+alias tracef = defaultLogFunctionf!(LogLevel.trace);
+/// Ditto
+alias infof = defaultLogFunctionf!(LogLevel.info);
+/// Ditto
+alias warningf = defaultLogFunctionf!(LogLevel.warning);
+/// Ditto
+alias errorf = defaultLogFunctionf!(LogLevel.error);
+/// Ditto
+alias criticalf = defaultLogFunctionf!(LogLevel.critical);
+/// Ditto
+alias fatalf = defaultLogFunctionf!(LogLevel.fatal);
+
+private struct MsgRange
+{
+ import std.traits : isSomeString, isSomeChar;
+
+ private Logger log;
+
+ this(Logger log) @safe
+ {
+ this.log = log;
+ }
+
+ void put(T)(T msg) @safe
+ if (isSomeString!T)
+ {
+ log.logMsgPart(msg);
+ }
+
+ void put(dchar elem) @safe
+ {
+ import std.utf : encode;
+ char[4] buffer;
+ size_t len = encode(buffer, elem);
+ log.logMsgPart(buffer[0 .. len]);
+ }
+}
+
+private void formatString(A...)(MsgRange oRange, A args)
+{
+ import std.format : formattedWrite;
+
+ foreach (arg; args)
+ {
+ formattedWrite(oRange, "%s", arg);
+ }
+}
+
+@system unittest
+{
+ void dummy() @safe
+ {
+ auto tl = new TestLogger();
+ auto dst = MsgRange(tl);
+ formatString(dst, "aaa", "bbb");
+ }
+
+ dummy();
+}
+
+/**
+There are eight usable logging level. These level are $(I all), $(I trace),
+$(I info), $(I warning), $(I error), $(I critical), $(I fatal), and $(I off).
+If a log function with $(D LogLevel.fatal) is called the shutdown handler of
+that logger is called.
+*/
+enum LogLevel : ubyte
+{
+ all = 1, /** Lowest possible assignable $(D LogLevel). */
+ trace = 32, /** $(D LogLevel) for tracing the execution of the program. */
+ info = 64, /** This level is used to display information about the
+ program. */
+ warning = 96, /** warnings about the program should be displayed with this
+ level. */
+ error = 128, /** Information about errors should be logged with this
+ level.*/
+ critical = 160, /** Messages that inform about critical errors should be
+ logged with this level. */
+ fatal = 192, /** Log messages that describe fatal errors should use this
+ level. */
+ off = ubyte.max /** Highest possible $(D LogLevel). */
+}
+
+/** This class is the base of every logger. In order to create a new kind of
+logger a deriving class needs to implement the $(D writeLogMsg) method. By
+default this is not thread-safe.
+
+It is also possible to $(D override) the three methods $(D beginLogMsg),
+$(D logMsgPart) and $(D finishLogMsg) together, this option gives more
+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.
+ */
+ protected struct LogEntry
+ {
+ /// the filename the log function was called from
+ string file;
+ /// the line number the log function was called from
+ int line;
+ /// the name of the function the log function was called from
+ string funcName;
+ /// the pretty formatted name of the function the log function was
+ /// called from
+ string prettyFuncName;
+ /// the name of the module the log message is coming from
+ string moduleName;
+ /// the $(D LogLevel) associated with the log message
+ LogLevel logLevel;
+ /// thread id of the log message
+ Tid threadId;
+ /// the time the message was logged
+ SysTime timestamp;
+ /// the message of the log message
+ string msg;
+ /// A refernce to the $(D Logger) used to create this $(D LogEntry)
+ Logger logger;
+ }
+
+ /**
+ 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`.
+
+ Params:
+ lv = `LogLevel` to use for this `Logger` instance.
+ */
+ this(LogLevel lv) @safe
+ {
+ this.logLevel_ = lv;
+ this.fatalHandler_ = delegate() {
+ throw new Error("A fatal log message was logged");
+ };
+
+ this.mutex = new Mutex();
+ }
+
+ /** A custom logger must implement this method in order to work in a
+ $(D MultiLogger) and $(D ArrayLogger).
+
+ Params:
+ payload = All information associated with call to log function.
+
+ See_Also: beginLogMsg, logMsgPart, finishLogMsg
+ */
+ abstract protected void writeLogMsg(ref LogEntry payload) @safe;
+
+ /* The default implementation will use an $(D std.array.appender)
+ internally to construct the message string. This means dynamic,
+ GC memory allocation. A logger can avoid this allocation by
+ reimplementing $(D beginLogMsg), $(D logMsgPart) and $(D finishLogMsg).
+ $(D beginLogMsg) is always called first, followed by any number of calls
+ to $(D logMsgPart) and one call to $(D finishLogMsg).
+
+ As an example for such a custom $(D Logger) compare this:
+ ----------------
+ class CLogger : Logger
+ {
+ override void beginLogMsg(string file, int line, string funcName,
+ string prettyFuncName, string moduleName, LogLevel logLevel,
+ Tid threadId, SysTime timestamp)
+ {
+ ... logic here
+ }
+
+ override void logMsgPart(const(char)[] msg)
+ {
+ ... logic here
+ }
+
+ override void finishLogMsg()
+ {
+ ... logic here
+ }
+
+ void writeLogMsg(ref LogEntry payload)
+ {
+ this.beginLogMsg(payload.file, payload.line, payload.funcName,
+ payload.prettyFuncName, payload.moduleName, payload.logLevel,
+ payload.threadId, payload.timestamp, payload.logger);
+
+ this.logMsgPart(payload.msg);
+ this.finishLogMsg();
+ }
+ }
+ ----------------
+ */
+ protected void beginLogMsg(string file, int line, string funcName,
+ string prettyFuncName, string moduleName, LogLevel logLevel,
+ Tid threadId, SysTime timestamp, Logger logger)
+ @safe
+ {
+ static if (isLoggingActive)
+ {
+ msgAppender = appender!string();
+ header = LogEntry(file, line, funcName, prettyFuncName,
+ moduleName, logLevel, threadId, timestamp, null, logger);
+ }
+ }
+
+ /** Logs a part of the log message. */
+ protected void logMsgPart(const(char)[] msg) @safe
+ {
+ static if (isLoggingActive)
+ {
+ msgAppender.put(msg);
+ }
+ }
+
+ /** Signals that the message has been written and no more calls to
+ $(D logMsgPart) follow. */
+ protected void finishLogMsg() @safe
+ {
+ static if (isLoggingActive)
+ {
+ header.msg = msgAppender.data;
+ this.writeLogMsg(header);
+ }
+ }
+
+ /** The $(D LogLevel) determines if the log call are processed or dropped
+ by the $(D Logger). In order for the log call to be processed the
+ $(D LogLevel) of the log call must be greater or equal to the $(D LogLevel)
+ of the $(D logger).
+
+ These two methods set and get the $(D LogLevel) of the used $(D Logger).
+
+ Example:
+ -----------
+ auto f = new FileLogger(stdout);
+ f.logLevel = LogLevel.info;
+ assert(f.logLevel == LogLevel.info);
+ -----------
+ */
+ @property final LogLevel logLevel() const pure @safe @nogc
+ {
+ return trustedLoad(this.logLevel_);
+ }
+
+ /// Ditto
+ @property final void logLevel(const LogLevel lv) @safe @nogc
+ {
+ synchronized (mutex) this.logLevel_ = lv;
+ }
+
+ /** This $(D delegate) is called in case a log message with
+ $(D LogLevel.fatal) gets logged.
+
+ By default an $(D Error) will be thrown.
+ */
+ @property final void delegate() fatalHandler() @safe @nogc
+ {
+ synchronized (mutex) return this.fatalHandler_;
+ }
+
+ /// Ditto
+ @property final void fatalHandler(void delegate() @safe fh) @safe @nogc
+ {
+ synchronized (mutex) this.fatalHandler_ = fh;
+ }
+
+ /** This method allows forwarding log entries from one logger to another.
+
+ $(D forwardMsg) will ensure proper synchronization and then call
+ $(D writeLogMsg). This is an API for implementing your own loggers and
+ should not be called by normal user code. A notable difference from other
+ logging functions is that the $(D globalLogLevel) wont be evaluated again
+ since it is assumed that the caller already checked that.
+ */
+ void forwardMsg(ref LogEntry payload) @trusted
+ {
+ static if (isLoggingActive) synchronized (mutex)
+ {
+ if (isLoggingEnabled(payload.logLevel, this.logLevel_,
+ globalLogLevel))
+ {
+ this.writeLogMsg(payload);
+
+ if (payload.logLevel == LogLevel.fatal)
+ this.fatalHandler_();
+ }
+ }
+ }
+
+ /** This template provides the log functions for the $(D Logger) $(D class)
+ with the $(D LogLevel) encoded in the function name.
+
+ For further information see the the two functions defined inside of this
+ template.
+
+ The aliases following this template create the public names of these log
+ functions.
+ */
+ template memLogFunctions(LogLevel ll)
+ {
+ /** This function logs data to the used $(D Logger).
+
+ In order for the resulting log message to be logged the $(D LogLevel)
+ must be greater or equal than the $(D LogLevel) of the used $(D Logger)
+ and must be greater or equal than the global $(D LogLevel).
+
+ Params:
+ args = The data that should be logged.
+
+ Example:
+ --------------------
+ auto s = new FileLogger(stdout);
+ s.trace(1337, "is number");
+ s.info(1337, "is number");
+ s.error(1337, "is number");
+ s.critical(1337, "is number");
+ s.fatal(1337, "is number");
+ --------------------
+ */
+ void logImpl(int line = __LINE__, string file = __FILE__,
+ string funcName = __FUNCTION__,
+ string prettyFuncName = __PRETTY_FUNCTION__,
+ string moduleName = __MODULE__, A...)(lazy A args)
+ if (args.length == 0 || (args.length > 0 && !is(A[0] : bool)))
+ {
+ static if (isLoggingActiveAt!ll && ll >= moduleLogLevel!moduleName)
+ synchronized (mutex)
+ {
+ if (isLoggingEnabled(ll, this.logLevel_, globalLogLevel))
+ {
+ this.beginLogMsg(file, line, funcName, prettyFuncName,
+ moduleName, ll, thisTid, Clock.currTime, this);
+
+ auto writer = MsgRange(this);
+ formatString(writer, args);
+
+ this.finishLogMsg();
+
+ static if (ll == LogLevel.fatal)
+ this.fatalHandler_();
+ }
+ }
+ }
+
+ /** This function logs data to the used $(D Logger) 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 used $(D Logger) and
+ must be greater or equal than the global $(D LogLevel) additionally the
+ condition passed must be $(D true).
+
+ Params:
+ condition = The condition must be $(D true) for the data to be logged.
+ args = The data that should be logged.
+
+ Example:
+ --------------------
+ auto s = new FileLogger(stdout);
+ s.trace(true, 1337, "is number");
+ s.info(false, 1337, "is number");
+ s.error(true, 1337, "is number");
+ s.critical(false, 1337, "is number");
+ s.fatal(true, 1337, "is number");
+ --------------------
+ */
+ void logImpl(int line = __LINE__, string file = __FILE__,
+ string funcName = __FUNCTION__,
+ string prettyFuncName = __PRETTY_FUNCTION__,
+ string moduleName = __MODULE__, A...)(lazy bool condition,
+ lazy A args)
+ {
+ static if (isLoggingActiveAt!ll && ll >= moduleLogLevel!moduleName)
+ synchronized (mutex)
+ {
+ if (isLoggingEnabled(ll, this.logLevel_, globalLogLevel,
+ condition))
+ {
+ this.beginLogMsg(file, line, funcName, prettyFuncName,
+ moduleName, ll, thisTid, Clock.currTime, this);
+
+ auto writer = MsgRange(this);
+ formatString(writer, args);
+
+ this.finishLogMsg();
+
+ static if (ll == LogLevel.fatal)
+ this.fatalHandler_();
+ }
+ }
+ }
+
+ /** This function logs data to the used $(D Logger) in a
+ $(D printf)-style manner.
+
+ In order for the resulting log message to be logged the $(D LogLevel)
+ must be greater or equal than the $(D LogLevel) of the used $(D Logger)
+ and must be greater or equal than the global $(D LogLevel) additionally
+ the passed condition must be $(D true).
+
+ Params:
+ condition = The condition must be $(D true) for the data to be logged.
+ msg = The $(D printf)-style string.
+ args = The data that should be logged.
+
+ Example:
+ --------------------
+ auto s = new FileLogger(stderr);
+ s.tracef(true, "is number %d", 1);
+ s.infof(true, "is number %d", 2);
+ s.errorf(false, "is number %d", 3);
+ s.criticalf(someFunc(), "is number %d", 4);
+ s.fatalf(true, "is number %d", 5);
+ --------------------
+ */
+ void logImplf(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)
+ {
+ static if (isLoggingActiveAt!ll && ll >= moduleLogLevel!moduleName)
+ synchronized (mutex)
+ {
+ import std.format : formattedWrite;
+
+ if (isLoggingEnabled(ll, this.logLevel_, globalLogLevel,
+ condition))
+ {
+ this.beginLogMsg(file, line, funcName, prettyFuncName,
+ moduleName, ll, thisTid, Clock.currTime, this);
+
+ auto writer = MsgRange(this);
+ formattedWrite(writer, msg, args);
+
+ this.finishLogMsg();
+
+ static if (ll == LogLevel.fatal)
+ this.fatalHandler_();
+ }
+ }
+ }
+
+ /** This function logs data to the used $(D Logger) in a
+ $(D printf)-style manner.
+
+ In order for the resulting log message to be logged the $(D LogLevel) must
+ be greater or equal than the $(D LogLevel) of the used $(D Logger) and
+ must be greater or equal than the global $(D LogLevel).
+
+ Params:
+ msg = The $(D printf)-style string.
+ args = The data that should be logged.
+
+ Example:
+ --------------------
+ auto s = new FileLogger(stderr);
+ s.tracef("is number %d", 1);
+ s.infof("is number %d", 2);
+ s.errorf("is number %d", 3);
+ s.criticalf("is number %d", 4);
+ s.fatalf("is number %d", 5);
+ --------------------
+ */
+ void logImplf(int line = __LINE__, string file = __FILE__,
+ string funcName = __FUNCTION__,
+ string prettyFuncName = __PRETTY_FUNCTION__,
+ string moduleName = __MODULE__, A...)(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))
+ {
+ this.beginLogMsg(file, line, funcName, prettyFuncName,
+ moduleName, ll, thisTid, Clock.currTime, this);
+
+ auto writer = MsgRange(this);
+ formattedWrite(writer, msg, args);
+
+ this.finishLogMsg();
+
+ static if (ll == LogLevel.fatal)
+ this.fatalHandler_();
+ }
+ }
+ }
+ }
+
+ /// Ditto
+ alias trace = memLogFunctions!(LogLevel.trace).logImpl;
+ /// Ditto
+ alias tracef = memLogFunctions!(LogLevel.trace).logImplf;
+ /// Ditto
+ alias info = memLogFunctions!(LogLevel.info).logImpl;
+ /// Ditto
+ alias infof = memLogFunctions!(LogLevel.info).logImplf;
+ /// Ditto
+ alias warning = memLogFunctions!(LogLevel.warning).logImpl;
+ /// Ditto
+ alias warningf = memLogFunctions!(LogLevel.warning).logImplf;
+ /// Ditto
+ alias error = memLogFunctions!(LogLevel.error).logImpl;
+ /// Ditto
+ alias errorf = memLogFunctions!(LogLevel.error).logImplf;
+ /// Ditto
+ alias critical = memLogFunctions!(LogLevel.critical).logImpl;
+ /// Ditto
+ alias criticalf = memLogFunctions!(LogLevel.critical).logImplf;
+ /// Ditto
+ alias fatal = memLogFunctions!(LogLevel.fatal).logImpl;
+ /// Ditto
+ alias fatalf = memLogFunctions!(LogLevel.fatal).logImplf;
+
+ /** This method logs data with the $(D LogLevel) of the used $(D Logger).
+
+ This method takes a $(D bool) as first argument. In order for the
+ data to be processed the $(D bool) must be $(D true) and the $(D LogLevel)
+ of the Logger must be greater or equal to the global $(D LogLevel).
+
+ Params:
+ args = The data that should be logged.
+ condition = The condition must be $(D true) for the data to be logged.
+ args = The data that is to be logged.
+
+ Returns: The logger used by the logging function as reference.
+
+ Example:
+ --------------------
+ auto l = new StdioLogger();
+ l.log(1337);
+ --------------------
+ */
+ 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)
+ if (args.length != 1)
+ {
+ static if (isLoggingActive) synchronized (mutex)
+ {
+ if (isLoggingEnabled(ll, this.logLevel_, globalLogLevel, condition))
+ {
+ this.beginLogMsg(file, line, funcName, prettyFuncName,
+ moduleName, ll, thisTid, Clock.currTime, this);
+
+ auto writer = MsgRange(this);
+ formatString(writer, args);
+
+ this.finishLogMsg();
+
+ if (ll == LogLevel.fatal)
+ this.fatalHandler_();
+ }
+ }
+ }
+
+ /// Ditto
+ 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__)
+ {
+ static if (isLoggingActive) synchronized (mutex)
+ {
+ if (isLoggingEnabled(ll, this.logLevel_, globalLogLevel,
+ condition) && ll >= moduleLogLevel!moduleName)
+ {
+ this.beginLogMsg(file, line, funcName, prettyFuncName,
+ moduleName, ll, thisTid, Clock.currTime, this);
+ auto writer = MsgRange(this);
+ formatString(writer, args);
+
+ this.finishLogMsg();
+
+ if (ll == LogLevel.fatal)
+ this.fatalHandler_();
+ }
+ }
+ }
+
+ /** This function logs data to the used $(D Logger) with a specific
+ $(D LogLevel).
+
+ In order for the resulting log message to be logged the $(D LogLevel)
+ must be greater or equal than the $(D LogLevel) of the used $(D Logger)
+ and must be greater or equal than the global $(D LogLevel).
+
+ Params:
+ ll = The specific $(D LogLevel) used for logging the log message.
+ args = The data that should be logged.
+
+ Example:
+ --------------------
+ auto s = new FileLogger(stdout);
+ s.log(LogLevel.trace, 1337, "is number");
+ s.log(LogLevel.info, 1337, "is number");
+ s.log(LogLevel.warning, 1337, "is number");
+ s.log(LogLevel.error, 1337, "is number");
+ s.log(LogLevel.fatal, 1337, "is number");
+ --------------------
+ */
+ 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)) || args.length == 0)
+ {
+ static if (isLoggingActive) synchronized (mutex)
+ {
+ if (isLoggingEnabled(ll, this.logLevel_, globalLogLevel))
+ {
+ this.beginLogMsg(file, line, funcName, prettyFuncName,
+ moduleName, ll, thisTid, Clock.currTime, this);
+
+ auto writer = MsgRange(this);
+ formatString(writer, args);
+
+ this.finishLogMsg();
+
+ if (ll == LogLevel.fatal)
+ this.fatalHandler_();
+ }
+ }
+ }
+
+ /// Ditto
+ 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__)
+ {
+ static if (isLoggingActive) synchronized (mutex)
+ {
+ if (isLoggingEnabled(ll, this.logLevel_, globalLogLevel))
+ {
+ this.beginLogMsg(file, line, funcName, prettyFuncName,
+ moduleName, ll, thisTid, Clock.currTime, this);
+ auto writer = MsgRange(this);
+ formatString(writer, args);
+
+ this.finishLogMsg();
+
+ if (ll == LogLevel.fatal)
+ this.fatalHandler_();
+ }
+ }
+ }
+
+ /** This function logs data to the used $(D Logger) depending on a
+ explicitly passed condition with the $(D LogLevel) of the used
+ $(D Logger).
+
+ In order for the resulting log message to be logged the $(D LogLevel)
+ of the used $(D Logger) must be greater or equal than the global
+ $(D LogLevel) and the condition must be $(D true).
+
+ Params:
+ condition = The condition must be $(D true) for the data to be logged.
+ args = The data that should be logged.
+
+ Example:
+ --------------------
+ auto s = new FileLogger(stdout);
+ s.log(true, 1337, "is number");
+ s.log(true, 1337, "is number");
+ s.log(true, 1337, "is number");
+ s.log(false, 1337, "is number");
+ s.log(false, 1337, "is number");
+ --------------------
+ */
+ 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)
+ {
+ static if (isLoggingActive) synchronized (mutex)
+ {
+ if (isLoggingEnabled(this.logLevel_, this.logLevel_,
+ globalLogLevel, condition))
+ {
+ this.beginLogMsg(file, line, funcName, prettyFuncName,
+ moduleName, this.logLevel_, thisTid, Clock.currTime, this);
+
+ auto writer = MsgRange(this);
+ formatString(writer, args);
+
+ this.finishLogMsg();
+
+ if (this.logLevel_ == LogLevel.fatal)
+ this.fatalHandler_();
+ }
+ }
+ }
+
+ /// Ditto
+ 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__)
+ {
+ static if (isLoggingActive) synchronized (mutex)
+ {
+ if (isLoggingEnabled(this.logLevel_, this.logLevel_, globalLogLevel,
+ condition))
+ {
+ this.beginLogMsg(file, line, funcName, prettyFuncName,
+ moduleName, this.logLevel_, thisTid, Clock.currTime, this);
+ auto writer = MsgRange(this);
+ formatString(writer, args);
+
+ this.finishLogMsg();
+
+ if (this.logLevel_ == LogLevel.fatal)
+ this.fatalHandler_();
+ }
+ }
+ }
+
+ /** This function logs data to the used $(D Logger) with the $(D LogLevel)
+ of the used $(D Logger).
+
+ In order for the resulting log message to be logged the $(D LogLevel)
+ of the used $(D Logger) must be greater or equal than the global
+ $(D LogLevel).
+
+ Params:
+ args = The data that should be logged.
+
+ Example:
+ --------------------
+ auto s = new FileLogger(stdout);
+ s.log(1337, "is number");
+ s.log(info, 1337, "is number");
+ s.log(1337, "is number");
+ s.log(1337, "is number");
+ s.log(1337, "is number");
+ --------------------
+ */
+ 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)
+ {
+ static if (isLoggingActive) synchronized (mutex)
+ {
+ if (isLoggingEnabled(this.logLevel_, this.logLevel_,
+ globalLogLevel))
+ {
+ this.beginLogMsg(file, line, funcName, prettyFuncName,
+ moduleName, this.logLevel_, thisTid, Clock.currTime, this);
+ auto writer = MsgRange(this);
+ formatString(writer, args);
+
+ this.finishLogMsg();
+
+ if (this.logLevel_ == LogLevel.fatal)
+ this.fatalHandler_();
+ }
+ }
+ }
+
+ /// Ditto
+ void log(T)(lazy T arg, int line = __LINE__, string file = __FILE__,
+ string funcName = __FUNCTION__,
+ string prettyFuncName = __PRETTY_FUNCTION__,
+ string moduleName = __MODULE__)
+ {
+ static if (isLoggingActive) synchronized (mutex)
+ {
+ if (isLoggingEnabled(this.logLevel_, this.logLevel_, globalLogLevel))
+ {
+ this.beginLogMsg(file, line, funcName, prettyFuncName,
+ moduleName, this.logLevel_, thisTid, Clock.currTime, this);
+ auto writer = MsgRange(this);
+ formatString(writer, arg);
+
+ this.finishLogMsg();
+
+ if (this.logLevel_ == LogLevel.fatal)
+ this.fatalHandler_();
+ }
+ }
+ }
+
+ /** This function logs data to the used $(D Logger) with a specific
+ $(D LogLevel) and depending on a condition in a $(D printf)-style manner.
+
+ In order for the resulting log message to be logged the $(D LogLevel)
+ must be greater or equal than the $(D LogLevel) of the used $(D Logger)
+ and must be greater or equal than the global $(D LogLevel) and the
+ condition must be $(D true).
+
+ Params:
+ ll = The specific $(D LogLevel) used for logging the log message.
+ condition = The condition must be $(D true) for the data to be logged.
+ msg = The format string used for this log call.
+ args = The data that should be logged.
+
+ Example:
+ --------------------
+ auto s = new FileLogger(stdout);
+ s.logf(LogLevel.trace, true ,"%d %s", 1337, "is number");
+ s.logf(LogLevel.info, true ,"%d %s", 1337, "is number");
+ s.logf(LogLevel.warning, true ,"%d %s", 1337, "is number");
+ s.logf(LogLevel.error, false ,"%d %s", 1337, "is number");
+ s.logf(LogLevel.fatal, true ,"%d %s", 1337, "is number");
+ --------------------
+ */
+ 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)
+ {
+ static if (isLoggingActive) synchronized (mutex)
+ {
+ import std.format : formattedWrite;
+
+ if (isLoggingEnabled(ll, this.logLevel_, globalLogLevel, condition))
+ {
+ this.beginLogMsg(file, line, funcName, prettyFuncName,
+ moduleName, ll, thisTid, Clock.currTime, this);
+
+ auto writer = MsgRange(this);
+ formattedWrite(writer, msg, args);
+
+ this.finishLogMsg();
+
+ if (ll == LogLevel.fatal)
+ this.fatalHandler_();
+ }
+ }
+ }
+
+ /** This function logs data to the used $(D Logger) with a specific
+ $(D LogLevel) in a $(D printf)-style manner.
+
+ In order for the resulting log message to be logged the $(D LogLevel)
+ must be greater or equal than the $(D LogLevel) of the used $(D Logger)
+ and must be greater or equal than the global $(D LogLevel).
+
+ Params:
+ ll = The specific $(D LogLevel) used for logging the log message.
+ msg = The format string used for this log call.
+ args = The data that should be logged.
+
+ Example:
+ --------------------
+ auto s = new FileLogger(stdout);
+ s.logf(LogLevel.trace, "%d %s", 1337, "is number");
+ s.logf(LogLevel.info, "%d %s", 1337, "is number");
+ s.logf(LogLevel.warning, "%d %s", 1337, "is number");
+ s.logf(LogLevel.error, "%d %s", 1337, "is number");
+ s.logf(LogLevel.fatal, "%d %s", 1337, "is number");
+ --------------------
+ */
+ 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)
+ {
+ static if (isLoggingActive) synchronized (mutex)
+ {
+ import std.format : formattedWrite;
+
+ if (isLoggingEnabled(ll, this.logLevel_, globalLogLevel))
+ {
+ this.beginLogMsg(file, line, funcName, prettyFuncName,
+ moduleName, ll, thisTid, Clock.currTime, this);
+
+ auto writer = MsgRange(this);
+ formattedWrite(writer, msg, args);
+
+ this.finishLogMsg();
+
+ if (ll == LogLevel.fatal)
+ this.fatalHandler_();
+ }
+ }
+ }
+
+ /** This function logs data to the used $(D Logger) depending on a
+ condition with the $(D LogLevel) of the used $(D Logger) in a
+ $(D printf)-style manner.
+
+ In order for the resulting log message to be logged the $(D LogLevel)
+ of the used $(D Logger) must be greater or equal than the global
+ $(D LogLevel) and the condition must be $(D true).
+
+ Params:
+ condition = The condition must be $(D true) for the data to be logged.
+ msg = The format string used for this log call.
+ args = The data that should be logged.
+
+ Example:
+ --------------------
+ auto s = new FileLogger(stdout);
+ s.logf(true ,"%d %s", 1337, "is number");
+ s.logf(true ,"%d %s", 1337, "is number");
+ s.logf(true ,"%d %s", 1337, "is number");
+ s.logf(false ,"%d %s", 1337, "is number");
+ s.logf(true ,"%d %s", 1337, "is number");
+ --------------------
+ */
+ 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)
+ {
+ static if (isLoggingActive) synchronized (mutex)
+ {
+ import std.format : formattedWrite;
+
+ if (isLoggingEnabled(this.logLevel_, this.logLevel_, globalLogLevel,
+ condition))
+ {
+ this.beginLogMsg(file, line, funcName, prettyFuncName,
+ moduleName, this.logLevel_, thisTid, Clock.currTime, this);
+
+ auto writer = MsgRange(this);
+ formattedWrite(writer, msg, args);
+
+ this.finishLogMsg();
+
+ if (this.logLevel_ == LogLevel.fatal)
+ this.fatalHandler_();
+ }
+ }
+ }
+
+ /** This method logs data to the used $(D Logger) with the $(D LogLevel)
+ of the this $(D Logger) in a $(D printf)-style manner.
+
+ In order for the data to be processed the $(D LogLevel) of the $(D Logger)
+ must be greater or equal to the global $(D LogLevel).
+
+ Params:
+ msg = The format string used for this log call.
+ args = The data that should be logged.
+
+ Example:
+ --------------------
+ auto s = new FileLogger(stdout);
+ s.logf("%d %s", 1337, "is number");
+ s.logf("%d %s", 1337, "is number");
+ s.logf("%d %s", 1337, "is number");
+ s.logf("%d %s", 1337, "is number");
+ s.logf("%d %s", 1337, "is number");
+ --------------------
+ */
+ 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)
+ {
+ static if (isLoggingActive) synchronized (mutex)
+ {
+ import std.format : formattedWrite;
+
+ if (isLoggingEnabled(this.logLevel_, this.logLevel_,
+ globalLogLevel))
+ {
+ this.beginLogMsg(file, line, funcName, prettyFuncName,
+ moduleName, this.logLevel_, thisTid, Clock.currTime, this);
+
+ auto writer = MsgRange(this);
+ formattedWrite(writer, msg, args);
+
+ this.finishLogMsg();
+
+ if (this.logLevel_ == LogLevel.fatal)
+ this.fatalHandler_();
+ }
+ }
+ }
+
+ private void delegate() @safe fatalHandler_;
+ private shared LogLevel logLevel_ = LogLevel.info;
+ private Mutex mutex;
+
+ protected Appender!string msgAppender;
+ protected LogEntry header;
+}
+
+// Thread Global
+
+private __gshared Logger stdSharedDefaultLogger;
+private shared Logger stdSharedLogger;
+private shared LogLevel stdLoggerGlobalLogLevel = LogLevel.all;
+
+/* This method returns the global default Logger.
+ * Marked @trusted because of excessive reliance on __gshared data
+ */
+private @property Logger defaultSharedLoggerImpl() @trusted
+{
+ import std.conv : emplace;
+ import std.stdio : stderr;
+
+ static __gshared align(FileLogger.alignof) void[__traits(classInstanceSize, FileLogger)] _buffer;
+
+ import std.concurrency : initOnce;
+ initOnce!stdSharedDefaultLogger({
+ auto buffer = cast(ubyte[]) _buffer;
+ return emplace!FileLogger(buffer, stderr, LogLevel.all);
+ }());
+
+ return stdSharedDefaultLogger;
+}
+
+/** This property sets and gets the default $(D Logger).
+
+Example:
+-------------
+sharedLog = new FileLogger(yourFile);
+-------------
+The example sets a new $(D FileLogger) as new $(D sharedLog).
+
+If at some point you want to use the original default logger again, you can
+use $(D sharedLog = null;). This will put back the original.
+
+Note:
+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;
+-------------
+*/
+@property Logger sharedLog() @safe
+{
+ static auto trustedLoad(ref shared Logger logger) @trusted
+ {
+ import core.atomic : atomicLoad, MemoryOrder;
+ return cast() atomicLoad!(MemoryOrder.acq)(logger);
+ //FIXME: Casting shared away here. Not good. See issue 16232.
+ }
+
+ // If we have set up our own logger use that
+ if (auto logger = trustedLoad(stdSharedLogger))
+ {
+ return logger;
+ }
+ else
+ {
+ // Otherwise resort to the default logger
+ return defaultSharedLoggerImpl;
+ }
+}
+
+/// Ditto
+@property void sharedLog(Logger logger) @trusted
+{
+ import core.atomic : atomicStore, MemoryOrder;
+ atomicStore!(MemoryOrder.rel)(stdSharedLogger, cast(shared) logger);
+}
+
+/** This methods get and set the global $(D LogLevel).
+
+Every log message with a $(D LogLevel) lower as the global $(D LogLevel)
+will be discarded before it reaches $(D writeLogMessage) method of any
+$(D Logger).
+*/
+/* Implementation note:
+For any public logging call, the global log level shall only be queried once on
+entry. Otherwise when another threads changes the level, we would work with
+different levels at different spots in the code.
+*/
+@property LogLevel globalLogLevel() @safe @nogc
+{
+ return trustedLoad(stdLoggerGlobalLogLevel);
+}
+
+/// Ditto
+@property void globalLogLevel(LogLevel ll) @safe
+{
+ trustedStore(stdLoggerGlobalLogLevel, ll);
+}
+
+// Thread Local
+
+/** The $(D StdForwardLogger) will always forward anything to the sharedLog.
+
+The $(D StdForwardLogger) will not throw if data is logged with $(D
+LogLevel.fatal).
+*/
+class StdForwardLogger : Logger
+{
+ /** The default constructor for the $(D StdForwardLogger).
+
+ Params:
+ lv = The $(D LogLevel) for the $(D MultiLogger). By default the $(D
+ LogLevel) is $(D all).
+ */
+ this(const LogLevel lv = LogLevel.all) @safe
+ {
+ super(lv);
+ this.fatalHandler = delegate() {};
+ }
+
+ override protected void writeLogMsg(ref LogEntry payload)
+ {
+ sharedLog.forwardMsg(payload);
+ }
+}
+
+///
+@safe unittest
+{
+ auto nl1 = new StdForwardLogger(LogLevel.all);
+}
+
+/** This $(D LogLevel) is unqiue to every thread.
+
+The thread local $(D Logger) will use this $(D LogLevel) to filter log calls
+every same way as presented earlier.
+*/
+//public LogLevel threadLogLevel = LogLevel.all;
+private Logger stdLoggerThreadLogger;
+private Logger stdLoggerDefaultThreadLogger;
+
+/* This method returns the thread local default Logger.
+*/
+private @property Logger stdThreadLocalLogImpl() @trusted
+{
+ import std.conv : emplace;
+
+ static void*[(__traits(classInstanceSize, StdForwardLogger) - 1) / (void*).sizeof + 1] _buffer;
+
+ auto buffer = cast(ubyte[]) _buffer;
+
+ if (stdLoggerDefaultThreadLogger is null)
+ {
+ stdLoggerDefaultThreadLogger = emplace!StdForwardLogger(buffer, LogLevel.all);
+ }
+ return stdLoggerDefaultThreadLogger;
+}
+
+/** This function returns a thread unique $(D Logger), that by default
+propergates all data logged to it to the $(D sharedLog).
+
+These properties can be used to set and get this $(D Logger). Every
+modification to this $(D Logger) will only be visible in the thread the
+modification has been done from.
+
+This $(D Logger) is called by the free standing log functions. This allows to
+create thread local redirections and still use the free standing log
+functions.
+*/
+@property Logger stdThreadLocalLog() @safe
+{
+ // If we have set up our own logger use that
+ if (auto logger = stdLoggerThreadLogger)
+ return logger;
+ else
+ // Otherwise resort to the default logger
+ return stdThreadLocalLogImpl;
+}
+
+/// Ditto
+@property void stdThreadLocalLog(Logger logger) @safe
+{
+ stdLoggerThreadLogger = logger;
+}
+
+/// Ditto
+@system unittest
+{
+ import std.experimental.logger.filelogger : FileLogger;
+ import std.file : deleteme, remove;
+ Logger l = stdThreadLocalLog;
+ stdThreadLocalLog = new FileLogger(deleteme ~ "-someFile.log");
+ scope(exit) remove(deleteme ~ "-someFile.log");
+
+ auto tempLog = stdThreadLocalLog;
+ stdThreadLocalLog = l;
+ destroy(tempLog);
+}
+
+@safe unittest
+{
+ LogLevel ll = globalLogLevel;
+ globalLogLevel = LogLevel.fatal;
+ assert(globalLogLevel == LogLevel.fatal);
+ globalLogLevel = ll;
+}
+
+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.all) @safe
+ {
+ super(lv);
+ }
+
+ 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;
+ }
+}
+
+version (unittest) private void testFuncNames(Logger logger) @safe
+{
+ string s = "I'm here";
+ logger.log(s);
+}
+
+@safe unittest
+{
+ auto tl1 = new TestLogger();
+ testFuncNames(tl1);
+ assert(tl1.func == "std.experimental.logger.core.testFuncNames", tl1.func);
+ assert(tl1.prettyFunc ==
+ "void std.experimental.logger.core.testFuncNames(Logger logger) @safe",
+ tl1.prettyFunc);
+ assert(tl1.msg == "I'm here", tl1.msg);
+}
+
+@safe unittest
+{
+ auto tl1 = new TestLogger(LogLevel.all);
+ tl1.log();
+ assert(tl1.line == __LINE__ - 1);
+ tl1.log(true);
+ assert(tl1.line == __LINE__ - 1);
+ tl1.log(false);
+ assert(tl1.line == __LINE__ - 3);
+ tl1.log(LogLevel.info);
+ assert(tl1.line == __LINE__ - 1);
+ tl1.log(LogLevel.off);
+ assert(tl1.line == __LINE__ - 3);
+ tl1.log(LogLevel.info, true);
+ assert(tl1.line == __LINE__ - 1);
+ tl1.log(LogLevel.info, false);
+ assert(tl1.line == __LINE__ - 3);
+
+ auto oldunspecificLogger = sharedLog;
+ scope(exit) {
+ sharedLog = oldunspecificLogger;
+ }
+
+ sharedLog = tl1;
+
+ log();
+ assert(tl1.line == __LINE__ - 1);
+
+ log(LogLevel.info);
+ assert(tl1.line == __LINE__ - 1);
+
+ log(true);
+ assert(tl1.line == __LINE__ - 1);
+
+ log(LogLevel.warning, true);
+ assert(tl1.line == __LINE__ - 1);
+
+ trace();
+ assert(tl1.line == __LINE__ - 1);
+}
+
+@safe unittest
+{
+ import std.experimental.logger.multilogger : MultiLogger;
+
+ auto tl1 = new TestLogger;
+ auto tl2 = new TestLogger;
+
+ auto ml = new MultiLogger();
+ ml.insertLogger("one", tl1);
+ ml.insertLogger("two", tl2);
+
+ string msg = "Hello Logger World";
+ ml.log(msg);
+ int lineNumber = __LINE__ - 1;
+ assert(tl1.msg == msg);
+ assert(tl1.line == lineNumber);
+ assert(tl2.msg == msg);
+ assert(tl2.line == lineNumber);
+
+ ml.removeLogger("one");
+ ml.removeLogger("two");
+ auto n = ml.removeLogger("one");
+ assert(n is null);
+}
+
+@safe unittest
+{
+ bool errorThrown = false;
+ auto tl = new TestLogger;
+ auto dele = delegate() {
+ errorThrown = true;
+ };
+ tl.fatalHandler = dele;
+ tl.fatal();
+ assert(errorThrown);
+}
+
+@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);
+ int lineNumber = __LINE__ - 1;
+ assert(l.msg == msg);
+ assert(l.line == lineNumber);
+ assert(l.logLevel == LogLevel.all);
+
+ l.log(true, msg);
+ lineNumber = __LINE__ - 1;
+ assert(l.msg == msg, l.msg);
+ assert(l.line == lineNumber);
+ assert(l.logLevel == LogLevel.all);
+
+ l.log(false, msg);
+ assert(l.msg == msg);
+ assert(l.line == lineNumber, to!string(l.line));
+ assert(l.logLevel == LogLevel.all);
+
+ msg = "%s Another message";
+ l.logf(msg, "Yet");
+ lineNumber = __LINE__ - 1;
+ assert(l.msg == msg.format("Yet"));
+ assert(l.line == lineNumber);
+ assert(l.logLevel == LogLevel.all);
+
+ l.logf(true, msg, "Yet");
+ lineNumber = __LINE__ - 1;
+ assert(l.msg == msg.format("Yet"));
+ assert(l.line == lineNumber);
+ assert(l.logLevel == LogLevel.all);
+
+ l.logf(false, msg, "Yet");
+ assert(l.msg == msg.format("Yet"));
+ assert(l.line == lineNumber);
+ assert(l.logLevel == LogLevel.all);
+
+ () @trusted {
+ assertThrown!Throwable(l.logf(LogLevel.fatal, msg, "Yet"));
+ } ();
+ lineNumber = __LINE__ - 2;
+ assert(l.msg == msg.format("Yet"));
+ assert(l.line == lineNumber);
+ assert(l.logLevel == LogLevel.all);
+
+ () @trusted {
+ assertThrown!Throwable(l.logf(LogLevel.fatal, true, msg, "Yet"));
+ } ();
+ lineNumber = __LINE__ - 2;
+ assert(l.msg == msg.format("Yet"));
+ assert(l.line == lineNumber);
+ assert(l.logLevel == LogLevel.all);
+
+ assertNotThrown(l.logf(LogLevel.fatal, false, msg, "Yet"));
+ assert(l.msg == msg.format("Yet"));
+ assert(l.line == lineNumber);
+ assert(l.logLevel == LogLevel.all);
+
+ auto oldunspecificLogger = sharedLog;
+
+ assert(oldunspecificLogger.logLevel == LogLevel.all,
+ to!string(oldunspecificLogger.logLevel));
+
+ assert(l.logLevel == LogLevel.all);
+ sharedLog = l;
+ assert(globalLogLevel == LogLevel.all,
+ to!string(globalLogLevel));
+
+ scope(exit)
+ {
+ sharedLog = oldunspecificLogger;
+ }
+
+ assert(sharedLog.logLevel == LogLevel.all);
+ assert(stdThreadLocalLog.logLevel == LogLevel.all);
+ assert(globalLogLevel == LogLevel.all);
+
+ msg = "Another message";
+ log(msg);
+ lineNumber = __LINE__ - 1;
+ assert(l.logLevel == LogLevel.all);
+ assert(l.line == lineNumber, to!string(l.line));
+ assert(l.msg == msg, l.msg);
+
+ log(true, msg);
+ lineNumber = __LINE__ - 1;
+ assert(l.msg == msg);
+ assert(l.line == lineNumber);
+ assert(l.logLevel == LogLevel.all);
+
+ log(false, msg);
+ assert(l.msg == msg);
+ assert(l.line == lineNumber);
+ assert(l.logLevel == LogLevel.all);
+
+ msg = "%s Another message";
+ logf(msg, "Yet");
+ lineNumber = __LINE__ - 1;
+ assert(l.msg == msg.format("Yet"));
+ assert(l.line == lineNumber);
+ assert(l.logLevel == LogLevel.all);
+
+ logf(true, msg, "Yet");
+ lineNumber = __LINE__ - 1;
+ assert(l.msg == msg.format("Yet"));
+ assert(l.line == lineNumber);
+ assert(l.logLevel == LogLevel.all);
+
+ logf(false, msg, "Yet");
+ assert(l.msg == msg.format("Yet"));
+ assert(l.line == lineNumber);
+ assert(l.logLevel == LogLevel.all);
+
+ msg = "%s Another message";
+ () @trusted {
+ assertThrown!Throwable(logf(LogLevel.fatal, msg, "Yet"));
+ } ();
+ lineNumber = __LINE__ - 2;
+ assert(l.msg == msg.format("Yet"));
+ assert(l.line == lineNumber);
+ assert(l.logLevel == LogLevel.all);
+
+ () @trusted {
+ assertThrown!Throwable(logf(LogLevel.fatal, true, msg, "Yet"));
+ } ();
+ lineNumber = __LINE__ - 2;
+ assert(l.msg == msg.format("Yet"));
+ assert(l.line == lineNumber);
+ assert(l.logLevel == LogLevel.all);
+
+ assertNotThrown(logf(LogLevel.fatal, false, msg, "Yet"));
+ assert(l.msg == msg.format("Yet"));
+ assert(l.line == lineNumber);
+ assert(l.logLevel == LogLevel.all);
+}
+
+@system unittest // default logger
+{
+ 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;
+
+ scope(exit)
+ {
+ remove(filename);
+ assert(!exists(filename));
+ sharedLog = oldunspecificLogger;
+ globalLogLevel = LogLevel.all;
+ }
+
+ string notWritten = "this should not be written to file";
+ string written = "this should be written to file";
+
+ globalLogLevel = LogLevel.critical;
+ assert(globalLogLevel == LogLevel.critical);
+
+ log(LogLevel.warning, notWritten);
+ log(LogLevel.critical, written);
+
+ l.file.flush();
+ l.file.close();
+
+ auto file = File(filename, "r");
+ assert(!file.eof);
+
+ string readLine = file.readln();
+ assert(readLine.indexOf(written) != -1, readLine);
+ assert(readLine.indexOf(notWritten) == -1, readLine);
+ file.close();
+}
+
+@system unittest
+{
+ import std.file : deleteme, remove;
+ import std.stdio : File;
+ import std.string : indexOf;
+
+ string filename = deleteme ~ __FUNCTION__ ~ ".tempLogFile";
+ auto oldunspecificLogger = sharedLog;
+
+ scope(exit)
+ {
+ remove(filename);
+ sharedLog = oldunspecificLogger;
+ globalLogLevel = LogLevel.all;
+ }
+
+ string notWritten = "this should not be written to file";
+ string written = "this should be written to file";
+
+ auto l = new FileLogger(filename);
+ sharedLog = l;
+ sharedLog.logLevel = LogLevel.critical;
+
+ log(LogLevel.error, false, notWritten);
+ log(LogLevel.critical, true, written);
+ destroy(l);
+
+ auto file = File(filename, "r");
+ auto readLine = file.readln();
+ assert(!readLine.empty, readLine);
+ assert(readLine.indexOf(written) != -1);
+ assert(readLine.indexOf(notWritten) == -1);
+ file.close();
+}
+
+@safe unittest
+{
+ import std.conv : to;
+
+ auto tl = new TestLogger(LogLevel.all);
+ int l = __LINE__;
+ tl.info("a");
+ assert(tl.line == l+1);
+ assert(tl.msg == "a");
+ assert(tl.logLevel == LogLevel.all);
+ assert(globalLogLevel == LogLevel.all);
+ l = __LINE__;
+ tl.trace("b");
+ assert(tl.msg == "b", tl.msg);
+ assert(tl.line == l+1, to!string(tl.line));
+}
+
+// testing possible log conditions
+@safe unittest
+{
+ import std.conv : to;
+ import std.format : format;
+ import std.string : indexOf;
+
+ auto oldunspecificLogger = sharedLog;
+
+ auto mem = new TestLogger;
+ mem.fatalHandler = delegate() {};
+ sharedLog = mem;
+
+ scope(exit)
+ {
+ sharedLog = oldunspecificLogger;
+ globalLogLevel = LogLevel.all;
+ }
+
+ int value = 0;
+ foreach (gll; [cast(LogLevel) LogLevel.all, LogLevel.trace,
+ LogLevel.info, LogLevel.warning, LogLevel.error,
+ LogLevel.critical, LogLevel.fatal, LogLevel.off])
+ {
+
+ globalLogLevel = gll;
+
+ foreach (ll; [cast(LogLevel) LogLevel.all, LogLevel.trace,
+ LogLevel.info, LogLevel.warning, LogLevel.error,
+ LogLevel.critical, LogLevel.fatal, LogLevel.off])
+ {
+
+ mem.logLevel = ll;
+
+ foreach (cond; [true, false])
+ {
+ foreach (condValue; [true, false])
+ {
+ foreach (memOrG; [true, false])
+ {
+ foreach (prntf; [true, false])
+ {
+ foreach (ll2; [cast(LogLevel) LogLevel.all, LogLevel.trace,
+ LogLevel.info, LogLevel.warning,
+ LogLevel.error, LogLevel.critical,
+ LogLevel.fatal, LogLevel.off])
+ {
+ foreach (singleMulti; 0 .. 2)
+ {
+ int lineCall;
+ mem.msg = "-1";
+ if (memOrG)
+ {
+ if (prntf)
+ {
+ if (cond)
+ {
+ if (singleMulti == 0)
+ {
+ mem.logf(ll2, condValue, "%s",
+ value);
+ lineCall = __LINE__;
+ }
+ else
+ {
+ mem.logf(ll2, condValue,
+ "%d %d", value, value);
+ lineCall = __LINE__;
+ }
+ }
+ else
+ {
+ if (singleMulti == 0)
+ {
+ mem.logf(ll2, "%s", value);
+ lineCall = __LINE__;
+ }
+ else
+ {
+ mem.logf(ll2, "%d %d",
+ value, value);
+ lineCall = __LINE__;
+ }
+ }
+ }
+ else
+ {
+ if (cond)
+ {
+ if (singleMulti == 0)
+ {
+ mem.log(ll2, condValue,
+ to!string(value));
+ lineCall = __LINE__;
+ }
+ else
+ {
+ mem.log(ll2, condValue,
+ to!string(value), value);
+ lineCall = __LINE__;
+ }
+ }
+ else
+ {
+ if (singleMulti == 0)
+ {
+ mem.log(ll2, to!string(value));
+ lineCall = __LINE__;
+ }
+ else
+ {
+ mem.log(ll2,
+ to!string(value),
+ value);
+ lineCall = __LINE__;
+ }
+ }
+ }
+ }
+ else
+ {
+ if (prntf)
+ {
+ if (cond)
+ {
+ if (singleMulti == 0)
+ {
+ logf(ll2, condValue, "%s",
+ value);
+ lineCall = __LINE__;
+ }
+ else
+ {
+ logf(ll2, condValue,
+ "%s %d", value, value);
+ lineCall = __LINE__;
+ }
+ }
+ else
+ {
+ if (singleMulti == 0)
+ {
+ logf(ll2, "%s", value);
+ lineCall = __LINE__;
+ }
+ else
+ {
+ logf(ll2, "%s %s", value,
+ value);
+ lineCall = __LINE__;
+ }
+ }
+ }
+ else
+ {
+ if (cond)
+ {
+ if (singleMulti == 0)
+ {
+ log(ll2, condValue,
+ to!string(value));
+ lineCall = __LINE__;
+ }
+ else
+ {
+ log(ll2, condValue, value,
+ to!string(value));
+ lineCall = __LINE__;
+ }
+ }
+ else
+ {
+ if (singleMulti == 0)
+ {
+ log(ll2, to!string(value));
+ lineCall = __LINE__;
+ }
+ else
+ {
+ log(ll2, value,
+ to!string(value));
+ lineCall = __LINE__;
+ }
+ }
+ }
+ }
+
+ string valueStr = to!string(value);
+ ++value;
+
+ bool ll2Off = (ll2 != LogLevel.off);
+ bool gllOff = (gll != LogLevel.off);
+ bool llOff = (ll != LogLevel.off);
+ bool condFalse = (cond ? condValue : true);
+ bool ll2VSgll = (ll2 >= gll);
+ bool ll2VSll = (ll2 >= ll);
+
+ bool shouldLog = ll2Off && gllOff && llOff
+ && condFalse && ll2VSgll && ll2VSll;
+
+ /*
+ writefln(
+ "go(%b) ll2o(%b) c(%b) lg(%b) ll(%b) s(%b)"
+ , gll != LogLevel.off, ll2 != LogLevel.off,
+ cond ? condValue : true,
+ ll2 >= gll, ll2 >= ll, shouldLog);
+ */
+
+
+ if (shouldLog)
+ {
+ assert(mem.msg.indexOf(valueStr) != -1,
+ format(
+ "lineCall(%d) ll2Off(%u) gll(%u) ll(%u) ll2(%u) " ~
+ "cond(%b) condValue(%b)" ~
+ " memOrG(%b) shouldLog(%b) %s == %s" ~
+ " %b %b %b %b %b",
+ lineCall, ll2Off, gll, ll, ll2, cond,
+ condValue, memOrG, shouldLog, mem.msg,
+ valueStr, gllOff, llOff, condFalse,
+ ll2VSgll, ll2VSll
+ ));
+ }
+ else
+ {
+ assert(mem.msg.indexOf(valueStr),
+ format(
+ "lineCall(%d) ll2Off(%u) gll(%u) ll(%u) ll2(%u) " ~
+ "cond(%b) condValue(%b)" ~
+ " memOrG(%b) shouldLog(%b) %s == %s" ~
+ " %b %b %b %b %b",
+ lineCall, ll2Off, gll, ll, ll2, cond,
+ condValue, memOrG, shouldLog, mem.msg,
+ valueStr, gllOff, llOff, condFalse,
+ ll2VSgll, ll2VSll
+ ));
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+}
+
+// more testing
+@safe unittest
+{
+ import std.conv : to;
+ import std.format : format;
+ import std.string : indexOf;
+
+ auto oldunspecificLogger = sharedLog;
+
+ auto mem = new TestLogger;
+ mem.fatalHandler = delegate() {};
+ sharedLog = mem;
+
+ scope(exit)
+ {
+ sharedLog = oldunspecificLogger;
+ globalLogLevel = LogLevel.all;
+ }
+
+ int value = 0;
+ foreach (gll; [cast(LogLevel) LogLevel.all, LogLevel.trace,
+ LogLevel.info, LogLevel.warning, LogLevel.error,
+ LogLevel.critical, LogLevel.fatal, LogLevel.off])
+ {
+
+ globalLogLevel = gll;
+
+ foreach (ll; [cast(LogLevel) LogLevel.all, LogLevel.trace,
+ LogLevel.info, LogLevel.warning, LogLevel.error,
+ LogLevel.critical, LogLevel.fatal, LogLevel.off])
+ {
+ mem.logLevel = ll;
+
+ foreach (tll; [cast(LogLevel) LogLevel.all, LogLevel.trace,
+ LogLevel.info, LogLevel.warning, LogLevel.error,
+ LogLevel.critical, LogLevel.fatal, LogLevel.off])
+ {
+ stdThreadLocalLog.logLevel = tll;
+
+ foreach (cond; [true, false])
+ {
+ foreach (condValue; [true, false])
+ {
+ foreach (memOrG; [true, false])
+ {
+ foreach (prntf; [true, false])
+ {
+ foreach (singleMulti; 0 .. 2)
+ {
+ int lineCall;
+ mem.msg = "-1";
+ if (memOrG)
+ {
+ if (prntf)
+ {
+ if (cond)
+ {
+ if (singleMulti == 0)
+ {
+ mem.logf(condValue, "%s",
+ value);
+ lineCall = __LINE__;
+ }
+ else
+ {
+ mem.logf(condValue,
+ "%d %d", value, value);
+ lineCall = __LINE__;
+ }
+ }
+ else
+ {
+ if (singleMulti == 0)
+ {
+ mem.logf("%s", value);
+ lineCall = __LINE__;
+ }
+ else
+ {
+ mem.logf("%d %d",
+ value, value);
+ lineCall = __LINE__;
+ }
+ }
+ }
+ else
+ {
+ if (cond)
+ {
+ if (singleMulti == 0)
+ {
+ mem.log(condValue,
+ to!string(value));
+ lineCall = __LINE__;
+ }
+ else
+ {
+ mem.log(condValue,
+ to!string(value), value);
+ lineCall = __LINE__;
+ }
+ }
+ else
+ {
+ if (singleMulti == 0)
+ {
+ mem.log(to!string(value));
+ lineCall = __LINE__;
+ }
+ else
+ {
+ mem.log(to!string(value),
+ value);
+ lineCall = __LINE__;
+ }
+ }
+ }
+ }
+ else
+ {
+ if (prntf)
+ {
+ if (cond)
+ {
+ if (singleMulti == 0)
+ {
+ logf(condValue, "%s", value);
+ lineCall = __LINE__;
+ }
+ else
+ {
+ logf(condValue, "%s %d", value,
+ value);
+ lineCall = __LINE__;
+ }
+ }
+ else
+ {
+ if (singleMulti == 0)
+ {
+ logf("%s", value);
+ lineCall = __LINE__;
+ }
+ else
+ {
+ logf("%s %s", value, value);
+ lineCall = __LINE__;
+ }
+ }
+ }
+ else
+ {
+ if (cond)
+ {
+ if (singleMulti == 0)
+ {
+ log(condValue,
+ to!string(value));
+ lineCall = __LINE__;
+ }
+ else
+ {
+ log(condValue, value,
+ to!string(value));
+ lineCall = __LINE__;
+ }
+ }
+ else
+ {
+ if (singleMulti == 0)
+ {
+ log(to!string(value));
+ lineCall = __LINE__;
+ }
+ else
+ {
+ log(value, to!string(value));
+ lineCall = __LINE__;
+ }
+ }
+ }
+ }
+
+ string valueStr = to!string(value);
+ ++value;
+
+ bool gllOff = (gll != LogLevel.off);
+ bool llOff = (ll != LogLevel.off);
+ bool tllOff = (tll != LogLevel.off);
+ bool llVSgll = (ll >= gll);
+ bool tllVSll =
+ (stdThreadLocalLog.logLevel >= ll);
+ bool condFalse = (cond ? condValue : true);
+
+ bool shouldLog = gllOff && llOff
+ && (memOrG ? true : tllOff)
+ && (memOrG ?
+ (ll >= gll) :
+ (tll >= gll && tll >= ll))
+ && condFalse;
+
+ if (shouldLog)
+ {
+ assert(mem.msg.indexOf(valueStr) != -1,
+ format("\ngll(%s) ll(%s) tll(%s) " ~
+ "cond(%s) condValue(%s) " ~
+ "memOrG(%s) prntf(%s) " ~
+ "singleMulti(%s)",
+ gll, ll, tll, cond, condValue,
+ memOrG, prntf, singleMulti)
+ ~ format(" gllOff(%s) llOff(%s) " ~
+ "llVSgll(%s) tllVSll(%s) " ~
+ "tllOff(%s) condFalse(%s) "
+ ~ "shoudlLog(%s)",
+ gll != LogLevel.off,
+ ll != LogLevel.off, llVSgll,
+ tllVSll, tllOff, condFalse,
+ shouldLog)
+ ~ format("msg(%s) line(%s) " ~
+ "lineCall(%s) valueStr(%s)",
+ mem.msg, mem.line, lineCall,
+ valueStr)
+ );
+ }
+ else
+ {
+ assert(mem.msg.indexOf(valueStr) == -1,
+ format("\ngll(%s) ll(%s) tll(%s) " ~
+ "cond(%s) condValue(%s) " ~
+ "memOrG(%s) prntf(%s) " ~
+ "singleMulti(%s)",
+ gll, ll, tll, cond, condValue,
+ memOrG, prntf, singleMulti)
+ ~ format(" gllOff(%s) llOff(%s) " ~
+ "llVSgll(%s) tllVSll(%s) " ~
+ "tllOff(%s) condFalse(%s) "
+ ~ "shoudlLog(%s)",
+ gll != LogLevel.off,
+ ll != LogLevel.off, llVSgll,
+ tllVSll, tllOff, condFalse,
+ shouldLog)
+ ~ format("msg(%s) line(%s) " ~
+ "lineCall(%s) valueStr(%s)",
+ mem.msg, mem.line, lineCall,
+ valueStr)
+ );
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+}
+
+// testing more possible log conditions
+@safe unittest
+{
+ bool fatalLog;
+ auto mem = new TestLogger;
+ mem.fatalHandler = delegate() { fatalLog = true; };
+ auto oldunspecificLogger = sharedLog;
+
+ stdThreadLocalLog.logLevel = LogLevel.all;
+
+ sharedLog = mem;
+ scope(exit)
+ {
+ sharedLog = oldunspecificLogger;
+ globalLogLevel = LogLevel.all;
+ }
+
+ foreach (gll; [cast(LogLevel) LogLevel.all, LogLevel.trace,
+ LogLevel.info, LogLevel.warning, LogLevel.error,
+ LogLevel.critical, LogLevel.fatal, LogLevel.off])
+ {
+
+ globalLogLevel = gll;
+
+ foreach (ll; [cast(LogLevel) LogLevel.all, LogLevel.trace,
+ LogLevel.info, LogLevel.warning, LogLevel.error,
+ LogLevel.critical, LogLevel.fatal, LogLevel.off])
+ {
+ mem.logLevel = ll;
+
+ foreach (tll; [cast(LogLevel) LogLevel.all, LogLevel.trace,
+ LogLevel.info, LogLevel.warning, LogLevel.error,
+ LogLevel.critical, LogLevel.fatal, LogLevel.off])
+ {
+ stdThreadLocalLog.logLevel = tll;
+
+ foreach (cond; [true, false])
+ {
+ assert(globalLogLevel == gll);
+ assert(mem.logLevel == ll);
+
+ bool gllVSll = LogLevel.trace >= globalLogLevel;
+ bool llVSgll = ll >= globalLogLevel;
+ bool lVSll = LogLevel.trace >= ll;
+ bool gllOff = globalLogLevel != LogLevel.off;
+ bool llOff = mem.logLevel != LogLevel.off;
+ bool tllOff = stdThreadLocalLog.logLevel != LogLevel.off;
+ bool tllVSll = tll >= ll;
+ bool tllVSgll = tll >= gll;
+ bool lVSgll = LogLevel.trace >= tll;
+
+ bool test = llVSgll && gllVSll && lVSll && gllOff && llOff && cond;
+ bool testG = gllOff && llOff && tllOff && lVSgll && tllVSll && tllVSgll && cond;
+
+ mem.line = -1;
+ /*
+ writefln("gll(%3u) ll(%3u) cond(%b) test(%b)",
+ gll, ll, cond, test);
+ writefln("%b %b %b %b %b %b test2(%b)", llVSgll, gllVSll, lVSll,
+ gllOff, llOff, cond, test2);
+ */
+
+ mem.trace(__LINE__); int line = __LINE__;
+ assert(test ? mem.line == line : true); line = -1;
+
+ trace(__LINE__); line = __LINE__;
+ assert(testG ? mem.line == line : true); line = -1;
+
+ mem.trace(cond, __LINE__); line = __LINE__;
+ assert(test ? mem.line == line : true); line = -1;
+
+ trace(cond, __LINE__); line = __LINE__;
+ assert(testG ? mem.line == line : true); line = -1;
+
+ mem.tracef("%d", __LINE__); line = __LINE__;
+ assert(test ? mem.line == line : true); line = -1;
+
+ tracef("%d", __LINE__); line = __LINE__;
+ assert(testG ? mem.line == line : true); line = -1;
+
+ mem.tracef(cond, "%d", __LINE__); line = __LINE__;
+ assert(test ? mem.line == line : true); line = -1;
+
+ tracef(cond, "%d", __LINE__); line = __LINE__;
+ assert(testG ? mem.line == line : true); line = -1;
+
+ llVSgll = ll >= globalLogLevel;
+ lVSll = LogLevel.info >= ll;
+ lVSgll = LogLevel.info >= tll;
+ test = llVSgll && gllVSll && lVSll && gllOff && llOff && cond;
+ testG = gllOff && llOff && tllOff && tllVSll && tllVSgll &&
+ lVSgll && cond;
+
+ mem.info(__LINE__); line = __LINE__;
+ assert(test ? mem.line == line : true); line = -1;
+
+ info(__LINE__); line = __LINE__;
+ assert(testG ? mem.line == line : true); line = -1;
+
+ mem.info(cond, __LINE__); line = __LINE__;
+ assert(test ? mem.line == line : true); line = -1;
+
+ info(cond, __LINE__); line = __LINE__;
+ assert(testG ? mem.line == line : true); line = -1;
+
+ mem.infof("%d", __LINE__); line = __LINE__;
+ assert(test ? mem.line == line : true); line = -1;
+
+ infof("%d", __LINE__); line = __LINE__;
+ assert(testG ? mem.line == line : true); line = -1;
+
+ mem.infof(cond, "%d", __LINE__); line = __LINE__;
+ assert(test ? mem.line == line : true); line = -1;
+
+ infof(cond, "%d", __LINE__); line = __LINE__;
+ assert(testG ? mem.line == line : true); line = -1;
+
+ llVSgll = ll >= globalLogLevel;
+ lVSll = LogLevel.warning >= ll;
+ lVSgll = LogLevel.warning >= tll;
+ test = llVSgll && gllVSll && lVSll && gllOff && llOff && cond;
+ testG = gllOff && llOff && tllOff && tllVSll && tllVSgll &&
+ lVSgll && cond;
+
+ mem.warning(__LINE__); line = __LINE__;
+ assert(test ? mem.line == line : true); line = -1;
+
+ warning(__LINE__); line = __LINE__;
+ assert(testG ? mem.line == line : true); line = -1;
+
+ mem.warning(cond, __LINE__); line = __LINE__;
+ assert(test ? mem.line == line : true); line = -1;
+
+ warning(cond, __LINE__); line = __LINE__;
+ assert(testG ? mem.line == line : true); line = -1;
+
+ mem.warningf("%d", __LINE__); line = __LINE__;
+ assert(test ? mem.line == line : true); line = -1;
+
+ warningf("%d", __LINE__); line = __LINE__;
+ assert(testG ? mem.line == line : true); line = -1;
+
+ mem.warningf(cond, "%d", __LINE__); line = __LINE__;
+ assert(test ? mem.line == line : true); line = -1;
+
+ warningf(cond, "%d", __LINE__); line = __LINE__;
+ assert(testG ? mem.line == line : true); line = -1;
+
+ llVSgll = ll >= globalLogLevel;
+ lVSll = LogLevel.critical >= ll;
+ lVSgll = LogLevel.critical >= tll;
+ test = llVSgll && gllVSll && lVSll && gllOff && llOff && cond;
+ testG = gllOff && llOff && tllOff && tllVSll && tllVSgll &&
+ lVSgll && cond;
+
+ mem.critical(__LINE__); line = __LINE__;
+ assert(test ? mem.line == line : true); line = -1;
+
+ critical(__LINE__); line = __LINE__;
+ assert(testG ? mem.line == line : true); line = -1;
+
+ mem.critical(cond, __LINE__); line = __LINE__;
+ assert(test ? mem.line == line : true); line = -1;
+
+ critical(cond, __LINE__); line = __LINE__;
+ assert(testG ? mem.line == line : true); line = -1;
+
+ mem.criticalf("%d", __LINE__); line = __LINE__;
+ assert(test ? mem.line == line : true); line = -1;
+
+ criticalf("%d", __LINE__); line = __LINE__;
+ assert(testG ? mem.line == line : true); line = -1;
+
+ mem.criticalf(cond, "%d", __LINE__); line = __LINE__;
+ assert(test ? mem.line == line : true); line = -1;
+
+ criticalf(cond, "%d", __LINE__); line = __LINE__;
+ assert(testG ? mem.line == line : true); line = -1;
+
+ llVSgll = ll >= globalLogLevel;
+ lVSll = LogLevel.fatal >= ll;
+ lVSgll = LogLevel.fatal >= tll;
+ test = llVSgll && gllVSll && lVSll && gllOff && llOff && cond;
+ testG = gllOff && llOff && tllOff && tllVSll && tllVSgll &&
+ lVSgll && cond;
+
+ mem.fatal(__LINE__); line = __LINE__;
+ assert(test ? mem.line == line : true); line = -1;
+ assert(test ? fatalLog : true);
+ fatalLog = false;
+
+ fatal(__LINE__); line = __LINE__;
+ assert(testG ? mem.line == line : true); line = -1;
+ assert(testG ? fatalLog : true);
+ fatalLog = false;
+
+ mem.fatal(cond, __LINE__); line = __LINE__;
+ assert(test ? mem.line == line : true); line = -1;
+ assert(test ? fatalLog : true);
+ fatalLog = false;
+
+ fatal(cond, __LINE__); line = __LINE__;
+ assert(testG ? mem.line == line : true); line = -1;
+ assert(testG ? fatalLog : true);
+ fatalLog = false;
+
+ mem.fatalf("%d", __LINE__); line = __LINE__;
+ assert(test ? mem.line == line : true); line = -1;
+ assert(test ? fatalLog : true);
+ fatalLog = false;
+
+ fatalf("%d", __LINE__); line = __LINE__;
+ assert(testG ? mem.line == line : true); line = -1;
+ assert(testG ? fatalLog : true);
+ fatalLog = false;
+
+ mem.fatalf(cond, "%d", __LINE__); line = __LINE__;
+ assert(test ? mem.line == line : true); line = -1;
+ assert(test ? fatalLog : true);
+ fatalLog = false;
+
+ fatalf(cond, "%d", __LINE__); line = __LINE__;
+ assert(testG ? mem.line == line : true); line = -1;
+ assert(testG ? fatalLog : true);
+ fatalLog = false;
+ }
+ }
+ }
+ }
+}
+
+// Issue #5
+@safe unittest
+{
+ import std.string : indexOf;
+
+ auto oldunspecificLogger = sharedLog;
+
+ scope(exit)
+ {
+ sharedLog = oldunspecificLogger;
+ globalLogLevel = LogLevel.all;
+ }
+
+ auto tl = new TestLogger(LogLevel.info);
+ sharedLog = tl;
+
+ trace("trace");
+ assert(tl.msg.indexOf("trace") == -1);
+}
+
+// Issue #5
+@safe unittest
+{
+ import std.experimental.logger.multilogger : MultiLogger;
+ import std.string : indexOf;
+
+ stdThreadLocalLog.logLevel = LogLevel.all;
+
+ auto oldunspecificLogger = sharedLog;
+
+ scope(exit)
+ {
+ sharedLog = oldunspecificLogger;
+ globalLogLevel = LogLevel.all;
+ }
+
+ auto logger = new MultiLogger(LogLevel.error);
+
+ auto tl = new TestLogger(LogLevel.info);
+ logger.insertLogger("required", tl);
+ sharedLog = logger;
+
+ trace("trace");
+ assert(tl.msg.indexOf("trace") == -1);
+ info("info");
+ assert(tl.msg.indexOf("info") == -1);
+ error("error");
+ assert(tl.msg.indexOf("error") == 0);
+}
+
+@system unittest
+{
+ import std.exception : assertThrown;
+ auto tl = new TestLogger();
+ assertThrown!Throwable(tl.fatal("fatal"));
+}
+
+// log objects with non-safe toString
+@system unittest
+{
+ struct Test
+ {
+ string toString() const @system
+ {
+ return "test";
+ }
+ }
+
+ auto tl = new TestLogger();
+ tl.info(Test.init);
+ assert(tl.msg == "test");
+}
+
+// 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
+@system unittest
+{
+ import core.atomic, core.thread, std.concurrency;
+
+ static shared logged_count = 0;
+
+ class TestLog : Logger
+ {
+ Tid tid;
+
+ this()
+ {
+ super (LogLevel.trace);
+ this.tid = thisTid;
+ }
+
+ override void writeLogMsg(ref LogEntry payload) @trusted
+ {
+ assert(thisTid == this.tid);
+ atomicOp!"+="(logged_count, 1);
+ }
+ }
+
+ class IgnoredLog : Logger
+ {
+ this()
+ {
+ super (LogLevel.trace);
+ }
+
+ override void writeLogMsg(ref LogEntry payload) @trusted
+ {
+ assert(false);
+ }
+ }
+
+ auto oldSharedLog = sharedLog;
+ scope(exit)
+ {
+ sharedLog = oldSharedLog;
+ }
+
+ sharedLog = new IgnoredLog;
+ Thread[] spawned;
+
+ foreach (i; 0 .. 4)
+ {
+ spawned ~= new Thread({
+ stdThreadLocalLog = new TestLog;
+ trace("zzzzzzzzzz");
+ });
+ spawned[$-1].start();
+ }
+
+ foreach (t; spawned)
+ t.join();
+
+ assert(atomicOp!"=="(logged_count, 4));
+}
+
+@safe unittest
+{
+ auto dl = cast(FileLogger) sharedLog;
+ assert(dl !is null);
+ assert(dl.logLevel == LogLevel.all);
+ assert(globalLogLevel == LogLevel.all);
+
+ 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);
+}
+
+// Issue 17328
+@safe unittest
+{
+ import std.format : format;
+
+ ubyte[] data = [0];
+ string s = format("%(%02x%)", data); // format 00
+ assert(s == "00");
+
+ auto tl = new TestLogger();
+
+ tl.infof("%(%02x%)", data); // infof 000
+
+ size_t i;
+ string fs = tl.msg;
+ for (; i < s.length; ++i)
+ {
+ assert(s[s.length - 1 - i] == fs[fs.length - 1 - i], fs);
+ }
+ assert(fs.length == 2);
+}
+
+// Issue 15954
+@safe unittest
+{
+ import std.conv : to;
+ auto tl = new TestLogger();
+ tl.log("123456789".to!wstring);
+ assert(tl.msg == "123456789");
+}
+
+// Issue 16256
+@safe unittest
+{
+ import std.conv : to;
+ auto tl = new TestLogger();
+ tl.log("123456789"d);
+ assert(tl.msg == "123456789");
+}
+
+// Issue 15517
+@system unittest
+{
+ import std.file : exists, remove;
+ import std.stdio : File;
+ import std.string : indexOf;
+
+ string fn = "logfile.log";
+ if (exists(fn))
+ {
+ remove(fn);
+ }
+
+ auto oldShared = sharedLog;
+ scope(exit)
+ {
+ sharedLog = oldShared;
+ if (exists(fn))
+ {
+ remove(fn);
+ }
+ }
+
+ auto ts = [ "Test log 1", "Test log 2", "Test log 3"];
+
+ auto fl = new FileLogger(fn);
+ sharedLog = fl;
+ assert(exists(fn));
+
+ foreach (t; ts)
+ {
+ log(t);
+ }
+
+ auto f = File(fn);
+ auto l = f.byLine();
+ assert(!l.empty);
+ size_t idx;
+ foreach (it; l)
+ {
+ assert(it.indexOf(ts[idx]) != -1, it);
+ ++idx;
+ }
+
+ assert(exists(fn));
+ fl.file.close();
+}
diff --git a/libphobos/src/std/experimental/logger/filelogger.d b/libphobos/src/std/experimental/logger/filelogger.d
new file mode 100644
index 0000000..8f97b5b
--- /dev/null
+++ b/libphobos/src/std/experimental/logger/filelogger.d
@@ -0,0 +1,265 @@
+///
+module std.experimental.logger.filelogger;
+
+import std.experimental.logger.core;
+import std.stdio;
+
+import std.typecons : Flag;
+
+/** An option to create $(LREF FileLogger) directory if it is non-existent.
+*/
+alias CreateFolder = Flag!"CreateFolder";
+
+/** This $(D Logger) implementation writes log messages to the associated
+file. The name of the file has to be passed on construction time. If the file
+is already present new log messages will be append at its end.
+*/
+class FileLogger : Logger
+{
+ import std.concurrency : Tid;
+ import std.datetime.systime : SysTime;
+ import std.format : formattedWrite;
+
+ /** A constructor for the $(D FileLogger) Logger.
+
+ Params:
+ 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
+
+ Example:
+ -------------
+ auto l1 = new FileLogger("logFile");
+ auto l2 = new FileLogger("logFile", LogLevel.fatal);
+ auto l3 = new FileLogger("logFile", LogLevel.fatal, CreateFolder.yes);
+ -------------
+ */
+ this(in string fn, const LogLevel lv = LogLevel.all) @safe
+ {
+ this(fn, lv, CreateFolder.yes);
+ }
+
+ /** A constructor for the $(D FileLogger) Logger that takes a reference to
+ a $(D File).
+
+ The $(D File) passed must be open for all the log call to the
+ $(D FileLogger). If the $(D File) gets closed, using the $(D FileLogger)
+ for logging will result in undefined behaviour.
+
+ Params:
+ fn = The file used for logging.
+ lv = The $(D LogLevel) for the $(D FileLogger). By default the
+ $(D LogLevel) for $(D FileLogger) is $(D LogLevel.all).
+ createFileNameFolder = if yes and fn contains a folder name, this
+ folder will be created.
+
+ Example:
+ -------------
+ auto file = File("logFile.log", "w");
+ auto l1 = new FileLogger(file);
+ auto l2 = new FileLogger(file, LogLevel.fatal);
+ -------------
+ */
+ this(in string fn, const LogLevel lv, CreateFolder createFileNameFolder) @safe
+ {
+ import std.file : exists, mkdirRecurse;
+ import std.path : dirName;
+ import std.conv : text;
+
+ super(lv);
+ this.filename = fn;
+
+ if (createFileNameFolder)
+ {
+ auto d = dirName(this.filename);
+ mkdirRecurse(d);
+ assert(exists(d), text("The folder the FileLogger should have",
+ " created in '", d,"' could not be created."));
+ }
+
+ this.file_.open(this.filename, "a");
+ }
+
+ /** A constructor for the $(D FileLogger) Logger that takes a reference to
+ a $(D File).
+
+ The $(D File) passed must be open for all the log call to the
+ $(D FileLogger). If the $(D File) gets closed, using the $(D FileLogger)
+ for logging will result in undefined behaviour.
+
+ 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.all).
+
+ Example:
+ -------------
+ auto file = File("logFile.log", "w");
+ auto l1 = new FileLogger(file);
+ auto l2 = new FileLogger(file, LogLevel.fatal);
+ -------------
+ */
+ this(File file, const LogLevel lv = LogLevel.all) @safe
+ {
+ super(lv);
+ this.file_ = file;
+ }
+
+ /** If the $(D FileLogger) is managing the $(D File) it logs to, this
+ method will return a reference to this File.
+ */
+ @property File file() @safe
+ {
+ return this.file_;
+ }
+
+ /* This method overrides the base class method in order to log to a file
+ without requiring heap allocated memory. Additionally, the $(D FileLogger)
+ local mutex is logged to serialize the log calls.
+ */
+ override protected void beginLogMsg(string file, int line, string funcName,
+ string prettyFuncName, string moduleName, LogLevel logLevel,
+ Tid threadId, SysTime timestamp, Logger logger)
+ @safe
+ {
+ import std.string : lastIndexOf;
+ ptrdiff_t fnIdx = file.lastIndexOf('/') + 1;
+ ptrdiff_t funIdx = funcName.lastIndexOf('.') + 1;
+
+ auto lt = this.file_.lockingTextWriter();
+ systimeToISOString(lt, timestamp);
+ formattedWrite(lt, ":%s:%s:%u ", file[fnIdx .. $],
+ funcName[funIdx .. $], line);
+ }
+
+ /* This methods overrides the base class method and writes the parts of
+ the log call directly to the file.
+ */
+ override protected void logMsgPart(const(char)[] msg)
+ {
+ formattedWrite(this.file_.lockingTextWriter(), "%s", msg);
+ }
+
+ /* This methods overrides the base class method and finalizes the active
+ log call. This requires flushing the $(D File) and releasing the
+ $(D FileLogger) local mutex.
+ */
+ override protected void finishLogMsg()
+ {
+ this.file_.lockingTextWriter().put("\n");
+ this.file_.flush();
+ }
+
+ /* This methods overrides the base class method and delegates the
+ $(D LogEntry) data to the actual implementation.
+ */
+ override protected void writeLogMsg(ref LogEntry payload)
+ {
+ this.beginLogMsg(payload.file, payload.line, payload.funcName,
+ payload.prettyFuncName, payload.moduleName, payload.logLevel,
+ payload.threadId, payload.timestamp, payload.logger);
+ this.logMsgPart(payload.msg);
+ this.finishLogMsg();
+ }
+
+ /** If the $(D FileLogger) was constructed with a filename, this method
+ returns this filename. Otherwise an empty $(D string) is returned.
+ */
+ string getFilename()
+ {
+ return this.filename;
+ }
+
+ private File file_;
+ private string filename;
+}
+
+@system unittest
+{
+ import std.array : empty;
+ import std.file : deleteme, remove;
+ import std.string : indexOf;
+
+ string filename = deleteme ~ __FUNCTION__ ~ ".tempLogFile";
+ auto l = new FileLogger(filename);
+
+ scope(exit)
+ {
+ remove(filename);
+ }
+
+ string notWritten = "this should not be written to file";
+ string written = "this should be written to file";
+
+ l.logLevel = LogLevel.critical;
+ l.log(LogLevel.warning, notWritten);
+ l.log(LogLevel.critical, written);
+ destroy(l);
+
+ auto file = File(filename, "r");
+ string readLine = file.readln();
+ assert(readLine.indexOf(written) != -1, readLine);
+ readLine = file.readln();
+ assert(readLine.indexOf(notWritten) == -1, readLine);
+}
+
+@safe unittest
+{
+ import std.file : rmdirRecurse, exists, deleteme;
+ import std.path : dirName;
+
+ const string tmpFolder = dirName(deleteme);
+ const string filepath = tmpFolder ~ "/bug15771/minas/oops/";
+ const string filename = filepath ~ "output.txt";
+ assert(!exists(filepath));
+
+ auto f = new FileLogger(filename, LogLevel.all, CreateFolder.yes);
+ scope(exit) () @trusted { rmdirRecurse(tmpFolder ~ "/bug15771"); }();
+
+ f.log("Hello World!");
+ assert(exists(filepath));
+ f.file.close();
+}
+
+@system unittest
+{
+ import std.array : empty;
+ import std.file : deleteme, remove;
+ import std.string : indexOf;
+
+ string filename = deleteme ~ __FUNCTION__ ~ ".tempLogFile";
+ auto file = File(filename, "w");
+ auto l = new FileLogger(file);
+
+ scope(exit)
+ {
+ remove(filename);
+ }
+
+ string notWritten = "this should not be written to file";
+ string written = "this should be written to file";
+
+ l.logLevel = LogLevel.critical;
+ l.log(LogLevel.warning, notWritten);
+ l.log(LogLevel.critical, written);
+ file.close();
+
+ file = File(filename, "r");
+ string readLine = file.readln();
+ assert(readLine.indexOf(written) != -1, readLine);
+ readLine = file.readln();
+ assert(readLine.indexOf(notWritten) == -1, readLine);
+ file.close();
+}
+
+@safe unittest
+{
+ auto dl = cast(FileLogger) sharedLog;
+ assert(dl !is null);
+ assert(dl.logLevel == LogLevel.all);
+ assert(globalLogLevel == LogLevel.all);
+
+ auto tl = cast(StdForwardLogger) stdThreadLocalLog;
+ assert(tl !is null);
+ stdThreadLocalLog.logLevel = LogLevel.all;
+}
diff --git a/libphobos/src/std/experimental/logger/multilogger.d b/libphobos/src/std/experimental/logger/multilogger.d
new file mode 100644
index 0000000..ed9cfd9
--- /dev/null
+++ b/libphobos/src/std/experimental/logger/multilogger.d
@@ -0,0 +1,197 @@
+///
+module std.experimental.logger.multilogger;
+
+import std.experimental.logger.core;
+import std.experimental.logger.filelogger;
+
+/** This Element is stored inside the $(D MultiLogger) and associates a
+$(D Logger) to a $(D string).
+*/
+struct MultiLoggerEntry
+{
+ string name; /// The name if the $(D Logger)
+ Logger logger; /// The stored $(D Logger)
+}
+
+/** MultiLogger logs to multiple $(D Logger). The $(D Logger)s are stored in an
+$(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 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.
+*/
+class MultiLogger : Logger
+{
+ /** A constructor for the $(D MultiLogger) Logger.
+
+ Params:
+ lv = The $(D LogLevel) for the $(D MultiLogger). By default the
+ $(D LogLevel) for $(D MultiLogger) is $(D LogLevel.all).
+
+ Example:
+ -------------
+ auto l1 = new MultiLogger(LogLevel.trace);
+ -------------
+ */
+ this(const LogLevel lv = LogLevel.all) @safe
+ {
+ super(lv);
+ }
+
+ /** 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).
+ */
+ protected MultiLoggerEntry[] logger;
+
+ /** This method inserts a new Logger into the $(D MultiLogger).
+
+ Params:
+ name = The name of the $(D Logger) to insert.
+ newLogger = The $(D Logger) to insert.
+ */
+ void insertLogger(string name, Logger newLogger) @safe
+ {
+ this.logger ~= MultiLoggerEntry(name, newLogger);
+ }
+
+ /** This method removes a Logger from the $(D MultiLogger).
+
+ Params:
+ toRemove = The name of the $(D Logger) to remove. If the $(D Logger)
+ is not found $(D null) will be returned. Only the first occurrence of
+ a $(D Logger) with the given name will be removed.
+
+ Returns: The removed $(D Logger).
+ */
+ Logger removeLogger(in char[] toRemove) @safe
+ {
+ import std.algorithm.mutation : copy;
+ import std.range.primitives : back, popBack;
+ for (size_t i = 0; i < this.logger.length; ++i)
+ {
+ if (this.logger[i].name == toRemove)
+ {
+ Logger ret = this.logger[i].logger;
+ this.logger[i] = this.logger.back;
+ this.logger.popBack();
+
+ return ret;
+ }
+ }
+
+ return null;
+ }
+
+ /* The override to pass the payload to all children of the
+ $(D MultiLoggerBase).
+ */
+ override protected void writeLogMsg(ref LogEntry payload) @safe
+ {
+ foreach (it; this.logger)
+ {
+ /* We don't perform any checks here to avoid race conditions.
+ Instead the child will check on its own if its log level matches
+ and assume LogLevel.all for the globalLogLevel (since we already
+ know the message passes this test).
+ */
+ it.logger.forwardMsg(payload);
+ }
+ }
+}
+
+@safe unittest
+{
+ import std.exception : assertThrown;
+ import std.experimental.logger.nulllogger;
+ auto a = new MultiLogger;
+ auto n0 = new NullLogger();
+ auto n1 = new NullLogger();
+ a.insertLogger("zero", n0);
+ a.insertLogger("one", n1);
+
+ auto n0_1 = a.removeLogger("zero");
+ assert(n0_1 is n0);
+ auto n = a.removeLogger("zero");
+ assert(n is null);
+
+ auto n1_1 = a.removeLogger("one");
+ assert(n1_1 is n1);
+ n = a.removeLogger("one");
+ assert(n is null);
+}
+
+@safe unittest
+{
+ auto a = new MultiLogger;
+ auto n0 = new TestLogger;
+ auto n1 = new TestLogger;
+ a.insertLogger("zero", n0);
+ a.insertLogger("one", n1);
+
+ a.log("Hello TestLogger"); int line = __LINE__;
+ assert(n0.msg == "Hello TestLogger");
+ assert(n0.line == line);
+ assert(n1.msg == "Hello TestLogger");
+ assert(n1.line == line);
+}
+
+// Issue #16
+@system unittest
+{
+ import std.file : deleteme;
+ import std.stdio : File;
+ import std.string : indexOf;
+ string logName = deleteme ~ __FUNCTION__ ~ ".log";
+ auto logFileOutput = File(logName, "w");
+ scope(exit)
+ {
+ import std.file : remove;
+ logFileOutput.close();
+ remove(logName);
+ }
+ auto traceLog = new FileLogger(logFileOutput, LogLevel.all);
+ auto infoLog = new TestLogger(LogLevel.info);
+
+ auto root = new MultiLogger(LogLevel.all);
+ root.insertLogger("fileLogger", traceLog);
+ root.insertLogger("stdoutLogger", infoLog);
+
+ string tMsg = "A trace message";
+ root.trace(tMsg); int line1 = __LINE__;
+
+ assert(infoLog.line != line1);
+ assert(infoLog.msg != tMsg);
+
+ string iMsg = "A info message";
+ root.info(iMsg); int line2 = __LINE__;
+
+ assert(infoLog.line == line2);
+ assert(infoLog.msg == iMsg, infoLog.msg ~ ":" ~ iMsg);
+
+ logFileOutput.close();
+ logFileOutput = File(logName, "r");
+ assert(logFileOutput.isOpen);
+ assert(!logFileOutput.eof);
+
+ auto line = logFileOutput.readln();
+ assert(line.indexOf(tMsg) != -1, line ~ ":" ~ tMsg);
+ assert(!logFileOutput.eof);
+ line = logFileOutput.readln();
+ assert(line.indexOf(iMsg) != -1, line ~ ":" ~ tMsg);
+}
+
+@safe unittest
+{
+ auto dl = cast(FileLogger) sharedLog;
+ assert(dl !is null);
+ assert(dl.logLevel == LogLevel.all);
+ assert(globalLogLevel == LogLevel.all);
+
+ auto tl = cast(StdForwardLogger) stdThreadLocalLog;
+ assert(tl !is null);
+ stdThreadLocalLog.logLevel = LogLevel.all;
+}
diff --git a/libphobos/src/std/experimental/logger/nulllogger.d b/libphobos/src/std/experimental/logger/nulllogger.d
new file mode 100644
index 0000000..fa511be
--- /dev/null
+++ b/libphobos/src/std/experimental/logger/nulllogger.d
@@ -0,0 +1,39 @@
+///
+module std.experimental.logger.nulllogger;
+
+import std.experimental.logger.core;
+
+/** The $(D NullLogger) will not process any log messages.
+
+In case of a log message with $(D LogLevel.fatal) nothing will happen.
+*/
+class NullLogger : Logger
+{
+ /** The default constructor for the $(D NullLogger).
+
+ Independent of the parameter this Logger will never log a message.
+
+ Params:
+ lv = The $(D LogLevel) for the $(D NullLogger). By default the $(D LogLevel)
+ for $(D NullLogger) is $(D LogLevel.all).
+ */
+ this(const LogLevel lv = LogLevel.all) @safe
+ {
+ super(lv);
+ this.fatalHandler = delegate() {};
+ }
+
+ override protected void writeLogMsg(ref LogEntry payload) @safe @nogc
+ {
+ }
+}
+
+///
+@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/libphobos/src/std/experimental/logger/package.d b/libphobos/src/std/experimental/logger/package.d
new file mode 100644
index 0000000..b9a075c
--- /dev/null
+++ b/libphobos/src/std/experimental/logger/package.d
@@ -0,0 +1,185 @@
+/**
+Implements logging facilities.
+
+Copyright: Copyright Robert "burner" Schadek 2013 --
+License: <a href="http://www.boost.org/LICENSE_1_0.txt">Boost License 1.0</a>.
+Authors: $(HTTP www.svs.informatik.uni-oldenburg.de/60865.html, Robert burner Schadek)
+
+$(H3 Basic Logging)
+
+Message logging is a common approach to expose runtime information of a
+program. Logging should be easy, but also flexible and powerful, therefore
+$(D D) provides a standard interface for logging.
+
+The easiest way to create a log message is to write:
+-------------
+import std.experimental.logger;
+
+void main() {
+ log("Hello World");
+}
+-------------
+This will print a message to the $(D stderr) device. The message will contain
+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:
+-------------
+log("Logging to the sharedLog with its default LogLevel");
+logf(LogLevel.info, 5 < 6, "%s to the sharedLog with its LogLevel.info", "Logging");
+info("Logging to the sharedLog with its info LogLevel");
+warning(5 < 6, "Logging to the sharedLog with its LogLevel.warning if 5 is less than 6");
+error("Logging to the sharedLog with its error LogLevel");
+errorf("Logging %s the sharedLog %s its error LogLevel", "to", "with");
+critical("Logging to the"," sharedLog with its error LogLevel");
+fatal("Logging to the sharedLog with its fatal LogLevel");
+
+auto fLogger = new FileLogger("NameOfTheLogFile");
+fLogger.log("Logging to the fileLogger with its default LogLevel");
+fLogger.info("Logging to the fileLogger with its default LogLevel");
+fLogger.warning(5 < 6, "Logging to the fileLogger with its LogLevel.warning if 5 is less than 6");
+fLogger.warningf(5 < 6, "Logging to the fileLogger with its LogLevel.warning if %s is %s than 6", 5, "less");
+fLogger.critical("Logging to the fileLogger with its info LogLevel");
+fLogger.log(LogLevel.trace, 5 < 6, "Logging to the fileLogger"," with its default LogLevel if 5 is less than 6");
+fLogger.fatal("Logging to the fileLogger with its warning LogLevel");
+-------------
+Additionally, this example shows how a new $(D FileLogger) is created.
+Individual $(D Logger) and the global log functions share commonly named
+functions to log data.
+
+The names of the functions are as follows:
+$(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 is a reference to the
+current default $(D Logger). This reference can be used to assign a new
+default $(D Logger).
+-------------
+sharedLog = new FileLogger("New_Default_Log_File.log");
+-------------
+
+Additional $(D Logger) can be created by creating a new instance of the
+required $(D Logger).
+
+$(H3 Logging Fundamentals)
+$(H4 LogLevel)
+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 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.
+This behaviour can be modified by using the member $(D fatalHandler) to
+assign a custom delegate to handle log call with $(D LogLevel) $(D fatal).
+
+$(H4 Conditional Logging)
+Conditional logging can be achieved be passing a $(D bool) as first
+argument to a log function. If conditional logging is used the condition must
+be $(D true) in order to have the log message logged.
+
+In order to combine an explicit $(D LogLevel) passing with conditional
+logging, the $(D LogLevel) has to be passed as first argument followed by the
+$(D bool).
+
+$(H4 Filtering Log Messages)
+Messages are logged if the $(D LogLevel) of the log message is greater than or
+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 a $(D LogLevel) of a $(D Logger) use the $(D logLevel) property of
+the logger.
+
+$(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 $(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) 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 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 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).
+
+$(H3 User Defined Logger)
+To customize the $(D Logger) behavior, create a new $(D class) that inherits from
+the abstract $(D Logger) $(D class), and implements the $(D writeLogMsg)
+method.
+-------------
+class MyCustomLogger : Logger
+{
+ this(LogLevel lv) @safe
+ {
+ super(lv);
+ }
+
+ override void writeLogMsg(ref LogEntry payload)
+ {
+ // log message in my custom way
+ }
+}
+
+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
+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
+version argument to the $(D D) compiler when compiling your program code.
+This will disable all logging functionality.
+Specific $(D LogLevel) can be disabled at compile time as well.
+In order to disable logging with the $(D trace) $(D LogLevel) pass
+$(D StdLoggerDisableTrace) as a version.
+The following table shows which version statement disables which
+$(D LogLevel).
+$(TABLE
+ $(TR $(TD $(D LogLevel.trace) ) $(TD StdLoggerDisableTrace))
+ $(TR $(TD $(D LogLevel.info) ) $(TD StdLoggerDisableInfo))
+ $(TR $(TD $(D LogLevel.warning) ) $(TD StdLoggerDisableWarning))
+ $(TR $(TD $(D LogLevel.error) ) $(TD StdLoggerDisableError))
+ $(TR $(TD $(D LogLevel.critical) ) $(TD StdLoggerDisableCritical))
+ $(TR $(TD $(D LogLevel.fatal) ) $(TD StdLoggerDisableFatal))
+)
+Such a version statement will only disable logging in the associated compile
+unit.
+
+$(H3 Provided Logger)
+By default four $(D Logger) implementations are given. The $(D FileLogger)
+logs data to files. It can also be used to log to $(D stdout) and $(D stderr)
+as these devices are files as well. A $(D Logger) that logs to $(D stdout) can
+therefore be created by $(D new FileLogger(stdout)).
+The $(D MultiLogger) is basically an associative array of $(D string)s to
+$(D Logger). It propagates log calls to its stored $(D Logger). The
+$(D ArrayLogger) contains an array of $(D Logger) and also propagates log
+calls to its stored $(D Logger). The $(D NullLogger) does not do anything. It
+will never log a message and will never throw on a log call with $(D LogLevel)
+$(D error).
+*/
+module std.experimental.logger;
+
+public import std.experimental.logger.core;
+public import std.experimental.logger.filelogger;
+public import std.experimental.logger.multilogger;
+public import std.experimental.logger.nulllogger;
diff --git a/libphobos/src/std/experimental/note.md b/libphobos/src/std/experimental/note.md
new file mode 100644
index 0000000..3773270
--- /dev/null
+++ b/libphobos/src/std/experimental/note.md
@@ -0,0 +1 @@
+This is intended for experimental modules.
diff --git a/libphobos/src/std/experimental/typecons.d b/libphobos/src/std/experimental/typecons.d
new file mode 100644
index 0000000..e1d90d7
--- /dev/null
+++ b/libphobos/src/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.algorithm, std.range;
+
+ 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++
+ ));
+}
diff --git a/libphobos/src/std/file.d b/libphobos/src/std/file.d
new file mode 100644
index 0000000..6b12c04
--- /dev/null
+++ b/libphobos/src/std/file.d
@@ -0,0 +1,4325 @@
+// Written in the D programming language.
+
+/**
+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 $(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)
+))
+)
+
+
+Copyright: Copyright Digital Mars 2007 - 2011.
+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)
+ */
+module std.file;
+
+import core.stdc.errno, core.stdc.stdlib, core.stdc.string;
+import core.time : abs, dur, hnsecs, seconds;
+
+import std.datetime.date : DateTime;
+import std.datetime.systime : Clock, SysTime, unixTimeToStdTime;
+import std.internal.cstring;
+import std.meta;
+import std.range.primitives;
+import std.traits;
+import std.typecons;
+
+version (Windows)
+{
+ import core.sys.windows.windows, std.windows.syserror;
+}
+else version (Posix)
+{
+ import core.sys.posix.dirent, core.sys.posix.fcntl, core.sys.posix.sys.stat,
+ core.sys.posix.sys.time, core.sys.posix.unistd, core.sys.posix.utime;
+}
+else
+ static assert(false, "Module " ~ .stringof ~ " not implemented for this OS.");
+
+// 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);
+
+// Purposefully not documented. Use at your own risk
+@property string deleteme() @safe
+{
+ import std.conv : to;
+ import std.path : buildPath;
+ import std.process : thisProcessID;
+
+ static _deleteme = "deleteme.dmd.unittest.pid";
+ static _first = true;
+
+ if (_first)
+ {
+ _deleteme = buildPath(tempDir(), _deleteme) ~ to!string(thisProcessID);
+ _first = false;
+ }
+
+ return _deleteme;
+}
+
+version (unittest) private struct TestAliasedString
+{
+ string get() @safe @nogc pure nothrow { return _s; }
+ alias get this;
+ @disable this(this);
+ string _s;
+}
+
+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";
+}
+
+
+/++
+ Exception thrown for file I/O errors.
+ +/
+class FileException : Exception
+{
+ import std.conv : text, to;
+
+ /++
+ OS error code.
+ +/
+ immutable uint errno;
+
+ /++
+ Constructor which takes an error message.
+
+ Params:
+ name = Name of file for which the error occurred.
+ msg = Message describing the error.
+ file = The _file where the error occurred.
+ line = The _line where the error occurred.
+ +/
+ this(in char[] name, in char[] msg, string file = __FILE__, size_t line = __LINE__) @safe pure
+ {
+ if (msg.empty)
+ super(name.idup, file, line);
+ else
+ super(text(name, ": ", msg), file, line);
+
+ errno = 0;
+ }
+
+ /++
+ Constructor which takes the error number ($(LUCKY GetLastError)
+ in Windows, $(D_PARAM errno) in Posix).
+
+ Params:
+ name = Name of file for which the error occurred.
+ errno = The error number.
+ file = The _file where the error occurred.
+ Defaults to $(D __FILE__).
+ line = The _line where the error occurred.
+ Defaults to $(D __LINE__).
+ +/
+ version (Windows) this(in char[] name,
+ uint errno = .GetLastError(),
+ string file = __FILE__,
+ size_t line = __LINE__) @safe
+ {
+ this(name, sysErrorString(errno), file, line);
+ this.errno = errno;
+ }
+ else version (Posix) this(in char[] name,
+ uint errno = .errno,
+ string file = __FILE__,
+ size_t line = __LINE__) @trusted
+ {
+ import std.exception : errnoString;
+ this(name, errnoString(errno), file, line);
+ this.errno = errno;
+ }
+}
+
+private T cenforce(T)(T condition, lazy const(char)[] name, string file = __FILE__, size_t line = __LINE__)
+{
+ if (condition)
+ return condition;
+ version (Windows)
+ {
+ throw new FileException(name, .GetLastError(), file, line);
+ }
+ else version (Posix)
+ {
+ throw new FileException(name, .errno, file, line);
+ }
+}
+
+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) {}
+}
+
+/* **********************************
+ * Basic File operations.
+ */
+
+/********************************************
+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.
+
+Params:
+ name = string or range of characters representing the file _name
+ upTo = if present, the maximum number of bytes to _read
+
+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)
+{
+ static if (isNarrowString!R && is(Unqual!(ElementEncodingType!R) == char))
+ return readImpl(name, name.tempCString!FSChar(), upTo);
+ else
+ return readImpl(null, name.tempCString!FSChar(), upTo);
+}
+
+///
+@safe unittest
+{
+ import std.utf : byChar;
+ scope(exit)
+ {
+ assert(exists(deleteme));
+ remove(deleteme);
+ }
+
+ 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)
+{
+ 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 core.memory : GC;
+ import std.algorithm.comparison : min;
+ import std.array : uninitializedArray;
+ import std.conv : to;
+
+ // A few internal configuration parameters {
+ enum size_t
+ minInitialAlloc = 1024 * 4,
+ maxInitialAlloc = size_t.max / 2,
+ sizeIncrement = 1024 * 16,
+ maxSlackMemoryAllowed = 1024;
+ // }
+
+ 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, namez);
+
+ immutable initialAlloc = min(upTo, to!size_t(statbuf.st_size
+ ? min(statbuf.st_size + 1, maxInitialAlloc)
+ : minInitialAlloc));
+ void[] result = uninitializedArray!(ubyte[])(initialAlloc);
+ 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, 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];
+ }
+
+ return result.length - size >= maxSlackMemoryAllowed
+ ? GC.realloc(result.ptr, size, GC.BlkAttr.NO_SCAN)[0 .. size]
+ : result[0 .. size];
+}
+
+
+version (Windows) private void[] readImpl(const(char)[] name, const(FSChar)* namez, size_t upTo = size_t.max) @safe
+{
+ import core.memory : GC;
+ import std.algorithm.comparison : min;
+ import std.array : uninitializedArray;
+ static trustedCreateFileW(const(wchar)* namez, DWORD dwDesiredAccess, DWORD dwShareMode,
+ SECURITY_ATTRIBUTES *lpSecurityAttributes, DWORD dwCreationDisposition,
+ DWORD dwFlagsAndAttributes, HANDLE hTemplateFile) @trusted
+ {
+ return CreateFileW(namez, dwDesiredAccess, dwShareMode,
+ lpSecurityAttributes, dwCreationDisposition,
+ dwFlagsAndAttributes, hTemplateFile);
+
+ }
+ static trustedCloseHandle(HANDLE hObject) @trusted
+ {
+ return CloseHandle(hObject);
+ }
+ static trustedGetFileSize(HANDLE hFile, out ulong fileSize) @trusted
+ {
+ 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, ulong nNumberOfBytesToRead) @trusted
+ {
+ // 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 =
+ 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(namez, defaults);
+
+ 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)
+ {
+ () @trusted { GC.free(buf.ptr); } ();
+ }
+
+ if (size)
+ cenforce(trustedReadFile(h, &buf[0], size), name, namez);
+ return buf[0 .. size];
+}
+
+version (linux) @safe unittest
+{
+ // A file with "zero" length that doesn't have 0 length at all
+ auto s = std.file.readText("/proc/sys/kernel/osrelease");
+ assert(s.length > 0);
+ //writefln("'%s'", s);
+}
+
+@safe unittest
+{
+ scope(exit) if (exists(deleteme)) remove(deleteme);
+ import std.stdio;
+ auto f = File(deleteme, "w");
+ f.write("abcd"); f.flush();
+ assert(read(deleteme) == "abcd");
+}
+
+/********************************************
+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.
+ */
+
+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; }
+ auto result = trustedCast(read(name));
+ validate(result);
+ return result;
+}
+
+///
+@safe unittest
+{
+ 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.
+
+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);
+}
+
+///
+@system unittest
+{
+ scope(exit)
+ {
+ assert(exists(deleteme));
+ remove(deleteme);
+ }
+
+ int[] a = [ 0, 1, 1, 2, 3, 5, 8 ];
+ write(deleteme, a); // deleteme is the name of a temporary file
+ assert(cast(int[]) read(deleteme) == a);
+}
+
+/// ditto
+void write(R)(auto ref R name, const void[] buffer)
+if (isConvertibleToString!R)
+{
+ write!(StringTypeOf!R)(name, buffer);
+}
+
+@safe unittest
+{
+ static assert(__traits(compiles, write(TestAliasedString(null), null)));
+}
+
+/*********************************************
+Appends $(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 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);
+}
+
+///
+@system unittest
+{
+ scope(exit)
+ {
+ assert(exists(deleteme));
+ remove(deleteme);
+ }
+
+ int[] a = [ 0, 1, 1, 2, 3, 5, 8 ];
+ write(deleteme, a); // deleteme is the name of a temporary file
+ int[] b = [ 13, 21 ];
+ append(deleteme, b);
+ assert(cast(int[]) read(deleteme) == a ~ b);
+}
+
+/// ditto
+void append(R)(auto ref R name, const void[] buffer)
+if (isConvertibleToString!R)
+{
+ append!(StringTypeOf!R)(name, buffer);
+}
+
+@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(const(char)[] name, const(FSChar)* namez,
+ in void[] buffer, bool append) @trusted
+{
+ 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;
+ 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, 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(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)
+ {
+ 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 ", f, " to ", t)));
+ }
+ }
+ else version (Posix)
+ {
+ static import core.stdc.stdio;
+
+ 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.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(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(namez), name, namez);
+ }
+ else version (Posix)
+ {
+ static import core.stdc.stdio;
+
+ 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(R)(R name)
+if (isInputRange!R && !isInfinite!R && isSomeChar!(ElementEncodingType!R))
+{
+ 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;
+}
+
+version (Windows) private ulong makeUlong(DWORD dwLow, DWORD dwHigh) @safe pure nothrow @nogc
+{
+ ULARGE_INTEGER li;
+ li.LowPart = dwLow;
+ li.HighPart = dwHigh;
+ return li.QuadPart;
+}
+
+/***************************************************
+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(R)(R name)
+if (isInputRange!R && !isInfinite!R && isSomeChar!(ElementEncodingType!R) &&
+ !isConvertibleToString!R)
+{
+ version (Windows)
+ {
+ with (getFileAttributesWin(name))
+ return makeUlong(nFileSizeLow, nFileSizeHigh);
+ }
+ else version (Posix)
+ {
+ auto namez = name.tempCString();
+
+ static trustedStat(const(FSChar)* namez, out stat_t buf) @trusted
+ {
+ 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(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
+ write(deleteme, "a");
+ scope(exit) { assert(exists(deleteme)); remove(deleteme); }
+ assert(getSize(deleteme) == 1);
+ // create a file of size 3
+ write(deleteme, "abc");
+ 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).
+
+ Params:
+ name = File/Folder _name to get times for.
+ accessTime = Time the file/folder was last accessed.
+ modificationTime = Time the file/folder was last modified.
+
+ Throws:
+ $(D FileException) on error.
+ +/
+void getTimes(R)(R name,
+ out SysTime accessTime,
+ out SysTime modificationTime)
+if (isInputRange!R && !isInfinite!R && isSomeChar!(ElementEncodingType!R) &&
+ !isConvertibleToString!R)
+{
+ version (Windows)
+ {
+ import std.datetime.systime : FILETIMEToSysTime;
+
+ with (getFileAttributesWin(name))
+ {
+ accessTime = FILETIMEToSysTime(&ftLastAccessTime);
+ modificationTime = FILETIMEToSysTime(&ftLastWriteTime);
+ }
+ }
+ else version (Posix)
+ {
+ auto namez = name.tempCString();
+
+ static auto trustedStat(const(FSChar)* namez, ref stat_t buf) @trusted
+ {
+ return stat(namez, &buf);
+ }
+ stat_t statbuf = void;
+
+ static if (isNarrowString!R && is(Unqual!(ElementEncodingType!R) == char))
+ alias names = name;
+ else
+ string names = null;
+ cenforce(trustedStat(namez, statbuf) == 0, names, namez);
+
+ accessTime = statTimeToStdTime!'a'(statbuf);
+ modificationTime = statTimeToStdTime!'m'(statbuf);
+ }
+}
+
+/// 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;
+
+ auto currTime = Clock.currTime();
+
+ write(deleteme, "a");
+ scope(exit) { assert(exists(deleteme)); remove(deleteme); }
+
+ SysTime accessTime1 = void;
+ SysTime modificationTime1 = void;
+
+ getTimes(deleteme, accessTime1, modificationTime1);
+
+ enum leeway = dur!"seconds"(5);
+
+ {
+ auto diffa = accessTime1 - currTime;
+ auto diffm = modificationTime1 - currTime;
+ scope(failure) writefln("[%s] [%s] [%s] [%s] [%s]", accessTime1, modificationTime1, currTime, diffa, diffm);
+
+ assert(abs(diffa) <= leeway);
+ assert(abs(diffm) <= leeway);
+ }
+
+ version (fullFileTests)
+ {
+ import core.thread;
+ enum sleepTime = dur!"seconds"(2);
+ Thread.sleep(sleepTime);
+
+ currTime = Clock.currTime();
+ write(deleteme, "b");
+
+ SysTime accessTime2 = void;
+ SysTime modificationTime2 = void;
+
+ getTimes(deleteme, accessTime2, modificationTime2);
+
+ {
+ auto diffa = accessTime2 - currTime;
+ auto diffm = modificationTime2 - currTime;
+ scope(failure) writefln("[%s] [%s] [%s] [%s] [%s]", accessTime2, modificationTime2, currTime, diffa, diffm);
+
+ //There is no guarantee that the access time will be updated.
+ assert(abs(diffa) <= leeway + sleepTime);
+ assert(abs(diffm) <= leeway);
+ }
+
+ assert(accessTime1 <= accessTime2);
+ assert(modificationTime1 <= modificationTime2);
+ }
+}
+
+
+version (StdDdoc)
+{
+ /++
+ $(BLUE This function is Windows-Only.)
+
+ 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.
+
+ 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.
+ +/
+ 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)
+{
+ void getTimesWin(R)(R name,
+ out SysTime fileCreationTime,
+ out SysTime fileAccessTime,
+ out SysTime fileModificationTime)
+ if (isInputRange!R && !isInfinite!R && isSomeChar!(ElementEncodingType!R) &&
+ !isConvertibleToString!R)
+ {
+ import std.datetime.systime : FILETIMEToSysTime;
+
+ with (getFileAttributesWin(name))
+ {
+ fileCreationTime = FILETIMEToSysTime(&ftCreationTime);
+ fileAccessTime = FILETIMEToSysTime(&ftLastAccessTime);
+ fileModificationTime = FILETIMEToSysTime(&ftLastWriteTime);
+ }
+ }
+
+ void getTimesWin(R)(auto ref R name,
+ out SysTime fileCreationTime,
+ out SysTime fileAccessTime,
+ out SysTime fileModificationTime)
+ if (isConvertibleToString!R)
+ {
+ getTimesWin!(StringTypeOf!R)(name, fileCreationTime, fileAccessTime, fileModificationTime);
+ }
+}
+
+version (Windows) @system unittest
+{
+ import std.stdio : writefln;
+ auto currTime = Clock.currTime();
+
+ write(deleteme, "a");
+ scope(exit) { assert(exists(deleteme)); remove(deleteme); }
+
+ SysTime creationTime1 = void;
+ SysTime accessTime1 = void;
+ SysTime modificationTime1 = void;
+
+ getTimesWin(deleteme, creationTime1, accessTime1, modificationTime1);
+
+ enum leeway = dur!"seconds"(5);
+
+ {
+ auto diffc = creationTime1 - currTime;
+ auto diffa = accessTime1 - currTime;
+ auto diffm = modificationTime1 - currTime;
+ scope(failure)
+ {
+ writefln("[%s] [%s] [%s] [%s] [%s] [%s] [%s]",
+ creationTime1, accessTime1, modificationTime1, currTime, diffc, diffa, diffm);
+ }
+
+ // Deleting and recreating a file doesn't seem to always reset the "file creation time"
+ //assert(abs(diffc) <= leeway);
+ assert(abs(diffa) <= leeway);
+ assert(abs(diffm) <= leeway);
+ }
+
+ version (fullFileTests)
+ {
+ import core.thread;
+ Thread.sleep(dur!"seconds"(2));
+
+ currTime = Clock.currTime();
+ write(deleteme, "b");
+
+ SysTime creationTime2 = void;
+ SysTime accessTime2 = void;
+ SysTime modificationTime2 = void;
+
+ getTimesWin(deleteme, creationTime2, accessTime2, modificationTime2);
+
+ {
+ auto diffa = accessTime2 - currTime;
+ auto diffm = modificationTime2 - currTime;
+ scope(failure)
+ {
+ writefln("[%s] [%s] [%s] [%s] [%s]",
+ accessTime2, modificationTime2, currTime, diffa, diffm);
+ }
+
+ assert(abs(diffa) <= leeway);
+ assert(abs(diffm) <= leeway);
+ }
+
+ assert(creationTime1 == creationTime2);
+ assert(accessTime1 <= accessTime2);
+ assert(modificationTime1 <= modificationTime2);
+ }
+
+ {
+ SysTime ctime, atime, mtime;
+ static assert(__traits(compiles, getTimesWin(TestAliasedString("foo"), ctime, atime, mtime)));
+ }
+}
+
+
+/++
+ Set access/modified times of file or folder $(D name).
+
+ Params:
+ name = File/Folder _name to get times for.
+ accessTime = Time the file/folder was last accessed.
+ modificationTime = Time the file/folder was last modified.
+
+ Throws:
+ $(D FileException) on error.
+ +/
+void setTimes(R)(R name,
+ SysTime accessTime,
+ SysTime modificationTime)
+if (isInputRange!R && !isInfinite!R && isSomeChar!(ElementEncodingType!R) &&
+ !isConvertibleToString!R)
+{
+ version (Windows)
+ {
+ import std.datetime.systime : SysTimeToFILETIME;
+
+ 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(namez, dwDesiredAccess, dwShareMode,
+ lpSecurityAttributes, dwCreationDisposition,
+ dwFlagsAndAttributes, hTemplateFile);
+
+ }
+ static auto trustedCloseHandle(HANDLE hObject) @trusted
+ {
+ return CloseHandle(hObject);
+ }
+ static auto trustedSetFileTime(HANDLE hFile, in FILETIME *lpCreationTime,
+ in ref FILETIME lpLastAccessTime, in ref FILETIME lpLastWriteTime) @trusted
+ {
+ return SetFileTime(hFile, lpCreationTime, &lpLastAccessTime, &lpLastWriteTime);
+ }
+
+ const ta = SysTimeToFILETIME(accessTime);
+ const tm = SysTimeToFILETIME(modificationTime);
+ alias defaults =
+ 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), names, namez);
+
+ cenforce(trustedSetFileTime(h, null, ta, tm), names, namez);
+ }
+ else version (Posix)
+ {
+ auto namez = name.tempCString!FSChar();
+ static if (is(typeof(&utimensat)))
+ {
+ 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);
+ }
+ 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();
+
+ static if (isNarrowString!R && is(Unqual!(ElementEncodingType!R) == char))
+ alias names = name;
+ else
+ string names = null;
+ cenforce(trustedUtimes(namez, t) == 0, names, namez);
+ }
+ }
+}
+
+/// 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";
+ string dir = newdir ~ r"/a/b/c";
+ string file = dir ~ "/file";
+
+ if (!exists(dir)) mkdirRecurse(dir);
+ { auto f = File(file, "w"); }
+
+ void testTimes(int hnsecValue)
+ {
+ 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);
+}
+
+/++
+ Returns the time that the given file was last modified.
+
+ Throws:
+ $(D FileException) if the given file does not exist.
++/
+SysTime timeLastModified(R)(R name)
+if (isInputRange!R && !isInfinite!R && isSomeChar!(ElementEncodingType!R) &&
+ !isConvertibleToString!R)
+{
+ version (Windows)
+ {
+ SysTime dummy;
+ SysTime ftm;
+
+ getTimesWin(name, dummy, dummy, ftm);
+
+ return ftm;
+ }
+ else version (Posix)
+ {
+ auto namez = name.tempCString!FSChar();
+ static auto trustedStat(const(FSChar)* namez, ref stat_t buf) @trusted
+ {
+ return stat(namez, &buf);
+ }
+ stat_t statbuf = void;
+
+ static if (isNarrowString!R && is(Unqual!(ElementEncodingType!R) == char))
+ alias names = name;
+ else
+ string names = null;
+ cenforce(trustedStat(namez, statbuf) == 0, names, namez);
+
+ 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
+ $(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
+ below. The code throws a $(D FileException) if $(D source) does not
+ exist (as it should). On the other hand, the $(D SysTime.min) default
+ makes a non-existing $(D target) seem infinitely old so the test
+ correctly prompts building it.
+
+ Params:
+ name = The _name of the file to get the modification time for.
+ returnIfMissing = The time to return if the given file does not exist.
+
+Example:
+--------------------
+if (timeLastModified(source) >= timeLastModified(target, SysTime.min))
+{
+ // must (re)build
+}
+else
+{
+ // target is up-to-date
+}
+--------------------
++/
+SysTime timeLastModified(R)(R name, SysTime returnIfMissing)
+if (isInputRange!R && !isInfinite!R && isSomeChar!(ElementEncodingType!R))
+{
+ version (Windows)
+ {
+ if (!exists(name))
+ return returnIfMissing;
+
+ SysTime dummy;
+ SysTime ftm;
+
+ getTimesWin(name, dummy, dummy, ftm);
+
+ return ftm;
+ }
+ else version (Posix)
+ {
+ auto namez = name.tempCString!FSChar();
+ static auto trustedStat(const(FSChar)* namez, ref stat_t buf) @trusted
+ {
+ return stat(namez, &buf);
+ }
+ stat_t statbuf = void;
+
+ return trustedStat(namez, statbuf) != 0 ?
+ returnIfMissing :
+ statTimeToStdTime!'m'(statbuf);
+ }
+}
+
+@safe unittest
+{
+ //std.process.system("echo a > deleteme") == 0 || assert(false);
+ if (exists(deleteme))
+ remove(deleteme);
+
+ write(deleteme, "a\n");
+
+ scope(exit)
+ {
+ assert(exists(deleteme));
+ remove(deleteme);
+ }
+
+ // assert(lastModified("deleteme") >
+ // lastModified("this file does not exist", SysTime.min));
+ //assert(lastModified("deleteme") > lastModified(__FILE__));
+}
+
+
+// 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 file _name 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(namez) != 0xFFFFFFFF;
+ }
+ else version (Posix)
+ {
+ /*
+ The reason why we use stat (and not access) here is
+ the quirky behavior of access for SUID programs: if
+ we used access, a file may not appear to "exist",
+ despite that the program would be able to open it
+ just fine. The behavior in question is described as
+ follows in the access man page:
+
+ > The check is done using the calling process's real
+ > UID and GID, rather than the effective IDs as is
+ > done when actually attempting an operation (e.g.,
+ > open(2)) on the file. This allows set-user-ID
+ > programs to easily determine the invoking user's
+ > authority.
+
+ While various operating systems provide eaccess or
+ euidaccess functions, these are not part of POSIX -
+ so it's safer to use stat instead.
+ */
+
+ stat_t statbuf = void;
+ return lstat(namez, &statbuf) == 0;
+ }
+ else
+ static assert(0);
+}
+
+@safe unittest
+{
+ assert(exists("."));
+ assert(!exists("this file does not exist"));
+ write(deleteme, "a\n");
+ scope(exit) { assert(exists(deleteme)); remove(deleteme); }
+ 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
+ $(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 $(HTTP en.wikipedia.org/wiki/Stat_%28Unix%29, $(D stat))
+ function.
+
+ On Posix systems, if the given file is a symbolic link, then
+ attributes are the attributes of the file pointed to by the symbolic
+ link.
+
+ Params:
+ name = The file to get the attributes of.
+
+ Throws: $(D FileException) on error.
+ +/
+uint getAttributes(R)(R name)
+if (isInputRange!R && !isInfinite!R && isSomeChar!(ElementEncodingType!R) &&
+ !isConvertibleToString!R)
+{
+ version (Windows)
+ {
+ auto namez = name.tempCString!FSChar();
+ static auto trustedGetFileAttributesW(const(FSChar)* namez) @trusted
+ {
+ return GetFileAttributesW(namez);
+ }
+ immutable result = trustedGetFileAttributesW(namez);
+
+ 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)
+ {
+ auto namez = name.tempCString!FSChar();
+ static auto trustedStat(const(FSChar)* namez, ref stat_t buf) @trusted
+ {
+ return stat(namez, &buf);
+ }
+ stat_t statbuf = void;
+
+ 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
+ symbolic link itself rather than file that it points to. If the given file
+ is $(I not) a symbolic link, then this function returns the same result
+ as getAttributes.
+
+ On Windows, getLinkAttributes is identical to getAttributes. It exists on
+ Windows so that you don't have to special-case code for Windows when dealing
+ with symbolic links.
+
+ Params:
+ name = The file to get the symbolic link attributes of.
+
+ Returns:
+ the attributes
+
+ Throws:
+ $(D FileException) on error.
+ +/
+uint getLinkAttributes(R)(R name)
+if (isInputRange!R && !isInfinite!R && isSomeChar!(ElementEncodingType!R) &&
+ !isConvertibleToString!R)
+{
+ version (Windows)
+ {
+ return getAttributes(name);
+ }
+ else version (Posix)
+ {
+ auto namez = name.tempCString!FSChar();
+ static auto trustedLstat(const(FSChar)* namez, ref stat_t buf) @trusted
+ {
+ return lstat(namez, &buf);
+ }
+ stat_t lstatbuf = void;
+ 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(R)(R name, uint attributes)
+if (isInputRange!R && !isInfinite!R && isSomeChar!(ElementEncodingType!R) &&
+ !isConvertibleToString!R)
+{
+ version (Windows)
+ {
+ auto namez = name.tempCString!FSChar();
+ static auto trustedSetFileAttributesW(const(FSChar)* namez, uint dwFileAttributes) @trusted
+ {
+ return SetFileAttributesW(namez, dwFileAttributes);
+ }
+ 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)
+ {
+ auto namez = name.tempCString!FSChar();
+ static auto trustedChmod(const(FSChar)* namez, mode_t mode) @trusted
+ {
+ return chmod(namez, mode);
+ }
+ assert(attributes <= mode_t.max);
+ 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.
+
+ Params:
+ name = The path to the file.
+
+ Returns:
+ true if name specifies a directory
+
+ Throws:
+ $(D FileException) if the given file does not exist.
+
+Example:
+--------------------
+assert(!"/etc/fonts/fonts.conf".isDir);
+assert("/usr/share/include".isDir);
+--------------------
+ +/
+@property bool isDir(R)(R name)
+if (isInputRange!R && !isInfinite!R && isSomeChar!(ElementEncodingType!R) &&
+ !isConvertibleToString!R)
+{
+ version (Windows)
+ {
+ return (getAttributes(name) & FILE_ATTRIBUTE_DIRECTORY) != 0;
+ }
+ else version (Posix)
+ {
+ return (getAttributes(name) & S_IFMT) == S_IFDIR;
+ }
+}
+
+/// 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)
+ assert("C:\\Program Files\\".isDir);
+
+ if ("C:\\Windows\\system.ini".exists)
+ assert(!"C:\\Windows\\system.ini".isDir);
+ }
+ else version (Posix)
+ {
+ if (system_directory.exists)
+ assert(system_directory.isDir);
+
+ 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.
+
+ Params:
+ attributes = The file _attributes.
+
+ Returns:
+ true if attributes specifies a directory
+
+Example:
+--------------------
+assert(!attrIsDir(getAttributes("/etc/fonts/fonts.conf")));
+assert(!attrIsDir(getLinkAttributes("/etc/fonts/fonts.conf")));
+--------------------
+ +/
+bool attrIsDir(uint attributes) @safe pure nothrow @nogc
+{
+ version (Windows)
+ {
+ return (attributes & FILE_ATTRIBUTE_DIRECTORY) != 0;
+ }
+ else version (Posix)
+ {
+ return (attributes & S_IFMT) == S_IFDIR;
+ }
+}
+
+@safe unittest
+{
+ version (Windows)
+ {
+ if ("C:\\Program Files\\".exists)
+ {
+ assert(attrIsDir(getAttributes("C:\\Program Files\\")));
+ assert(attrIsDir(getLinkAttributes("C:\\Program Files\\")));
+ }
+
+ if ("C:\\Windows\\system.ini".exists)
+ {
+ assert(!attrIsDir(getAttributes("C:\\Windows\\system.ini")));
+ assert(!attrIsDir(getLinkAttributes("C:\\Windows\\system.ini")));
+ }
+ }
+ else version (Posix)
+ {
+ if (system_directory.exists)
+ {
+ assert(attrIsDir(getAttributes(system_directory)));
+ assert(attrIsDir(getLinkAttributes(system_directory)));
+ }
+
+ if (system_file.exists)
+ {
+ assert(!attrIsDir(getAttributes(system_file)));
+ assert(!attrIsDir(getLinkAttributes(system_file)));
+ }
+ }
+}
+
+
+/++
+ Returns whether the given file (or directory) is a file.
+
+ On Windows, if a file is not a directory, then it's a file. So,
+ either $(D isFile) or $(D isDir) will return true for any given file.
+
+ On Posix systems, if $(D isFile) is $(D true), that indicates that the file
+ is a regular file (e.g. not a block not device). So, on Posix systems, it's
+ possible for both $(D isFile) and $(D isDir) to be $(D false) for a
+ particular file (in which case, it's a special file). You can use
+ $(D getAttributes) to get the attributes to figure out what type of special
+ it is, or you can use $(D DirEntry) to get at its $(D statBuf), which is the
+ result from $(D stat). In either case, see the man page for $(D stat) for
+ more information.
+
+ Params:
+ name = The path to the file.
+
+ Returns:
+ true if name specifies a file
+
+ Throws:
+ $(D FileException) if the given file does not exist.
+
+Example:
+--------------------
+assert("/etc/fonts/fonts.conf".isFile);
+assert(!"/usr/share/include".isFile);
+--------------------
+ +/
+@property bool isFile(R)(R name)
+if (isInputRange!R && !isInfinite!R && isSomeChar!(ElementEncodingType!R) &&
+ !isConvertibleToString!R)
+{
+ version (Windows)
+ return !name.isDir;
+ else version (Posix)
+ 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)
+ assert(!"C:\\Program Files\\".isFile);
+
+ if ("C:\\Windows\\system.ini".exists)
+ assert("C:\\Windows\\system.ini".isFile);
+ }
+ else version (Posix)
+ {
+ if (system_directory.exists)
+ assert(!system_directory.isFile);
+
+ if (system_file.exists)
+ assert(system_file.isFile);
+ }
+}
+
+
+/++
+ Returns whether the given file _attributes are for a file.
+
+ On Windows, if a file is not a directory, it's a file. So, either
+ $(D attrIsFile) or $(D attrIsDir) will return $(D true) for the
+ _attributes of any given file.
+
+ On Posix systems, if $(D attrIsFile) is $(D true), that indicates that the
+ file is a regular file (e.g. not a block not device). So, on Posix systems,
+ it's possible for both $(D attrIsFile) and $(D attrIsDir) to be $(D false)
+ for a particular file (in which case, it's a special file). If a file is a
+ special file, you can use the _attributes to check what type of special file
+ it is (see the man page for $(D stat) for more information).
+
+ Params:
+ attributes = The file _attributes.
+
+ 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")));
+--------------------
+ +/
+bool attrIsFile(uint attributes) @safe pure nothrow @nogc
+{
+ version (Windows)
+ {
+ return (attributes & FILE_ATTRIBUTE_DIRECTORY) == 0;
+ }
+ else version (Posix)
+ {
+ return (attributes & S_IFMT) == S_IFREG;
+ }
+}
+
+@safe unittest
+{
+ version (Windows)
+ {
+ if ("C:\\Program Files\\".exists)
+ {
+ assert(!attrIsFile(getAttributes("C:\\Program Files\\")));
+ assert(!attrIsFile(getLinkAttributes("C:\\Program Files\\")));
+ }
+
+ if ("C:\\Windows\\system.ini".exists)
+ {
+ assert(attrIsFile(getAttributes("C:\\Windows\\system.ini")));
+ assert(attrIsFile(getLinkAttributes("C:\\Windows\\system.ini")));
+ }
+ }
+ else version (Posix)
+ {
+ if (system_directory.exists)
+ {
+ assert(!attrIsFile(getAttributes(system_directory)));
+ assert(!attrIsFile(getLinkAttributes(system_directory)));
+ }
+
+ if (system_file.exists)
+ {
+ assert(attrIsFile(getAttributes(system_file)));
+ assert(attrIsFile(getLinkAttributes(system_file)));
+ }
+ }
+}
+
+
+/++
+ Returns whether the given file is a symbolic link.
+
+ On Windows, returns $(D true) when the file is either a symbolic link or a
+ junction point.
+
+ 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(R)(R name)
+if (isInputRange!R && !isInfinite!R && isSomeChar!(ElementEncodingType!R) &&
+ !isConvertibleToString!R)
+{
+ version (Windows)
+ return (getAttributes(name) & FILE_ATTRIBUTE_REPARSE_POINT) != 0;
+ else version (Posix)
+ return (getLinkAttributes(name) & S_IFMT) == S_IFLNK;
+}
+
+/// 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)
+ assert(!"C:\\Program Files\\".isSymlink);
+
+ if ("C:\\Users\\".exists && "C:\\Documents and Settings\\".exists)
+ assert("C:\\Documents and Settings\\".isSymlink);
+
+ enum fakeSymFile = "C:\\Windows\\system.ini";
+ if (fakeSymFile.exists)
+ {
+ assert(!fakeSymFile.isSymlink);
+
+ assert(!fakeSymFile.isSymlink);
+ assert(!attrIsSymlink(getAttributes(fakeSymFile)));
+ assert(!attrIsSymlink(getLinkAttributes(fakeSymFile)));
+
+ assert(attrIsFile(getAttributes(fakeSymFile)));
+ assert(attrIsFile(getLinkAttributes(fakeSymFile)));
+ assert(!attrIsDir(getAttributes(fakeSymFile)));
+ assert(!attrIsDir(getLinkAttributes(fakeSymFile)));
+
+ assert(getAttributes(fakeSymFile) == getLinkAttributes(fakeSymFile));
+ }
+ }
+ else version (Posix)
+ {
+ if (system_directory.exists)
+ {
+ assert(!system_directory.isSymlink);
+
+ immutable symfile = deleteme ~ "_slink\0";
+ scope(exit) if (symfile.exists) symfile.remove();
+
+ core.sys.posix.unistd.symlink(system_directory, symfile.ptr);
+
+ assert(symfile.isSymlink);
+ assert(!attrIsSymlink(getAttributes(symfile)));
+ assert(attrIsSymlink(getLinkAttributes(symfile)));
+
+ assert(attrIsDir(getAttributes(symfile)));
+ assert(!attrIsDir(getLinkAttributes(symfile)));
+
+ assert(!attrIsFile(getAttributes(symfile)));
+ assert(!attrIsFile(getLinkAttributes(symfile)));
+ }
+
+ if (system_file.exists)
+ {
+ assert(!system_file.isSymlink);
+
+ immutable symfile = deleteme ~ "_slink\0";
+ scope(exit) if (symfile.exists) symfile.remove();
+
+ core.sys.posix.unistd.symlink(system_file, symfile.ptr);
+
+ assert(symfile.isSymlink);
+ assert(!attrIsSymlink(getAttributes(symfile)));
+ assert(attrIsSymlink(getLinkAttributes(symfile)));
+
+ assert(!attrIsDir(getAttributes(symfile)));
+ assert(!attrIsDir(getLinkAttributes(symfile)));
+
+ assert(attrIsFile(getAttributes(symfile)));
+ assert(!attrIsFile(getLinkAttributes(symfile)));
+ }
+ }
+
+ static assert(__traits(compiles, () @safe { return "dummy".isSymlink; }));
+}
+
+
+/++
+ Returns whether the given file attributes are for a symbolic link.
+
+ On Windows, return $(D true) when the file is either a symbolic link or a
+ junction point.
+
+ Params:
+ attributes = The file attributes.
+
+ Returns:
+ true if attributes are for a symbolic link
+
+Example:
+--------------------
+core.sys.posix.unistd.symlink("/etc/fonts/fonts.conf", "/tmp/alink");
+
+assert(!getAttributes("/tmp/alink").isSymlink);
+assert(getLinkAttributes("/tmp/alink").isSymlink);
+--------------------
+ +/
+bool attrIsSymlink(uint attributes) @safe pure nothrow @nogc
+{
+ version (Windows)
+ return (attributes & FILE_ATTRIBUTE_REPARSE_POINT) != 0;
+ else version (Posix)
+ return (attributes & S_IFMT) == S_IFLNK;
+}
+
+
+/****************************************************
+ * Change directory to $(D pathname).
+ * Throws: $(D FileException) on error.
+ */
+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 trustedChdir(const(FSChar)* pathz) @trusted
+ {
+ return SetCurrentDirectoryW(pathz);
+ }
+ }
+ else version (Posix)
+ {
+ static auto trustedChdir(const(FSChar)* pathz) @trusted
+ {
+ return core.sys.posix.unistd.chdir(pathz) == 0;
+ }
+ }
+ 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))));
+}
+
+/****************************************************
+Make directory $(D pathname).
+
+Throws: $(D FileException) on Posix or $(D WindowsException) on Windows
+ if an error occured.
+ */
+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(const(FSChar)* pathz) @trusted
+ {
+ return CreateDirectoryW(pathz, null);
+ }
+ static if (isNarrowString!R && is(Unqual!(ElementEncodingType!R) == char))
+ alias pathStr = pathname;
+ else
+ string pathStr = null;
+ wenforce(trustedCreateDirectoryW(pathz), pathStr, pathz);
+ }
+ else version (Posix)
+ {
+ import std.conv : octal;
+
+ static auto trustedMkdir(const(FSChar)* pathz, mode_t mode) @trusted
+ {
+ return core.sys.posix.sys.stat.mkdir(pathz, mode);
+ }
+ 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)
+{
+ import std.exception : enforce;
+ const pathz = pathname.tempCString!FSChar();
+
+ version (Windows)
+ {
+ if (() @trusted { return CreateDirectoryW(pathz, null); }())
+ return true;
+ cenforce(GetLastError() == ERROR_ALREADY_EXISTS, pathname.idup);
+ }
+ else version (Posix)
+ {
+ import std.conv : octal;
+
+ if (() @trusted { return core.sys.posix.sys.stat.mkdir(pathz, octal!777); }() == 0)
+ return true;
+ cenforce(errno == EEXIST || errno == EISDIR, pathname);
+ }
+ enforce(pathname.isDir, new FileException(pathname.idup));
+ return false;
+}
+
+/****************************************************
+ * 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) @safe
+{
+ import std.path : dirName, baseName;
+
+ const left = dirName(pathname);
+ if (left.length != pathname.length && !exists(left))
+ {
+ mkdirRecurse(left);
+ }
+ if (!baseName(pathname).empty)
+ {
+ ensureDirExists(pathname);
+ }
+}
+
+@safe unittest
+{
+ import std.exception : assertThrown;
+ {
+ import std.path : buildPath, buildNormalizedPath;
+
+ immutable basepath = deleteme ~ "_dir";
+ scope(exit) () @trusted { rmdirRecurse(basepath); }();
+
+ auto path = buildPath(basepath, "a", "..", "b");
+ mkdirRecurse(path);
+ path = path.buildNormalizedPath;
+ assert(path.isDir);
+
+ path = buildPath(basepath, "c");
+ write(path, "");
+ assertThrown!FileException(mkdirRecurse(path));
+
+ path = buildPath(basepath, "d");
+ mkdirRecurse(path);
+ mkdirRecurse(path); // should not throw
+ }
+
+ version (Windows)
+ {
+ assertThrown!FileException(mkdirRecurse(`1:\foobar`));
+ }
+
+ // bug3570
+ {
+ immutable basepath = deleteme ~ "_dir";
+ version (Windows)
+ {
+ immutable path = basepath ~ "\\fake\\here\\";
+ }
+ else version (Posix)
+ {
+ immutable path = basepath ~ `/fake/here/`;
+ }
+
+ mkdirRecurse(path);
+ assert(basepath.exists && basepath.isDir);
+ scope(exit) () @trusted { rmdirRecurse(basepath); }();
+ assert(path.exists && path.isDir);
+ }
+}
+
+/****************************************************
+Remove directory $(D pathname).
+
+Params:
+ pathname = Range or string specifying the directory name
+
+Throws: $(D FileException) on error.
+ */
+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)
+ {
+ static auto trustedRmdir(const(FSChar)* pathz) @trusted
+ {
+ return RemoveDirectoryW(pathz);
+ }
+ }
+ else version (Posix)
+ {
+ 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 symbolic _link (_symlink).
+
+ Params:
+ 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
+ exists).
+ +/
+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 if (isConvertibleToString!RO || isConvertibleToString!RL)
+ {
+ 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));
+ }
+}
+
+version (Posix) @safe unittest
+{
+ if (system_directory.exists)
+ {
+ immutable symfile = deleteme ~ "_slink\0";
+ scope(exit) if (symfile.exists) symfile.remove();
+
+ symlink(system_directory, symfile);
+
+ assert(symfile.exists);
+ assert(symfile.isSymlink);
+ assert(!attrIsSymlink(getAttributes(symfile)));
+ assert(attrIsSymlink(getLinkAttributes(symfile)));
+
+ assert(attrIsDir(getAttributes(symfile)));
+ assert(!attrIsDir(getLinkAttributes(symfile)));
+
+ assert(!attrIsFile(getAttributes(symfile)));
+ assert(!attrIsFile(getLinkAttributes(symfile)));
+ }
+
+ if (system_file.exists)
+ {
+ assert(!system_file.isSymlink);
+
+ immutable symfile = deleteme ~ "_slink\0";
+ scope(exit) if (symfile.exists) symfile.remove();
+
+ symlink(system_file, symfile);
+
+ assert(symfile.exists);
+ assert(symfile.isSymlink);
+ assert(!attrIsSymlink(getAttributes(symfile)));
+ assert(attrIsSymlink(getLinkAttributes(symfile)));
+
+ assert(!attrIsDir(getAttributes(symfile)));
+ assert(!attrIsDir(getLinkAttributes(symfile)));
+
+ assert(attrIsFile(getAttributes(symfile)));
+ assert(!attrIsFile(getLinkAttributes(symfile)));
+ }
+}
+
+version (Posix) @safe unittest
+{
+ static assert(__traits(compiles,
+ symlink(TestAliasedString(null), TestAliasedString(null))));
+}
+
+
+/++
+ $(BLUE This function is Posix-Only.)
+
+ Returns the path to the file pointed to by a symlink. Note that the
+ path could be either relative or absolute depending on the symlink.
+ If the path is relative, it's relative to the symlink, not the current
+ working directory.
+
+ Throws:
+ $(D FileException) on error.
+ +/
+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 if (isConvertibleToString!R)
+ {
+ return readLink!(convertToString!R)(link);
+ }
+ else
+ {
+ 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));
+
+ if (size <= dynamicBuffer.length - maxCodeUnits)
+ {
+ dynamicBuffer.length = size;
+ return () @trusted {
+ return assumeUnique(dynamicBuffer);
+ } ();
+ }
+
+ dynamicBuffer.length = dynamicBuffer.length * 3 / 2;
+ }
+
+ 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])
+ {
+ if (file.exists)
+ {
+ immutable symfile = deleteme ~ "_slink\0";
+ scope(exit) if (symfile.exists) symfile.remove();
+
+ symlink(file, symfile);
+ assert(readLink(symfile) == file, format("Failed file: %s", file));
+ }
+ }
+
+ 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.
+ * Throws: $(D FileException) on error.
+ */
+version (Windows) string getcwd()
+{
+ import std.conv : to;
+ /* GetCurrentDirectory's return value:
+ 1. function succeeds: the number of characters that are written to
+ the buffer, not including the terminating null character.
+ 2. function fails: zero
+ 3. the buffer (lpBuffer) is not large enough: the required size of
+ the buffer, in characters, including the null-terminating character.
+ */
+ wchar[4096] buffW = void; //enough for most common case
+ 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)
+ {
+ return buffW[0 .. n].to!string;
+ }
+ else //staticBuff isn't enough
+ {
+ auto ptr = cast(wchar*) malloc(wchar.sizeof * n);
+ scope(exit) free(ptr);
+ immutable n2 = GetCurrentDirectoryW(n, ptr);
+ cenforce(n2 && n2 < n, "getcwd");
+ return ptr[0 .. n2].to!string;
+ }
+}
+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),
+ "cannot get cwd");
+ scope(exit) core.stdc.stdlib.free(p);
+ return p[0 .. core.stdc.string.strlen(p)].idup;
+}
+
+@system unittest
+{
+ auto s = getcwd();
+ assert(s.length);
+}
+
+version (OSX)
+ private extern (C) int _NSGetExecutablePath(char* buf, uint* bufsize);
+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:
+ * $(REF1 Exception, object)
+ */
+@trusted string thisExePath ()
+{
+ version (OSX)
+ {
+ import core.sys.posix.stdlib : realpath;
+ import std.conv : to;
+ import std.exception : errnoEnforce;
+
+ uint size;
+
+ _NSGetExecutablePath(null, &size); // get the length of the path
+ auto buffer = new char[size];
+ _NSGetExecutablePath(buffer.ptr, &size);
+
+ auto absolutePath = realpath(buffer.ptr, null); // let the function allocate
+
+ scope (exit)
+ {
+ if (absolutePath)
+ free(absolutePath);
+ }
+
+ errnoEnforce(absolutePath);
+ return to!(string)(absolutePath);
+ }
+ else version (linux)
+ {
+ return readLink("/proc/self/exe");
+ }
+ else version (Windows)
+ {
+ import std.conv : to;
+ import std.exception : enforce;
+
+ wchar[MAX_PATH] buf;
+ wchar[] buffer = buf[];
+
+ while (true)
+ {
+ auto len = GetModuleFileNameW(null, buffer.ptr, cast(DWORD) buffer.length);
+ enforce(len, sysErrorString(GetLastError()));
+ if (len != buffer.length)
+ return to!(string)(buffer[0 .. len]);
+ buffer.length *= 2;
+ }
+ }
+ else version (FreeBSD)
+ {
+ import std.exception : errnoEnforce, assumeUnique;
+ enum
+ {
+ CTL_KERN = 1,
+ KERN_PROC = 14,
+ KERN_PROC_PATHNAME = 12
+ }
+
+ int[4] mib = [CTL_KERN, KERN_PROC, KERN_PROC_PATHNAME, -1];
+ size_t len;
+
+ auto result = sysctl(mib.ptr, mib.length, null, &len, null, 0); // get the length of the path
+ errnoEnforce(result == 0);
+
+ auto buffer = new char[len - 1];
+ result = sysctl(mib.ptr, mib.length, buffer.ptr, &len, null, 0);
+ errnoEnforce(result == 0);
+
+ return buffer.assumeUnique;
+ }
+ else version (NetBSD)
+ {
+ return readLink("/proc/self/exe");
+ }
+ else version (Solaris)
+ {
+ import core.sys.posix.unistd : getpid;
+ import std.string : format;
+
+ // Only Solaris 10 and later
+ return readLink(format("/proc/%d/path/a.out", getpid()));
+ }
+ else
+ static assert(0, "thisExePath is not supported on this platform");
+}
+
+@safe unittest
+{
+ import std.path : isAbsolute;
+ auto path = thisExePath();
+
+ assert(path.exists);
+ assert(path.isAbsolute);
+ assert(path.isFile);
+}
+
+version (StdDdoc)
+{
+ /++
+ Info on a file, similar to what you'd get from stat on a Posix system.
+ +/
+ struct DirEntry
+ {
+ /++
+ Constructs a $(D DirEntry) for the given file (or directory).
+
+ Params:
+ path = The file (or directory) to get a DirEntry for.
+
+ Throws:
+ $(D FileException) if the file does not exist.
+ +/
+ this(string path);
+
+ version (Windows)
+ {
+ private this(string path, in WIN32_FIND_DATAW *fd);
+ }
+ else version (Posix)
+ {
+ private this(string path, core.sys.posix.dirent.dirent* fd);
+ }
+
+ /++
+ Returns the path to the file represented by this $(D DirEntry).
+
+Example:
+--------------------
+auto de1 = DirEntry("/etc/fonts/fonts.conf");
+assert(de1.name == "/etc/fonts/fonts.conf");
+
+auto de2 = DirEntry("/usr/share/include");
+assert(de2.name == "/usr/share/include");
+--------------------
+ +/
+ @property string name() const;
+
+
+ /++
+ Returns whether the file represented by this $(D DirEntry) is a
+ directory.
+
+Example:
+--------------------
+auto de1 = DirEntry("/etc/fonts/fonts.conf");
+assert(!de1.isDir);
+
+auto de2 = DirEntry("/usr/share/include");
+assert(de2.isDir);
+--------------------
+ +/
+ @property bool isDir();
+
+
+ /++
+ Returns whether the file represented by this $(D DirEntry) is a file.
+
+ On Windows, if a file is not a directory, then it's a file. So,
+ either $(D isFile) or $(D isDir) will return $(D true).
+
+ On Posix systems, if $(D isFile) is $(D true), that indicates that
+ the file is a regular file (e.g. not a block not device). So, on
+ Posix systems, it's possible for both $(D isFile) and $(D isDir) to
+ be $(D false) for a particular file (in which case, it's a special
+ file). You can use $(D attributes) or $(D statBuf) to get more
+ information about a special file (see the stat man page for more
+ details).
+
+Example:
+--------------------
+auto de1 = DirEntry("/etc/fonts/fonts.conf");
+assert(de1.isFile);
+
+auto de2 = DirEntry("/usr/share/include");
+assert(!de2.isFile);
+--------------------
+ +/
+ @property bool isFile();
+
+ /++
+ Returns whether the file represented by this $(D DirEntry) is a
+ symbolic link.
+
+ On Windows, return $(D true) when the file is either a symbolic
+ link or a junction point.
+ +/
+ @property bool isSymlink();
+
+ /++
+ Returns the size of the the file represented by this $(D DirEntry)
+ in bytes.
+ +/
+ @property ulong size();
+
+ /++
+ $(BLUE This function is Windows-Only.)
+
+ Returns the creation time of the file represented by this
+ $(D DirEntry).
+ +/
+ @property SysTime timeCreated() const;
+
+ /++
+ Returns the time that the file represented by this $(D DirEntry) was
+ last accessed.
+
+ Note that many file systems do not update the access time for files
+ (generally for performance reasons), so there's a good chance that
+ $(D timeLastAccessed) will return the same value as
+ $(D timeLastModified).
+ +/
+ @property SysTime timeLastAccessed();
+
+ /++
+ Returns the time that the file represented by this $(D DirEntry) was
+ last modified.
+ +/
+ @property SysTime timeLastModified();
+
+ /++
+ Returns the _attributes of the file represented by this $(D DirEntry).
+
+ Note that the file _attributes on Windows and Posix systems are
+ completely different. On, Windows, they're what is returned by
+ $(D 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).
+
+ On Posix systems, if the file represented by this $(D DirEntry) is a
+ symbolic link, then _attributes are the _attributes of the file
+ pointed to by the symbolic link.
+ +/
+ @property uint attributes();
+
+ /++
+ On Posix systems, if the file represented by this $(D DirEntry) is a
+ symbolic link, then $(D linkAttributes) are the attributes of the
+ symbolic link itself. Otherwise, $(D linkAttributes) is identical to
+ $(D attributes).
+
+ On Windows, $(D linkAttributes) is identical to $(D attributes). It
+ exists on Windows so that you don't have to special-case code for
+ Windows when dealing with symbolic links.
+ +/
+ @property uint linkAttributes();
+
+ version (Windows)
+ alias stat_t = void*;
+
+ /++
+ $(BLUE This function is Posix-Only.)
+
+ The $(D stat) struct gotten from calling $(D stat).
+ +/
+ @property stat_t statBuf();
+ }
+}
+else version (Windows)
+{
+ struct DirEntry
+ {
+ public:
+ alias name this;
+
+ this(string path)
+ {
+ import std.datetime.systime : FILETIMEToSysTime;
+
+ if (!path.exists())
+ throw new FileException(path, "File does not exist");
+
+ _name = path;
+
+ with (getFileAttributesWin(path))
+ {
+ _size = makeUlong(nFileSizeLow, nFileSizeHigh);
+ _timeCreated = FILETIMEToSysTime(&ftCreationTime);
+ _timeLastAccessed = FILETIMEToSysTime(&ftLastAccessTime);
+ _timeLastModified = FILETIMEToSysTime(&ftLastWriteTime);
+ _attributes = dwFileAttributes;
+ }
+ }
+
+ private this(string path, in WIN32_FIND_DATAW *fd)
+ {
+ import core.stdc.wchar_ : wcslen;
+ import std.conv : to;
+ import std.datetime.systime : FILETIMEToSysTime;
+ import std.path : buildPath;
+
+ size_t clength = wcslen(fd.cFileName.ptr);
+ _name = buildPath(path, fd.cFileName[0 .. clength].to!string);
+ _size = (cast(ulong) fd.nFileSizeHigh << 32) | fd.nFileSizeLow;
+ _timeCreated = FILETIMEToSysTime(&fd.ftCreationTime);
+ _timeLastAccessed = FILETIMEToSysTime(&fd.ftLastAccessTime);
+ _timeLastModified = FILETIMEToSysTime(&fd.ftLastWriteTime);
+ _attributes = fd.dwFileAttributes;
+ }
+
+ @property string name() const pure nothrow
+ {
+ return _name;
+ }
+
+ @property bool isDir() const pure nothrow
+ {
+ return (attributes & FILE_ATTRIBUTE_DIRECTORY) != 0;
+ }
+
+ @property bool isFile() const pure nothrow
+ {
+ //Are there no options in Windows other than directory and file?
+ //If there are, then this probably isn't the best way to determine
+ //whether this DirEntry is a file or not.
+ return !isDir;
+ }
+
+ @property bool isSymlink() const pure nothrow
+ {
+ return (attributes & FILE_ATTRIBUTE_REPARSE_POINT) != 0;
+ }
+
+ @property ulong size() const pure nothrow
+ {
+ return _size;
+ }
+
+ @property SysTime timeCreated() const pure nothrow
+ {
+ return cast(SysTime)_timeCreated;
+ }
+
+ @property SysTime timeLastAccessed() const pure nothrow
+ {
+ return cast(SysTime)_timeLastAccessed;
+ }
+
+ @property SysTime timeLastModified() const pure nothrow
+ {
+ return cast(SysTime)_timeLastModified;
+ }
+
+ @property uint attributes() const pure nothrow
+ {
+ return _attributes;
+ }
+
+ @property uint linkAttributes() const pure nothrow
+ {
+ return _attributes;
+ }
+
+ private:
+ string _name; /// The file or directory represented by this DirEntry.
+
+ SysTime _timeCreated; /// The time when the file was created.
+ SysTime _timeLastAccessed; /// The time when the file was last accessed.
+ SysTime _timeLastModified; /// The time when the file was last modified.
+
+ ulong _size; /// The size of the file in bytes.
+ uint _attributes; /// The file attributes from WIN32_FIND_DATAW.
+ }
+}
+else version (Posix)
+{
+ struct DirEntry
+ {
+ public:
+ alias name this;
+
+ this(string path)
+ {
+ if (!path.exists)
+ throw new FileException(path, "File does not exist");
+
+ _name = path;
+
+ _didLStat = false;
+ _didStat = false;
+ _dTypeSet = false;
+ }
+
+ 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]);
+
+ _didLStat = false;
+ _didStat = false;
+
+ //fd_d_type doesn't work for all file systems,
+ //in which case the result is DT_UNKOWN. But we
+ //can determine the correct type from lstat, so
+ //we'll only set the dtype here if we could
+ //correctly determine it (not lstat in the case
+ //of DT_UNKNOWN in case we don't ever actually
+ //need the dtype, thus potentially avoiding the
+ //cost of calling lstat).
+ static if (__traits(compiles, fd.d_type != DT_UNKNOWN))
+ {
+ if (fd.d_type != DT_UNKNOWN)
+ {
+ _dType = fd.d_type;
+ _dTypeSet = true;
+ }
+ else
+ _dTypeSet = false;
+ }
+ else
+ {
+ // e.g. Solaris does not have the d_type member
+ _dTypeSet = false;
+ }
+ }
+
+ @property string name() const pure nothrow
+ {
+ return _name;
+ }
+
+ @property bool isDir()
+ {
+ _ensureStatOrLStatDone();
+
+ return (_statBuf.st_mode & S_IFMT) == S_IFDIR;
+ }
+
+ @property bool isFile()
+ {
+ _ensureStatOrLStatDone();
+
+ return (_statBuf.st_mode & S_IFMT) == S_IFREG;
+ }
+
+ @property bool isSymlink()
+ {
+ _ensureLStatDone();
+
+ return (_lstatMode & S_IFMT) == S_IFLNK;
+ }
+
+ @property ulong size()
+ {
+ _ensureStatDone();
+ return _statBuf.st_size;
+ }
+
+ @property SysTime timeStatusChanged()
+ {
+ _ensureStatDone();
+
+ return statTimeToStdTime!'c'(_statBuf);
+ }
+
+ @property SysTime timeLastAccessed()
+ {
+ _ensureStatDone();
+
+ return statTimeToStdTime!'a'(_statBuf);
+ }
+
+ @property SysTime timeLastModified()
+ {
+ _ensureStatDone();
+
+ return statTimeToStdTime!'m'(_statBuf);
+ }
+
+ @property uint attributes()
+ {
+ _ensureStatDone();
+
+ return _statBuf.st_mode;
+ }
+
+ @property uint linkAttributes()
+ {
+ _ensureLStatDone();
+
+ return _lstatMode;
+ }
+
+ @property stat_t statBuf()
+ {
+ _ensureStatDone();
+
+ return _statBuf;
+ }
+
+ private:
+ /++
+ This is to support lazy evaluation, because doing stat's is
+ expensive and not always needed.
+ +/
+ void _ensureStatDone() @safe
+ {
+ import std.exception : enforce;
+
+ static auto trustedStat(in char[] path, stat_t* buf) @trusted
+ {
+ return stat(path.tempCString(), buf);
+ }
+ if (_didStat)
+ return;
+
+ enforce(trustedStat(_name, &_statBuf) == 0,
+ "Failed to stat file `" ~ _name ~ "'");
+
+ _didStat = true;
+ }
+
+ /++
+ This is to support lazy evaluation, because doing stat's is
+ expensive and not always needed.
+
+ Try both stat and lstat for isFile and isDir
+ to detect broken symlinks.
+ +/
+ void _ensureStatOrLStatDone()
+ {
+ if (_didStat)
+ return;
+
+ if ( stat(_name.tempCString(), &_statBuf) != 0 )
+ {
+ _ensureLStatDone();
+
+ _statBuf = stat_t.init;
+ _statBuf.st_mode = S_IFLNK;
+ }
+ else
+ {
+ _didStat = true;
+ }
+ }
+
+ /++
+ This is to support lazy evaluation, because doing stat's is
+ expensive and not always needed.
+ +/
+ void _ensureLStatDone()
+ {
+ import std.exception : enforce;
+
+ if (_didLStat)
+ return;
+
+ stat_t statbuf = void;
+
+ enforce(lstat(_name.tempCString(), &statbuf) == 0,
+ "Failed to stat file `" ~ _name ~ "'");
+
+ _lstatMode = statbuf.st_mode;
+
+ _dTypeSet = true;
+ _didLStat = true;
+ }
+
+ string _name; /// The file or directory represented by this DirEntry.
+
+ stat_t _statBuf = void; /// The result of stat().
+ uint _lstatMode; /// The stat mode from lstat().
+ ubyte _dType; /// The type of the file.
+
+ bool _didLStat = false; /// Whether lstat() has been called for this DirEntry.
+ bool _didStat = false; /// Whether stat() has been called for this DirEntry.
+ bool _dTypeSet = false; /// Whether the dType of the file has been set.
+ }
+}
+
+@system unittest
+{
+ version (Windows)
+ {
+ if ("C:\\Program Files\\".exists)
+ {
+ auto de = DirEntry("C:\\Program Files\\");
+ assert(!de.isFile);
+ assert(de.isDir);
+ assert(!de.isSymlink);
+ }
+
+ 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)
+ {
+ auto de = DirEntry("C:\\Windows\\system.ini");
+ assert(de.isFile);
+ assert(!de.isDir);
+ assert(!de.isSymlink);
+ }
+ }
+ else version (Posix)
+ {
+ import std.exception : assertThrown;
+
+ if (system_directory.exists)
+ {
+ {
+ auto de = DirEntry(system_directory);
+ assert(!de.isFile);
+ assert(de.isDir);
+ assert(!de.isSymlink);
+ }
+
+ immutable symfile = deleteme ~ "_slink\0";
+ scope(exit) if (symfile.exists) symfile.remove();
+
+ core.sys.posix.unistd.symlink(system_directory, symfile.ptr);
+
+ {
+ auto de = DirEntry(symfile);
+ assert(!de.isFile);
+ assert(de.isDir);
+ assert(de.isSymlink);
+ }
+
+ symfile.remove();
+ core.sys.posix.unistd.symlink((deleteme ~ "_broken_symlink\0").ptr, symfile.ptr);
+
+ {
+ //Issue 8298
+ DirEntry de = DirEntry(symfile);
+
+ assert(!de.isFile);
+ assert(!de.isDir);
+ assert(de.isSymlink);
+ assertThrown(de.size);
+ assertThrown(de.timeStatusChanged);
+ assertThrown(de.timeLastAccessed);
+ assertThrown(de.timeLastModified);
+ assertThrown(de.attributes);
+ assertThrown(de.statBuf);
+ assert(symfile.exists);
+ symfile.remove();
+ }
+ }
+
+ if (system_file.exists)
+ {
+ auto de = DirEntry(system_file);
+ assert(de.isFile);
+ assert(!de.isDir);
+ assert(!de.isSymlink);
+ }
+ }
+}
+
+alias PreserveAttributes = Flag!"preserveAttributes";
+
+version (StdDdoc)
+{
+ /// Defaults to $(D Yes.preserveAttributes) on Windows, and the opposite on all other platforms.
+ PreserveAttributes preserveAttributesDefault;
+}
+else version (Windows)
+{
+ enum preserveAttributesDefault = Yes.preserveAttributes;
+}
+else
+{
+ 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 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(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.preserveAttributes);
+ immutable result = CopyFileW(fromz, toz, false);
+ if (!result)
+ {
+ 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)
+ {
+ static import core.stdc.stdio;
+ import std.conv : to, octal;
+
+ 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 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");
+ }
+
+ 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);
+ if (!buf)
+ {
+ import core.exception : onOutOfMemoryError;
+ onOutOfMemoryError();
+ }
+ }
+ scope(exit) core.stdc.stdlib.free(buf);
+
+ for (auto size = statbufr.st_size; size; )
+ {
+ immutable toxfer = (size > BUFSIZ) ? BUFSIZ : cast(size_t) size;
+ cenforce(
+ core.sys.posix.unistd.read(fdr, buf, toxfer) == toxfer
+ && core.sys.posix.unistd.write(fdw, buf, toxfer) == toxfer,
+ f, fromz);
+ assert(size >= toxfer);
+ size -= toxfer;
+ }
+ if (preserve)
+ cenforce(fchmod(fdw, to!mode_t(statbufr.st_mode)) == 0, f, fromz);
+ }
+
+ cenforce(core.sys.posix.unistd.close(fdw) != -1, f, fromz);
+
+ utimbuf utim = void;
+ utim.actime = cast(time_t) statbufr.st_atime;
+ utim.modtime = cast(time_t) statbufr.st_mtime;
+
+ cenforce(utime(toz, &utim) != -1, f, fromz);
+ }
+}
+
+@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, "11");
+ copy(t1, t2);
+ 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");
+}
+
+@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");
+ setAttributes(t1, octal!767);
+ copy(t1, t2, Yes.preserveAttributes);
+ assert(readText(t2) == "1");
+ 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.
+
+ Throws:
+ $(D FileException) if there is an error (including if the given
+ file is not a directory).
+ +/
+void rmdirRecurse(in char[] pathname)
+{
+ //No references to pathname will be kept after rmdirRecurse,
+ //so the cast is safe
+ rmdirRecurse(DirEntry(cast(string) pathname));
+}
+
+/++
+ Remove directory and all of its content and subdirectories,
+ recursively.
+
+ Throws:
+ $(D FileException) if there is an error (including if the given
+ file is not a directory).
+ +/
+void rmdirRecurse(ref DirEntry de)
+{
+ if (!de.isDir)
+ throw new FileException(de.name, "Not a directory");
+
+ if (de.isSymlink)
+ {
+ version (Windows)
+ rmdir(de.name);
+ else
+ remove(de.name);
+ }
+ else
+ {
+ // all children, recursively depth-first
+ foreach (DirEntry e; dirEntries(de.name, SpanMode.depth, false))
+ {
+ attrIsDir(e.linkAttributes) ? rmdir(e.name) : remove(e.name);
+ }
+
+ // the dir itself
+ rmdir(de.name);
+ }
+}
+///ditto
+//Note, without this overload, passing an RValue DirEntry still works, but
+//actually fully reconstructs a DirEntry inside the
+//"rmdirRecurse(in char[] pathname)" implementation. That is needlessly
+//expensive.
+//A DirEntry is a bit big (72B), so keeping the "by ref" signature is desirable.
+void rmdirRecurse(DirEntry de)
+{
+ rmdirRecurse(de);
+}
+
+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) @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)));
+ mkdirRecurse(d);
+ core.sys.posix.unistd.symlink((deleteme~"/a/b/c\0").ptr,
+ (deleteme~"/link\0").ptr);
+ rmdirRecurse(deleteme~"/link");
+ enforce(exists(d));
+ rmdirRecurse(deleteme);
+ enforce(!exists(deleteme));
+
+ d = deleteme~"/a/b/c/d/e/f/g";
+ mkdirRecurse(d);
+ version (Android) string link_cmd = "ln -s ";
+ else string link_cmd = "ln -sf ";
+ executeShell(link_cmd~deleteme~"/a/b/c "~deleteme~"/link");
+ rmdirRecurse(deleteme);
+ enforce(!exists(deleteme));
+}
+
+@system unittest
+{
+ void[] buf;
+
+ buf = new void[10];
+ (cast(byte[]) buf)[] = 3;
+ string unit_file = deleteme ~ "-unittest_write.tmp";
+ if (exists(unit_file)) remove(unit_file);
+ write(unit_file, buf);
+ void[] buf2 = read(unit_file);
+ assert(buf == buf2);
+
+ string unit2_file = deleteme ~ "-unittest_write2.tmp";
+ copy(unit_file, unit2_file);
+ buf2 = read(unit2_file);
+ assert(buf == buf2);
+
+ remove(unit_file);
+ assert(!exists(unit_file));
+ remove(unit2_file);
+ assert(!exists(unit2_file));
+}
+
+/**
+ * Dictates directory spanning policy for $(D_PARAM dirEntries) (see below).
+ */
+enum SpanMode
+{
+ /** Only spans one directory. */
+ shallow,
+ /** Spans the directory in
+ $(HTTPS en.wikipedia.org/wiki/Tree_traversal#Post-order,
+ _depth-first $(B post)-order), i.e. the content of any
+ subdirectory is spanned before that subdirectory itself. Useful
+ e.g. when recursively deleting files. */
+ depth,
+ /** Spans the directory in
+ $(HTTPS en.wikipedia.org/wiki/Tree_traversal#Pre-order, depth-first
+ $(B pre)-order), i.e. the content of any subdirectory is spanned
+ right after that subdirectory itself.
+
+ Note that $(D SpanMode.breadth) will not result in all directory
+ members occurring before any subdirectory members, i.e. it is not
+ _true
+ $(HTTPS en.wikipedia.org/wiki/Tree_traversal#Breadth-first_search,
+ _breadth-first traversal).
+ */
+ breadth,
+}
+
+private struct DirIteratorImpl
+{
+ import std.array : Appender, appender;
+ SpanMode _mode;
+ // Whether we should follow symlinked directories while iterating.
+ // It also indicates whether we should avoid functions which call
+ // stat (since we should only need lstat in this case and it would
+ // be more efficient to not call stat in addition to lstat).
+ bool _followSymlink;
+ DirEntry _cur;
+ Appender!(DirHandle[]) _stack;
+ Appender!(DirEntry[]) _stashed; //used in depth first mode
+ //stack helpers
+ void pushExtra(DirEntry de){ _stashed.put(de); }
+ //ditto
+ bool hasExtra(){ return !_stashed.data.empty; }
+ //ditto
+ DirEntry popExtra()
+ {
+ DirEntry de;
+ de = _stashed.data[$-1];
+ _stashed.shrinkTo(_stashed.data.length - 1);
+ return de;
+
+ }
+ version (Windows)
+ {
+ struct DirHandle
+ {
+ string dirpath;
+ HANDLE h;
+ }
+
+ bool stepIn(string directory)
+ {
+ import std.path : chainPath;
+
+ auto search_pattern = chainPath(directory, "*.*");
+ WIN32_FIND_DATAW findinfo;
+ HANDLE h = FindFirstFileW(search_pattern.tempCString!FSChar(), &findinfo);
+ cenforce(h != INVALID_HANDLE_VALUE, directory);
+ _stack.put(DirHandle(directory, h));
+ return toNext(false, &findinfo);
+ }
+
+ bool next()
+ {
+ if (_stack.data.empty)
+ return false;
+ WIN32_FIND_DATAW findinfo;
+ return toNext(true, &findinfo);
+ }
+
+ bool toNext(bool fetch, WIN32_FIND_DATAW* findinfo)
+ {
+ import core.stdc.wchar_ : wcscmp;
+
+ if (fetch)
+ {
+ if (FindNextFileW(_stack.data[$-1].h, findinfo) == FALSE)
+ {
+ popDirStack();
+ return false;
+ }
+ }
+ while ( wcscmp(findinfo.cFileName.ptr, ".") == 0
+ || wcscmp(findinfo.cFileName.ptr, "..") == 0)
+ if (FindNextFileW(_stack.data[$-1].h, findinfo) == FALSE)
+ {
+ popDirStack();
+ return false;
+ }
+ _cur = DirEntry(_stack.data[$-1].dirpath, findinfo);
+ return true;
+ }
+
+ void popDirStack()
+ {
+ assert(!_stack.data.empty);
+ FindClose(_stack.data[$-1].h);
+ _stack.shrinkTo(_stack.data.length-1);
+ }
+
+ void releaseDirStack()
+ {
+ foreach ( d; _stack.data)
+ FindClose(d.h);
+ }
+
+ bool mayStepIn()
+ {
+ return _followSymlink ? _cur.isDir : _cur.isDir && !_cur.isSymlink;
+ }
+ }
+ else version (Posix)
+ {
+ struct DirHandle
+ {
+ string dirpath;
+ DIR* h;
+ }
+
+ bool stepIn(string 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)
+ return false;
+ for (dirent* fdata; (fdata = readdir(_stack.data[$-1].h)) != null; )
+ {
+ // Skip "." and ".."
+ if (core.stdc.string.strcmp(fdata.d_name.ptr, ".") &&
+ core.stdc.string.strcmp(fdata.d_name.ptr, "..") )
+ {
+ _cur = DirEntry(_stack.data[$-1].dirpath, fdata);
+ return true;
+ }
+ }
+ popDirStack();
+ return false;
+ }
+
+ void popDirStack()
+ {
+ assert(!_stack.data.empty);
+ closedir(_stack.data[$-1].h);
+ _stack.shrinkTo(_stack.data.length-1);
+ }
+
+ void releaseDirStack()
+ {
+ foreach ( d; _stack.data)
+ closedir(d.h);
+ }
+
+ bool mayStepIn()
+ {
+ return _followSymlink ? _cur.isDir : attrIsDir(_cur.linkAttributes);
+ }
+ }
+
+ 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)
+ _stashed = appender(cast(DirEntry[])[]);
+
+ 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())
+ {
+ auto thisDir = _cur;
+ if (stepIn(_cur.name))
+ {
+ pushExtra(thisDir);
+ }
+ else
+ break;
+ }
+ }
+ }
+ @property bool empty(){ return _stashed.data.empty && _stack.data.empty; }
+ @property DirEntry front(){ return _cur; }
+ void popFront()
+ {
+ switch (_mode)
+ {
+ case SpanMode.depth:
+ if (next())
+ {
+ while (mayStepIn())
+ {
+ auto thisDir = _cur;
+ if (stepIn(_cur.name))
+ {
+ pushExtra(thisDir);
+ }
+ else
+ break;
+ }
+ }
+ else if (hasExtra())
+ _cur = popExtra();
+ break;
+ case SpanMode.breadth:
+ if (mayStepIn())
+ {
+ if (!stepIn(_cur.name))
+ while (!empty && !next()){}
+ }
+ else
+ while (!empty && !next()){}
+ break;
+ default:
+ next();
+ }
+ }
+
+ ~this()
+ {
+ releaseDirStack();
+ }
+}
+
+struct DirIterator
+{
+private:
+ RefCounted!(DirIteratorImpl, RefCountedAutoInitialize.no) impl;
+ this(string pathname, SpanMode mode, bool followSymlink)
+ {
+ impl = typeof(impl)(pathname, mode, followSymlink);
+ }
+public:
+ @property bool empty(){ return impl.empty; }
+ @property DirEntry front(){ return impl.front; }
+ void popFront(){ impl.popFront(); }
+
+}
+/++
+ Returns an input range of $(D DirEntry) that lazily iterates a given directory,
+ also provides two ways of foreach iteration. The iteration variable can be of
+ type $(D string) if only the name is needed, or $(D DirEntry)
+ 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.
+
+ pattern = Optional string with wildcards, such as $(RED
+ "*.d"). When present, it is used to filter the
+ results by their file name. The supported wildcard
+ strings are described under $(REF globMatch,
+ std,_path).
+
+ mode = Whether the directory's sub-directories should be
+ iterated in depth-first port-order ($(LREF depth)),
+ depth-first pre-order ($(LREF breadth)), or not at all
+ ($(LREF shallow)).
+
+ followSymlink = Whether symbolic links which point to directories
+ should be treated as directories and their contents
+ iterated over.
+
+ Throws:
+ $(D FileException) if the directory does not exist.
+
+Example:
+--------------------
+// Iterate a directory in depth
+foreach (string name; dirEntries("destroy/me", SpanMode.depth))
+{
+ remove(name);
+}
+
+// Iterate the current directory in breadth
+foreach (string name; dirEntries("", SpanMode.breadth))
+{
+ 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);
+}
+
+// Iterate over all *.d files in current directory and all its subdirectories
+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
+{
+ string cmd = "dmd -c " ~ d.name;
+ writeln(cmd);
+ std.process.system(cmd);
+}
+
+// Iterate over all D source files in current directory and all its
+// subdirectories
+auto dFiles = dirEntries("","*.{d,di}",SpanMode.depth);
+foreach (d; dFiles)
+ writeln(d.name);
+--------------------
+ +/
+auto dirEntries(string path, SpanMode mode, bool followSymlink = true)
+{
+ return DirIterator(path, mode, followSymlink);
+}
+
+/// Duplicate functionality of D1's $(D std.file.listdir()):
+@safe unittest
+{
+ string[] listdir(string pathname)
+ {
+ import std.algorithm;
+ import std.array;
+ import std.file;
+ import std.path;
+
+ return std.file.dirEntries(pathname, SpanMode.shallow)
+ .filter!(a => a.isFile)
+ .map!(a => std.path.baseName(a.name))
+ .array;
+ }
+
+ void main(string[] args)
+ {
+ import std.stdio;
+
+ string[] files = listdir(args[1]);
+ writefln("%s", files);
+ }
+}
+
+@system unittest
+{
+ 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,
+ // ie an apk
+ else
+ string testdir = "deleteme.dmd.unittest.std.file" ~ to!string(thisProcessID); // needs to be relative
+ mkdirRecurse(buildPath(testdir, "somedir"));
+ scope(exit) rmdirRecurse(testdir);
+ write(buildPath(testdir, "somefile"), null);
+ write(buildPath(testdir, "somedir", "somedeepfile"), null);
+
+ // 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 => absolutePath(a.name))(dirEntries(relpath, mode)),
+ map!(a => a.name)(dirEntries(absolutePath(relpath), mode))));
+ return len;
+ }
+
+ assert(equalEntries(testdir, SpanMode.shallow) == 2);
+ assert(equalEntries(testdir, SpanMode.depth) == 3);
+ assert(equalEntries(testdir, SpanMode.breadth) == 3);
+
+ // testing opApply
+ foreach (string name; dirEntries(testdir, SpanMode.breadth))
+ {
+ //writeln(name);
+ assert(name.startsWith(testdir));
+ }
+ foreach (DirEntry e; dirEntries(absolutePath(testdir), SpanMode.breadth))
+ {
+ //writeln(name);
+ assert(e.isFile || e.isDir, e.name);
+ }
+
+ //issue 7264
+ foreach (string name; dirEntries(testdir, "*.d", SpanMode.breadth))
+ {
+
+ }
+ foreach (entry; dirEntries(testdir, SpanMode.breadth))
+ {
+ static assert(is(typeof(entry) == DirEntry));
+ }
+ //issue 7138
+ auto a = array(dirEntries(testdir, SpanMode.shallow));
+
+ // issue 11392
+ auto dFiles = dirEntries(testdir, SpanMode.shallow);
+ foreach (d; dFiles){}
+
+ // issue 15146
+ dirEntries("", SpanMode.shallow).walkLength();
+}
+
+/// Ditto
+auto dirEntries(string path, string pattern, SpanMode mode,
+ bool followSymlink = true)
+{
+ 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));
+}
+
+@system unittest
+{
+ import std.stdio : writefln;
+ immutable dpath = deleteme ~ "_dir";
+ immutable fpath = deleteme ~ "_file";
+ immutable sdpath = deleteme ~ "_sdir";
+ immutable sfpath = deleteme ~ "_sfile";
+ scope(exit)
+ {
+ if (dpath.exists) rmdirRecurse(dpath);
+ if (fpath.exists) remove(fpath);
+ if (sdpath.exists) remove(sdpath);
+ if (sfpath.exists) remove(sfpath);
+ }
+
+ mkdir(dpath);
+ write(fpath, "hello world");
+ version (Posix)
+ {
+ core.sys.posix.unistd.symlink((dpath ~ '\0').ptr, (sdpath ~ '\0').ptr);
+ core.sys.posix.unistd.symlink((fpath ~ '\0').ptr, (sfpath ~ '\0').ptr);
+ }
+
+ static struct Flags { bool dir, file, link; }
+ auto tests = [dpath : Flags(true), fpath : Flags(false, true)];
+ version (Posix)
+ {
+ tests[sdpath] = Flags(true, false, true);
+ tests[sfpath] = Flags(false, true, true);
+ }
+
+ auto past = Clock.currTime() - 2.seconds;
+ auto future = past + 4.seconds;
+
+ foreach (path, flags; tests)
+ {
+ auto de = DirEntry(path);
+ assert(de.name == path);
+ assert(de.isDir == flags.dir);
+ assert(de.isFile == flags.file);
+ assert(de.isSymlink == flags.link);
+
+ assert(de.isDir == path.isDir);
+ assert(de.isFile == path.isFile);
+ assert(de.isSymlink == path.isSymlink);
+ assert(de.size == path.getSize());
+ assert(de.attributes == getAttributes(path));
+ assert(de.linkAttributes == getLinkAttributes(path));
+
+ scope(failure) writefln("[%s] [%s] [%s] [%s]", past, de.timeLastAccessed, de.timeLastModified, future);
+ assert(de.timeLastAccessed > past);
+ assert(de.timeLastAccessed < future);
+ assert(de.timeLastModified > past);
+ assert(de.timeLastModified < future);
+
+ assert(attrIsDir(de.attributes) == flags.dir);
+ assert(attrIsDir(de.linkAttributes) == (flags.dir && !flags.link));
+ assert(attrIsFile(de.attributes) == flags.file);
+ assert(attrIsFile(de.linkAttributes) == (flags.file && !flags.link));
+ assert(!attrIsSymlink(de.attributes));
+ assert(attrIsSymlink(de.linkAttributes) == flags.link);
+
+ version (Windows)
+ {
+ assert(de.timeCreated > past);
+ assert(de.timeCreated < future);
+ }
+ else version (Posix)
+ {
+ assert(de.timeStatusChanged > past);
+ assert(de.timeStatusChanged < future);
+ assert(de.attributes == de.statBuf.st_mode);
+ }
+ }
+}
+
+
+/**
+ * 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 std, _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)
+{
+ import std.array : appender;
+ import std.conv : text;
+ import std.exception : enforce;
+ import std.format : formattedRead;
+ import std.stdio : File;
+
+ auto app = appender!(typeof(return))();
+ ElementType!(typeof(return)) toAdd;
+ auto f = File(filename);
+ scope(exit) f.close();
+ foreach (line; f.byLine())
+ {
+ formattedRead(line, format, &toAdd);
+ enforce(line.empty,
+ text("Trailing characters at the end of line: `", line,
+ "'"));
+ app.put(toAdd);
+ }
+ return app.data;
+}
+
+///
+@system unittest
+{
+ import std.typecons : tuple;
+
+ 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));
+ assert(a[1] == tuple(345, 1.125));
+}
+
+
+/**
+Returns the path to a directory for temporary files.
+
+On Windows, this function returns the result of calling the Windows API function
+$(LINK2 http://msdn.microsoft.com/en-us/library/windows/desktop/aa364992.aspx, $(D GetTempPath)).
+
+On POSIX platforms, it searches through the following list of directories
+and returns the first one which is found to exist:
+$(OL
+ $(LI The directory given by the $(D TMPDIR) environment variable.)
+ $(LI The directory given by the $(D TEMP) environment variable.)
+ $(LI The directory given by the $(D TMP) environment variable.)
+ $(LI $(D /tmp))
+ $(LI $(D /var/tmp))
+ $(LI $(D /usr/tmp))
+)
+
+On all platforms, $(D tempDir) returns $(D ".") on failure, representing
+the current working directory.
+
+The return value of the function is cached, so the procedures described
+above will only be performed the first time the function is called. All
+subsequent runs will return the same string, regardless of whether
+environment variables and directory structures have changed in the
+meantime.
+
+The POSIX $(D tempDir) algorithm is inspired by Python's
+$(LINK2 http://docs.python.org/library/tempfile.html#tempfile.tempdir, $(D tempfile.tempdir)).
+*/
+string tempDir() @trusted
+{
+ static string cache;
+ if (cache is null)
+ {
+ version (Windows)
+ {
+ import std.conv : to;
+ // http://msdn.microsoft.com/en-us/library/windows/desktop/aa364992(v=vs.85).aspx
+ wchar[MAX_PATH + 2] buf;
+ DWORD len = GetTempPathW(buf.length, buf.ptr);
+ if (len) cache = buf[0 .. len].to!string;
+ }
+ else version (Android)
+ {
+ // Don't check for a global temporary directory as
+ // Android doesn't have one.
+ }
+ else version (Posix)
+ {
+ import std.process : environment;
+ // This function looks through the list of alternative directories
+ // and returns the first one which exists and is a directory.
+ static string findExistingDir(T...)(lazy T alternatives)
+ {
+ foreach (dir; alternatives)
+ if (!dir.empty && exists(dir)) return dir;
+ return null;
+ }
+
+ cache = findExistingDir(environment.get("TMPDIR"),
+ environment.get("TEMP"),
+ environment.get("TMP"),
+ "/tmp",
+ "/var/tmp",
+ "/usr/tmp");
+ }
+ else static assert(false, "Unsupported platform");
+
+ if (cache is null) cache = getcwd();
+ }
+ return cache;
+}
diff --git a/libphobos/src/std/format.d b/libphobos/src/std/format.d
new file mode 100644
index 0000000..64b1bd3
--- /dev/null
+++ b/libphobos/src/std/format.d
@@ -0,0 +1,6028 @@
+// Written in the D programming language.
+
+/**
+ This module implements the formatting functionality for strings and
+ I/O. It's comparable to C99's $(D vsprintf()) and uses a similar
+ _format encoding scheme.
+
+ For an introductory look at $(B std._format)'s capabilities and how to use
+ this module see the dedicated
+ $(LINK2 http://wiki.dlang.org/Defining_custom_print_format_specifiers, DWiki article).
+
+ This module centers around two functions:
+
+$(BOOKTABLE ,
+$(TR $(TH Function Name) $(TH Description)
+)
+ $(TR $(TD $(LREF formattedRead))
+ $(TD Reads values according to the _format string from an InputRange.
+ ))
+ $(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 $(LREF formattedWrite) for a
+ description of the _format string.
+
+ Two functions have been added for convenience:
+
+$(BOOKTABLE ,
+$(TR $(TH Function Name) $(TH Description)
+)
+ $(TR $(TD $(LREF _format))
+ $(TD Returns a GC-allocated string with the formatting result.
+ ))
+ $(TR $(TD $(LREF sformat))
+ $(TD Puts the formatting result into a preallocated array.
+ ))
+)
+
+ These two functions are publicly imported by $(MREF std, string)
+ to be easily available.
+
+ The functions $(LREF formatValue) and $(LREF unformatValue) are
+ used for the plumbing.
+ Copyright: Copyright Digital Mars 2000-2013.
+
+ License: $(HTTP boost.org/LICENSE_1_0.txt, Boost License 1.0).
+
+ Authors: $(HTTP walterbright.com, Walter Bright), $(HTTP erdani.com,
+ Andrei Alexandrescu), and Kenji Hara
+
+ Source: $(PHOBOSSRC std/_format.d)
+ */
+module std.format;
+
+//debug=format; // uncomment to turn on debugging printf's
+
+import core.vararg;
+import std.exception;
+import std.meta;
+import std.range.primitives;
+import std.traits;
+
+
+/**********************************************************************
+ * Signals a mismatch between a format and its corresponding argument.
+ */
+class FormatException : Exception
+{
+ @safe pure nothrow
+ this()
+ {
+ super("format error");
+ }
+
+ @safe pure nothrow
+ this(string msg, string fn = __FILE__, size_t ln = __LINE__, Throwable next = null)
+ {
+ super(msg, fn, ln, next);
+ }
+}
+
+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 $(D $(REF isOutputRange, std,range,primitives)!(Writer, Char)).
+
+ The variadic arguments are normally consumed in order. POSIX-style
+ $(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
+ arguments as specified in the format string are consumed and
+ formatted. If there are fewer arguments than format specifiers, a
+ $(D FormatException) is thrown. If there are more remaining arguments
+ than needed by the format specification, they are ignored but only
+ if at least one argument was formatted.
+
+ The format string supports the formatting of array and nested array elements
+ via the grouping format specifiers $(B %&#40;) and $(B %&#41;). Each
+ matching pair of $(B %&#40;) and $(B %&#41;) corresponds with a single array
+ argument. The enclosed sub-format string is applied to individual array
+ elements. The trailing portion of the sub-format string following the
+ conversion specifier for the array element is interpreted as the array
+ delimiter, and is therefore omitted following the last array element. The
+ $(B %|) specifier may be used to explicitly indicate the start of the
+ delimiter, so that the preceding portion of the string will be included
+ following the last array element. (See below for explicit examples.)
+
+ Params:
+
+ w = Output is sent to this writer. Typical output writers include
+ $(REF Appender!string, std,array) and $(REF LockingTextWriter, std,stdio).
+
+ fmt = Format string.
+
+ args = Variadic argument list.
+
+ Returns: Formatted number of arguments.
+
+ Throws: Mismatched arguments and formats result in a $(D
+ FormatException) being thrown.
+
+ Format_String: <a name="format-string">$(I Format strings)</a>
+ 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.
+
+ The format string has the following grammar:
+
+$(PRE
+$(I FormatString):
+ $(I FormatStringItem)*
+$(I FormatStringItem):
+ $(B '%%')
+ $(B '%') $(I Position) $(I Flags) $(I Width) $(I Separator) $(I Precision) $(I FormatChar)
+ $(B '%$(LPAREN)') $(I FormatString) $(B '%$(RPAREN)')
+ $(I OtherCharacterExceptPercent)
+$(I Position):
+ $(I empty)
+ $(I Integer) $(B '$')
+$(I Flags):
+ $(I empty)
+ $(B '-') $(I Flags)
+ $(B '+') $(I Flags)
+ $(B '#') $(I Flags)
+ $(B '0') $(I Flags)
+ $(B ' ') $(I Flags)
+$(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 '.')
+ $(B '.') $(I Integer)
+ $(B '.*')
+$(I Integer):
+ $(I Digit)
+ $(I Digit) $(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 '|')
+)
+
+ $(BOOKTABLE Flags affect formatting depending on the specifier as
+ follows., $(TR $(TH Flag) $(TH Types&nbsp;affected) $(TH Semantics))
+
+ $(TR $(TD $(B '-')) $(TD numeric) $(TD Left justify the result in
+ the field. It overrides any $(B 0) flag.))
+
+ $(TR $(TD $(B '+')) $(TD numeric) $(TD Prefix positive numbers in
+ a signed conversion with a $(B +). It overrides any $(I space)
+ flag.))
+
+ $(TR $(TD $(B '#')) $(TD integral ($(B 'o'))) $(TD 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.))
+
+ $(TR $(TD $(B '#')) $(TD integral ($(B 'x'), $(B 'X'))) $(TD If
+ non-zero, prefix result with $(B 0x) ($(B 0X)).))
+
+ $(TR $(TD $(B '#')) $(TD floating) $(TD Always insert the decimal
+ point and print trailing zeros.))
+
+ $(TR $(TD $(B '0')) $(TD numeric) $(TD Use leading
+ zeros to pad rather than spaces (except for the floating point
+ values $(D nan) and $(D infinity)). Ignore if there's a $(I
+ Precision).))
+
+ $(TR $(TD $(B ' ')) $(TD numeric) $(TD Prefix positive
+ numbers in a signed conversion with a space.)))
+
+ $(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.)
+
+ $(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.)
+
+ $(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:
+ $(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.)
+ $(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(field<sub>0</sub>, field<sub>1</sub>, ...) where
+ field<sub>n</sub> is the nth element formatted with the default
+ 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.)
+ $(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.)
+ $(DT non-string static and dynamic arrays)
+ $(DD The result is [s<sub>0</sub>, s<sub>1</sub>, ...]
+ where s<sub>n</sub> is the nth element
+ 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].)
+ ))
+
+ $(DT $(B 'c'))
+ $(DD The corresponding argument must be a character 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
+ 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.)
+
+ $(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),
+ &plusmn;, followed by at least a two digit exponent:
+ $(I d.dddddd)e$(I &plusmn;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.)
+
+ $(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.)
+
+ $(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
+ 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.)
+
+ $(DT $(B 'a','A'))
+ $(DD A floating point number is formatted in hexadecimal
+ exponential notation 0x$(I h.hhhhhh)p$(I &plusmn;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<sup>$(I &plusmn;d)</sup>.
+ The exponent for zero is zero.
+ The hexadecimal digits, x and p are in upper case if the
+ $(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.
+ 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.
+
+ 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
+ the largest positional parameter already used.
+
+ Example using array and nested array formatting:
+ -------------------------
+ import std.stdio;
+
+ void main()
+ {
+ writefln("My items are %(%s %).", [1,2,3]);
+ writefln("My items are %(%s, %).", [1,2,3]);
+ }
+ -------------------------
+ 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
+ following the last array item. The $(B %|) delimiter specifier may be used
+ to indicate where the delimiter begins, so that the portion of the format
+ string prior to it will be retained in the last array element:
+ -------------------------
+ import std.stdio;
+
+ void main()
+ {
+ writefln("My items are %(-%s-%|, %).", [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:
+ -------------------------
+ import std.stdio;
+ void main() {
+ auto mat = [[1, 2, 3],
+ [4, 5, 6],
+ [7, 8, 9]];
+
+ writefln("%(%(%d %)\n%)", mat);
+ writeln();
+
+ writefln("[%(%(%d %)\n %)]", mat);
+ writeln();
+
+ writefln("[%([%(%d %)]%|\n %)]", mat);
+ writeln();
+ }
+ -------------------------
+ The output is:
+$(CONSOLE
+1 2 3
+4 5 6
+7 8 9
+
+[1 2 3
+ 4 5 6
+ 7 8 9]
+
+[[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
+ $(D "%$(LPAREN)").
+ -------------------------
+ import std.stdio;
+
+ void main()
+ {
+ writefln("My friends are %s.", ["John", "Nancy"]);
+ writefln("My friends are %(%s, %).", ["John", "Nancy"]);
+ writefln("My friends are %-(%s, %).", ["John", "Nancy"]);
+ }
+ -------------------------
+ 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...)(auto ref 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...)(auto ref Writer w, in Char[] fmt, A args)
+{
+ import std.conv : text;
+
+ auto spec = FormatSpec!Char(fmt);
+
+ // Are we already done with formats? Then just dump each parameter in turn
+ uint currentArg = 0;
+ while (spec.writeUpToNextSpec(w))
+ {
+ if (currentArg == A.length && !spec.indexStart)
+ {
+ // leftover spec?
+ enforceFmt(fmt.length == 0,
+ text("Orphan format specifier: %", spec.spec));
+ break;
+ }
+
+ if (spec.width == spec.DYNAMIC)
+ {
+ auto width = getNthInt!"integer width"(currentArg, args);
+ if (width < 0)
+ {
+ spec.flDash = true;
+ width = -width;
+ }
+ spec.width = width;
+ ++currentArg;
+ }
+ else if (spec.width < 0)
+ {
+ // means: get width as a positional parameter
+ auto index = cast(uint) -spec.width;
+ assert(index > 0);
+ auto width = getNthInt!"integer width"(index - 1, args);
+ if (currentArg < index) currentArg = index;
+ if (width < 0)
+ {
+ spec.flDash = true;
+ width = -width;
+ }
+ spec.width = width;
+ }
+
+ if (spec.precision == spec.DYNAMIC)
+ {
+ auto precision = getNthInt!"integer precision"(currentArg, args);
+ if (precision >= 0) spec.precision = precision;
+ // else negative precision is same as no precision
+ else spec.precision = spec.UNSPECIFIED;
+ ++currentArg;
+ }
+ else if (spec.precision < 0)
+ {
+ // means: get precision as a positional parameter
+ auto index = cast(uint) -spec.precision;
+ assert(index > 0);
+ auto precision = getNthInt!"integer precision"(index- 1, args);
+ if (currentArg < index) currentArg = index;
+ if (precision >= 0) spec.precision = precision;
+ // else negative precision is same as no precision
+ else spec.precision = spec.UNSPECIFIED;
+ }
+
+ if (spec.separators == spec.DYNAMIC)
+ {
+ auto separators = getNthInt!"separator digit width"(currentArg, args);
+ spec.separators = separators;
+ ++currentArg;
+ }
+
+ if (spec.separatorCharPos == spec.DYNAMIC)
+ {
+ auto separatorChar =
+ getNth!("separator character", 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 an argument
+ // This switch uses a static foreach to generate a jump table.
+ // Currently `spec.indexStart` use the special value '0' to signal
+ // we should use the current argument. An enhancement would be to
+ // always store the index.
+ size_t index = currentArg;
+ if (spec.indexStart != 0)
+ index = spec.indexStart - 1;
+ else
+ ++currentArg;
+ SWITCH: switch (index)
+ {
+ foreach (i, Tunused; A)
+ {
+ case i:
+ formatValue(w, args[i], spec);
+ if (currentArg < spec.indexEnd)
+ currentArg = spec.indexEnd;
+ // A little know feature of format is to format a range
+ // of arguments, e.g. `%1:3$` will format the first 3
+ // arguments. Since they have to be consecutive we can
+ // just use explicit fallthrough to cover that case.
+ if (i + 1 < spec.indexEnd)
+ {
+ // You cannot goto case if the next case is the default
+ static if (i + 1 < A.length)
+ goto case;
+ else
+ goto default;
+ }
+ else
+ break SWITCH;
+ }
+ default:
+ throw new FormatException(
+ text("Positional specifier %", spec.indexStart, '$', spec.spec,
+ " index exceeds ", A.length));
+ }
+ }
+ 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;
+ auto w = appender!string();
+ formattedWrite(w, "%s %d", "@safe/pure", 42);
+ assert(w.data == "@safe/pure 42");
+}
+
+/**
+Reads characters from input range $(D r), converts them according
+to $(D fmt), and writes them to $(D args).
+
+Params:
+ r = The range to read from.
+ fmt = The format of the data to read.
+ args = The drain of the data read.
+
+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(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;
+
+ auto spec = FormatSpec!Char(fmt);
+ static if (!S.length)
+ {
+ spec.readUpToNextSpec(r);
+ 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()
+ {
+ for (;;)
+ {
+ spec.readUpToNextSpec(r);
+ if (spec.width != spec.DYNAMIC) break;
+ // must skip this field
+ skipData(r, spec);
+ }
+ }
+
+ skipUnstoredFields();
+ if (r.empty)
+ {
+ // Input is empty, nothing to read
+ return 0;
+ }
+ static if (hasPointer)
+ alias A = typeof(*args[0]);
+ else
+ alias A = typeof(args[0]);
+
+ static if (isTuple!A)
+ {
+ foreach (i, T; A.Types)
+ {
+ static if (hasPointer)
+ (*args[0])[i] = unformatValue!(T)(r, spec);
+ else
+ args[0][i] = unformatValue!(T)(r, spec);
+ skipUnstoredFields();
+ }
+ }
+ else
+ {
+ static if (hasPointer)
+ *args[0] = unformatValue!(A)(r, spec);
+ else
+ args[0] = unformatValue!(A)(r, spec);
+ }
+ return 1 + formattedRead(r, spec.trailing, args[1 .. $]);
+ }
+}
+
+/// 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;
+ s.formattedRead!"%s!%s:%s"(a, b, c);
+ assert(a == "hello" && b == 124 && c == 34.5);
+}
+
+@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(s.empty);
+ assert(approxEqual(x, 1.2));
+ assert(approxEqual(y, 3.4));
+ assert(isNaN(z));
+}
+
+// for backwards compatibility
+@system pure 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);
+
+ // mix pointers and auto-ref
+ s = "world!200:42.25";
+ formattedRead(s, "%s!%s:%s", a, &b, &c);
+ assert(a == "world" && b == 200 && c == 42.25);
+
+ s = "world1!201:42.5";
+ formattedRead(s, "%s!%s:%s", &a, &b, c);
+ assert(a == "world1" && b == 201 && c == 42.5);
+
+ s = "world2!202:42.75";
+ formattedRead(s, "%s!%s:%s", a, b, &c);
+ assert(a == "world2" && b == 202 && c == 42.75);
+}
+
+// for backwards compatibility
+@system pure 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(s.empty);
+ assert(approxEqual(x, 1.2));
+ assert(approxEqual(y, 3.4));
+ assert(isNaN(z));
+}
+
+@system pure unittest
+{
+ string line;
+
+ bool f1;
+
+ line = "true";
+ formattedRead(line, "%s", &f1);
+ assert(f1);
+
+ line = "TrUE";
+ formattedRead(line, "%s", &f1);
+ assert(f1);
+
+ line = "false";
+ formattedRead(line, "%s", &f1);
+ assert(!f1);
+
+ line = "fALsE";
+ formattedRead(line, "%s", &f1);
+ assert(!f1);
+
+ line = "1";
+ formattedRead(line, "%d", &f1);
+ assert(f1);
+
+ line = "-1";
+ formattedRead(line, "%d", &f1);
+ assert(f1);
+
+ line = "0";
+ formattedRead(line, "%d", &f1);
+ assert(!f1);
+
+ line = "-0";
+ formattedRead(line, "%d", &f1);
+ assert(!f1);
+}
+
+@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);
+}
+
+@system pure unittest
+{
+ union A
+ {
+ 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);
+}
+
+@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);
+
+ 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);
+
+ line = "1 7643 2.125".dup;
+ formattedRead(line, "%s %*u %s", &t);
+ assert(t[0] == 1 && t[1] == 2.125);
+}
+
+@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.algorithm.searching : startsWith;
+ import std.ascii : isDigit, isPunctuation, isAlpha;
+ import std.conv : parse, text, to;
+
+ /**
+ Minimum _width, default $(D 0).
+ */
+ int width = 0;
+
+ /**
+ Precision. Its semantics depends on the argument type. For
+ floating point numbers, _precision dictates the number of
+ decimals printed.
+ */
+ int precision = UNSPECIFIED;
+
+ /**
+ Number of digits printed between _separators.
+ */
+ int separators = UNSPECIFIED;
+
+ /**
+ Set to `DYNAMIC` when the separator character is supplied at runtime.
+ */
+ int separatorCharPos = UNSPECIFIED;
+
+ /**
+ Character to insert between digits.
+ */
+ 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.
+ */
+ enum int DYNAMIC = int.max;
+
+ /**
+ Special value for precision, meaning the format specifier
+ contained no explicit precision.
+ */
+ enum int UNSPECIFIED = DYNAMIC - 1;
+
+ /**
+ The actual format specifier, $(D 's') by default.
+ */
+ char spec = 's';
+
+ /**
+ Index of the argument for positional parameters, from $(D 1) to
+ $(D ubyte.max). ($(D 0) means not used).
+ */
+ ubyte indexStart;
+
+ /**
+ Index of the last argument for positional parameter range, from
+ $(D 1) to $(D ubyte.max). ($(D 0) means not used).
+ */
+ ubyte indexEnd;
+
+ version (StdDdoc)
+ {
+ /**
+ 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)(ref 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
+ {
+ allFlags = 0;
+ }
+
+ width = 0;
+ precision = UNSPECIFIED;
+ nested = null;
+ // Parse the spec (we assume we're past '%' already)
+ for (size_t i = 0; i < trailing.length; )
+ {
+ switch (trailing[i])
+ {
+ case '(':
+ // Embedded format specifier.
+ auto j = i + 1;
+ // Get the matching balanced paren
+ for (uint innerParens;;)
+ {
+ enforceFmt(j + 1 < trailing.length,
+ text("Incorrect format specifier: %", trailing[i .. $]));
+ if (trailing[j++] != '%')
+ {
+ // skip, we're waiting for %( and %)
+ continue;
+ }
+ if (trailing[j] == '-') // for %-(
+ {
+ ++j; // skip
+ enforceFmt(j < trailing.length,
+ text("Incorrect format specifier: %", trailing[i .. $]));
+ }
+ if (trailing[j] == ')')
+ {
+ if (innerParens-- == 0) break;
+ }
+ else if (trailing[j] == '|')
+ {
+ if (innerParens == 0) break;
+ }
+ else if (trailing[j] == '(')
+ {
+ ++innerParens;
+ }
+ }
+ if (trailing[j] == '|')
+ {
+ auto k = j;
+ for (++j;;)
+ {
+ if (trailing[j++] != '%')
+ continue;
+ if (trailing[j] == '%')
+ ++j;
+ else if (trailing[j] == ')')
+ break;
+ else
+ throw new Exception(
+ text("Incorrect format specifier: %",
+ trailing[j .. $]));
+ }
+ nested = trailing[i + 1 .. k - 1];
+ sep = trailing[k + 1 .. j - 1];
+ }
+ else
+ {
+ nested = trailing[i + 1 .. j - 1];
+ sep = null; // no separator
+ }
+ //this = FormatSpec(innerTrailingSpec);
+ spec = '(';
+ // We practically found the format specifier
+ trailing = trailing[j + 1 .. $];
+ return;
+ case '-': flDash = true; ++i; break;
+ case '+': flPlus = true; ++i; break;
+ case '#': flHash = true; ++i; break;
+ case '0': flZero = true; ++i; break;
+ case ' ': flSpace = true; ++i; break;
+ case '*':
+ if (isDigit(trailing[++i]))
+ {
+ // a '*' followed by digits and '$' is a
+ // positional format
+ trailing = trailing[1 .. $];
+ width = -parse!(typeof(width))(trailing);
+ i = 0;
+ enforceFmt(trailing[i++] == '$',
+ "$ expected");
+ }
+ else
+ {
+ // read result
+ width = DYNAMIC;
+ }
+ break;
+ case '1': .. case '9':
+ auto tmp = trailing[i .. $];
+ const widthOrArgIndex = parse!uint(tmp);
+ enforceFmt(tmp.length,
+ text("Incorrect format specifier %", trailing[i .. $]));
+ i = arrayPtrDiff(tmp, trailing);
+ if (tmp.startsWith('$'))
+ {
+ // index of the form %n$
+ indexEnd = indexStart = to!ubyte(widthOrArgIndex);
+ ++i;
+ }
+ else if (tmp.startsWith(':'))
+ {
+ // two indexes of the form %m:n$, or one index of the form %m:$
+ indexStart = to!ubyte(widthOrArgIndex);
+ tmp = tmp[1 .. $];
+ if (tmp.startsWith('$'))
+ {
+ indexEnd = indexEnd.max;
+ }
+ else
+ {
+ indexEnd = parse!(typeof(indexEnd))(tmp);
+ }
+ i = arrayPtrDiff(tmp, trailing);
+ enforceFmt(trailing[i++] == '$',
+ "$ expected");
+ }
+ else
+ {
+ // 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
+ if (trailing[++i] == '*')
+ {
+ if (isDigit(trailing[++i]))
+ {
+ // a '.*' followed by digits and '$' is a
+ // positional precision
+ trailing = trailing[i .. $];
+ i = 0;
+ precision = -parse!int(trailing);
+ enforceFmt(trailing[i++] == '$',
+ "$ expected");
+ }
+ else
+ {
+ // read result
+ precision = DYNAMIC;
+ }
+ }
+ else if (trailing[i] == '-')
+ {
+ // negative precision, as good as 0
+ precision = 0;
+ 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));
+ }
+
+ //--------------------------------------------------------------------------
+ private bool readUpToNextSpec(R)(ref R r)
+ {
+ import std.ascii : isLower, isWhite;
+ import std.utf : stride;
+
+ // Reset content
+ if (__ctfe)
+ {
+ flDash = false;
+ flZero = false;
+ flSpace = false;
+ flPlus = false;
+ flHash = false;
+ flSeparator = false;
+ }
+ else
+ {
+ allFlags = 0;
+ }
+ width = 0;
+ precision = UNSPECIFIED;
+ nested = null;
+ // Parse the spec
+ while (trailing.length)
+ {
+ const c = trailing[0];
+ if (c == '%' && trailing.length > 1)
+ {
+ 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
+ {
+ 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;
+ }
+
+ 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 (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 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[0] == '%')
+ {
+ if (tr.length > 1 && tr[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,
+ "\nflSeparator = ", flSeparator,
+ "\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
+@safe 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));
+}
+
+@safe unittest
+{
+ import std.array : appender;
+ auto a = appender!(string)();
+
+ auto f = FormatSpec!char("%,d");
+ f.writeUpToNextSpec(a);
+
+ assert(f.spec == 'd', format("%s", f.spec));
+ assert(f.precision == FormatSpec!char.UNSPECIFIED);
+ assert(f.separators == 3);
+
+ f = FormatSpec!char("%5,10f");
+ f.writeUpToNextSpec(a);
+ assert(f.spec == 'f', format("%s", f.spec));
+ assert(f.separators == 10);
+ assert(f.width == 5);
+
+ 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);
+}
+
+/**
+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.
+Throws:
+ An `Exception` when more than one specifier is given or the specifier
+ is malformed.
+ */
+FormatSpec!Char singleSpec(Char)(Char[] fmt)
+{
+ 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");
+
+ 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;
+}
+
+///
+@safe pure unittest
+{
+ import std.exception : assertThrown;
+ 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)(auto ref Writer w, T obj, const 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);
+}
+
+///
+@safe pure 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" );
+ });
+}
+@system 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" );
+}
+
+@safe pure 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)(auto ref Writer w, T obj, const ref FormatSpec!Char f)
+if (is(Unqual!T == typeof(null)) && !is(T == enum) && !hasToString!(T, Char))
+{
+ enforceFmt(f.spec == 's',
+ "null literal cannot match %" ~ f.spec);
+
+ put(w, "null");
+}
+
+///
+@safe pure unittest
+{
+ import std.array : appender;
+ auto w = appender!string();
+ auto spec = singleSpec("%s");
+ formatValue(w, null, spec);
+
+ assert(w.data == "null");
+}
+
+@safe pure unittest
+{
+ assert(collectExceptionMsg!FormatException(format("%p", null)).back == 'p');
+
+ 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)(auto ref 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
+
+ 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 (needToSwapEndianess(f))
+ {
+ foreach_reverse (c; raw)
+ put(w, c);
+ }
+ else
+ {
+ foreach (c; raw)
+ put(w, c);
+ }
+ return;
+ }
+
+ 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);
+
+ // 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);
+}
+
+///
+@safe pure 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)(ref Writer w, const(T) val, const ref FormatSpec!Char fs,
+ uint base, ulong mask)
+{
+ T arg = val;
+
+ immutable negative = (base == 10 && arg < 0);
+ if (negative)
+ {
+ arg = -arg;
+ }
+
+ // 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);
+}
+
+private void formatUnsigned(Writer, T, Char)
+(ref Writer w, T arg, const ref FormatSpec!Char fs, uint base, bool negative)
+{
+ /* Write string:
+ * leftpad prefix1 prefix2 zerofill digits rightpad
+ */
+
+ /* 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)
+ {
+ // 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)
+ {
+ --i;
+ char c = cast(char) (arg % base);
+ arg /= base;
+ if (c < 10)
+ buffer[i] = cast(char)(c + '0');
+ else
+ buffer[i] = cast(char)(c + (fs.spec == 'x' ? 'a' - 10 : 'A' - 10));
+ }
+ digits = buffer[i .. $]; // got the digits without the sign
+ }
+
+
+ immutable precision = (fs.precision == fs.UNSPECIFIED) ? 1 : fs.precision;
+
+ char padChar = 0;
+ if (!fs.flDash)
+ {
+ padChar = (fs.flZero && fs.precision == fs.UNSPECIFIED) ? '0' : ' ';
+ }
+
+ // Compute prefix1 and prefix2
+ char prefix1 = 0;
+ char prefix2 = 0;
+ if (base == 10)
+ {
+ if (negative)
+ prefix1 = '-';
+ else if (fs.flPlus)
+ prefix1 = '+';
+ else if (fs.flSpace)
+ prefix1 = ' ';
+ }
+ else if (base == 16 && fs.flHash && digits.length)
+ {
+ prefix1 = '0';
+ prefix2 = fs.spec == 'x' ? 'x' : 'X';
+ }
+ // 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
+ {
+ if (padChar == '0')
+ zerofill += spacesToPrint;
+ else if (padChar)
+ leftpad = spacesToPrint;
+ else
+ rightpad = spacesToPrint;
+ }
+
+ // Print
+ foreach (i ; 0 .. leftpad)
+ put(w, ' ');
+
+ if (prefix1) put(w, prefix1);
+ if (prefix2) put(w, prefix2);
+
+ foreach (i ; 0 .. zerofill)
+ put(w, '0');
+
+ if (fs.flSeparator)
+ {
+ for (size_t j = 0; j < digits.length; ++j)
+ {
+ if (j != 0 && (digits.length - j) % fs.separators == 0)
+ {
+ put(w, fs.separatorChar);
+ }
+ put(w, digits[j]);
+ }
+ }
+ else
+ {
+ put(w, digits);
+ }
+
+ foreach (i ; 0 .. rightpad)
+ put(w, ' ');
+}
+
+@safe pure unittest
+{
+ assert(collectExceptionMsg!FormatException(format("%c", 5)).back == 'c');
+
+ assertCTFEable!(
+ {
+ formatTest(9, "9");
+ formatTest( 10, "10" );
+ });
+}
+
+@system 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
+@safe 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";
+ }
+ }
+
+ static struct Bar
+ {
+ Foo foo;
+ alias foo this;
+ string toString()
+ {
+ return "Bar";
+ }
+ }
+
+ 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");
+
+ result = null;
+
+ int i = 9;
+ formattedWrite(&put, "%s", 9);
+ assert(result == "9");
+}
+
+private enum ctfpMessage = "Cannot format floating point types at compile-time";
+
+/**
+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)(auto ref Writer w, T obj, const ref FormatSpec!Char f)
+if (is(FloatingPointTypeOf!T) && !is(T == enum) && !hasToString!(T, Char))
+{
+ import std.algorithm.comparison : min;
+ import std.algorithm.searching : find;
+ import std.string : indexOf, indexOfAny, indexOfNeither;
+
+ 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 (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);
+
+ 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 (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)
+ {
+ 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)
+ {
+ afterDotIdx = ePos;
+ }
+ else
+ {
+ afterDotIdx = len;
+ }
+
+ if (dot != -1)
+ {
+ 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]);
+ }
+
+ // rest
+ if (ePos != -1)
+ {
+ put(w, buf[afterDotIdx .. len]);
+ }
+ }
+ else
+ {
+ put(w, buf[0 .. len]);
+ }
+}
+
+///
+@safe 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;
+
+ assert(collectExceptionMsg!FormatException(format("%d", 5.1)).back == 'd');
+
+ 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" );
+
+ formatTest( T.nan, "nan" );
+ }
+}
+
+@system 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)(auto ref Writer w, T obj, const ref FormatSpec!Char f)
+if (is(Unqual!T : creal) && !is(T == enum) && !hasToString!(T, Char))
+{
+ immutable 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; AliasSeq!(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; AliasSeq!(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" );
+ }
+}
+
+@system 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.
+
+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)(auto ref Writer w, T obj, const ref FormatSpec!Char f)
+if (is(Unqual!T : ireal) && !is(T == enum) && !hasToString!(T, Char))
+{
+ immutable ireal val = obj;
+
+ formatValue(w, val.im, f);
+ put(w, 'i');
+}
+
+@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" );
+ }
+}
+
+@system 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)(auto ref Writer w, T obj, const 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')
+ {
+ put(w, val);
+ }
+ else
+ {
+ alias U = AliasSeq!(ubyte, ushort, uint)[CharTypeOf!T.sizeof/2];
+ formatValue(w, cast(U) val, f);
+ }
+}
+
+///
+@safe pure 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" );
+ });
+}
+
+@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" );
+}
+
+@safe 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'] );
+
+ //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'] );
+}
+
+/**
+Strings 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)(auto ref 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);
+}
+
+///
+@safe pure unittest
+{
+ import std.array : appender;
+ auto w = appender!string();
+ auto spec = singleSpec("%s");
+ formatValue(w, "hello", spec);
+
+ assert(w.data == "hello");
+}
+
+@safe unittest
+{
+ formatTest( "abc", "abc" );
+}
+
+@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" );
+
+ // 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" );
+}
+
+@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" );
+
+ struct S3 { string val; alias val this;
+ string toString() const { return "S"; } }
+ formatTest( S3("s3"), "S" );
+}
+
+@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'] );
+
+ //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)(auto ref 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");
+}
+
+@safe unittest // Test for issue 8310
+{
+ import std.array : appender;
+ FormatSpec!char f;
+ auto w = appender!string();
+
+ char[2] two = ['a', 'b'];
+ formatValue(w, two, f);
+
+ char[2] getTwo(){ return two; }
+ formatValue(w, getTwo(), f);
+}
+
+/**
+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.))
+
+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)(auto ref Writer w, T obj, const ref FormatSpec!Char f)
+if (is(DynamicArrayTypeOf!T) && !is(StringTypeOf!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)
+ {
+ alias U = Unqual!(ArrayTypeOf!T);
+ static assert(isInputRange!U);
+ U val = obj;
+ formatValue(w, val, f);
+ }
+ else
+ {
+ formatRange(w, obj, f);
+ }
+}
+
+///
+@safe pure unittest
+{
+ import std.array : appender;
+ auto w = appender!string();
+ auto spec = singleSpec("%s");
+ auto two = [1, 2];
+ formatValue(w, two, spec);
+
+ assert(w.data == "[1, 2]");
+}
+
+// alias this, input range I/F, and toString()
+@system 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)
+ {
+ 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");
+}
+
+@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, "[]");
+
+ void[3] sval = cast(void[3]) cast(ubyte[3])[1, 2, 3];
+ formatTest( sval, "[1, 2, 3]" );
+}
+
+@safe unittest
+{
+ // const(T[]) -> const(T)[]
+ const short[] a = [1, 2, 3];
+ formatTest( a, "[1, 2, 3]" );
+
+ struct S { const(int[]) arr; alias arr this; }
+ auto s = S([1,2,3]);
+ formatTest( s, "[1, 2, 3]" );
+}
+
+@safe unittest
+{
+ // 6640
+ struct Range
+ {
+ @safe:
+ string value;
+ @property bool empty() const { return !value.length; }
+ @property dchar front() const { return value.front; }
+ void popFront() { value.popFront(); }
+
+ @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]);
+ }
+}
+
+@system unittest
+{
+ // string literal from valid UTF sequence is encoding free.
+ foreach (StrType; AliasSeq!(string, wstring, dstring))
+ {
+ // Valid and printable (ASCII)
+ formatTest( [cast(StrType)"hello"],
+ `["hello"]` );
+
+ // 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"]` );
+
+ // 1 character optional escape sequences
+ formatTest( [cast(StrType)"\'\?"],
+ `["'?"]` );
+
+ // Valid and non-printable code point (<= U+FF)
+ formatTest( [cast(StrType)"\x10\x1F\x20test"],
+ `["\x10\x1F test"]` );
+
+ // 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]` );
+ }
+}
+
+@safe unittest
+{
+ // nested range formatting with array of string
+ formatTest( "%({%(%02x %)}%| %)", ["test", "msg"],
+ `{74 65 73 74} {6d 73 67}` );
+}
+
+@safe unittest
+{
+ // 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`] );
+}
+
+// input range formatting
+private void formatRange(Writer, T, Char)(ref Writer w, ref T val, const ref FormatSpec!Char f)
+if (isInputRange!T)
+{
+ import std.conv : text;
+
+ // Formatting character ranges like string
+ if (f.spec == 's')
+ {
+ alias E = ElementType!T;
+
+ static if (!is(E == enum) && is(CharTypeOf!E))
+ {
+ 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;
+
+ 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);
+ }
+
+ 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')
+ {
+ 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);
+ }
+ }
+ }
+ 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);
+ }
+ }
+ }
+ else
+ throw new Exception(text("Incorrect format specifier for range: %", f.spec));
+}
+
+@safe pure unittest
+{
+ assert(collectExceptionMsg(format("%d", "hi")).back == 'd');
+}
+
+// character formatting with ecaping
+private void formatChar(Writer)(ref Writer w, in dchar c, in char quote)
+{
+ import std.uni : isGraphical;
+
+ string fmt;
+ if (isGraphical(c))
+ {
+ if (c == quote || c == '\\')
+ put(w, '\\');
+ put(w, c);
+ return;
+ }
+ else if (c <= 0xFF)
+ {
+ 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";
+ }
+ else if (c <= 0xFFFF)
+ fmt = "\\u%04X";
+ else
+ fmt = "\\U%08X";
+
+ formattedWrite(w, fmt, cast(uint) c);
+}
+
+// undocumented because of deprecation
+// string elements are formatted like UTF-8 string literals.
+void formatElement(Writer, T, Char)(auto ref Writer w, T val, const ref FormatSpec!Char f)
+if (is(StringTypeOf!T) && !is(T == enum))
+{
+ import std.array : appender;
+ import std.utf : UTFException;
+
+ StringTypeOf!T str = val; // bug 8015
+
+ if (f.spec == 's')
+ {
+ 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;
+
+ 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)
+ {
+ }
+
+ // 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);
+}
+
+@safe pure unittest
+{
+ import std.array : appender;
+ auto w = appender!string();
+ auto spec = singleSpec("%s");
+ formatElement(w, "Hello World", spec);
+
+ assert(w.data == "\"Hello World\"");
+}
+
+@safe unittest
+{
+ // Test for bug 8015
+ import std.typecons;
+
+ struct MyStruct {
+ string str;
+ @property string toStr() {
+ return str;
+ }
+ alias toStr this;
+ }
+
+ Tuple!(MyStruct) t;
+}
+
+// undocumented because of deprecation
+// Character elements are formatted like UTF-8 character literals.
+void formatElement(Writer, T, Char)(auto ref Writer w, T val, const ref FormatSpec!Char f)
+if (is(CharTypeOf!T) && !is(T == enum))
+{
+ if (f.spec == 's')
+ {
+ put(w, '\'');
+ formatChar(w, val, '\'');
+ put(w, '\'');
+ }
+ else
+ formatValue(w, val, f);
+}
+
+///
+@safe unittest
+{
+ import std.array : appender;
+ auto w = appender!string();
+ auto spec = singleSpec("%s");
+ formatElement(w, "H", spec);
+
+ assert(w.data == "\"H\"", w.data);
+}
+
+// undocumented
+// Maybe T is noncopyable struct, so receive it by 'auto ref'.
+void formatElement(Writer, T, Char)(auto ref Writer w, auto ref T val, const ref FormatSpec!Char f)
+if (!is(StringTypeOf!T) && !is(CharTypeOf!T) || is(T == enum))
+{
+ formatValue(w, val, f);
+}
+
+/**
+ 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)(auto ref 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)
+ {
+ 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);
+ }
+ }
+ if (f.spec == 's')
+ put(w, f.seqAfter);
+}
+
+///
+@safe pure unittest
+{
+ import std.array : appender;
+ auto w = appender!string();
+ auto spec = singleSpec("%s");
+ auto aa = ["H":"W"];
+ formatElement(w, aa, spec);
+
+ assert(w.data == "[\"H\":\"W\"]", w.data);
+}
+
+@safe unittest
+{
+ assert(collectExceptionMsg!FormatException(format("%d", [0:1])).back == 'd');
+
+ int[string] aa0;
+ formatTest( aa0, `[]` );
+
+ // elements escaping
+ formatTest( ["aaa":1, "bbb":2],
+ [`["aaa":1, "bbb":2]`, `["bbb":2, "aaa":1]`] );
+ formatTest( ['c':"str"],
+ `['c':"str"]` );
+ formatTest( ['"':"\"", '\'':"'"],
+ [`['"':"\"", '\'':"'"]`, `['\'':"'", '"':"\""]`] );
+
+ // 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]}`] );
+
+ // issue 12135
+ formatTest("%(%s:<%s>%|,%)", [1:2], "1:<2>");
+ formatTest("%(%s:<%s>%|%)" , [1:2], "1:<2>");
+}
+
+@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; }
+ 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" );
+
+ 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" );
+}
+
+@safe 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]");
+}
+
+template 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
+ {
+ enum hasToString = 0;
+ }
+}
+
+// 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);
+}
+
+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 ~ "'");
+ }
+}
+
+@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 ""; } }
+
+ 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"); } }
+
+ static union KU1 { void toString(scope void delegate(const(char)[]) sink,
+ FormatSpec!char) const { sink("KU1"); } }
+
+ static class KC1 { void toString(scope void delegate(const(char)[]) sink,
+ FormatSpec!char) const { sink("KC1"); } }
+
+ 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()));
+
+ 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()));
+
+ 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");
+}
+
+/**
+ 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:
+
+---
+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();
+---
+
+ 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.))
+
+ 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, ...)).))
+
+ Otherwise, are formatted just as their type name.
+ */
+void formatValue(Writer, T, Char)(auto ref 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");
+
+ 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);
+ }
+ }
+ }
+}
+
+/++
+ $(D formatValue) allows to reuse existing format specifiers:
+ +/
+@system unittest
+{
+ import std.format;
+
+ struct Point
+ {
+ int x, y;
+
+ void toString(scope void delegate(const(char)[]) sink,
+ FormatSpec!char fmt) const
+ {
+ sink("(");
+ sink.formatValue(x, fmt);
+ sink(",");
+ sink.formatValue(y, fmt);
+ sink(")");
+ }
+ }
+
+ auto p = Point(16,11);
+ assert(format("%03d", p) == "(016,011)");
+ assert(format("%02x", p) == "(10,0b)");
+}
+
+/++
+ The following code compares the use of $(D formatValue) and $(D formattedWrite).
+ +/
+@safe pure unittest
+{
+ import std.array : appender;
+ import std.format;
+
+ auto writer1 = appender!string();
+ writer1.formattedWrite("%08b", 42);
+
+ auto writer2 = appender!string();
+ auto f = singleSpec("%08b");
+ writer2.formatValue(42, f);
+
+ assert(writer1.data == writer2.data && writer1.data == "00101010");
+}
+
+@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" );
+}
+
+@system unittest
+{
+ // 5354
+ // If the class has both range I/F and custom toString, the use of custom
+ // toString routine is prioritized.
+
+ // Enable the use of custom toString that gets a sink delegate
+ // for class formatting.
+
+ 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..$]; }
+ };
+
+ 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);
+ }
+
+ 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]" );
+}
+
+/// ditto
+void formatValue(Writer, T, Char)(auto ref 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);
+ }
+ }
+ }
+}
+
+@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" );
+
+ // interface (downcast to Object)
+ interface Whatever {}
+ class C : Whatever
+ {
+ override @property string toString() const { return "ab"; }
+ }
+ Whatever val = new C;
+ formatTest( val, "ab" );
+
+ // Issue 11175
+ version (Windows)
+ {
+ import core.sys.windows.com : IUnknown, IID;
+ import core.sys.windows.windows : HRESULT;
+
+ interface IUnknown2 : IUnknown { }
+
+ 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; }
+ }
+
+ IUnknown2 d = new D;
+ string expected = format("%X", cast(void*) d);
+ formatTest(d, expected);
+ }
+}
+
+/// ditto
+// Maybe T is noncopyable struct, so receive it by 'auto ref'.
+void formatValue(Writer, T, Char)(auto ref 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 = ")";
+
+ 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
+ {
+ put(w, T.stringof);
+ }
+}
+
+@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))` );
+}
+
+@system unittest
+{
+ // union formatting without toString
+ union U1
+ {
+ int n;
+ string s;
+ }
+ U1 u1;
+ formatTest( u1, "U1" );
+
+ // union formatting with toString
+ union U2
+ {
+ int n;
+ string s;
+ string toString() const { return s; }
+ }
+ U2 u2;
+ u2.s = "hello";
+ formatTest( u2, "hello" );
+}
+
+@system unittest
+{
+ import std.array;
+ // 7230
+ static struct Bug7230
+ {
+ string s = "hello";
+ union {
+ string a;
+ int b;
+ double c;
+ }
+ long x = 10;
+ }
+
+ Bug7230 bug;
+ bug.b = 123;
+
+ FormatSpec!char f;
+ auto w = appender!(char[])();
+ formatValue(w, bug, f);
+ assert(w.data == `Bug7230("hello", #{overlap a, b, c}, 10)`);
+}
+
+@safe unittest
+{
+ import std.array;
+ static struct S{ @disable this(this); }
+ S s;
+
+ FormatSpec!char f;
+ auto w = appender!string();
+ formatValue(w, s, f);
+ assert(w.data == "S()");
+}
+
+/**
+$(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)(auto ref Writer w, T val, const ref FormatSpec!Char f)
+if (is(T == enum))
+{
+ if (f.spec == 's')
+ {
+ foreach (i, e; EnumMembers!T)
+ {
+ if (val == e)
+ {
+ formatValue(w, __traits(allMembers, T)[i], f);
+ return;
+ }
+ }
+
+ // val is not a member of T, output cast(T) rawValue instead.
+ put(w, "cast(" ~ T.stringof ~ ")");
+ static assert(!is(OriginalType!T == T));
+ }
+ formatValue(w, cast(OriginalType!T) val, f);
+}
+
+///
+@safe pure unittest
+{
+ import std.array : appender;
+ auto w = appender!string();
+ auto spec = singleSpec("%s");
+
+ enum A { first, second, third }
+
+ formatElement(w, A.second, spec);
+
+ assert(w.data == "second");
+}
+
+@safe unittest
+{
+ 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\&oacute;n", "cast(A)mill\&oacute;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");
+}
+
+/**
+ Pointers are formatted as hex integers.
+ */
+void formatValue(Writer, T, Char)(auto ref Writer w, T val, const ref FormatSpec!Char f)
+if (isPointer!T && !is(T == enum) && !hasToString!(T, Char))
+{
+ static if (isInputRange!T)
+ {
+ if (val !is null)
+ {
+ formatRange(w, *val, f);
+ return;
+ }
+ }
+
+ static if (is(typeof({ shared const void* p = val; })))
+ alias SharedOf(T) = shared(T);
+ else
+ alias SharedOf(T) = T;
+
+ const SharedOf!(void*) p = val;
+ const pnum = ()@trusted{ return cast(ulong) p; }();
+
+ if (f.spec == 's')
+ {
+ 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);
+ }
+ else
+ {
+ enforceFmt(f.spec == 'X' || f.spec == 'x',
+ "Expected one of %s, %x or %X for pointer type.");
+ formatValue(w, pnum, f);
+ }
+}
+
+@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" );
+}
+
+@system pure unittest
+{
+ // Test for issue 7869
+ struct S
+ {
+ string toString() const { return ""; }
+ }
+ S* p = null;
+ formatTest( p, "null" );
+
+ S* q = cast(S*) 0xFFEECCAA;
+ formatTest( q, "FFEECCAA" );
+}
+
+@system unittest
+{
+ // Test for issue 8186
+ class B
+ {
+ int*a;
+ this(){ a = new int; }
+ alias a this;
+ }
+ formatTest( B.init, "null" );
+}
+
+@system pure unittest
+{
+ // 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" );
+}
+
+/**
+ Delegates are formatted by 'ReturnType delegate(Parameters) FunctionAttributes'
+ */
+void formatValue(Writer, T, Char)(auto ref Writer w, scope T, const ref FormatSpec!Char f)
+if (isDelegate!T)
+{
+ formatValue(w, T.stringof, f);
+}
+
+///
+@safe pure unittest
+{
+ import std.conv : to;
+
+ int i;
+
+ int foo(short k) @nogc
+ {
+ return i + k;
+ }
+
+ @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");
+}
+
+@safe unittest
+{
+ void func() @system { __gshared int x; ++x; throw new Exception("msg"); }
+ version (linux) formatTest( &func, "void delegate() @system" );
+}
+
+@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" );
+
+ int[0] empt = [];
+ formatTest( "(%s)", empt,
+ "([])" );
+}
+
+//------------------------------------------------------------------------------
+// Fix for issue 1591
+private int getNthInt(string kind, A...)(uint index, A args)
+{
+ return getNth!(kind, isIntegral,int)(index, args);
+}
+
+private T getNth(string kind, alias Condition, T, A...)(uint index, A args)
+{
+ import std.conv : text, to;
+
+ switch (index)
+ {
+ foreach (n, _; A)
+ {
+ case n:
+ static if (Condition!(typeof(args[n])))
+ {
+ return to!T(args[n]);
+ }
+ else
+ {
+ throw new FormatException(
+ text(kind, " expected, not ", typeof(args[n]).stringof,
+ " for argument #", index + 1));
+ }
+ }
+ default:
+ throw new FormatException(
+ text("Missing ", kind, " argument"));
+ }
+}
+
+@safe unittest
+{
+ // width/precision
+ assert(collectExceptionMsg!FormatException(format("%*.d", 5.1, 2))
+ == "integer width expected, not double for argument #1");
+ assert(collectExceptionMsg!FormatException(format("%-1*.d", 5.1, 2))
+ == "integer width expected, not double for argument #1");
+
+ assert(collectExceptionMsg!FormatException(format("%.*d", '5', 2))
+ == "integer precision expected, not char for argument #1");
+ assert(collectExceptionMsg!FormatException(format("%-1.*d", 4.7, 3))
+ == "integer precision expected, not double for argument #1");
+ assert(collectExceptionMsg!FormatException(format("%.*d", 5))
+ == "Orphan format specifier: %d");
+ assert(collectExceptionMsg!FormatException(format("%*.*d", 5))
+ == "Missing integer precision argument");
+
+ // separatorCharPos
+ assert(collectExceptionMsg!FormatException(format("%,?d", 5))
+ == "separator character expected, not int for argument #1");
+ assert(collectExceptionMsg!FormatException(format("%,?d", '?'))
+ == "Orphan format specifier: %d");
+ assert(collectExceptionMsg!FormatException(format("%.*,*?d", 5))
+ == "Missing separator digit width argument");
+}
+
+/* ======================== Unit Tests ====================================== */
+
+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);
+}
+
+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 core.exception : AssertError;
+ import std.array : appender;
+ import std.conv : text;
+ FormatSpec!char f;
+ auto w = appender!string();
+ formatValue(w, val, f);
+ foreach (cur; expected)
+ {
+ if (w.data == cur) return;
+ }
+ enforce!AssertError(
+ false,
+ text("expected one of `", expected, "`, result = `", w.data, "`"), fn, ln);
+}
+
+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);
+ foreach (cur; expected)
+ {
+ if (w.data == cur) return;
+ }
+ enforce!AssertError(
+ false,
+ text("expected one of `", expected, "`, result = `", w.data, "`"), fn, ln);
+}
+
+@safe /*pure*/ unittest // formatting floating point values is now impure
+{
+ import std.array;
+
+ auto stream = appender!string();
+ formattedWrite(stream, "%s", 1.1);
+ assert(stream.data == "1.1", stream.data);
+}
+
+@safe pure unittest
+{
+ import std.algorithm;
+ import std.array;
+
+ 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");
+}
+
+@safe pure unittest
+{
+ import std.array;
+ auto stream = appender!string();
+ formattedWrite(stream, "%u", 42);
+ assert(stream.data == "42", stream.data);
+}
+
+@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);
+}
+
+@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);
+ assert(collectExceptionMsg!FormatException(formattedWrite(w, "%1$s, %3$s", 1, 2))
+ == "Positional specifier %3$s index exceeds 2");
+
+ 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);
+}
+
+@safe unittest
+{
+ import core.stdc.string : strlen;
+ import std.array : appender;
+ import std.conv : text, octal;
+ import core.stdc.stdio : snprintf;
+
+ 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 (CRuntime_Glibc)
+ {
+ assert(stream.data == "1.67 -0X1.47AE147AE147BP+0 nan",
+ stream.data);
+ }
+ else version (OSX)
+ {
+ assert(stream.data == "1.67 -0X1.47AE147AE147BP+0 nan",
+ stream.data);
+ }
+ else version (MinGW)
+ {
+ assert(stream.data == "1.67 -0XA.3D70A3D70A3D8P-3 nan",
+ stream.data);
+ }
+ else version (CRuntime_Microsoft)
+ {
+ assert(stream.data == "1.67 -0X1.47AE14P+0 nan"
+ || stream.data == "1.67 -0X1.47AE147AE147BP+0 nan", // MSVCRT 14+ (VS 2015)
+ stream.data);
+ }
+ 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"
+ || stream.data == "0x1.51eb851eb851fp+0 0X1.B1EB860000000P+2"); // MSVCRT 14+ (VS 2015)
+ 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();
+
+ 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();
+
+ 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();
+
+ 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();
+
+ formattedWrite(stream, "%.0d", 0);
+ assert(stream.data == "");
+ stream.clear();
+
+ formattedWrite(stream, "%.g", .34);
+ assert(stream.data == "0.3");
+ stream.clear();
+
+ stream.clear(); formattedWrite(stream, "%.0g", .34);
+ assert(stream.data == "0.3");
+
+ stream.clear(); formattedWrite(stream, "%.2g", .34);
+ assert(stream.data == "0.34");
+
+ stream.clear(); formattedWrite(stream, "%0.0008f", 1e-08);
+ assert(stream.data == "0.00000001");
+
+ stream.clear(); formattedWrite(stream, "%0.0008f", 1e-05);
+ assert(stream.data == "0.00001000");
+
+ //return;
+ //core.stdc.stdio.fwrite(stream.data.ptr, stream.data.length, 1, stderr);
+
+ 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");
+
+ 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);
+
+ 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);
+
+ 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]");
+
+ 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[] 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]");
+
+ 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]");
+
+ 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]");
+
+ 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);
+
+ stream.clear(); formattedWrite(stream, "%.8d", 7);
+ assert(stream.data == "00000007");
+
+ stream.clear(); formattedWrite(stream, "%.8x", 10);
+ assert(stream.data == "0000000a");
+
+ stream.clear(); formattedWrite(stream, "%-3d", 7);
+ assert(stream.data == "7 ");
+
+ stream.clear(); formattedWrite(stream, "%*d", -3, 7);
+ assert(stream.data == "7 ");
+
+ stream.clear(); formattedWrite(stream, "%.*d", -3, 7);
+ //writeln(stream.data);
+ assert(stream.data == "7");
+
+ 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");
+
+here:
+ @trusted void* deadBeef() { return cast(void*) 0xDEADBEEF; }
+ stream.clear(); formattedWrite(stream, "%s", deadBeef());
+ assert(stream.data == "DEADBEEF", stream.data);
+
+ stream.clear(); formattedWrite(stream, "%#x", 0xabcd);
+ assert(stream.data == "0xabcd");
+ stream.clear(); formattedWrite(stream, "%#X", 0xABCD);
+ assert(stream.data == "0XABCD");
+
+ stream.clear(); formattedWrite(stream, "%#o", octal!12345);
+ assert(stream.data == "012345");
+ stream.clear(); formattedWrite(stream, "%o", 9);
+ assert(stream.data == "11");
+
+ 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");
+
+ stream.clear(); formattedWrite(stream, "%%");
+ assert(stream.data == "%");
+
+ stream.clear(); formattedWrite(stream, "%d", true);
+ assert(stream.data == "1");
+ stream.clear(); formattedWrite(stream, "%d", false);
+ assert(stream.data == "0");
+
+ 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");
+
+ 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");
+
+ 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");
+
+ 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()
+ {
+ Object c = null;
+ stream.clear(); formattedWrite(stream, "%s", c);
+ assert(stream.data == "null");
+ }
+ ObjectTest();
+
+ enum TestEnum
+ {
+ Value1, Value2
+ }
+ 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)
+ {
+ stream.clear(); formattedWrite(stream, " %d", ds[j]);
+ if (j == 0)
+ assert(stream.data == " 97");
+ else
+ assert(stream.data == " 98");
+ }
+
+ stream.clear(); formattedWrite(stream, "%.-3d", 7);
+ assert(stream.data == "7", ">" ~ stream.data ~ "<");
+}
+
+@safe unittest
+{
+ import std.array;
+ import std.stdio;
+
+ immutable(char[5])[int] aa = ([3:"hello", 4:"betty"]);
+ assert(aa[3] == "hello");
+ assert(aa[4] == "betty");
+
+ 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");
+ }
+
+ stream.clear();
+ formattedWrite(stream, "%s", aa);
+}
+
+@system 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);
+}
+
+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);
+
+ 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, 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);
+}
+
+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;
+
+ 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);
+
+ 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, 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);
+}
+
+@system 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`);
+ }
+
+ 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`);
+ }
+
+ 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`);
+ }
+
+ 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`);
+ }
+
+ 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>%| $ %)]", `[<h> $ <e> $ <l> $ <l> $ <o>]`);
+ }
+
+ void daTest()
+ {
+ 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>]`);
+ }
+
+ 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>]`);
+ }
+
+ 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]}`]);
+ }
+
+ import std.exception;
+ assertCTFEable!(
+ {
+ booleanTest();
+ integerTest();
+ if (!__ctfe) floatingTest(); // snprintf
+ charTest();
+ strTest();
+ daTest();
+ saTest();
+ aaTest();
+ return true;
+ });
+}
+
+//------------------------------------------------------------------------------
+private void skipData(Range, Char)(ref Range input, const ref FormatSpec!Char spec)
+{
+ import std.ascii : isDigit;
+ import std.conv : text;
+
+ 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));
+ }
+}
+
+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 = "";
+}
+
+/**
+ * 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);
+}
+
+/// Booleans
+@safe pure unittest
+{
+ auto str = "false";
+ auto spec = singleSpec("%s");
+ assert(unformatValue!bool(str, spec) == false);
+
+ str = "1";
+ spec = singleSpec("%d");
+ assert(unformatValue!bool(str, spec));
+}
+
+/// Null values
+@safe pure unittest
+{
+ auto str = "null";
+ auto spec = singleSpec("%s");
+ assert(str.unformatValue!(typeof(null))(spec) == null);
+}
+
+/// 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));
+}
+
+/// Character input ranges
+@safe pure unittest
+{
+ auto str = "aaa";
+ auto spec = singleSpec("%s");
+ assert(str.unformatValue!char(spec) == 'a');
+
+ // Using a numerical format spec reads a Unicode value from a string
+ str = "65";
+ spec = singleSpec("%d");
+ assert(str.unformatValue!char(spec) == 'A');
+
+ str = "41";
+ spec = singleSpec("%x");
+ assert(str.unformatValue!char(spec) == 'A');
+
+ str = "10003";
+ spec = singleSpec("%d");
+ assert(str.unformatValue!dchar(spec) == '✓');
+}
+
+/// Arrays and static arrays
+@safe pure unittest
+{
+ string str = "aaa";
+ auto spec = singleSpec("%s");
+ assert(str.unformatValue!(dchar[])(spec) == "aaa"d);
+
+ str = "aaa";
+ spec = singleSpec("%s");
+ dchar[3] ret = ['a', 'a', 'a'];
+ assert(str.unformatValue!(dchar[3])(spec) == ret);
+
+ str = "[1, 2, 3, 4]";
+ spec = singleSpec("%s");
+ assert(str.unformatValue!(int[])(spec) == [1, 2, 3, 4]);
+
+ str = "[1, 2, 3, 4]";
+ spec = singleSpec("%s");
+ int[4] ret2 = [1, 2, 3, 4];
+ assert(str.unformatValue!(int[4])(spec) == ret2);
+}
+
+/// 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]);
+}
+
+@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');
+}
+
+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;
+
+ if (spec.spec == 's') return parse!T(input);
+
+ enforce(find(acceptedSpecs!long, spec.spec).length,
+ text("Wrong unformat specifier '%", spec.spec , "' for ", T.stringof));
+
+ return unformatValue!long(input, spec) != 0;
+}
+
+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));
+
+ return parse!T(input);
+}
+
+/// 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))
+{
+
+ import std.algorithm.searching : find;
+ import std.conv : parse, text;
+
+ 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.");
+ }
+
+ enforce(find(acceptedSpecs!T, spec.spec).length,
+ text("Wrong unformat specifier '%", spec.spec , "' for ", T.stringof));
+
+ enforce(spec.width == 0, "Parsing integers with a width specification is not implemented"); // TODO
+
+ 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);
+
+ return parse!T(input, base);
+
+}
+
+/// 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;
+
+ 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.");
+ }
+
+ enforce(find(acceptedSpecs!T, spec.spec).length,
+ 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 && 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));
+
+ 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);
+}
+
+/// 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));
+
+ 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));
+
+ return parse!T(input);
+}
+
+/**
+ * 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)
+ {
+ x.raw[i] = input[0];
+ input = input[1 .. $];
+ }
+ else
+ {
+ // TODO: recheck this
+ x.raw[i] = input.front;
+ input.popFront();
+ }
+ }
+ return x.typed;
+}
+
+//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");
+
+ 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] == '%')
+ {
+ 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);
+
+ bool checkEnd()
+ {
+ return input.empty || !cont.empty && input.front == cont.front;
+ }
+
+ if (!checkEnd())
+ {
+ for (;;)
+ {
+ auto fmt = FormatSpec!Char(spec.nested);
+ fmt.readUpToNextSpec(input);
+ enforce(!input.empty, "Unexpected end of input when parsing range");
+
+ 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
+
+ result[key] = unformatElement!(typeof(T.init.values[0]))(input, fmt);
+ }
+ debug (unformatRange) {
+ if (input.empty) printf("-> front = [empty] ");
+ else printf("-> front = %c ", input.front);
+ }
+
+ 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));
+ }
+
+ 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");
+ }
+
+ if (checkEnd())
+ break;
+
+ 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;
+}
+
+// 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);
+ }
+ }
+
+ return unformatValue!T(input, spec);
+}
+
+
+// Legacy implementation
+
+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',
+
+ Tifloat = 'o',
+ Tidouble = 'p',
+ Tireal = 'j',
+ Tcfloat = 'q',
+ Tcdouble = 'r',
+ Tcreal = 'c',
+
+ Tchar = 'a',
+ Twchar = 'u',
+ Tdchar = 'w',
+
+ Tarray = 'A',
+ Tsarray = 'G',
+ Taarray = 'H',
+ Tpointer = 'P',
+ Tfunction = 'F',
+ Tident = 'I',
+ Tclass = 'C',
+ Tstruct = 'S',
+ Tenum = 'E',
+ Ttypedef = 'T',
+ Tdelegate = 'D',
+
+ Tconst = 'x',
+ Timmutable = 'y',
+}
+
+// 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)
+ ];
+ }
+ auto p = m in dic;
+ return p ? *p : null;
+}
+
+private bool needToSwapEndianess(Char)(const ref FormatSpec!Char f)
+{
+ import std.system : endian, Endian;
+
+ return endian == Endian.littleEndian && f.flPlus
+ || endian == Endian.bigEndian && f.flDash;
+}
+
+/* ======================== Unit Tests ====================================== */
+
+@system unittest
+{
+ import std.conv : octal;
+
+ int i;
+ string s;
+
+ debug(format) printf("std.format.format.unittest\n");
+
+ s = format("hello world! %s %s %s%s%s", true, 57, 1_000_000_000, 'x', " foo");
+ assert(s == "hello world! true 57 1000000000x foo");
+
+ s = format("%s %A %s", 1.67, -1.28, float.nan);
+ /* 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)
+ // assert(s == "1.67 -0XA.3D70A3D70A3D8P-3 nan");
+ //else version (OSX)
+ // assert(s == "1.67 -0XA.3D70A3D70A3D8P-3 nan", s);
+ //else
+ version (MinGW)
+ assert(s == "1.67 -0XA.3D70A3D70A3D8P-3 nan", s);
+ else version (CRuntime_Microsoft)
+ 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);
+
+ s = format("%x %X", 0x1234AF, 0xAFAFAFAF);
+ assert(s == "1234af AFAFAFAF");
+
+ s = format("%b %o", 0x1234AF, 0xAFAFAFAF);
+ assert(s == "100100011010010101111 25753727657");
+
+ s = format("%d %s", 0x1234AF, 0xAFAFAFAF);
+ assert(s == "1193135 2947526575");
+
+ //version (X86_64)
+ //{
+ // pragma(msg, "several format tests disabled on x86_64 due to bug 5625");
+ //}
+ //else
+ //{
+ s = format("%s", 1.2 + 3.4i);
+ assert(s == "1.2+3.4i", s);
+
+ //s = format("%x %X", 1.32, 6.78f);
+ //assert(s == "3ff51eb851eb851f 40D8F5C3");
+
+ //}
+
+ s = format("%#06.*f",2,12.345);
+ assert(s == "012.35");
+
+ s = format("%#0*.*f",6,2,12.345);
+ assert(s == "012.35");
+
+ s = format("%7.4g:", 12.678);
+ assert(s == " 12.68:");
+
+ s = format("%7.4g:", 12.678L);
+ assert(s == " 12.68:");
+
+ s = format("%04f|%05d|%#05x|%#5x",-4.0,-10,1,1);
+ assert(s == "-4.000000|-0010|0x001| 0x1");
+
+ i = -10;
+ s = format("%d|%3d|%03d|%1d|%01.4f",i,i,i,i,cast(double) i);
+ assert(s == "-10|-10|-10|-10|-10.0000");
+
+ i = -5;
+ s = format("%d|%3d|%03d|%1d|%01.4f",i,i,i,i,cast(double) i);
+ assert(s == "-5| -5|-05|-5|-5.0000");
+
+ i = 0;
+ s = format("%d|%3d|%03d|%1d|%01.4f",i,i,i,i,cast(double) i);
+ assert(s == "0| 0|000|0|0.0000");
+
+ i = 5;
+ s = format("%d|%3d|%03d|%1d|%01.4f",i,i,i,i,cast(double) i);
+ assert(s == "5| 5|005|5|5.0000");
+
+ i = 10;
+ s = format("%d|%3d|%03d|%1d|%01.4f",i,i,i,i,cast(double) i);
+ assert(s == "10| 10|010|10|10.0000");
+
+ s = format("%.0d", 0);
+ assert(s == "");
+
+ s = format("%.g", .34);
+ assert(s == "0.3");
+
+ s = format("%.0g", .34);
+ assert(s == "0.3");
+
+ s = format("%.2g", .34);
+ assert(s == "0.34");
+
+ s = format("%0.0008f", 1e-08);
+ assert(s == "0.00000001");
+
+ s = format("%0.0008f", 1e-05);
+ assert(s == "0.00001000");
+
+ s = "helloworld";
+ string r;
+ r = format("%.2s", s[0 .. 5]);
+ assert(r == "he");
+ r = format("%.20s", s[0 .. 5]);
+ assert(r == "hello");
+ r = format("%8s", s[0 .. 5]);
+ assert(r == " hello");
+
+ byte[] arrbyte = new byte[4];
+ arrbyte[0] = 100;
+ arrbyte[1] = -99;
+ arrbyte[3] = 0;
+ r = format("%s", arrbyte);
+ assert(r == "[100, -99, 0, 0]");
+
+ ubyte[] arrubyte = new ubyte[4];
+ arrubyte[0] = 100;
+ arrubyte[1] = 200;
+ arrubyte[3] = 0;
+ r = format("%s", arrubyte);
+ assert(r == "[100, 200, 0, 0]");
+
+ short[] arrshort = new short[4];
+ arrshort[0] = 100;
+ arrshort[1] = -999;
+ arrshort[3] = 0;
+ r = format("%s", arrshort);
+ assert(r == "[100, -999, 0, 0]");
+
+ ushort[] arrushort = new ushort[4];
+ arrushort[0] = 100;
+ arrushort[1] = 20_000;
+ arrushort[3] = 0;
+ r = format("%s", arrushort);
+ assert(r == "[100, 20000, 0, 0]");
+
+ int[] arrint = new int[4];
+ arrint[0] = 100;
+ arrint[1] = -999;
+ arrint[3] = 0;
+ r = format("%s", arrint);
+ assert(r == "[100, -999, 0, 0]");
+
+ long[] arrlong = new long[4];
+ arrlong[0] = 100;
+ arrlong[1] = -999;
+ arrlong[3] = 0;
+ r = format("%s", arrlong);
+ assert(r == "[100, -999, 0, 0]");
+
+ ulong[] arrulong = new ulong[4];
+ arrulong[0] = 100;
+ arrulong[1] = 999;
+ arrulong[3] = 0;
+ r = format("%s", arrulong);
+ assert(r == "[100, 999, 0, 0]");
+
+ string[] arr2 = new string[4];
+ arr2[0] = "hello";
+ arr2[1] = "world";
+ arr2[3] = "foo";
+ r = format("%s", arr2);
+ assert(r == `["hello", "world", "", "foo"]`);
+
+ r = format("%.8d", 7);
+ assert(r == "00000007");
+ r = format("%.8x", 10);
+ assert(r == "0000000a");
+
+ r = format("%-3d", 7);
+ assert(r == "7 ");
+
+ r = format("%-1*d", 4, 3);
+ assert(r == "3 ");
+
+ r = format("%*d", -3, 7);
+ assert(r == "7 ");
+
+ r = format("%.*d", -3, 7);
+ assert(r == "7");
+
+ r = format("%-1.*f", 2, 3.1415);
+ assert(r == "3.14");
+
+ r = format("abc"c);
+ assert(r == "abc");
+
+ //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");
+
+ r = format("%#x", 0xabcd);
+ assert(r == "0xabcd");
+ r = format("%#X", 0xABCD);
+ assert(r == "0XABCD");
+
+ r = format("%#o", octal!12345);
+ 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");
+ r = format("%+d", -123);
+ assert(r == "-123");
+ r = format("% d", 123);
+ assert(r == " 123");
+ r = format("% d", -123);
+ assert(r == "-123");
+
+ r = format("%%");
+ assert(r == "%");
+
+ r = format("%d", true);
+ assert(r == "1");
+ r = format("%d", false);
+ assert(r == "0");
+
+ r = format("%d", 'a');
+ assert(r == "97");
+ wchar wc = 'a';
+ r = format("%d", wc);
+ assert(r == "97");
+ dchar dc = 'a';
+ r = format("%d", dc);
+ assert(r == "97");
+
+ byte b = byte.max;
+ r = format("%x", b);
+ assert(r == "7f");
+ r = format("%x", ++b);
+ assert(r == "80");
+ r = format("%x", ++b);
+ assert(r == "81");
+
+ short sh = short.max;
+ r = format("%x", sh);
+ assert(r == "7fff");
+ r = format("%x", ++sh);
+ assert(r == "8000");
+ r = format("%x", ++sh);
+ assert(r == "8001");
+
+ i = int.max;
+ r = format("%x", i);
+ assert(r == "7fffffff");
+ r = format("%x", ++i);
+ assert(r == "80000000");
+ r = format("%x", ++i);
+ assert(r == "80000001");
+
+ r = format("%x", 10);
+ assert(r == "a");
+ r = format("%X", 10);
+ assert(r == "A");
+ r = format("%x", 15);
+ assert(r == "f");
+ r = format("%X", 15);
+ assert(r == "F");
+
+ Object c = null;
+ r = format("%s", c);
+ assert(r == "null");
+
+ enum TestEnum
+ {
+ Value1, Value2
+ }
+ r = format("%s", TestEnum.Value2);
+ assert(r == "Value2");
+
+ immutable(char[5])[int] aa = ([3:"hello", 4:"betty"]);
+ r = format("%s", aa.values);
+ assert(r == `["hello", "betty"]` || r == `["betty", "hello"]`);
+ r = format("%s", aa);
+ assert(r == `[3:"hello", 4:"betty"]` || r == `[4:"betty", 3:"hello"]`);
+
+ static const dchar[] ds = ['a','b'];
+ for (int j = 0; j < ds.length; ++j)
+ {
+ r = format(" %d", ds[j]);
+ if (j == 0)
+ assert(r == " 97");
+ else
+ assert(r == " 98");
+ }
+
+ r = format(">%14d<, %s", 15, [1,2,3]);
+ assert(r == "> 15<, [1, 2, 3]");
+
+ assert(format("%8s", "bar") == " bar");
+ assert(format("%8s", "b\u00e9ll\u00f4") == " b\u00e9ll\u00f4");
+}
+
+@safe unittest
+{
+ // bugzilla 3479
+ import std.array;
+ auto stream = appender!(char[])();
+ formattedWrite(stream, "%2$.*1$d", 12, 10);
+ assert(stream.data == "000000000010", stream.data);
+}
+
+@safe unittest
+{
+ // bug 6893
+ import std.array;
+ enum E : ulong { A, B, C }
+ auto stream = appender!(char[])();
+ formattedWrite(stream, "%s", E.C);
+ 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 $(LREF formattedWrite).
+ * args = Variadic list of arguments to _format into returned string.
+ */
+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.array : appender;
+ import std.format : formattedWrite, FormatException;
+ auto w = appender!(immutable(Char)[]);
+ auto n = formattedWrite(w, fmt, args);
+ version (all)
+ {
+ // In the future, this check will be removed to increase consistency
+ // with formattedWrite
+ import std.conv : text;
+ import std.exception : enforce;
+ enforce(n == args.length, new FormatException(
+ text("Orphan format arguments: args[", n, "..", args.length, "]")));
+ }
+ return w.data;
+}
+
+@safe pure unittest
+{
+ import core.exception;
+ import std.exception;
+ import std.format;
+ assertCTFEable!(
+ {
+// assert(format(null) == "");
+ assert(format("foo") == "foo");
+ assert(format("foo%%") == "foo%");
+ assert(format("foo%s", 'C') == "fooC");
+ assert(format("%s foo", "bar") == "bar foo");
+ assert(format("%s foo %s", "bar", "abc") == "bar foo abc");
+ assert(format("foo %d", -123) == "foo -123");
+ assert(format("foo %d", 123) == "foo 123");
+
+ assertThrown!FormatException(format("foo %s"));
+ assertThrown!FormatException(format("foo %s", 123, 456));
+
+ 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.
+ *
+ * Returns:
+ * The slice of `buf` containing the formatted string.
+ *
+ * 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 : RangeError;
+ import std.format : formattedWrite, FormatException;
+ import std.utf : encode;
+
+ size_t i;
+
+ struct Sink
+ {
+ void put(dchar c)
+ {
+ char[4] enc;
+ auto n = encode(enc, c);
+
+ if (buf.length < i + n)
+ throw new RangeError(__FILE__, __LINE__);
+
+ buf[i .. i + n] = enc[0 .. n];
+ i += n;
+ }
+ void put(const(char)[] s)
+ {
+ if (buf.length < i + s.length)
+ throw new RangeError(__FILE__, __LINE__);
+
+ buf[i .. i + s.length] = s[];
+ i += s.length;
+ }
+ void put(const(wchar)[] s)
+ {
+ for (; !s.empty; s.popFront())
+ put(s.front);
+ }
+ void put(const(dchar)[] s)
+ {
+ for (; !s.empty; s.popFront())
+ put(s.front);
+ }
+ }
+ auto n = formattedWrite(Sink(), fmt, args);
+ version (all)
+ {
+ // In the future, this check will be removed to increase consistency
+ // with formattedWrite
+ import std.conv : text;
+ import std.exception : enforce;
+ enforce!FormatException(
+ n == args.length,
+ text("Orphan format arguments: args[", n, " .. ", args.length, "]")
+ );
+ }
+ return buf[0 .. i];
+}
+
+/// 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;
+
+ debug(string) trustedPrintf("std.string.sformat.unittest\n");
+
+ import std.exception;
+ assertCTFEable!(
+ {
+ char[10] buf;
+
+ assert(sformat(buf[], "foo") == "foo");
+ assert(sformat(buf[], "foo%%") == "foo%");
+ assert(sformat(buf[], "foo%s", 'C') == "fooC");
+ assert(sformat(buf[], "%s foo", "bar") == "bar foo");
+ assertThrown!RangeError(sformat(buf[], "%s foo %s", "bar", "abc"));
+ assert(sformat(buf[], "foo %d", -123) == "foo -123");
+ assert(sformat(buf[], "foo %d", 123) == "foo 123");
+
+ assertThrown!FormatException(sformat(buf[], "foo %s"));
+ assertThrown!FormatException(sformat(buf[], "foo %s", 123, 456));
+
+ 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 ~ "'");
+}
+
+// Test for multiple indexes
+@safe unittest
+{
+ auto tmp = format("%2:5$s", 1, 2, 3, 4, 5);
+ assert(tmp == "2345", tmp);
+}
diff --git a/libphobos/src/std/functional.d b/libphobos/src/std/functional.d
new file mode 100644
index 0000000..f35d6ff
--- /dev/null
+++ b/libphobos/src/std/functional.d
@@ -0,0 +1,1564 @@
+// Written in the D programming language.
+
+/**
+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
+$(MREF std, algorithm) or $(MREF std, range).
+
+$(SCRIPT inhibitQuickIndex = 1;)
+$(BOOKTABLE ,
+$(TR $(TH Function Name) $(TH Description)
+)
+ $(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 $(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 $(LREF forward))
+ $(TD Forwards function arguments while saving ref-ness.
+ ))
+ $(TR $(TD $(LREF lessThan), $(LREF greaterThan), $(LREF equalTo))
+ $(TD Ready-made predicate functions to compare two values.
+ ))
+ $(TR $(TD $(LREF memoize))
+ $(TD Creates a function that caches its result for fast re-evaluation.
+ ))
+ $(TR $(TD $(LREF not))
+ $(TD Creates a function that negates another.
+ ))
+ $(TR $(TD $(LREF partial))
+ $(TD Creates a function that binds the first argument of a given function
+ to a given value.
+ ))
+ $(TR $(TD $(LREF reverseArgs), $(LREF binaryReverseArgs))
+ $(TD Predicate that reverses the order of its arguments.
+ ))
+ $(TR $(TD $(LREF toDelegate))
+ $(TD Converts a callable to a delegate.
+ ))
+ $(TR $(TD $(LREF unaryFun), $(LREF binaryFun))
+ $(TD Create a unary or binary function from a string. Most often
+ used when defining algorithms on ranges.
+ ))
+)
+
+Copyright: Copyright Andrei Alexandrescu 2008 - 2009.
+License: $(HTTP boost.org/LICENSE_1_0.txt, Boost License 1.0).
+Authors: $(HTTP erdani.org, Andrei Alexandrescu)
+Source: $(PHOBOSSRC std/_functional.d)
+*/
+/*
+ Copyright Andrei Alexandrescu 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.functional;
+
+import std.meta; // AliasSeq, Reverse
+import std.traits; // isCallable, Parameters
+
+
+private template needOpCallAlias(alias fun)
+{
+ /* Determine whether or not unaryFun and binaryFun need to alias to fun or
+ * fun.opCall. Basically, fun is a function object if fun(...) compiles. We
+ * want is(unaryFun!fun) (resp., is(binaryFun!fun)) to be true if fun is
+ * any function object. There are 4 possible cases:
+ *
+ * 1) fun is the type of a function object with static opCall;
+ * 2) fun is an instance of a function object with static opCall;
+ * 3) fun is the type of a function object with non-static opCall;
+ * 4) fun is an instance of a function object with non-static opCall.
+ *
+ * In case (1), is(unaryFun!fun) should compile, but does not if unaryFun
+ * aliases itself to fun, because typeof(fun) is an error when fun itself
+ * is a type. So it must be aliased to fun.opCall instead. All other cases
+ * should be aliased to fun directly.
+ */
+ static if (is(typeof(fun.opCall) == function))
+ {
+ enum needOpCallAlias = !is(typeof(fun)) && __traits(compiles, () {
+ return fun(Parameters!fun.init);
+ });
+ }
+ else
+ enum needOpCallAlias = false;
+}
+
+/**
+Transforms a string representing an expression into a unary
+function. The string must either use symbol name $(D a) as
+the parameter or provide the symbol via the $(D parmName) argument.
+If $(D fun) is not a string, $(D unaryFun) aliases itself away to $(D fun).
+*/
+
+template unaryFun(alias fun, string parmName = "a")
+{
+ static if (is(typeof(fun) : string))
+ {
+ static if (!fun._ctfeMatchUnary(parmName))
+ {
+ import std.algorithm, std.conv, std.exception, std.math, std.range, std.string;
+ import std.meta, std.traits, std.typecons;
+ }
+ auto unaryFun(ElementType)(auto ref ElementType __a)
+ {
+ mixin("alias " ~ parmName ~ " = __a ;");
+ return mixin(fun);
+ }
+ }
+ else static if (needOpCallAlias!fun)
+ {
+ // Issue 9906
+ alias unaryFun = fun.opCall;
+ }
+ else
+ {
+ alias unaryFun = fun;
+ }
+}
+
+///
+@safe unittest
+{
+ // Strings are compiled into functions:
+ alias isEven = unaryFun!("(a & 1) == 0");
+ assert(isEven(2) && !isEven(1));
+}
+
+@safe unittest
+{
+ static int f1(int a) { return a + 1; }
+ static assert(is(typeof(unaryFun!(f1)(1)) == int));
+ assert(unaryFun!(f1)(41) == 42);
+ int f2(int a) { return a + 1; }
+ static assert(is(typeof(unaryFun!(f2)(1)) == int));
+ assert(unaryFun!(f2)(41) == 42);
+ assert(unaryFun!("a + 1")(41) == 42);
+ //assert(unaryFun!("return a + 1;")(41) == 42);
+
+ int num = 41;
+ assert(unaryFun!"a + 1"(num) == 42);
+
+ // Issue 9906
+ struct Seen
+ {
+ static bool opCall(int n) { return true; }
+ }
+ static assert(needOpCallAlias!Seen);
+ static assert(is(typeof(unaryFun!Seen(1))));
+ assert(unaryFun!Seen(1));
+
+ Seen s;
+ static assert(!needOpCallAlias!s);
+ static assert(is(typeof(unaryFun!s(1))));
+ assert(unaryFun!s(1));
+
+ struct FuncObj
+ {
+ bool opCall(int n) { return true; }
+ }
+ FuncObj fo;
+ static assert(!needOpCallAlias!fo);
+ static assert(is(typeof(unaryFun!fo)));
+ assert(unaryFun!fo(1));
+
+ // Function object with non-static opCall can only be called with an
+ // instance, not with merely the type.
+ static assert(!is(typeof(unaryFun!FuncObj)));
+}
+
+/**
+Transforms a string representing an expression into a binary function. The
+string must either use symbol names $(D a) and $(D b) as the parameters or
+provide the symbols via the $(D parm1Name) and $(D parm2Name) arguments.
+If $(D fun) is not a string, $(D binaryFun) aliases itself away to
+$(D fun).
+*/
+
+template binaryFun(alias fun, string parm1Name = "a",
+ string parm2Name = "b")
+{
+ static if (is(typeof(fun) : string))
+ {
+ static if (!fun._ctfeMatchBinary(parm1Name, parm2Name))
+ {
+ import std.algorithm, std.conv, std.exception, std.math, std.range, std.string;
+ import std.meta, std.traits, std.typecons;
+ }
+ auto binaryFun(ElementType1, ElementType2)
+ (auto ref ElementType1 __a, auto ref ElementType2 __b)
+ {
+ mixin("alias "~parm1Name~" = __a ;");
+ mixin("alias "~parm2Name~" = __b ;");
+ return mixin(fun);
+ }
+ }
+ else static if (needOpCallAlias!fun)
+ {
+ // Issue 9906
+ alias binaryFun = fun.opCall;
+ }
+ else
+ {
+ alias binaryFun = fun;
+ }
+}
+
+///
+@safe unittest
+{
+ alias less = binaryFun!("a < b");
+ assert(less(1, 2) && !less(2, 1));
+ alias greater = binaryFun!("a > b");
+ assert(!greater("1", "2") && greater("2", "1"));
+}
+
+@safe unittest
+{
+ static int f1(int a, string b) { return a + 1; }
+ static assert(is(typeof(binaryFun!(f1)(1, "2")) == int));
+ assert(binaryFun!(f1)(41, "a") == 42);
+ string f2(int a, string b) { return b ~ "2"; }
+ static assert(is(typeof(binaryFun!(f2)(1, "1")) == string));
+ assert(binaryFun!(f2)(1, "4") == "42");
+ assert(binaryFun!("a + b")(41, 1) == 42);
+ //@@BUG
+ //assert(binaryFun!("return a + b;")(41, 1) == 42);
+
+ // Issue 9906
+ struct Seen
+ {
+ static bool opCall(int x, int y) { return true; }
+ }
+ static assert(is(typeof(binaryFun!Seen)));
+ assert(binaryFun!Seen(1,1));
+
+ struct FuncObj
+ {
+ bool opCall(int x, int y) { return true; }
+ }
+ FuncObj fo;
+ static assert(!needOpCallAlias!fo);
+ static assert(is(typeof(binaryFun!fo)));
+ assert(unaryFun!fo(1,1));
+
+ // Function object with non-static opCall can only be called with an
+ // instance, not with merely the type.
+ static assert(!is(typeof(binaryFun!FuncObj)));
+}
+
+// skip all ASCII chars except a .. z, A .. Z, 0 .. 9, '_' and '.'.
+private uint _ctfeSkipOp(ref string op)
+{
+ if (!__ctfe) assert(false);
+ import std.ascii : isASCII, isAlphaNum;
+ immutable oldLength = op.length;
+ while (op.length)
+ {
+ immutable front = op[0];
+ if (front.isASCII() && !(front.isAlphaNum() || front == '_' || front == '.'))
+ op = op[1..$];
+ else
+ break;
+ }
+ return oldLength != op.length;
+}
+
+// skip all digits
+private uint _ctfeSkipInteger(ref string op)
+{
+ if (!__ctfe) assert(false);
+ import std.ascii : isDigit;
+ immutable oldLength = op.length;
+ while (op.length)
+ {
+ immutable front = op[0];
+ if (front.isDigit())
+ op = op[1..$];
+ else
+ break;
+ }
+ return oldLength != op.length;
+}
+
+// skip name
+private uint _ctfeSkipName(ref string op, string name)
+{
+ if (!__ctfe) assert(false);
+ if (op.length >= name.length && op[0 .. name.length] == name)
+ {
+ op = op[name.length..$];
+ return 1;
+ }
+ return 0;
+}
+
+// returns 1 if $(D fun) is trivial unary function
+private uint _ctfeMatchUnary(string fun, string name)
+{
+ if (!__ctfe) assert(false);
+ fun._ctfeSkipOp();
+ for (;;)
+ {
+ immutable h = fun._ctfeSkipName(name) + fun._ctfeSkipInteger();
+ if (h == 0)
+ {
+ fun._ctfeSkipOp();
+ break;
+ }
+ else if (h == 1)
+ {
+ if (!fun._ctfeSkipOp())
+ break;
+ }
+ else
+ return 0;
+ }
+ return fun.length == 0;
+}
+
+@safe unittest
+{
+ static assert(!_ctfeMatchUnary("sqrt(ё)", "ё"));
+ static assert(!_ctfeMatchUnary("ё.sqrt", "ё"));
+ static assert(!_ctfeMatchUnary(".ё+ё", "ё"));
+ static assert(!_ctfeMatchUnary("_ё+ё", "ё"));
+ static assert(!_ctfeMatchUnary("ёё", "ё"));
+ 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("1 != a", "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"));
+ static assert(_ctfeMatchUnary("123 >a", "a"));
+ static assert(_ctfeMatchUnary("a>82", "a"));
+ static assert(_ctfeMatchUnary("ё>82", "ё"));
+ static assert(_ctfeMatchUnary("ё[ё(ё)]", "ё"));
+ static assert(_ctfeMatchUnary("ё[21]", "ё"));
+}
+
+// returns 1 if $(D fun) is trivial binary function
+private uint _ctfeMatchBinary(string fun, string name1, string name2)
+{
+ if (!__ctfe) assert(false);
+ fun._ctfeSkipOp();
+ for (;;)
+ {
+ immutable h = fun._ctfeSkipName(name1) + fun._ctfeSkipName(name2) + fun._ctfeSkipInteger();
+ if (h == 0)
+ {
+ fun._ctfeSkipOp();
+ break;
+ }
+ else if (h == 1)
+ {
+ if (!fun._ctfeSkipOp())
+ break;
+ }
+ else
+ return 0;
+ }
+ return fun.length == 0;
+}
+
+@safe unittest
+{
+
+ static assert(!_ctfeMatchBinary("sqrt(ё)", "ё", "b"));
+ static assert(!_ctfeMatchBinary("ё.sqrt", "ё", "b"));
+ static assert(!_ctfeMatchBinary(".ё+ё", "ё", "b"));
+ static assert(!_ctfeMatchBinary("_ё+ё", "ё", "b"));
+ static assert(!_ctfeMatchBinary("ёё", "ё", "b"));
+ 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("1 != a", "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"));
+ static assert(_ctfeMatchBinary("123 >a", "a", "b"));
+ static assert(_ctfeMatchBinary("a>82", "a", "b"));
+ static assert(_ctfeMatchBinary("ё>82", "ё", "q"));
+ static assert(_ctfeMatchBinary("ё[ё(10)]", "ё", "q"));
+ static assert(_ctfeMatchBinary("ё[21]", "ё", "q"));
+
+ static assert(!_ctfeMatchBinary("sqrt(ё)+b", "b", "ё"));
+ static assert(!_ctfeMatchBinary("ё.sqrt-b", "b", "ё"));
+ static assert(!_ctfeMatchBinary(".ё+b", "b", "ё"));
+ static assert(!_ctfeMatchBinary("_b+ё", "b", "ё"));
+ static assert(!_ctfeMatchBinary("ba", "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"));
+ static assert(_ctfeMatchBinary("b != a", "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"));
+ static assert(_ctfeMatchBinary("b >a", "b", "a"));
+ static assert(_ctfeMatchBinary("a>b", "b", "a"));
+ static assert(_ctfeMatchBinary("ё>b", "b", "ё"));
+ static assert(_ctfeMatchBinary("b[ё(-1)]", "b", "ё"));
+ static assert(_ctfeMatchBinary("ё[-21]", "b", "ё"));
+}
+
+//undocumented
+template safeOp(string 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");
+ }
+
+ 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))
+ {
+ static if (S == "<=" || S == "<")
+ {
+ static if (mostNegative!T0 < 0)
+ immutable result = a < 0 || unsafeOp(a, b);
+ else
+ immutable result = b >= 0 && unsafeOp(a, b);
+ }
+ else
+ {
+ static if (mostNegative!T0 < 0)
+ immutable result = a >= 0 && unsafeOp(a, b);
+ else
+ immutable result = b < 0 || unsafeOp(a, b);
+ }
+ }
+ else
+ {
+ static assert(is(typeof(mixin("a "~S~" b"))),
+ "Invalid arguments: Cannot compare types " ~ T0.stringof ~ " and " ~ T1.stringof ~ ".");
+
+ immutable result = mixin("a "~S~" b");
+ }
+ return result;
+ }
+}
+
+@safe unittest //check user defined types
+{
+ import std.algorithm.comparison : equal;
+ struct Foo
+ {
+ int a;
+ auto opEquals(Foo foo)
+ {
+ return a == foo.a;
+ }
+ }
+ assert(safeOp!"!="(Foo(1), Foo(2)));
+}
+
+/**
+ Predicate that returns $(D_PARAM a < b).
+ Correctly compares signed and unsigned integers, ie. -1 < 2U.
+*/
+alias lessThan = safeOp!"<";
+
+///
+pure @safe @nogc nothrow unittest
+{
+ assert(lessThan(2, 3));
+ assert(lessThan(2U, 3U));
+ assert(lessThan(2, 3.0));
+ assert(lessThan(-2, 3U));
+ assert(lessThan(2, 3U));
+ assert(!lessThan(3U, -2));
+ assert(!lessThan(3U, 2));
+ assert(!lessThan(0, 0));
+ assert(!lessThan(0U, 0));
+ assert(!lessThan(0, 0U));
+}
+
+/**
+ Predicate that returns $(D_PARAM a > b).
+ Correctly compares signed and unsigned integers, ie. 2U > -1.
+*/
+alias greaterThan = safeOp!">";
+
+///
+@safe unittest
+{
+ assert(!greaterThan(2, 3));
+ assert(!greaterThan(2U, 3U));
+ assert(!greaterThan(2, 3.0));
+ assert(!greaterThan(-2, 3U));
+ assert(!greaterThan(2, 3U));
+ assert(greaterThan(3U, -2));
+ assert(greaterThan(3U, 2));
+ assert(!greaterThan(0, 0));
+ assert(!greaterThan(0U, 0));
+ assert(!greaterThan(0, 0U));
+}
+
+/**
+ Predicate that returns $(D_PARAM a == b).
+ Correctly compares signed and unsigned integers, ie. !(-1 == ~0U).
+*/
+alias equalTo = safeOp!"==";
+
+///
+@safe unittest
+{
+ assert(equalTo(0U, 0));
+ assert(equalTo(0, 0U));
+ assert(!equalTo(-1, ~0U));
+}
+/**
+ N-ary predicate that reverses the order of arguments, e.g., given
+ $(D pred(a, b, c)), returns $(D pred(c, b, a)).
+*/
+template reverseArgs(alias pred)
+{
+ auto reverseArgs(Args...)(auto ref Args args)
+ if (is(typeof(pred(Reverse!args))))
+ {
+ return pred(Reverse!args);
+ }
+}
+
+///
+@safe unittest
+{
+ alias gt = reverseArgs!(binaryFun!("a < b"));
+ assert(gt(2, 1) && !gt(1, 1));
+ int x = 42;
+ bool xyz(int a, int b) { return a * x < b / x; }
+ auto foo = &xyz;
+ 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());
+}
+
+/**
+ Binary predicate that reverses the order of arguments, e.g., given
+ $(D pred(a, b)), returns $(D pred(b, a)).
+*/
+template binaryReverseArgs(alias pred)
+{
+ auto binaryReverseArgs(ElementType1, ElementType2)
+ (auto ref ElementType1 a, auto ref ElementType2 b)
+ {
+ return pred(b, a);
+ }
+}
+
+///
+@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;
+ foo(4, 5);
+ alias zyx = binaryReverseArgs!(foo);
+ assert(zyx(5, 4) == foo(4, 5));
+}
+
+/**
+Negates predicate $(D pred).
+ */
+template not(alias pred)
+{
+ auto not(T...)(auto ref T args)
+ {
+ static if (is(typeof(!pred(args))))
+ return !pred(args);
+ else static if (T.length == 1)
+ return !unaryFun!pred(args);
+ else static if (T.length == 2)
+ return !binaryFun!pred(args);
+ else
+ static assert(0);
+ }
+}
+
+///
+@safe unittest
+{
+ import std.algorithm.searching : find;
+ import std.functional;
+ import std.uni : isWhite;
+ string a = " Hello, world!";
+ assert(find!(not!isWhite)(a) == "Hello, world!");
+}
+
+@safe unittest
+{
+ assert(not!"a != 5"(5));
+ assert(not!"a != b"(5, 5));
+
+ assert(not!(() => false)());
+ assert(not!(a => a != 5)(5));
+ assert(not!((a, b) => a != b)(5, 5));
+ assert(not!((a, b, c) => a * b * c != 125 )(5, 5, 5));
+}
+
+/**
+$(LINK2 http://en.wikipedia.org/wiki/Partial_application, Partially
+applies) $(D_PARAM fun) by tying its first argument to $(D_PARAM arg).
+ */
+template partial(alias fun, alias arg)
+{
+ static if (is(typeof(fun) == delegate) || is(typeof(fun) == function))
+ {
+ import std.traits : ReturnType;
+ ReturnType!fun partial(Parameters!fun[1..$] args2)
+ {
+ return fun(arg, args2);
+ }
+ }
+ else
+ {
+ auto partial(Ts...)(Ts args2)
+ {
+ static if (is(typeof(fun(arg, args2))))
+ {
+ return fun(arg, args2);
+ }
+ else
+ {
+ static string errormsg()
+ {
+ string msg = "Cannot call '" ~ fun.stringof ~ "' with arguments " ~
+ "(" ~ arg.stringof;
+ foreach (T; Ts)
+ msg ~= ", " ~ T.stringof;
+ msg ~= ").";
+ return msg;
+ }
+ static assert(0, errormsg());
+ }
+ }
+ }
+}
+
+///
+@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
+@safe unittest
+{
+ static int f1(int a, int b) { return a + b; }
+ assert(partial!(f1, 5)(6) == 11);
+
+ int f2(int a, int b) { return a + b; }
+ int x = 5;
+ assert(partial!(f2, x)(6) == 11);
+ x = 7;
+ assert(partial!(f2, x)(6) == 13);
+ static assert(partial!(f2, 5)(6) == 11);
+
+ auto dg = &f2;
+ auto f3 = &partial!(dg, x);
+ assert(f3(6) == 13);
+
+ static int funOneArg(int a) { return a; }
+ assert(partial!(funOneArg, 1)() == 1);
+
+ static int funThreeArgs(int a, int b, int c) { return a + b + c; }
+ alias funThreeArgs1 = partial!(funThreeArgs, 1);
+ assert(funThreeArgs1(2, 3) == 6);
+ static assert(!is(typeof(funThreeArgs1(2))));
+
+ enum xe = 5;
+ alias fe = partial!(f2, xe);
+ static assert(fe(6) == 11);
+}
+
+// tests for partially evaluating templated/overloaded callables
+@safe unittest
+{
+ static auto add(A, B)(A x, B y)
+ {
+ return x + y;
+ }
+
+ alias add5 = partial!(add, 5);
+ assert(add5(6) == 11);
+ static assert(!is(typeof(add5())));
+ static assert(!is(typeof(add5(6, 7))));
+
+ // taking address of templated partial evaluation needs explicit type
+ auto dg = &add5!(int);
+ assert(dg(6) == 11);
+
+ int x = 5;
+ alias addX = partial!(add, x);
+ assert(addX(6) == 11);
+
+ static struct Callable
+ {
+ static string opCall(string a, string b) { return a ~ b; }
+ int opCall(int a, int b) { return a * b; }
+ double opCall(double a, double b) { return a + b; }
+ }
+ Callable callable;
+ assert(partial!(Callable, "5")("6") == "56");
+ assert(partial!(callable, 5)(6) == 30);
+ assert(partial!(callable, 7.0)(3.0) == 7.0 + 3.0);
+
+ static struct TCallable
+ {
+ auto opCall(A, B)(A a, B b)
+ {
+ return a + b;
+ }
+ }
+ TCallable tcallable;
+ assert(partial!(tcallable, 5)(6) == 11);
+ static assert(!is(typeof(partial!(tcallable, "5")(6))));
+
+ static A funOneArg(A)(A a) { return a; }
+ alias funOneArg1 = partial!(funOneArg, 1);
+ assert(funOneArg1() == 1);
+
+ static auto funThreeArgs(A, B, C)(A a, B b, C c) { return a + b + c; }
+ alias funThreeArgs1 = partial!(funThreeArgs, 1);
+ assert(funThreeArgs1(2, 3) == 6);
+ static assert(!is(typeof(funThreeArgs1(1))));
+
+ auto dg2 = &funOneArg1!();
+ assert(dg2() == 1);
+}
+
+/**
+Takes multiple functions and adjoins them together. The result is a
+$(REF Tuple, std,typecons) with one element per passed-in function. Upon
+invocation, the returned tuple is the adjoined results of all
+functions.
+
+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)
+{
+ alias adjoin = F[0];
+}
+/// ditto
+template adjoin(F...)
+if (F.length > 1)
+{
+ auto adjoin(V...)(auto ref V a)
+ {
+ import std.typecons : tuple;
+ static if (F.length == 2)
+ {
+ return tuple(F[0](a), F[1](a));
+ }
+ else static if (F.length == 3)
+ {
+ return tuple(F[0](a), F[1](a), F[2](a));
+ }
+ else
+ {
+ import std.format : format;
+ import std.range : iota;
+ return mixin (q{tuple(%(F[%s](a)%|, %))}.format(iota(0, F.length)));
+ }
+ }
+}
+
+///
+@safe unittest
+{
+ 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);
+ assert(is(typeof(x) == Tuple!(bool, int)));
+ assert(x[0] == true && x[1] == 2);
+}
+
+@safe unittest
+{
+ 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; }
+ auto x2 = adjoin!(F1, F2)(5);
+ assert(is(typeof(x2) == Tuple!(bool, int)));
+ assert(x2[0] && x2[1] == 2);
+ auto x3 = adjoin!(F1, F2, F2)(5);
+ assert(is(typeof(x3) == Tuple!(bool, int, int)));
+ assert(x3[0] && x3[1] == 2 && x3[2] == 2);
+
+ bool F4(int a) { return a != x1; }
+ alias eff4 = adjoin!(F4);
+ static struct S
+ {
+ bool delegate(int) @safe store;
+ int fun() { return 42 + store(5); }
+ }
+ S s;
+ s.store = (int a) { return eff4(a); };
+ auto x4 = s.fun();
+ assert(x4 == 43);
+}
+
+@safe unittest
+{
+ 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;
+ assert(afun(5) == tuple(5, 10, 15, 25, -5));
+
+ static class C{}
+ alias IC = immutable(C);
+ IC foo(){return typeof(return).init;}
+ Tuple!(IC, IC, IC, IC) ret1 = adjoin!(foo, foo, foo, foo)();
+
+ static struct S{int* p;}
+ alias IS = immutable(S);
+ IS bar(){return typeof(return).init;}
+ enum Tuple!(IS, IS, IS, IS) ret2 = adjoin!(bar, bar, bar, bar)();
+}
+
+/**
+ 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.
+
+ See_Also: $(LREF pipe)
+*/
+template compose(fun...)
+{
+ static if (fun.length == 1)
+ {
+ alias compose = unaryFun!(fun[0]);
+ }
+ else static if (fun.length == 2)
+ {
+ // starch
+ alias fun0 = unaryFun!(fun[0]);
+ alias fun1 = unaryFun!(fun[1]);
+
+ // protein: the core composition operation
+ typeof({ E a; return fun0(fun1(a)); }()) compose(E)(E a)
+ {
+ return fun0(fun1(a));
+ }
+ }
+ else
+ {
+ // protein: assembling operations
+ alias compose = compose!(fun[0], compose!(fun[1 .. $]));
+ }
+}
+
+///
+@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
+ lead to more readable code in some situation because the order of
+ execution is the same as lexical order.
+
+ Example:
+
+----
+// Read an entire text file, split the resulting string in
+// whitespace-separated tokens, and then convert each token into an
+// integer
+int[] a = pipe!(readText, split, map!(to!(int)))("file.txt");
+----
+
+ See_Also: $(LREF compose)
+ */
+alias pipe(fun...) = compose!(Reverse!(fun));
+
+@safe unittest
+{
+ import std.conv : to;
+ string foo(int a) { return to!(string)(a); }
+ int bar(string a) { return to!(int)(a) + 1; }
+ double baz(int a) { return a + 0.5; }
+ assert(compose!(baz, bar, foo)(1) == 2.5);
+ assert(pipe!(foo, bar, baz)(1) == 2.5);
+
+ assert(compose!(baz, `to!(int)(a) + 1`, foo)(1) == 2.5);
+ assert(compose!(baz, bar)("1"[]) == 2.5);
+
+ assert(compose!(baz, bar)("1") == 2.5);
+
+ assert(compose!(`a + 0.5`, `to!(int)(a) + 1`, foo)(1) == 2.5);
+}
+
+/**
+ * $(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 $(HTTP docs.google.com/viewer?url=http%3A%2F%2Fhop.perl.plover.com%2Fbook%2Fpdf%2F03CachingAndMemoization.pdf, this book chapter).
+
+Example:
+----
+double transmogrify(int a, string b)
+{
+ ... expensive computation ...
+}
+alias fastTransmogrify = memoize!transmogrify;
+unittest
+{
+ auto slow = transmogrify(2, "hello");
+ auto fast = fastTransmogrify(2, "hello");
+ assert(slow == fast);
+}
+----
+
+Technically the memoized function should be pure because $(D memoize) assumes it will
+always return the same result for a given tuple of arguments. However, $(D memoize) does not
+enforce that because sometimes it
+is useful to memoize an impure function, too.
+*/
+template memoize(alias fun)
+{
+ import std.traits : ReturnType;
+ // alias Args = Parameters!fun; // Bugzilla 13580
+
+ ReturnType!fun memoize(Parameters!fun args)
+ {
+ alias Args = Parameters!fun;
+ import std.typecons : Tuple;
+
+ static ReturnType!fun[Tuple!Args] memo;
+ auto t = Tuple!Args(args);
+ if (auto p = t in memo)
+ return *p;
+ return memo[t] = fun(args);
+ }
+}
+
+/// ditto
+template memoize(alias fun, uint maxSize)
+{
+ 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 { Parameters!fun args; ReturnType!fun res; }
+ static Value[] memo;
+ static size_t[] initialized;
+
+ if (!memo.length)
+ {
+ 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];
+ 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];
+ }
+
+ import core.bitop : bt, bts;
+ import std.conv : emplace;
+
+ size_t hash;
+ foreach (ref arg; args)
+ hash = hashOf(arg, hash);
+ // cuckoo hashing
+ immutable idx1 = hash % maxSize;
+ if (!bt(initialized.ptr, idx1))
+ {
+ emplace(&memo[idx1], args, fun(args));
+ bts(initialized.ptr, idx1); // only set to initialized after setting args and value (bugzilla 14025)
+ return memo[idx1].res;
+ }
+ else if (memo[idx1].args == args)
+ return memo[idx1].res;
+ // FNV prime
+ immutable idx2 = (hash * 16_777_619) % maxSize;
+ if (!bt(initialized.ptr, idx2))
+ {
+ emplace(&memo[idx2], memo[idx1]);
+ bts(initialized.ptr, idx2); // only set to initialized after setting args and value (bugzilla 14025)
+ }
+ else if (memo[idx2].args == args)
+ return memo[idx2].res;
+ else if (idx1 != idx2)
+ memo[idx2] = memo[idx1];
+
+ memo[idx1] = Value(args, fun(args));
+ return memo[idx1].res;
+ }
+}
+
+/**
+ * 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:
+ */
+@safe unittest
+{
+ ulong fib(ulong n) @safe
+ {
+ return n < 2 ? n : memoize!fib(n - 2) + memoize!fib(n - 1);
+ }
+ assert(fib(10) == 55);
+}
+
+/**
+ * To improve the speed of the factorial function,
+ */
+@safe unittest
+{
+ ulong fact(ulong n) @safe
+ {
+ return n < 2 ? 1 : n * memoize!fact(n - 1);
+ }
+ assert(fact(10) == 3628800);
+}
+
+/**
+ * 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.
+ */
+@safe unittest
+{
+ ulong factImpl(ulong n) @safe
+ {
+ return n < 2 ? 1 : n * factImpl(n - 1);
+ }
+ alias fact = memoize!factImpl;
+ assert(fact(10) == 3628800);
+}
+
+/**
+ * When the $(D maxSize) parameter is specified, memoize will used
+ * a fixed size hash table to limit the number of cached entries.
+ */
+@system unittest // not @safe due to memoize
+{
+ ulong fact(ulong n)
+ {
+ // Memoize no more than 8 values
+ return n < 2 ? 1 : n * memoize!(fact, 8)(n - 1);
+ }
+ assert(fact(8) == 40320);
+ // using more entries than maxSize will overwrite existing entries
+ assert(fact(10) == 3628800);
+}
+
+@system unittest // not @safe due to memoize
+{
+ import core.math : sqrt;
+ alias msqrt = memoize!(function double(double x) { return sqrt(x); });
+ auto y = msqrt(2.0);
+ assert(y == msqrt(2.0));
+ y = msqrt(4.0);
+ assert(y == sqrt(4.0));
+
+ // alias mrgb2cmyk = memoize!rgb2cmyk;
+ // auto z = mrgb2cmyk([43, 56, 76]);
+ // assert(z == mrgb2cmyk([43, 56, 76]));
+
+ //alias mfib = memoize!fib;
+
+ static ulong fib(ulong n) @safe
+ {
+ alias mfib = memoize!fib;
+ return n < 2 ? 1 : mfib(n - 2) + mfib(n - 1);
+ }
+
+ auto z = fib(10);
+ assert(z == 89);
+
+ static ulong fact(ulong n) @safe
+ {
+ alias mfact = memoize!fact;
+ return n < 2 ? 1 : n * mfact(n - 1);
+ }
+ assert(fact(10) == 3628800);
+
+ // Issue 12568
+ static uint len2(const string s) { // Error
+ alias mLen2 = memoize!len2;
+ if (s.length == 0)
+ return 0;
+ else
+ return 1 + mLen2(s[1 .. $]);
+ }
+
+ int _func(int x) @safe { return 1; }
+ alias func = memoize!(_func, 10);
+ assert(func(int.init) == 1);
+ assert(func(int.init) == 1);
+}
+
+// 16079: memoize should work with arrays
+@safe unittest
+{
+ int executed = 0;
+ T median(T)(const T[] nums) {
+ import std.algorithm.sorting : sort;
+ executed++;
+ auto arr = nums.dup;
+ arr.sort();
+ if (arr.length % 2)
+ return arr[$ / 2];
+ else
+ return (arr[$ / 2 - 1]
+ + arr[$ / 2]) / 2;
+ }
+
+ alias fastMedian = memoize!(median!int);
+
+ assert(fastMedian([7, 5, 3]) == 5);
+ assert(fastMedian([7, 5, 3]) == 5);
+
+ assert(executed == 1);
+}
+
+// 16079: memoize should work with structs
+@safe unittest
+{
+ int executed = 0;
+ T pickFirst(T)(T first)
+ {
+ executed++;
+ return first;
+ }
+
+ struct Foo { int k; }
+ Foo A = Foo(3);
+
+ alias first = memoize!(pickFirst!Foo);
+ assert(first(Foo(3)) == A);
+ assert(first(Foo(3)) == A);
+ assert(executed == 1);
+}
+
+// 16079: memoize should work with classes
+@safe unittest
+{
+ int executed = 0;
+ T pickFirst(T)(T first)
+ {
+ executed++;
+ return first;
+ }
+
+ class Bar
+ {
+ size_t k;
+ this(size_t k)
+ {
+ this.k = k;
+ }
+ override size_t toHash()
+ {
+ return k;
+ }
+ override bool opEquals(Object o)
+ {
+ auto b = cast(Bar) o;
+ return b && k == b.k;
+ }
+ }
+
+ alias firstClass = memoize!(pickFirst!Bar);
+ assert(firstClass(new Bar(3)).k == 3);
+ assert(firstClass(new Bar(3)).k == 3);
+ assert(executed == 1);
+}
+
+private struct DelegateFaker(F)
+{
+ import std.typecons : FuncInfo, MemberFunctionGenerator;
+
+ // for @safe
+ static F castToF(THIS)(THIS x) @trusted
+ {
+ return cast(F) x;
+ }
+
+ /*
+ * What all the stuff below does is this:
+ *--------------------
+ * struct DelegateFaker(F) {
+ * extern(linkage)
+ * [ref] ReturnType!F doIt(Parameters!F args) [@attributes]
+ * {
+ * auto fp = cast(F) &this;
+ * return fp(args);
+ * }
+ * }
+ *--------------------
+ */
+
+ // We will use MemberFunctionGenerator in std.typecons. This is a policy
+ // configuration for generating the doIt().
+ template GeneratingPolicy()
+ {
+ // Inform the genereator that we only have type information.
+ enum WITHOUT_SYMBOL = true;
+
+ // Generate the function body of doIt().
+ template generateFunctionBody(unused...)
+ {
+ enum generateFunctionBody =
+ // [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
+ // a function pointer that points to the function to be called.
+ // Cast it to the correct type and call it.
+
+ auto fp = castToF(&this);
+ return fp(args);
+ };
+ }
+ }
+ // Type information used by the generated code.
+ alias FuncInfo_doIt = FuncInfo!(F);
+
+ // Generate the member function doIt().
+ mixin( MemberFunctionGenerator!(GeneratingPolicy!())
+ .generateFunction!("FuncInfo_doIt", "doIt", F) );
+}
+
+/**
+ * Convert a callable to a delegate with the same parameter list and
+ * return type, avoiding heap allocations and use of auxiliary storage.
+ *
+ * Example:
+ * ----
+ * void doStuff() {
+ * writeln("Hello, world.");
+ * }
+ *
+ * void runDelegate(void delegate() myDelegate) {
+ * myDelegate();
+ * }
+ *
+ * auto delegateToPass = toDelegate(&doStuff);
+ * runDelegate(delegateToPass); // Calls doStuff, prints "Hello, world."
+ * ----
+ *
+ * BUGS:
+ * $(UL
+ * $(LI Does not work with $(D @safe) functions.)
+ * $(LI Ignores C-style / D-style variadic arguments.)
+ * )
+ */
+auto toDelegate(F)(auto ref F fp)
+if (isCallable!(F))
+{
+ static if (is(F == delegate))
+ {
+ return fp;
+ }
+ else static if (is(typeof(&F.opCall) == delegate)
+ || (is(typeof(&F.opCall) V : V*) && is(V == function)))
+ {
+ return toDelegate(&fp.opCall);
+ }
+ else
+ {
+ alias DelType = typeof(&(new DelegateFaker!(F)).doIt);
+
+ static struct DelegateFields {
+ union {
+ DelType del;
+ //pragma(msg, typeof(del));
+
+ struct {
+ void* contextPtr;
+ void* funcPtr;
+ }
+ }
+ }
+
+ // fp is stored in the returned delegate's context pointer.
+ // The returned delegate's function pointer points to
+ // DelegateFaker.doIt.
+ DelegateFields df;
+
+ df.contextPtr = cast(void*) fp;
+
+ DelegateFaker!(F) dummy;
+ auto dummyDel = &dummy.doIt;
+ df.funcPtr = dummyDel.funcptr;
+
+ return df.del;
+ }
+}
+
+///
+@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;
+ }
+
+ uint myNum = 0;
+ auto incMyNumDel = toDelegate(&inc);
+ int delegate(ref uint) dg = incMyNumDel;
+ auto returnVal = incMyNumDel(myNum);
+ assert(myNum == 1);
+
+ interface I { int opCall(); }
+ class C: I { int opCall() { inc(myNum); return myNum;} }
+ auto c = new C;
+ auto i = cast(I) c;
+
+ auto getvalc = toDelegate(c);
+ assert(getvalc() == 2);
+
+ auto getvali = toDelegate(i);
+ assert(getvali() == 3);
+
+ struct S1 { int opCall() { inc(myNum); return myNum; } }
+ static assert(!is(typeof(&s1.opCall) == delegate));
+ S1 s1;
+ auto getvals1 = toDelegate(s1);
+ assert(getvals1() == 4);
+
+ struct S2 { static int opCall() { return 123456; } }
+ static assert(!is(typeof(&S2.opCall) == delegate));
+ S2 s2;
+ auto getvals2 =&S2.opCall;
+ assert(getvals2() == 123456);
+
+ /* test for attributes */
+ {
+ static int refvar = 0xDeadFace;
+
+ static ref int func_ref() { return refvar; }
+ static int func_pure() pure { return 1; }
+ static int func_nothrow() nothrow { return 2; }
+ static int func_property() @property { return 3; }
+ static int func_safe() @safe { return 4; }
+ 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 nothrow @safe { return 8; }
+
+ auto dg_ref = toDelegate(&func_ref);
+ 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]
+
+ assert(dg_ref() == refvar);
+ assert(dg_pure() == 1);
+ assert(dg_nothrow() == 2);
+ assert(dg_property() == 3);
+ assert(dg_safe() == 4);
+ assert(dg_trusted() == 5);
+ assert(dg_system() == 6);
+ assert(dg_pure_nothrow() == 7);
+ assert(dg_pure_nothrow_safe() == 8);
+ }
+ /* test for linkage */
+ {
+ struct S
+ {
+ extern(C) static void xtrnC() {}
+ extern(D) static void xtrnD() {}
+ }
+ auto dg_xtrnC = toDelegate(&S.xtrnC);
+ auto dg_xtrnD = toDelegate(&S.xtrnD);
+ static assert(! is(typeof(dg_xtrnC) == typeof(dg_xtrnD)));
+ }
+}
+
+/**
+Forwards function arguments with saving ref-ness.
+*/
+template forward(args...)
+{
+ static if (args.length)
+ {
+ 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 = AliasSeq!(fwd, forward!(args[1..$]));
+ }
+ else
+ alias forward = AliasSeq!();
+}
+
+///
+@safe unittest
+{
+ class C
+ {
+ static int foo(int n) { return 1; }
+ static int foo(ref int n) { return 2; }
+ }
+ int bar()(auto ref int x) { return C.foo(forward!x); }
+
+ assert(bar(1) == 1);
+ int i;
+ assert(bar(i) == 2);
+}
+
+///
+@safe unittest
+{
+ 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); }
+
+ // forwards all arguments with swapping order
+ void baz(Args...)(auto ref Args args) { return foo(forward!args[$/2..$], forward!args[0..$/2]); }
+
+ string s;
+ bar(1, s);
+ assert(s == "Hello");
+ baz(s, 2);
+ assert(s == "HelloHello");
+}
+
+@safe unittest
+{
+ auto foo(TL...)(auto ref TL args)
+ {
+ string result = "";
+ foreach (i, _; args)
+ {
+ //pragma(msg, "[",i,"] ", __traits(isRef, args[i]) ? "L" : "R");
+ result ~= __traits(isRef, args[i]) ? "L" : "R";
+ }
+ return result;
+ }
+
+ string bar(TL...)(auto ref TL args)
+ {
+ return foo(forward!args);
+ }
+ string baz(TL...)(auto ref TL args)
+ {
+ int x;
+ return foo(forward!args[3], forward!args[2], 1, forward!args[1], forward!args[0], x);
+ }
+
+ struct S {}
+ S makeS(){ return S(); }
+ int n;
+ string s;
+ assert(bar(S(), makeS(), n, s) == "RRLL");
+ assert(baz(S(), makeS(), n, s) == "LLRRRL");
+}
+
+@safe unittest
+{
+ ref int foo(return ref int a) { return a; }
+ ref int bar(Args)(auto ref Args args)
+ {
+ return foo(forward!args);
+ }
+ static assert(!__traits(compiles, { auto x1 = bar(3); })); // case of NG
+ int value = 3;
+ auto x2 = bar(value); // case of OK
+}
diff --git a/libphobos/src/std/getopt.d b/libphobos/src/std/getopt.d
new file mode 100644
index 0000000..5beddcc
--- /dev/null
+++ b/libphobos/src/std/getopt.d
@@ -0,0 +1,1857 @@
+// Written in the D programming language.
+
+/**
+Processing of command line options.
+
+The getopt module implements a $(D getopt) function, which adheres to
+the POSIX syntax for command line options. GNU extensions are
+supported in the form of long options introduced by a double dash
+("--"). Support for bundling of command line options, as was the case
+with the more traditional single-letter approach, is provided but not
+enabled by default.
+
+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
+ the passed-in pointers.
+Source: $(PHOBOSSRC std/_getopt.d)
+*/
+/*
+ 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)
+*/
+module std.getopt;
+
+import std.exception; // basicExceptionCtors
+import std.traits;
+
+/**
+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
+{
+ 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 a string array.
+
+ Synopsis:
+
+---------
+import std.getopt;
+
+string data = "file.dat";
+int length = 24;
+bool verbose;
+enum Color { no, yes };
+Color color;
+
+void main(string[] args)
+{
+ auto helpInformation = getopt(
+ args,
+ "length", &length, // numeric
+ "file", &data, // string
+ "verbose", &verbose, // flag
+ "color", "Information about this color", &color); // enum
+ ...
+
+ if (helpInformation.helpWanted)
+ {
+ defaultGetoptPrinter("Some information about the program.",
+ helpInformation.options);
+ }
+}
+---------
+
+ 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 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.
+
+ In all cases, the command-line options that were parsed and used by
+ $(D getopt) are removed from $(D args). Whatever in the
+ arguments did not look like an option is left in $(D args) for
+ further processing by the program. Values that were unaffected by the
+ 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
+ but not present), a $(D ConvException) exception is thrown.
+ 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.
+
+ Depending on the type of the pointer being bound, $(D getopt)
+ recognizes the following kinds of options:
+
+ $(OL
+ $(LI $(I Boolean options). A lone argument sets the option to $(D true).
+ Additionally $(B true) or $(B false) can be set within the option separated
+ with an "=" sign:
+
+---------
+ bool verbose = false, debugging = true;
+ getopt(args, "verbose", &verbose, "debug", &debugging);
+---------
+
+ To set $(D verbose) to $(D true), invoke the program with either
+ $(D --verbose) or $(D --verbose=true).
+
+ To set $(D debugging) to $(D false), invoke the program with
+ $(D --debugging=false).
+ )
+
+ $(LI $(I Numeric options.) If an option is bound to a numeric type, a
+ number is expected as the next option, or right within the option separated
+ with an "=" sign:
+
+---------
+ uint timeout;
+ getopt(args, "timeout", &timeout);
+---------
+
+ To set $(D timeout) to $(D 5), invoke the program with either
+ $(D --timeout=5) or $(D --timeout 5).
+ )
+
+ $(LI $(I Incremental options.) If an option name has a "+" suffix and is
+ bound to a numeric type, then the option's value tracks the number of times
+ the option occurred on the command line:
+
+---------
+ uint paranoid;
+ getopt(args, "paranoid+", &paranoid);
+---------
+
+ 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
+ $(D paranoid) to 42; instead, $(D paranoid) is set to 2 and "42" is not
+ considered as part of the normal program arguments.
+ )
+
+ $(LI $(I Enum options.) If an option is bound to an enum, an enum symbol as
+ a string is expected as the next option, or right within the option
+ separated with an "=" sign:
+
+---------
+ enum Color { no, yes };
+ Color color; // default initialized to Color.no
+ getopt(args, "color", &color);
+---------
+
+ To set $(D color) to $(D Color.yes), invoke the program with either
+ $(D --color=yes) or $(D --color yes).
+ )
+
+ $(LI $(I String options.) If an option is bound to a string, a string is
+ expected as the next option, or right within the option separated with an
+ "=" sign:
+
+---------
+string outputFile;
+getopt(args, "output", &outputFile);
+---------
+
+ Invoking the program with "--output=myfile.txt" or "--output myfile.txt"
+ will set $(D outputFile) to "myfile.txt". If you want to pass a string
+ containing spaces, you need to use the quoting that is appropriate to your
+ shell, e.g. --output='my file.txt'.
+ )
+
+ $(LI $(I Array options.) If an option is bound to an array, a new element
+ is appended to the array each time the option occurs:
+
+---------
+string[] outputFiles;
+getopt(args, "output", &outputFiles);
+---------
+
+ Invoking the program with "--output=myfile.txt --output=yourfile.txt" or
+ "--output myfile.txt --output yourfile.txt" will set $(D outputFiles) to
+ $(D [ "myfile.txt", "yourfile.txt" ]).
+
+ Alternatively you can set $(LREF arraySep) as the element separator:
+
+---------
+string[] outputFiles;
+arraySep = ","; // defaults to "", separation by whitespace
+getopt(args, "output", &outputFiles);
+---------
+
+ With the above code you can invoke the program with
+ "--output=myfile.txt,yourfile.txt", or "--output myfile.txt,yourfile.txt".)
+
+ $(LI $(I Hash options.) If an option is bound to an associative array, a
+ string of the form "name=value" is expected as the next option, or right
+ within the option separated with an "=" sign:
+
+---------
+double[string] tuningParms;
+getopt(args, "tune", &tuningParms);
+---------
+
+ Invoking the program with e.g. "--tune=alpha=0.5 --tune beta=0.6" will set
+ $(D tuningParms) to [ "alpha" : 0.5, "beta" : 0.6 ].
+
+ Alternatively you can set $(LREF arraySep) as the element separator:
+
+---------
+double[string] tuningParms;
+arraySep = ","; // defaults to "", separation by whitespace
+getopt(args, "tune", &tuningParms);
+---------
+
+ With the above code you can invoke the program with
+ "--tune=alpha=0.5,beta=0.6", or "--tune alpha=0.5,beta=0.6".
+
+ In general, the keys and values can be of any parsable types.
+ )
+
+ $(LI $(I Callback options.) An option can be bound to a function or
+ delegate with the signature $(D void function()), $(D void function(string
+ option)), $(D void function(string option, string value)), or their
+ delegate equivalents.
+
+ $(UL
+ $(LI If the callback doesn't take any arguments, the callback is
+ invoked whenever the option is seen.
+ )
+
+ $(LI If the callback takes one string argument, the option string
+ (without the leading dash(es)) is passed to the callback. After that,
+ the option string is considered handled and removed from the options
+ array.
+
+---------
+void main(string[] args)
+{
+ uint verbosityLevel = 1;
+ void myHandler(string option)
+ {
+ if (option == "quiet")
+ {
+ verbosityLevel = 0;
+ }
+ else
+ {
+ assert(option == "verbose");
+ verbosityLevel = 2;
+ }
+ }
+ getopt(args, "verbose", &myHandler, "quiet", &myHandler);
+}
+---------
+
+ )
+
+ $(LI If the callback takes two string arguments, the option string is
+ handled as an option with one argument, and parsed accordingly. The
+ option and its value are passed to the callback. After that, whatever
+ was passed to the callback is considered handled and removed from the
+ list.
+
+---------
+int main(string[] args)
+{
+ uint verbosityLevel = 1;
+ bool handlerFailed = false;
+ void myHandler(string option, string value)
+ {
+ switch (value)
+ {
+ case "quiet": verbosityLevel = 0; break;
+ case "verbose": verbosityLevel = 2; break;
+ case "shouting": verbosityLevel = verbosityLevel.max; break;
+ default :
+ stderr.writeln("Unknown verbosity level ", value);
+ handlerFailed = true;
+ break;
+ }
+ }
+ getopt(args, "verbosity", &myHandler);
+ return handlerFailed ? 1 : 0;
+}
+---------
+ )
+ ))
+)
+
+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,
+using "|" as a separator:
+
+---------
+bool verbose;
+getopt(args, "verbose|loquacious|garrulous", &verbose);
+---------
+
+Case:
+By default options are case-insensitive. You can change that behavior
+by passing $(D getopt) the $(D caseSensitive) directive like this:
+
+---------
+bool foo, bar;
+getopt(args,
+ std.getopt.config.caseSensitive,
+ "foo", &foo,
+ "bar", &bar);
+---------
+
+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:
+
+---------
+bool foo, bar;
+getopt(args,
+ std.getopt.config.caseSensitive,
+ "foo", &foo,
+ std.getopt.config.caseInsensitive,
+ "bar", &bar);
+---------
+
+The option "--Foo" is rejected due to $(D
+std.getopt.config.caseSensitive), but not "--Bar", "--bAr"
+etc. because the directive $(D
+std.getopt.config.caseInsensitive) turned sensitivity off before
+option "bar" was parsed.
+
+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
+single-letter option behaves the same as a multi-letter option. When
+used with a single dash, a single-letter option is accepted. If the
+option has a parameter, that must be "stuck" to the option without
+any intervening space or "=":
+
+---------
+uint timeout;
+getopt(args, "timeout|t", &timeout);
+---------
+
+To set $(D timeout) to $(D 5), use either of the following: $(D --timeout=5),
+$(D --timeout 5), $(D --t=5), $(D --t 5), or $(D -t5). Forms such as $(D -t 5)
+and $(D -timeout=5) will be not accepted.
+
+For more details about short options, refer also to the next section.
+
+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:
+
+---------
+bool foo, bar;
+getopt(args,
+ std.getopt.config.bundling,
+ "foo|f", &foo,
+ "bar|b", &bar);
+---------
+
+In case you want to only enable bundling for some of the parameters,
+bundling can be turned off with $(D std.getopt.config.noBundling).
+
+Required:
+An option can be marked as required. If that option is not present in the
+arguments an exception will be thrown.
+
+---------
+bool foo, bar;
+getopt(args,
+ std.getopt.config.required,
+ "foo|f", &foo,
+ "bar|b", &bar);
+---------
+
+Only the option directly following $(D std.getopt.config.required) is
+required.
+
+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):
+
+---------
+bool foo, bar;
+getopt(args,
+ std.getopt.config.passThrough,
+ "foo", &foo,
+ "bar", &bar);
+---------
+
+An unrecognized option such as "--baz" will be found untouched in
+$(D args) after $(D getopt) returns.
+
+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 $(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.
+
+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 unless the $(D std.getopt.config.keepEndOfOptions)
+directive is given.
+*/
+GetoptResult getopt(T...)(ref string[] args, T opts)
+{
+ import std.exception : enforce;
+ enforce(args.length,
+ "Invalid arguments string passed: program name missing");
+ configuration cfg;
+ GetoptResult rslt;
+
+ GetOptException excep;
+ void[][string] visitedLongOpts, visitedShortOpts;
+ getoptImpl(args, cfg, rslt, excep, visitedLongOpts, visitedShortOpts, opts);
+
+ if (!rslt.helpWanted && excep !is null)
+ {
+ throw excep;
+ }
+
+ return rslt;
+}
+
+///
+@system unittest
+{
+ auto args = ["prog", "--foo", "-b"];
+
+ bool foo;
+ bool bar;
+ auto rslt = getopt(args, "foo|f", "Some information about foo.", &foo, "bar|b",
+ "Some help message about bar.", &bar);
+
+ if (rslt.helpWanted)
+ {
+ defaultGetoptPrinter("Some information about the program.",
+ rslt.options);
+ }
+}
+
+/**
+ Configuration options for $(D getopt).
+
+ You can pass them to $(D getopt) in any position, except in between an option
+ string and its bound pointer.
+*/
+enum config {
+ /// Turn case sensitivity on
+ caseSensitive,
+ /// Turn case sensitivity off (default)
+ caseInsensitive,
+ /// Turn bundling on
+ bundling,
+ /// Turn bundling off (default)
+ noBundling,
+ /// Pass unrecognized arguments through
+ passThrough,
+ /// 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,
+ /// Make the next option a required option
+ required
+}
+
+/** The result of the $(D getopt) function.
+
+$(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
+}
+
+/** 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
+}
+
+private pure Option splitAndGet(string opt) @trusted nothrow
+{
+ import std.array : split;
+ auto sp = split(opt, "|");
+ Option ret;
+ if (sp.length > 1)
+ {
+ ret.optShort = "-" ~ (sp[0].length < sp[1].length ?
+ sp[0] : sp[1]);
+ ret.optLong = "--" ~ (sp[0].length > sp[1].length ?
+ sp[0] : sp[1]);
+ }
+ 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.format : format;
+ import std.typecons : staticIota;
+
+ 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, ref GetOptException excep,
+ void[][string] visitedLongOpts, void[][string] visitedShortOpts, T opts)
+{
+ enum validationMessage = optionValidator!T;
+ static assert(validationMessage == "", validationMessage);
+
+ import std.algorithm.mutation : remove;
+ import std.conv : to;
+ static if (opts.length)
+ {
+ static if (is(typeof(opts[0]) : config))
+ {
+ // it's a configuration flag, act on it
+ setConfig(cfg, opts[0]);
+ 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];
+ optionHelp.help = opts[1];
+ immutable lowSliceIdx = 3;
+ }
+ else
+ {
+ auto receiver = opts[1];
+ immutable lowSliceIdx = 2;
+ }
+
+ rslt.options ~= optionHelp;
+
+ bool incremental;
+ // Handle options of the form --blah+
+ if (option.length && option[$ - 1] == autoIncrementChar)
+ {
+ option = option[0 .. $ - 1];
+ incremental = true;
+ }
+
+ bool optWasHandled = handleOption(option, receiver, args, cfg, incremental);
+
+ if (cfg.required && !optWasHandled)
+ {
+ excep = new GetOptException("Required option "
+ ~ option ~ " was not supplied", excep);
+ }
+ cfg.required = false;
+
+ getoptImpl(args, cfg, rslt, excep, visitedLongOpts,
+ visitedShortOpts, opts[lowSliceIdx .. $]);
+ }
+ }
+ else
+ {
+ // no more options to look for, potentially some arguments left
+ for (size_t i = 1; i < args.length;)
+ {
+ auto a = args[i];
+ if (endOfOptions.length && a == endOfOptions)
+ {
+ // Consume the "--" if keepEndOfOptions is not specified
+ if (!cfg.keepEndOfOptions)
+ args = args.remove(i);
+ break;
+ }
+ if (!a.length || a[0] != optionChar)
+ {
+ // not an option
+ if (cfg.stopOnFirstNonOption) break;
+ ++i;
+ continue;
+ }
+ if (a == "--help" || a == "-h")
+ {
+ rslt.helpWanted = true;
+ args = args.remove(i);
+ continue;
+ }
+ if (!cfg.passThrough)
+ {
+ throw new GetOptException("Unrecognized option "~a, excep);
+ }
+ ++i;
+ }
+
+ Option helpOpt;
+ helpOpt.optShort = "-h";
+ helpOpt.optLong = "--help";
+ helpOpt.help = "This help information.";
+ rslt.options ~= helpOpt;
+ }
+}
+
+private bool handleOption(R)(string option, R receiver, ref string[] args,
+ ref configuration cfg, bool incremental)
+{
+ import std.algorithm.iteration : map, splitter;
+ import std.ascii : isAlpha;
+ import std.conv : text, to;
+ // Scan arguments looking for a match for this option
+ bool ret = false;
+ for (size_t i = 1; i < args.length; )
+ {
+ auto a = args[i];
+ if (endOfOptions.length && a == endOfOptions) break;
+ if (cfg.stopOnFirstNonOption && (!a.length || a[0] != optionChar))
+ {
+ // first non-option is end of options
+ break;
+ }
+ // Unbundle bundled arguments if necessary
+ if (cfg.bundling && a.length > 2 && a[0] == optionChar &&
+ a[1] != optionChar)
+ {
+ string[] expanded;
+ foreach (j, dchar c; a[1 .. $])
+ {
+ // If the character is not alpha, stop right there. This allows
+ // e.g. -j100 to work as "pass argument 100 to option -j".
+ if (!isAlpha(c))
+ {
+ if (c == '=')
+ j++;
+ expanded ~= a[j + 1 .. $];
+ break;
+ }
+ expanded ~= text(optionChar, c);
+ }
+ args = args[0 .. i] ~ expanded ~ args[i + 1 .. $];
+ continue;
+ }
+
+ string val;
+ if (!optMatch(a, option, val, cfg))
+ {
+ ++i;
+ continue;
+ }
+
+ ret = true;
+
+ // found it
+ // from here on, commit to eat args[i]
+ // (and potentially args[i + 1] too, but that comes later)
+ args = args[0 .. i] ~ args[i + 1 .. $];
+
+ static if (is(typeof(*receiver) == bool))
+ {
+ if (val.length)
+ {
+ // parse '--b=true/false'
+ *receiver = to!(typeof(*receiver))(val);
+ }
+ else
+ {
+ // no argument means set it to true
+ *receiver = true;
+ }
+ }
+ else
+ {
+ import std.exception : enforce;
+ // non-boolean option, which might include an argument
+ //enum isCallbackWithOneParameter = is(typeof(receiver("")) : void);
+ enum isCallbackWithLessThanTwoParameters =
+ (is(typeof(receiver) == delegate) || is(typeof(*receiver) == function)) &&
+ !is(typeof(receiver("", "")));
+ if (!isCallbackWithLessThanTwoParameters && !(val.length) && !incremental)
+ {
+ // Eat the next argument too. Check to make sure there's one
+ // to be eaten first, though.
+ enforce(i < args.length,
+ "Missing value for argument " ~ a ~ ".");
+ val = args[i];
+ args = args[0 .. i] ~ args[i + 1 .. $];
+ }
+ static if (is(typeof(*receiver) == enum))
+ {
+ *receiver = to!(typeof(*receiver))(val);
+ }
+ else static if (is(typeof(*receiver) : real))
+ {
+ // numeric receiver
+ if (incremental) ++*receiver;
+ else *receiver = to!(typeof(*receiver))(val);
+ }
+ else static if (is(typeof(*receiver) == string))
+ {
+ // string receiver
+ *receiver = to!(typeof(*receiver))(val);
+ }
+ else static if (is(typeof(receiver) == delegate) ||
+ is(typeof(*receiver) == function))
+ {
+ static if (is(typeof(receiver("", "")) : void))
+ {
+ // option with argument
+ receiver(option, val);
+ }
+ else static if (is(typeof(receiver("")) : void))
+ {
+ static assert(is(typeof(receiver("")) : void));
+ // boolean-style receiver
+ receiver(option);
+ }
+ else
+ {
+ static assert(is(typeof(receiver()) : void));
+ // boolean-style receiver without argument
+ receiver();
+ }
+ }
+ else static if (isArray!(typeof(*receiver)))
+ {
+ // array receiver
+ import std.range : ElementEncodingType;
+ alias E = ElementEncodingType!(typeof(*receiver));
+
+ if (arraySep == "")
+ {
+ *receiver ~= to!E(val);
+ }
+ else
+ {
+ foreach (elem; val.splitter(arraySep).map!(a => to!E(a))())
+ *receiver ~= elem;
+ }
+ }
+ else static if (isAssociativeArray!(typeof(*receiver)))
+ {
+ // hash receiver
+ alias K = typeof(receiver.keys[0]);
+ alias V = typeof(receiver.values[0]);
+
+ import std.range : only;
+ import std.string : indexOf;
+ import std.typecons : Tuple, tuple;
+
+ static Tuple!(K, V) getter(string input)
+ {
+ auto j = indexOf(input, assignChar);
+ enforce!GetOptException(j != -1, "Could not find '"
+ ~ to!string(assignChar) ~ "' in argument '" ~ input ~ "'.");
+ auto key = input[0 .. j];
+ auto value = input[j + 1 .. $];
+ return tuple(to!K(key), to!V(value));
+ }
+
+ static void setHash(Range)(R receiver, Range range)
+ {
+ foreach (k, v; range.map!getter)
+ (*receiver)[k] = v;
+ }
+
+ if (arraySep == "")
+ setHash(receiver, val.only);
+ else
+ setHash(receiver, val.splitter(arraySep));
+ }
+ else
+ static assert(false, "getopt does not know how to handle the type " ~ typeof(receiver).stringof);
+ }
+ }
+
+ return ret;
+}
+
+// 17574
+@system unittest
+{
+ import std.algorithm.searching : startsWith;
+
+ try
+ {
+ string[string] mapping;
+ immutable as = arraySep;
+ arraySep = ",";
+ scope (exit)
+ arraySep = as;
+ string[] args = ["testProgram", "-m", "a=b,c=\"d,e,f\""];
+ args.getopt("m", &mapping);
+ assert(false, "Exception not thrown");
+ }
+ catch (GetOptException goe)
+ assert(goe.msg.startsWith("Could not find"));
+}
+
+// 5316 - arrays with arraySep
+@system unittest
+{
+ import std.conv;
+
+ arraySep = ",";
+ scope (exit) arraySep = "";
+
+ string[] names;
+ auto args = ["program.name", "-nfoo,bar,baz"];
+ getopt(args, "name|n", &names);
+ assert(names == ["foo", "bar", "baz"], to!string(names));
+
+ names = names.init;
+ args = ["program.name", "-n", "foo,bar,baz"];
+ getopt(args, "name|n", &names);
+ assert(names == ["foo", "bar", "baz"], to!string(names));
+
+ names = names.init;
+ args = ["program.name", "--name=foo,bar,baz"];
+ getopt(args, "name|n", &names);
+ assert(names == ["foo", "bar", "baz"], to!string(names));
+
+ names = names.init;
+ args = ["program.name", "--name", "foo,bar,baz"];
+ getopt(args, "name|n", &names);
+ assert(names == ["foo", "bar", "baz"], to!string(names));
+}
+
+// 5316 - associative arrays with arraySep
+@system unittest
+{
+ import std.conv;
+
+ arraySep = ",";
+ scope (exit) arraySep = "";
+
+ int[string] values;
+ values = values.init;
+ auto args = ["program.name", "-vfoo=0,bar=1,baz=2"];
+ getopt(args, "values|v", &values);
+ assert(values == ["foo":0, "bar":1, "baz":2], to!string(values));
+
+ values = values.init;
+ args = ["program.name", "-v", "foo=0,bar=1,baz=2"];
+ getopt(args, "values|v", &values);
+ assert(values == ["foo":0, "bar":1, "baz":2], to!string(values));
+
+ values = values.init;
+ args = ["program.name", "--values=foo=0,bar=1,baz=2"];
+ getopt(args, "values|t", &values);
+ assert(values == ["foo":0, "bar":1, "baz":2], to!string(values));
+
+ values = values.init;
+ args = ["program.name", "--values", "foo=0,bar=1,baz=2"];
+ getopt(args, "values|v", &values);
+ assert(values == ["foo":0, "bar":1, "baz":2], to!string(values));
+}
+
+/**
+ The option character (default '-').
+
+ Defaults to '-' but it can be assigned to prior to calling $(D getopt).
+ */
+dchar optionChar = '-';
+
+/**
+ The string that conventionally marks the end of all options (default '--').
+
+ Defaults to "--" but can be assigned to prior to calling $(D getopt). Assigning an
+ empty string to $(D endOfOptions) effectively disables it.
+ */
+string endOfOptions = "--";
+
+/**
+ The assignment character used in options with parameters (default '=').
+
+ Defaults to '=' but can be assigned to prior to calling $(D getopt).
+ */
+dchar assignChar = '=';
+
+/**
+ The string used to separate the elements of an array or associative array
+ (default is "" which means the elements are separated by whitespace).
+
+ Defaults to "" but can be assigned to prior to calling $(D getopt).
+ */
+string arraySep = "";
+
+private enum autoIncrementChar = '+';
+
+private struct configuration
+{
+ import std.bitmanip : bitfields;
+ mixin(bitfields!(
+ bool, "caseSensitive", 1,
+ bool, "bundling", 1,
+ bool, "passThrough", 1,
+ bool, "stopOnFirstNonOption", 1,
+ bool, "keepEndOfOptions", 1,
+ bool, "required", 1,
+ ubyte, "", 2));
+}
+
+private bool optMatch(string arg, string optPattern, ref string value,
+ configuration cfg) @safe
+{
+ import std.array : split;
+ import std.string : indexOf;
+ import std.uni : toUpper;
+ //writeln("optMatch:\n ", arg, "\n ", optPattern, "\n ", value);
+ //scope(success) writeln("optMatch result: ", value);
+ if (arg.length < 2 || arg[0] != optionChar) return false;
+ // yank the leading '-'
+ arg = arg[1 .. $];
+ immutable isLong = arg.length > 1 && arg[0] == optionChar;
+ //writeln("isLong: ", isLong);
+ // yank the second '-' if present
+ if (isLong) arg = arg[1 .. $];
+ immutable eqPos = indexOf(arg, assignChar);
+ if (isLong && eqPos >= 0)
+ {
+ // argument looks like --opt=value
+ value = arg[eqPos + 1 .. $];
+ arg = arg[0 .. eqPos];
+ }
+ 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
+ value = arg[1 .. $];
+ arg = arg[0 .. 1];
+ }
+ else
+ {
+ // argument looks like --opt, or -oxyz with bundling
+ value = null;
+ }
+ }
+ //writeln("Arg: ", arg, " pattern: ", optPattern, " value: ", value);
+ // Split the option
+ const variants = split(optPattern, "|");
+ foreach (v ; variants)
+ {
+ //writeln("Trying variant: ", v, " against ", arg);
+ if (arg == v || !cfg.caseSensitive && toUpper(arg) == toUpper(v))
+ return true;
+ if (cfg.bundling && !isLong && v.length == 1
+ && indexOf(arg, v) >= 0)
+ {
+ //writeln("success");
+ return true;
+ }
+ }
+ return false;
+}
+
+private void setConfig(ref configuration cfg, config option) @safe pure nothrow @nogc
+{
+ final switch (option)
+ {
+ case config.caseSensitive: cfg.caseSensitive = true; break;
+ case config.caseInsensitive: cfg.caseSensitive = false; break;
+ case config.bundling: cfg.bundling = true; break;
+ case config.noBundling: cfg.bundling = false; break;
+ case config.passThrough: cfg.passThrough = true; break;
+ case config.noPassThrough: cfg.passThrough = false; break;
+ case config.required: cfg.required = true; break;
+ case config.stopOnFirstNonOption:
+ cfg.stopOnFirstNonOption = true; break;
+ case config.keepEndOfOptions:
+ cfg.keepEndOfOptions = true; break;
+ }
+}
+
+@system unittest
+{
+ import std.conv;
+ import std.math;
+
+ uint paranoid = 2;
+ string[] args = ["program.name", "--paranoid", "--paranoid", "--paranoid"];
+ getopt(args, "paranoid+", &paranoid);
+ assert(paranoid == 5, to!(string)(paranoid));
+
+ enum Color { no, yes }
+ Color color;
+ args = ["program.name", "--color=yes",];
+ getopt(args, "color", &color);
+ assert(color, to!(string)(color));
+
+ color = Color.no;
+ args = ["program.name", "--color", "yes",];
+ getopt(args, "color", &color);
+ assert(color, to!(string)(color));
+
+ string data = "file.dat";
+ int length = 24;
+ bool verbose = false;
+ args = ["program.name", "--length=5", "--file", "dat.file", "--verbose"];
+ getopt(
+ args,
+ "length", &length,
+ "file", &data,
+ "verbose", &verbose);
+ assert(args.length == 1);
+ assert(data == "dat.file");
+ assert(length == 5);
+ assert(verbose);
+
+ //
+ string[] outputFiles;
+ args = ["program.name", "--output=myfile.txt", "--output", "yourfile.txt"];
+ getopt(args, "output", &outputFiles);
+ assert(outputFiles.length == 2
+ && outputFiles[0] == "myfile.txt" && outputFiles[1] == "yourfile.txt");
+
+ outputFiles = [];
+ arraySep = ",";
+ args = ["program.name", "--output", "myfile.txt,yourfile.txt"];
+ getopt(args, "output", &outputFiles);
+ assert(outputFiles.length == 2
+ && outputFiles[0] == "myfile.txt" && outputFiles[1] == "yourfile.txt");
+ arraySep = "";
+
+ foreach (testArgs;
+ [["program.name", "--tune=alpha=0.5", "--tune", "beta=0.6"],
+ ["program.name", "--tune=alpha=0.5,beta=0.6"],
+ ["program.name", "--tune", "alpha=0.5,beta=0.6"]])
+ {
+ arraySep = ",";
+ double[string] tuningParms;
+ getopt(testArgs, "tune", &tuningParms);
+ assert(testArgs.length == 1);
+ assert(tuningParms.length == 2);
+ assert(approxEqual(tuningParms["alpha"], 0.5));
+ assert(approxEqual(tuningParms["beta"], 0.6));
+ arraySep = "";
+ }
+
+ uint verbosityLevel = 1;
+ void myHandler(string option)
+ {
+ if (option == "quiet")
+ {
+ verbosityLevel = 0;
+ }
+ else
+ {
+ assert(option == "verbose");
+ verbosityLevel = 2;
+ }
+ }
+ args = ["program.name", "--quiet"];
+ getopt(args, "verbose", &myHandler, "quiet", &myHandler);
+ assert(verbosityLevel == 0);
+ args = ["program.name", "--verbose"];
+ getopt(args, "verbose", &myHandler, "quiet", &myHandler);
+ assert(verbosityLevel == 2);
+
+ verbosityLevel = 1;
+ void myHandler2(string option, string value)
+ {
+ assert(option == "verbose");
+ verbosityLevel = 2;
+ }
+ args = ["program.name", "--verbose", "2"];
+ getopt(args, "verbose", &myHandler2);
+ assert(verbosityLevel == 2);
+
+ verbosityLevel = 1;
+ void myHandler3()
+ {
+ verbosityLevel = 2;
+ }
+ args = ["program.name", "--verbose"];
+ getopt(args, "verbose", &myHandler3);
+ assert(verbosityLevel == 2);
+
+ bool foo, bar;
+ args = ["program.name", "--foo", "--bAr"];
+ getopt(args,
+ std.getopt.config.caseSensitive,
+ std.getopt.config.passThrough,
+ "foo", &foo,
+ "bar", &bar);
+ assert(args[1] == "--bAr");
+
+ // test stopOnFirstNonOption
+
+ args = ["program.name", "--foo", "nonoption", "--bar"];
+ foo = bar = false;
+ getopt(args,
+ std.getopt.config.stopOnFirstNonOption,
+ "foo", &foo,
+ "bar", &bar);
+ assert(foo && !bar && args[1] == "nonoption" && args[2] == "--bar");
+
+ args = ["program.name", "--foo", "nonoption", "--zab"];
+ foo = bar = false;
+ getopt(args,
+ std.getopt.config.stopOnFirstNonOption,
+ "foo", &foo,
+ "bar", &bar);
+ assert(foo && !bar && args[1] == "nonoption" && args[2] == "--zab");
+
+ args = ["program.name", "--fb1", "--fb2=true", "--tb1=false"];
+ bool fb1, fb2;
+ bool tb1 = true;
+ getopt(args, "fb1", &fb1, "fb2", &fb2, "tb1", &tb1);
+ assert(fb1 && fb2 && !tb1);
+
+ // test keepEndOfOptions
+
+ args = ["program.name", "--foo", "nonoption", "--bar", "--", "--baz"];
+ getopt(args,
+ std.getopt.config.keepEndOfOptions,
+ "foo", &foo,
+ "bar", &bar);
+ assert(args == ["program.name", "nonoption", "--", "--baz"]);
+
+ // Ensure old behavior without the keepEndOfOptions
+
+ args = ["program.name", "--foo", "nonoption", "--bar", "--", "--baz"];
+ getopt(args,
+ "foo", &foo,
+ "bar", &bar);
+ assert(args == ["program.name", "nonoption", "--baz"]);
+
+ // test function callbacks
+
+ static class MyEx : Exception
+ {
+ this() { super(""); }
+ this(string option) { this(); this.option = option; }
+ this(string option, string value) { this(option); this.value = value; }
+
+ string option;
+ string value;
+ }
+
+ static void myStaticHandler1() { throw new MyEx(); }
+ args = ["program.name", "--verbose"];
+ try { getopt(args, "verbose", &myStaticHandler1); assert(0); }
+ catch (MyEx ex) { assert(ex.option is null && ex.value is null); }
+
+ static void myStaticHandler2(string option) { throw new MyEx(option); }
+ args = ["program.name", "--verbose"];
+ try { getopt(args, "verbose", &myStaticHandler2); assert(0); }
+ catch (MyEx ex) { assert(ex.option == "verbose" && ex.value is null); }
+
+ static void myStaticHandler3(string option, string value) { throw new MyEx(option, value); }
+ args = ["program.name", "--verbose", "2"];
+ try { getopt(args, "verbose", &myStaticHandler3); assert(0); }
+ catch (MyEx ex) { assert(ex.option == "verbose" && ex.value == "2"); }
+}
+
+@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;
+ string[] args = [ "", "-nl" ];
+ getopt
+ (
+ args,
+ std.getopt.config.bundling,
+ //std.getopt.config.caseSensitive,
+ "linenum|l", &f_linenum,
+ "filename|n", &f_filename
+ );
+ assert(f_linenum);
+ assert(f_filename);
+}
+
+@system unittest
+{
+ // From bugzilla 6887
+ string[] p;
+ string[] args = ["", "-pa"];
+ getopt(args, "p", &p);
+ assert(p.length == 1);
+ assert(p[0] == "a");
+}
+
+@system unittest
+{
+ // From bugzilla 6888
+ int[string] foo;
+ auto args = ["", "-t", "a=1"];
+ getopt(args, "t", &foo);
+ assert(foo == ["a":1]);
+}
+
+@system unittest
+{
+ // From bugzilla 9583
+ int opt;
+ auto args = ["prog", "--opt=123", "--", "--a", "--b", "--c"];
+ getopt(args, "opt", &opt);
+ assert(args == ["prog", "--a", "--b", "--c"]);
+}
+
+@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"];
+ getopt(args, config.bundling, "a|addr", &a);
+ assert(a == "-0x12", a);
+ 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");
+}
+
+@system unittest // 5228
+{
+ import std.conv;
+ import std.exception;
+
+ auto args = ["prog", "--foo=bar"];
+ int abc;
+ assertThrown!GetOptException(getopt(args, "abc", &abc));
+
+ args = ["prog", "--abc=string"];
+ assertThrown!ConvException(getopt(args, "abc", &abc));
+}
+
+@system unittest // From bugzilla 7693
+{
+ import std.exception;
+
+ enum Foo {
+ bar,
+ baz
+ }
+
+ auto args = ["prog", "--foo=barZZZ"];
+ Foo foo;
+ assertThrown(getopt(args, "foo", &foo));
+ args = ["prog", "--foo=bar"];
+ assertNotThrown(getopt(args, "foo", &foo));
+ args = ["prog", "--foo", "barZZZ"];
+ assertThrown(getopt(args, "foo", &foo));
+ args = ["prog", "--foo", "baz"];
+ assertNotThrown(getopt(args, "foo", &foo));
+}
+
+@system unittest // same bug as 7693 only for bool
+{
+ import std.exception;
+
+ auto args = ["prog", "--foo=truefoobar"];
+ bool foo;
+ assertThrown(getopt(args, "foo", &foo));
+ args = ["prog", "--foo"];
+ getopt(args, "foo", &foo);
+ assert(foo);
+}
+
+@system unittest
+{
+ bool foo;
+ auto args = ["prog", "--foo"];
+ getopt(args, "foo", &foo);
+ assert(foo);
+}
+
+@system unittest
+{
+ bool foo;
+ bool bar;
+ auto args = ["prog", "--foo", "-b"];
+ getopt(args, config.caseInsensitive,"foo|f", "Some foo", &foo,
+ config.caseSensitive, "bar|b", "Some bar", &bar);
+ assert(foo);
+ assert(bar);
+}
+
+@system unittest
+{
+ bool foo;
+ bool bar;
+ auto args = ["prog", "-b", "--foo", "-z"];
+ getopt(args, config.caseInsensitive, config.required, "foo|f", "Some foo",
+ &foo, config.caseSensitive, "bar|b", "Some bar", &bar,
+ config.passThrough);
+ assert(foo);
+ assert(bar);
+}
+
+@system unittest
+{
+ import std.exception;
+
+ bool foo;
+ bool bar;
+ auto args = ["prog", "-b", "-z"];
+ assertThrown(getopt(args, config.caseInsensitive, config.required, "foo|f",
+ "Some foo", &foo, config.caseSensitive, "bar|b", "Some bar", &bar,
+ config.passThrough));
+}
+
+@system unittest
+{
+ import std.exception;
+
+ bool foo;
+ bool bar;
+ auto args = ["prog", "--foo", "-z"];
+ assertNotThrown(getopt(args, config.caseInsensitive, config.required,
+ "foo|f", "Some foo", &foo, config.caseSensitive, "bar|b", "Some bar",
+ &bar, config.passThrough));
+ assert(foo);
+ assert(!bar);
+}
+
+@system unittest
+{
+ bool foo;
+ auto args = ["prog", "-f"];
+ auto r = getopt(args, config.caseInsensitive, "help|f", "Some foo", &foo);
+ assert(foo);
+ assert(!r.helpWanted);
+}
+
+@safe unittest // implicit help option without config.passThrough
+{
+ string[] args = ["program", "--help"];
+ auto r = getopt(args);
+ assert(r.helpWanted);
+}
+
+// Issue 13316 - std.getopt: implicit help option breaks the next argument
+@system unittest
+{
+ string[] args = ["program", "--help", "--", "something"];
+ getopt(args);
+ assert(args == ["program", "something"]);
+
+ args = ["program", "--help", "--"];
+ getopt(args);
+ assert(args == ["program"]);
+
+ bool b;
+ args = ["program", "--help", "nonoption", "--option"];
+ getopt(args, config.stopOnFirstNonOption, "option", &b);
+ assert(args == ["program", "nonoption", "--option"]);
+}
+
+// Issue 13317 - std.getopt: endOfOptions broken when it doesn't look like an option
+@system unittest
+{
+ auto endOfOptionsBackup = endOfOptions;
+ scope(exit) endOfOptions = endOfOptionsBackup;
+ endOfOptions = "endofoptions";
+ string[] args = ["program", "endofoptions", "--option"];
+ bool b = false;
+ getopt(args, "option", &b);
+ assert(!b);
+ assert(args == ["program", "--option"]);
+}
+
+/** 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, 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 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)
+{
+ writefln("%*s %*s%s%s", lengthOfLongestShortOption, it.optShort,
+ lengthOfLongestLongOption, it.optLong,
+ it.required ? " Required: " : " ", it.help);
+}
+------------
+
+Params:
+ text = The text to printed at the beginning of the help output.
+ opt = The $(D Option) extracted from the $(D getopt) parameter.
+*/
+void defaultGetoptPrinter(string text, Option[] opt)
+{
+ import std.stdio : stdout;
+
+ defaultGetoptFormatter(stdout.lockingTextWriter(), text, opt);
+}
+
+/** This function writes the passed text and $(D Option) into an output range
+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 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.algorithm.comparison : min, max;
+ import std.format : formattedWrite;
+
+ output.formattedWrite("%s\n", text);
+
+ size_t ls, ll;
+ bool hasRequired = false;
+ foreach (it; opt)
+ {
+ ls = max(ls, it.optShort.length);
+ ll = max(ll, it.optLong.length);
+
+ hasRequired = hasRequired || it.required;
+ }
+
+ string re = " Required: ";
+
+ foreach (it; opt)
+ {
+ output.formattedWrite("%*s %*s%*s%s\n", ls, it.optShort, ll, it.optLong,
+ hasRequired ? re.length : 1, it.required ? re : " ", it.help);
+ }
+}
+
+@system unittest
+{
+ import std.conv;
+
+ import std.array;
+ import std.string;
+ bool a;
+ auto args = ["prog", "--foo"];
+ auto t = getopt(args, "foo|f", "Help", &a);
+ string s;
+ auto app = appender!string();
+ defaultGetoptFormatter(app, "Some Text", t.options);
+
+ string helpMsg = app.data;
+ //writeln(helpMsg);
+ assert(helpMsg.length);
+ assert(helpMsg.count("\n") == 3, to!string(helpMsg.count("\n")) ~ " "
+ ~ helpMsg);
+ assert(helpMsg.indexOf("--foo") != -1);
+ assert(helpMsg.indexOf("-f") != -1);
+ assert(helpMsg.indexOf("-h") != -1);
+ assert(helpMsg.indexOf("--help") != -1);
+ assert(helpMsg.indexOf("Help") != -1);
+
+ string wanted = "Some Text\n-f --foo Help\n-h --help This help "
+ ~ "information.\n";
+ assert(wanted == helpMsg);
+}
+
+@system unittest
+{
+ import std.array ;
+ import std.conv;
+ import std.string;
+ bool a;
+ auto args = ["prog", "--foo"];
+ auto t = getopt(args, config.required, "foo|f", "Help", &a);
+ string s;
+ auto app = appender!string();
+ defaultGetoptFormatter(app, "Some Text", t.options);
+
+ string helpMsg = app.data;
+ //writeln(helpMsg);
+ assert(helpMsg.length);
+ assert(helpMsg.count("\n") == 3, to!string(helpMsg.count("\n")) ~ " "
+ ~ helpMsg);
+ assert(helpMsg.indexOf("Required:") != -1);
+ assert(helpMsg.indexOf("--foo") != -1);
+ assert(helpMsg.indexOf("-f") != -1);
+ assert(helpMsg.indexOf("-h") != -1);
+ assert(helpMsg.indexOf("--help") != -1);
+ assert(helpMsg.indexOf("Help") != -1);
+
+ string wanted = "Some Text\n-f --foo Required: Help\n-h --help "
+ ~ " 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);
+}
+
+@system unittest // Hyphens at the start of option values; Issue 17650
+{
+ auto args = ["program", "-m", "-5", "-n", "-50", "-c", "-", "-f", "-"];
+
+ int m;
+ int n;
+ char c;
+ string f;
+
+ getopt(args,
+ "m|mm", "integer", &m,
+ "n|nn", "integer", &n,
+ "c|cc", "character", &c,
+ "f|file", "filename or hyphen for stdin", &f);
+
+ assert(m == -5);
+ assert(n == -50);
+ assert(c == '-');
+ assert(f == "-");
+}
diff --git a/libphobos/src/std/internal/cstring.d b/libphobos/src/std/internal/cstring.d
new file mode 100644
index 0000000..257a100
--- /dev/null
+++ b/libphobos/src/std/internal/cstring.d
@@ -0,0 +1,267 @@
+/**
+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).
+
+Copyright: Denis Shelomovskij 2013-2014
+
+License: $(HTTP boost.org/LICENSE_1_0.txt, Boost License 1.0).
+
+Authors: Denis Shelomovskij
+
+Macros:
+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.range;
+import std.traits;
+
+version (unittest)
+@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])
+ ++length;
+ return cstr[0 .. length];
+}
+
+/**
+Creates temporary 0-terminated $(I C string) with copy of passed text.
+
+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
+assigned to a variable it will be destroyed at the end of creating
+primary expression.
+
+Implementation_note:
+For small strings tempCString will use stack allocated buffer,
+for large strings (approximately 250 characters and more) it will
+allocate temporary one using C's $(D malloc).
+
+Note:
+This function is intended to be used in function call expression (like
+$(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)(From str)
+if (isSomeChar!To && (isInputRange!From || isSomeString!From) &&
+ isSomeChar!(ElementEncodingType!From))
+{
+
+ 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 pure
+ {
+ return _ptr == useStack ? _buff.ptr : _ptr;
+ }
+
+ @property const(To)* ptr() const pure
+ {
+ return buffPtr;
+ }
+
+ const(To)[] opIndex() const pure
+ {
+ return buffPtr[0 .. _length];
+ }
+
+ ~this()
+ {
+ if (_ptr != useStack)
+ {
+ import core.stdc.stdlib : free;
+ free(_ptr);
+ }
+ }
+
+ private:
+ To* _ptr;
+ 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
+ }
+
+ To[buffLength] _buff; // the 'small string optimization'
+
+ 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.
+
+ 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.stdlib : malloc, realloc;
+ import core.stdc.string : memcpy;
+
+ 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 @system unittest
+{
+ import core.stdc.string;
+
+ string str = "abc";
+
+ // Intended usage
+ assert(strlen(str.tempCString()) == 3);
+
+ // Correct usage
+ auto tmp = str.tempCString();
+ assert(strlen(tmp) == 3); // or `tmp.ptr`, or `tmp.buffPtr`
+
+ // $(RED WARNING): $(RED Incorrect usage)
+ auto pInvalid1 = str.tempCString().ptr;
+ const char* pInvalid2 = str.tempCString();
+ // Both pointers refer to invalid memory here as
+ // returned values aren't assigned to a variable and
+ // both primary expressions are ended.
+}
+
+@safe nothrow @nogc unittest
+{
+ assert("abc".tempCString().asArray == "abc");
+ assert("abc"d.tempCString().ptr.asArray == "abc");
+ assert("abc".tempCString!wchar().buffPtr.asArray == "abc"w);
+
+ 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);
+}
+
+// Bugzilla 14980
+nothrow @nogc @safe unittest
+{
+ const(char[]) str = null;
+ auto res = tempCString(str);
+ const char* ptr = res;
+ assert(ptr is null);
+}
+
+version (Windows)
+ alias tempCStringW = tempCString!(wchar, const(char)[]);
diff --git a/libphobos/src/std/internal/digest/sha_SSSE3.d b/libphobos/src/std/internal/digest/sha_SSSE3.d
new file mode 100644
index 0000000..4060f34
--- /dev/null
+++ b/libphobos/src/std/internal/digest/sha_SSSE3.d
@@ -0,0 +1,729 @@
+// Written in the D programming language.
+
+/**
+ * Computes SHA1 digests of arbitrary data, using an optimized algorithm with SSSE3 instructions.
+ *
+ * Authors:
+ * The general idea is described by Dean Gaudet.
+ * Another important observation is published by Max Locktyukhin.
+ * (Both implementations are public domain.)
+ * Translation to X86 and D by Kai Nacke <kai@redstar.de>
+ *
+ * References:
+ * $(LINK2 http://arctic.org/~dean/crypto/sha1.html)
+ * $(LINK2 http://software.intel.com/en-us/articles/improving-the-performance-of-the-secure-hash-algorithm-1/, Fast implementation of SHA1)
+ */
+module std.internal.digest.sha_SSSE3;
+
+version (D_InlineAsm_X86)
+{
+ version (D_PIC) {} // Bugzilla 9378
+ else
+ {
+ private version = USE_SSSE3;
+ private version = _32Bit;
+ }
+}
+else version (D_InlineAsm_X86_64)
+{
+ private version = USE_SSSE3;
+ private version = _64Bit;
+}
+
+/*
+ * The idea is quite simple. The SHA-1 specification defines the following message schedule:
+ * W[i] = (W[i-3] ^ W[i-8] ^ W[i-14] ^ W[i-16]) rol 1
+ *
+ * To employ SSE, simply write down the formula four times:
+ * W[i ] = (W[i-3] ^ W[i-8] ^ W[i-14] ^ W[i-16]) rol 1
+ * W[i+1] = (W[i-2] ^ W[i-7] ^ W[i-13] ^ W[i-15]) rol 1
+ * W[i+2] = (W[i-1] ^ W[i-6] ^ W[i-12] ^ W[i-14]) rol 1
+ * W[i+3] = (W[i ] ^ W[i-5] ^ W[i-11] ^ W[i-13]) rol 1
+ * The last formula requires value W[i] computed with the first formula.
+ * Because the xor operation and the rotate operation are commutative, we can replace the
+ * last formula with
+ * W[i+3] = ( 0 ^ W[i-5] ^ W[i-11] ^ W[i-13]) rol 1
+ * and then calculate
+ * W[i+3] ^= W[i] rol 1
+ * which unfortunately requires many additional operations. This approach was described by
+ * Dean Gaudet.
+ *
+ * Max Locktyukhin observed that
+ * W[i] = W[i-A] ^ W[i-B]
+ * is equivalent to
+ * W[i] = W[i-2*A] ^ W[i-2*B]
+ * (if the indices are still in valid ranges). Using this observation, the formula is
+ * translated to
+ * W[i] = (W[i-6] ^ W[i-16] ^ W[i-28] ^ W[i-32]) rol 2
+ * Again, to employ SSE the formula is used four times.
+ *
+ * Later on, the expression W[i] + K(i) is used. (K(i) is the constant used in round i.)
+ * Once the 4 W[i] are calculated, we can also add the four K(i) values with one SSE instruction.
+ *
+ * The 32bit and 64bit implementations are almost identical. The main difference is that there
+ * are only 8 XMM registers in 32bit mode. Therefore, space on the stack is needed to save
+ * computed values.
+ */
+
+version (USE_SSSE3)
+{
+ /*
+ * The general idea is to use the XMM registers as a sliding window over
+ * message schedule. XMM0 to XMM7 are used to store the last 64 byte of
+ * the message schedule. In 64 bit mode this is fine because of the number of
+ * registers. The main difference of the 32 bit code is that a part of the
+ * calculated message schedule is saved on the stack because 2 temporary
+ * registers are needed.
+ */
+
+ /* Number of message words we are precalculating. */
+ private immutable int PRECALC_AHEAD = 16;
+
+ /* T1 and T2 are used for intermediate results of computations. */
+ private immutable string T1 = "EAX";
+ private immutable string T2 = "EBX";
+
+ /* The registers used for the SHA-1 variables. */
+ private immutable string A = "ECX";
+ private immutable string B = "ESI";
+ private immutable string C = "EDI";
+ private immutable string D = "EBP";
+ private immutable string E = "EDX";
+
+ /* */
+ version (_32Bit)
+ {
+ private immutable string SP = "ESP";
+ private immutable string BUFFER_PTR = "EAX";
+ private immutable string STATE_PTR = "EBX";
+
+ // Control byte for shuffle instruction (only used in round 0-15)
+ private immutable string X_SHUFFLECTL = "XMM6";
+
+ // Round constant (only used in round 0-15)
+ private immutable string X_CONSTANT = "XMM7";
+ }
+ version (_64Bit)
+ {
+ 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";
+ private immutable string W_TMP2 = "XMM9";
+
+ // Control byte for shuffle instruction (only used in round 0-15)
+ private immutable string X_SHUFFLECTL = "XMM12";
+
+ // Round constant
+ private immutable string X_CONSTANT = "XMM13";
+ }
+
+ /* 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
+ 0x6ed9eba1, 0x6ed9eba1, 0x6ed9eba1, 0x6ed9eba1,
+ // Constants for round 40-59
+ 0x8f1bbcdc, 0x8f1bbcdc, 0x8f1bbcdc, 0x8f1bbcdc,
+ // Constants for round 60-79
+ 0xca62c1d6, 0xca62c1d6, 0xca62c1d6, 0xca62c1d6
+ ];
+
+ /** 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);
+ 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)
+ {
+ 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 */
+ private nothrow pure uint regno(uint i)
+ {
+ return (i/4)&7;
+ }
+
+ /** 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]. */
+ private nothrow pure string WiKiV(uint i)
+ {
+ return "["~SP~" + WI_PLUS_KI_PTR + "~to_string((i/4)&3)~"*16]";
+ }
+
+ /** Returns reference to storage of value W[i] + K[i]. */
+ private nothrow pure string WiKi(uint i)
+ {
+ return "["~SP~" + WI_PLUS_KI_PTR + 4*"~to_string(i&15)~"]";
+ }
+
+ /**
+ * Chooses the instruction sequence based on the 32bit or 64bit model.
+ */
+ private nothrow pure string[] swt3264(string[] insn32, string[] insn64)
+ {
+ version (_32Bit)
+ {
+ return insn32;
+ }
+ version (_64Bit)
+ {
+ return insn64;
+ }
+ }
+
+ /**
+ * Flattens the instruction sequence and wraps it in an asm block.
+ */
+ private nothrow pure string wrap(string[] insn)
+ {
+ string s = "asm pure nothrow @nogc {";
+ foreach (t; insn) s ~= (t ~ "; \n");
+ s ~= "}";
+ return s;
+ // Is not CTFE:
+ // return "asm pure nothrow @nogc { " ~ join(insn, "; \n") ~ "}";
+ }
+
+ /**
+ * Weaves the 2 instruction sequences together.
+ */
+ private nothrow pure string[] weave(string[] seq1, string[] seq2, uint dist = 1)
+ {
+ string[] res = [];
+ auto i1 = 0, i2 = 0;
+ while (i1 < seq1.length || i2 < seq2.length)
+ {
+ if (i2 < seq2.length)
+ {
+ res ~= seq2[i2 .. i2+1];
+ i2 += 1;
+ }
+ if (i1 < seq1.length)
+ {
+ import std.algorithm.comparison : min;
+
+ res ~= seq1[i1 .. min(i1+dist, $)];
+ i1 += dist;
+ }
+ }
+ return res;
+ }
+
+ /**
+ * Generates instructions to load state from memory into registers.
+ */
+ private nothrow pure string[] loadstate(string base, string a, string b, string c, string d, string e)
+ {
+ return ["mov "~a~",["~base~" + 0*4]",
+ "mov "~b~",["~base~" + 1*4]",
+ "mov "~c~",["~base~" + 2*4]",
+ "mov "~d~",["~base~" + 3*4]",
+ "mov "~e~",["~base~" + 4*4]" ];
+ }
+
+ /**
+ * Generates instructions to update state from registers, saving result in memory.
+ */
+ private nothrow pure string[] savestate(string base, string a, string b, string c, string d, string e)
+ {
+ return ["add ["~base~" + 0*4],"~a,
+ "add ["~base~" + 1*4],"~b,
+ "add ["~base~" + 2*4],"~c,
+ "add ["~base~" + 3*4],"~d,
+ "add ["~base~" + 4*4],"~e ];
+ }
+
+ /** Calculates Ch(x, y, z) = z ^ (x & (y ^ z)) */
+ private nothrow pure string[] Ch(string x, string y, string z)
+ {
+ return ["mov "~T1~","~y,
+ "xor "~T1~","~z,
+ "and "~T1~","~x,
+ "xor "~T1~","~z ];
+ }
+
+ /** Calculates Parity(x, y, z) = x ^ y ^ z */
+ private nothrow pure string[] Parity(string x, string y, string z)
+ {
+ return ["mov "~T1~","~z,
+ "xor "~T1~","~y,
+ "xor "~T1~","~x ];
+ }
+
+ /** Calculates Maj(x, y, z) = (x & y) | (z & (x ^ y)) */
+ private nothrow pure string[] Maj(string x, string y, string z)
+ {
+ return ["mov "~T1~","~y,
+ "mov "~T2~","~x,
+ "or "~T1~","~x,
+ "and "~T2~","~y,
+ "and "~T1~","~z,
+ "or "~T1~","~T2 ];
+ }
+
+ /** Returns function for round i. Function returns result in T1 and may destroy T2. */
+ private nothrow pure string[] F(int i, string b, string c, string d)
+ {
+ string[] insn;
+ if (i >= 0 && i <= 19) insn = Ch(b, c, d);
+ else if (i >= 20 && i <= 39) insn = Parity(b, c, d);
+ else if (i >= 40 && i <= 59) insn = Maj(b, c, d);
+ else if (i >= 60 && i <= 79) insn = Parity(b, c, d);
+ else assert(false, "Coding error");
+ return insn;
+ }
+
+ /** Returns instruction used to setup a round. */
+ private nothrow pure string[] xsetup(int i)
+ {
+ if (i == 0)
+ {
+ return swt3264(["movdqa "~X_SHUFFLECTL~","~bswap_shufb_ctl(),
+ "movdqa "~X_CONSTANT~","~constant(i)],
+ ["movdqa "~X_SHUFFLECTL~","~bswap_shufb_ctl(),
+ "movdqa "~X_CONSTANT~","~constant(i)]);
+ }
+ version (_64Bit)
+ {
+ if (i%20 == 0)
+ {
+ return ["movdqa "~X_CONSTANT~","~constant(i)];
+ }
+ }
+ return [];
+ }
+
+ /**
+ * Loads the message words and performs the little to big endian conversion.
+ * Requires that the shuffle control word and the round constant is loaded
+ * into required XMM register. The BUFFER_PTR register must point to the
+ * buffer.
+ */
+ private nothrow pure string[] precalc_00_15(int i)
+ {
+ int regno = regno(i);
+
+ string W = "XMM" ~ to_string(regno);
+ version (_32Bit)
+ {
+ string W_TMP = "XMM" ~ to_string(regno+2);
+ }
+ version (_64Bit)
+ {
+ string W_TMP = "XMM" ~ to_string(regno+8);
+ }
+
+ if ((i & 3) == 0)
+ {
+ return ["movdqu "~W~",["~BUFFER_PTR~" + "~to_string(regno)~"*16]"];
+ }
+ else if ((i & 3) == 1)
+ {
+ return ["pshufb "~W~","~X_SHUFFLECTL] ~
+ swt3264(["movdqa "~WiV(i)~","~W], []);
+ }
+ else if ((i & 3) == 2)
+ {
+ return ["movdqa "~W_TMP~","~W,
+ "paddd "~W_TMP~","~X_CONSTANT,
+ ];
+ }
+ else
+ {
+ return ["movdqa "~WiKiV(i)~","~W_TMP,
+ ];
+ }
+ }
+
+ /**
+ * Done on 4 consequtive W[i] values in a single XMM register
+ * W[i ] = (W[i-3] ^ W[i-8] ^ W[i-14] ^ W[i-16]) rol 1
+ * W[i+1] = (W[i-2] ^ W[i-7] ^ W[i-13] ^ W[i-15]) rol 1
+ * W[i+2] = (W[i-1] ^ W[i-6] ^ W[i-12] ^ W[i-14]) rol 1
+ * W[i+3] = ( 0 ^ W[i-5] ^ W[i-11] ^ W[i-13]) rol 1
+ *
+ * This additional calculation unfortunately requires many additional operations
+ * W[i+3] ^= W[i] rol 1
+ *
+ * Once we have 4 W[i] values in XMM we can also add four K values with one instruction
+ * W[i:i+3] += {K,K,K,K}
+ */
+ private nothrow pure string[] precalc_16_31(int i)
+ {
+ int regno = regno(i);
+
+ string W = "XMM" ~ to_string(regno);
+ string W_minus_4 = "XMM" ~ to_string((regno-1)&7);
+ string W_minus_8 = "XMM" ~ to_string((regno-2)&7);
+ string W_minus_12 = "XMM" ~ to_string((regno-3)&7);
+ string W_minus_16 = "XMM" ~ to_string((regno-4)&7);
+ version (_32Bit)
+ {
+ string W_TMP = "XMM" ~ to_string((regno+1)&7);
+ string W_TMP2 = "XMM" ~ to_string((regno+2)&7);
+ }
+
+ if ((i & 3) == 0)
+ {
+ return ["movdqa "~W~","~W_minus_12,
+ "palignr "~W~","~W_minus_16~",8", // W[i] = W[i-14]
+ "pxor "~W~","~W_minus_16, // W[i] ^= W[i-16]
+ "pxor "~W~","~W_minus_8, // W[i] ^= W[i-8]
+ "movdqa "~W_TMP~","~W_minus_4,
+ ];
+ }
+ else if ((i & 3) == 1)
+ {
+ return ["psrldq "~W_TMP~",4", // W[i-3]
+ "pxor "~W~","~W_TMP, // W[i] ^= W[i-3]
+ "movdqa "~W_TMP~","~W,
+ "psrld "~W~",31",
+ "pslld "~W_TMP~",1",
+ ];
+ }
+ else if ((i & 3) == 2)
+ {
+ return ["por "~W~","~W_TMP,
+ "movdqa "~W_TMP~","~W,
+ "pslldq "~W_TMP~",12",
+ "movdqa "~W_TMP2~","~W_TMP,
+ "pslld "~W_TMP~",1",
+ ];
+ }
+ else
+ {
+ return ["psrld "~W_TMP2~",31",
+ "por "~W_TMP~","~W_TMP2,
+ "pxor "~W~","~W_TMP,
+ "movdqa "~W_TMP~","~W ] ~
+ swt3264(["movdqa "~WiV(i)~","~W,
+ "paddd "~W_TMP~","~constant(i) ],
+ ["paddd "~W_TMP~","~X_CONSTANT ]) ~
+ ["movdqa "~WiKiV(i)~","~W_TMP];
+ }
+ }
+
+ /** Performs the main calculation as decribed above. */
+ private nothrow pure string[] precalc_32_79(int i)
+ {
+ int regno = regno(i);
+
+ string W = "XMM" ~ to_string(regno);
+ string W_minus_4 = "XMM" ~ to_string((regno-1)&7);
+ string W_minus_8 = "XMM" ~ to_string((regno-2)&7);
+ string W_minus_16 = "XMM" ~ to_string((regno-4)&7);
+ version (_32Bit)
+ {
+ string W_minus_28 = "[ESP + WI_PTR + "~ to_string((regno-7)&7)~"*16]";
+ string W_minus_32 = "[ESP + WI_PTR + "~ to_string((regno-8)&7)~"*16]";
+ string W_TMP = "XMM" ~ to_string((regno+1)&7);
+ string W_TMP2 = "XMM" ~ to_string((regno+2)&7);
+ }
+ version (_64Bit)
+ {
+ string W_minus_28 = "XMM" ~ to_string((regno-7)&7);
+ string W_minus_32 = "XMM" ~ to_string((regno-8)&7);
+ }
+
+ if ((i & 3) == 0)
+ {
+ return swt3264(["movdqa "~W~","~W_minus_32], []) ~
+ ["movdqa "~W_TMP~","~W_minus_4,
+ "pxor "~W~","~W_minus_28, // W is W_minus_32 before xor
+ "palignr "~W_TMP~","~W_minus_8~",8",
+ ];
+ }
+ else if ((i & 3) == 1)
+ {
+ return ["pxor "~W~","~W_minus_16,
+ "pxor "~W~","~W_TMP,
+ "movdqa "~W_TMP~","~W,
+ ];
+ }
+ else if ((i & 3) == 2)
+ {
+ return ["psrld "~W~",30",
+ "pslld "~W_TMP~",2",
+ "por "~W_TMP~","~W,
+ ];
+ }
+ else
+ {
+ if (i < 76)
+ return ["movdqa "~W~","~W_TMP] ~
+ swt3264(["movdqa "~WiV(i)~","~W,
+ "paddd "~W_TMP~","~constant(i)],
+ ["paddd "~W_TMP~","~X_CONSTANT]) ~
+ ["movdqa "~WiKiV(i)~","~W_TMP];
+ else
+ return swt3264(["paddd "~W_TMP~","~constant(i)],
+ ["paddd "~W_TMP~","~X_CONSTANT]) ~
+ ["movdqa "~WiKiV(i)~","~W_TMP];
+ }
+ }
+
+ /** Choose right precalc method. */
+ private nothrow pure string[] precalc(int i)
+ {
+ if (i >= 0 && i < 16) return precalc_00_15(i);
+ if (i >= 16 && i < 32) return precalc_16_31(i);
+ if (i >= 32 && i < 80) return precalc_32_79(i);
+ return [];
+ }
+
+ /**
+ * Return code for round i and i+1.
+ * Performs the following rotation:
+ * in=>out: A=>D, B=>E, C=>A, D=>B, E=>C
+ */
+ private nothrow pure string[] round(int i, string a, string b, string c, string d, string e)
+ {
+ return xsetup(PRECALC_AHEAD + i) ~
+ weave(F(i, b, c, d) ~ // Returns result in T1; may destroy T2
+ ["add "~e~","~WiKi(i),
+ "ror "~b~",2",
+ "mov "~T2~","~a,
+ "add "~d~","~WiKi(i+1),
+ "rol "~T2~",5",
+ "add "~e~","~T1 ],
+ precalc(PRECALC_AHEAD + i), 2) ~
+ weave(
+ ["add "~T2~","~e, // T2 = (A <<< 5) + F(B, C, D) + Wi + Ki + E
+ "mov "~e~","~T2,
+ "rol "~T2~",5",
+ "add "~d~","~T2 ] ~
+ F(i+1, a, b, c) ~ // Returns result in T1; may destroy T2
+ ["add "~d~","~T1,
+ "ror "~a~",2"],
+ precalc(PRECALC_AHEAD + i+1), 2);
+ }
+
+ // Offset into stack (see below)
+ version (_32Bit)
+ {
+ private enum { STATE_OFS = 4, WI_PLUS_KI_PTR = 8, WI_PTR = 72 };
+ }
+ version (_64Bit)
+ {
+ private enum { WI_PLUS_KI_PTR = 0 };
+ }
+
+ /** The prologue sequence. */
+ private nothrow pure string[] prologue()
+ {
+ version (_32Bit)
+ {
+ /*
+ * Parameters:
+ * EAX contains pointer to input buffer
+ *
+ * Stack layout as follows:
+ * +----------------+
+ * | ptr to state |
+ * +----------------+
+ * | return address |
+ * +----------------+
+ * | EBP |
+ * +----------------+
+ * | ESI |
+ * +----------------+
+ * | EDI |
+ * +----------------+
+ * | EBX |
+ * +----------------+
+ * | Space for |
+ * | Wi | <- ESP+72
+ * +----------------+
+ * | Space for |
+ * | Wi+Ki | <- ESP+8
+ * +----------------+ <- 16byte aligned
+ * | ptr to state | <- ESP+4
+ * +----------------+
+ * | old ESP | <- ESP
+ * +----------------+
+ */
+ static assert(BUFFER_PTR == "EAX");
+ static assert(STATE_PTR == "EBX");
+ return [// Save registers according to calling convention
+ "push EBP",
+ "push ESI",
+ "push EDI",
+ "push EBX",
+ // Load parameters
+ "mov EBX, [ESP + 5*4]", //pointer to state
+ // Align stack
+ "mov EBP, ESP",
+ "sub ESP, 4*16 + 8*16",
+ "and ESP, 0xffff_fff0",
+ "push EBX",
+ "push EBP",
+ ];
+ }
+ version (_64Bit)
+ {
+ /*
+ * Parameters:
+ * RDX contains pointer to state
+ * RSI contains pointer to input buffer
+ * RDI contains pointer to constants
+ *
+ * Stack layout as follows:
+ * +----------------+
+ * | return address |
+ * +----------------+
+ * | RBP |
+ * +----------------+
+ * | RBX |
+ * +----------------+
+ * | Unused |
+ * +----------------+
+ * | Space for |
+ * | Wi+Ki | <- RSP
+ * +----------------+ <- 16byte aligned
+ */
+ return [// Save registers according to calling convention
+ "push RBP",
+ "push RBX",
+ // Save parameters
+ "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",
+ ];
+ }
+ }
+
+ /**
+ * The epilogue sequence. Just pop the saved registers from stack and return to caller.
+ */
+ private nothrow pure string[] epilogue()
+ {
+ version (_32Bit)
+ {
+ return ["pop ESP",
+ "pop EBX",
+ "pop EDI",
+ "pop ESI",
+ "pop EBP",
+ "ret 4",
+ ];
+ }
+ version (_64Bit)
+ {
+ return ["add RSP,4*16+8",
+ "pop RBX",
+ "pop RBP",
+ "ret 0",
+ ];
+ }
+ }
+
+ // 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, ExtraArgs) pure nothrow @nogc
+ {
+ mixin(wrap(["naked;"] ~ prologue()));
+ // Precalc first 4*16=64 bytes
+ mixin(wrap(xsetup(0)));
+ mixin(wrap(weave(precalc(0)~precalc(1)~precalc(2)~precalc(3),
+ precalc(4)~precalc(5)~precalc(6)~precalc(7))));
+ mixin(wrap(weave(loadstate(STATE_PTR, A, B, C, D, E),
+ weave(precalc(8)~precalc(9)~precalc(10)~precalc(11),
+ precalc(12)~precalc(13)~precalc(14)~precalc(15)))));
+ // Round 1
+ mixin(wrap(round( 0, A, B, C, D, E)));
+ mixin(wrap(round( 2, D, E, A, B, C)));
+ mixin(wrap(round( 4, B, C, D, E, A)));
+ mixin(wrap(round( 6, E, A, B, C, D)));
+ mixin(wrap(round( 8, C, D, E, A, B)));
+ mixin(wrap(round(10, A, B, C, D, E)));
+ mixin(wrap(round(12, D, E, A, B, C)));
+ mixin(wrap(round(14, B, C, D, E, A)));
+ mixin(wrap(round(16, E, A, B, C, D)));
+ mixin(wrap(round(18, C, D, E, A, B)));
+ // Round 2
+ mixin(wrap(round(20, A, B, C, D, E)));
+ mixin(wrap(round(22, D, E, A, B, C)));
+ mixin(wrap(round(24, B, C, D, E, A)));
+ mixin(wrap(round(26, E, A, B, C, D)));
+ mixin(wrap(round(28, C, D, E, A, B)));
+ mixin(wrap(round(30, A, B, C, D, E)));
+ mixin(wrap(round(32, D, E, A, B, C)));
+ mixin(wrap(round(34, B, C, D, E, A)));
+ mixin(wrap(round(36, E, A, B, C, D)));
+ mixin(wrap(round(38, C, D, E, A, B)));
+ // Round 3
+ mixin(wrap(round(40, A, B, C, D, E)));
+ mixin(wrap(round(42, D, E, A, B, C)));
+ mixin(wrap(round(44, B, C, D, E, A)));
+ mixin(wrap(round(46, E, A, B, C, D)));
+ mixin(wrap(round(48, C, D, E, A, B)));
+ mixin(wrap(round(50, A, B, C, D, E)));
+ mixin(wrap(round(52, D, E, A, B, C)));
+ mixin(wrap(round(54, B, C, D, E, A)));
+ mixin(wrap(round(56, E, A, B, C, D)));
+ mixin(wrap(round(58, C, D, E, A, B)));
+ // Round 4
+ mixin(wrap(round(60, A, B, C, D, E)));
+ mixin(wrap(round(62, D, E, A, B, C)));
+ mixin(wrap(round(64, B, C, D, E, A)));
+ mixin(wrap(round(66, E, A, B, C, D)));
+ mixin(wrap(round(68, C, D, E, A, B)));
+ mixin(wrap(round(70, A, B, C, D, E)));
+ mixin(wrap(round(72, D, E, A, B, C)));
+ mixin(wrap(round(74, B, C, D, E, A)));
+ mixin(wrap(round(76, E, A, B, C, D)));
+ mixin(wrap(round(78, C, D, E, A, B)));
+ version (_32Bit)
+ {
+ // Load pointer to state
+ mixin(wrap(["mov "~STATE_PTR~",[ESP + STATE_OFS]"]));
+ }
+ mixin(wrap(savestate(STATE_PTR, A, B, C, D, E)));
+ mixin(wrap(epilogue()));
+ }
+}
diff --git a/libphobos/src/std/internal/math/biguintcore.d b/libphobos/src/std/internal/math/biguintcore.d
new file mode 100644
index 0000000..f5cd769
--- /dev/null
+++ b/libphobos/src/std/internal/math/biguintcore.d
@@ -0,0 +1,2571 @@
+/** Fundamental operations for arbitrary-precision arithmetic
+ *
+ * These functions are for internal use only.
+ */
+/* Copyright Don Clugston 2008 - 2010.
+ * 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)
+ */
+/* References:
+ "Modern Computer Arithmetic" (MCA) is the primary reference for all
+ algorithms used in this library.
+ - R.P. Brent and P. Zimmermann, "Modern Computer Arithmetic",
+ Version 0.5.9, (Oct 2010).
+ - C. Burkinel and J. Ziegler, "Fast Recursive Division", MPI-I-98-1-022,
+ Max-Planck Institute fuer Informatik, (Oct 1998).
+ - G. Hanrot, M. Quercia, and P. Zimmermann, "The Middle Product Algorithm, I.",
+ INRIA 4664, (Dec 2002).
+ - M. Bodrato and A. Zanoni, "What about Toom-Cook Matrices Optimality?",
+ http://bodrato.it/papers (2006).
+ - A. Fog, "Optimizing subroutines in assembly language",
+ www.agner.org/optimize (2008).
+ - A. Fog, "The microarchitecture of Intel and AMD CPU's",
+ www.agner.org/optimize (2008).
+ - A. Fog, "Instruction tables: Lists of instruction latencies, throughputs
+ and micro-operation breakdowns for Intel and AMD CPU's.", www.agner.org/optimize (2008).
+
+Idioms:
+ Many functions in this module use
+ 'func(Tulong)(Tulong x) if (is(Tulong == ulong))' rather than 'func(ulong x)'
+ in order to disable implicit conversion.
+
+*/
+module std.internal.math.biguintcore;
+
+version (D_InlineAsm_X86)
+{
+ import std.internal.math.biguintx86;
+}
+else
+{
+ import std.internal.math.biguintnoasm;
+}
+
+alias multibyteAdd = multibyteAddSub!('+');
+alias multibyteSub = multibyteAddSub!('-');
+
+
+import core.cpuid;
+public import std.ascii : LetterCase;
+import std.range.primitives;
+import std.traits;
+
+shared static this()
+{
+ CACHELIMIT = core.cpuid.datacache[0].size*1024/2;
+}
+
+private:
+// Limits for when to switch between algorithms.
+immutable size_t CACHELIMIT; // Half the size of the data cache.
+enum size_t FASTDIVLIMIT = 100; // crossover to recursive division
+
+
+// These constants are used by shift operations
+static if (BigDigit.sizeof == int.sizeof)
+{
+ enum { LG2BIGDIGITBITS = 5, BIGDIGITSHIFTMASK = 31 };
+ alias BIGHALFDIGIT = ushort;
+}
+else static if (BigDigit.sizeof == long.sizeof)
+{
+ alias BIGHALFDIGIT = uint;
+ enum { LG2BIGDIGITBITS = 6, BIGDIGITSHIFTMASK = 63 };
+}
+else static assert(0, "Unsupported BigDigit size");
+
+import std.exception : assumeUnique;
+import std.traits : isIntegral;
+enum BigDigitBits = BigDigit.sizeof*8;
+template maxBigDigits(T)
+if (isIntegral!T)
+{
+ enum maxBigDigits = (T.sizeof+BigDigit.sizeof-1)/BigDigit.sizeof;
+}
+
+static immutable BigDigit[] ZERO = [0];
+static immutable BigDigit[] ONE = [1];
+static immutable BigDigit[] TWO = [2];
+static immutable BigDigit[] TEN = [10];
+
+
+public:
+
+/// BigUint performs memory management and wraps the low-level calls.
+struct BigUint
+{
+private:
+ pure invariant()
+ {
+ 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);
+ }
+
+ enum trustedAssumeUnique = function(BigDigit[] input) pure @trusted @nogc {
+ return assumeUnique(input);
+ };
+public:
+ // Length in uints
+ @property size_t uintLength() pure nothrow const @safe @nogc
+ {
+ static if (BigDigit.sizeof == uint.sizeof)
+ {
+ return data.length;
+ }
+ else static if (BigDigit.sizeof == ulong.sizeof)
+ {
+ return data.length * 2 -
+ ((data[$-1] & 0xFFFF_FFFF_0000_0000L) ? 1 : 0);
+ }
+ }
+ @property size_t ulongLength() pure nothrow const @safe @nogc
+ {
+ static if (BigDigit.sizeof == uint.sizeof)
+ {
+ return (data.length + 1) >> 1;
+ }
+ else static if (BigDigit.sizeof == ulong.sizeof)
+ {
+ return data.length;
+ }
+ }
+
+ // 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 );
+ }
+ else static if (BigDigit.sizeof == long.sizeof)
+ {
+ return data[n];
+ }
+ }
+ uint peekUint(int n) pure nothrow const @safe @nogc
+ {
+ static if (BigDigit.sizeof == int.sizeof)
+ {
+ return data[n];
+ }
+ else
+ {
+ immutable x = data[n >> 1];
+ return (n & 1) ? cast(uint)(x >> 32) : cast(uint) x;
+ }
+ }
+public:
+ ///
+ void opAssign(Tulong)(Tulong u) pure nothrow @safe if (is (Tulong == ulong))
+ {
+ if (u == 0) data = ZERO;
+ else if (u == 1) data = ONE;
+ else if (u == 2) data = TWO;
+ else if (u == 10) data = TEN;
+ else
+ {
+ static if (BigDigit.sizeof == int.sizeof)
+ {
+ uint ulo = cast(uint)(u & 0xFFFF_FFFF);
+ uint uhi = cast(uint)(u >> 32);
+ if (uhi == 0)
+ {
+ data = [ulo];
+ }
+ else
+ {
+ data = [ulo, uhi];
+ }
+ }
+ else static if (BigDigit.sizeof == long.sizeof)
+ {
+ data = [u];
+ }
+ }
+ }
+ void opAssign(Tdummy = void)(BigUint y) pure nothrow @nogc @safe
+ {
+ this.data = y.data;
+ }
+
+ ///
+ int opCmp(Tdummy = void)(const BigUint y) pure nothrow @nogc const @safe
+ {
+ if (data.length != y.data.length)
+ return (data.length > y.data.length) ? 1 : -1;
+ size_t k = highestDifferentDigit(data, y.data);
+ if (data[k] == y.data[k])
+ return 0;
+ return data[k] > y.data[k] ? 1 : -1;
+ }
+
+ ///
+ int opCmp(Tulong)(Tulong y) pure nothrow @nogc const @safe if (is (Tulong == ulong))
+ {
+ if (data.length > maxBigDigits!Tulong)
+ return 1;
+
+ foreach_reverse (i; 0 .. maxBigDigits!Tulong)
+ {
+ BigDigit tmp = cast(BigDigit)(y>>(i*BigDigitBits));
+ if (tmp == 0)
+ 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.
+ return (data[i] > 0) ? 1 : 0;
+ }
+ else
+ continue;
+ else
+ if (i+1 > data.length)
+ return -1;
+ else if (tmp != data[i])
+ return data[i] > tmp ? 1 : -1;
+ }
+ return 0;
+ }
+
+ bool opEquals(Tdummy = void)(ref const BigUint y) pure nothrow @nogc const @safe
+ {
+ return y.data[] == data[];
+ }
+
+ bool opEquals(Tdummy = void)(ulong y) pure nothrow @nogc const @safe
+ {
+ if (data.length > 2)
+ return false;
+ uint ylo = cast(uint)(y & 0xFFFF_FFFF);
+ uint yhi = cast(uint)(y >> 32);
+ if (data.length == 2 && data[1]!=yhi)
+ return false;
+ if (data.length == 1 && yhi != 0)
+ return false;
+ return (data[0] == ylo);
+ }
+
+ bool isZero() pure const nothrow @safe @nogc
+ {
+ return data.length == 1 && data[0] == 0;
+ }
+
+ size_t numBytes() pure nothrow const @safe @nogc
+ {
+ return data.length * BigDigit.sizeof;
+ }
+
+ // the extra bytes are added to the start of the string
+ char [] toDecimalString(int frontExtraBytes) const pure nothrow
+ {
+ 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..$];
+ }
+
+ /** Convert to a hex string, printing a minimum number of digits 'minPadding',
+ * allocating an additional 'frontExtraBytes' at the start of the string.
+ * Padding is done with padChar, which may be '0' or ' '.
+ * 'separator' is a digit separation character. If non-zero, it is inserted
+ * between every 8 digits.
+ * Separator characters do not contribute to the minPadding.
+ */
+ char [] toHexString(int frontExtraBytes, char separator = 0,
+ 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)
+ ? minPadding - data.length * 2 * BigDigit.sizeof : 0;
+
+ // Length not including separator bytes
+ size_t lenBytes = data.length * 2 * BigDigit.sizeof;
+
+ // Calculate number of separator bytes
+ size_t mainSeparatorBytes = separator ? (lenBytes / 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, letterCase);
+ if (extraPad > 0)
+ {
+ if (separator)
+ {
+ size_t start = frontExtraBytes; // first index to pad
+ if (extraPad &7)
+ {
+ // Do 1 to 7 extra zeros.
+ buff[frontExtraBytes .. frontExtraBytes + (extraPad & 7)] = padChar;
+ buff[frontExtraBytes + (extraPad & 7)] = (padChar == ' ' ? ' ' : separator);
+ start += (extraPad & 7) + 1;
+ }
+ for (int i=0; i< (extraPad >> 3); ++i)
+ {
+ buff[start .. start + 8] = padChar;
+ buff[start + 8] = (padChar == ' ' ? ' ' : separator);
+ start += 9;
+ }
+ }
+ else
+ {
+ buff[frontExtraBytes .. frontExtraBytes + extraPad]=padChar;
+ }
+ }
+ int z = frontExtraBytes;
+ if (lenBytes > minPadding)
+ {
+ // Strip leading zeros.
+ ptrdiff_t maxStrip = lenBytes - minPadding;
+ while (z< buff.length-1 && (buff[z]=='0' || buff[z]==padChar) && maxStrip>0)
+ {
+ ++z;
+ --maxStrip;
+ }
+ }
+ if (padChar!='0')
+ {
+ // Convert leading zeros into padChars.
+ for (size_t k= z; k< buff.length-1 && (buff[k]=='0' || buff[k]==padChar); ++k)
+ {
+ if (buff[k]=='0') buff[k]=padChar;
+ }
+ }
+ 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(Range)(Range s) if (
+ isBidirectionalRange!Range && isSomeChar!(ElementType!Range))
+ {
+ import std.range : walkLength;
+
+ //Strip leading zeros
+ 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));
+ ++partcount;
+
+ if (partcount == 8)
+ {
+ tmp[sofar] = part;
+ ++sofar;
+ partcount = 0;
+ part = 0;
+ }
+ }
+ if (part)
+ {
+ for ( ; partcount != 8; ++partcount) part >>= 4;
+ tmp[sofar] = part;
+ ++sofar;
+ }
+ if (sofar == 0)
+ data = ZERO;
+ else
+ data = trustedAssumeUnique(tmp[0 .. sofar]);
+
+ return true;
+ }
+
+ // return true if OK; false if erroneous characters found
+ bool fromDecimalString(Range)(Range s) if (
+ isForwardRange!Range && isSomeChar!(ElementType!Range))
+ {
+ import std.range : walkLength;
+
+ while (!s.empty && s.front == '0')
+ {
+ s.popFront;
+ }
+
+ if (s.empty)
+ {
+ data = ZERO;
+ return true;
+ }
+
+ 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;
+ }
+
+ ////////////////////////
+ //
+ // All of these member functions create a new BigUint.
+
+ // return x >> y
+ 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 words = cast(uint)(y >> LG2BIGDIGITBITS);
+ if (bits == 0)
+ {
+ return BigUint(data[words..$]);
+ }
+ else
+ {
+ uint [] result = new BigDigit[data.length - words];
+ multibyteShr(result, data[words..$], bits);
+
+ if (result.length > 1 && result[$-1] == 0)
+ return BigUint(trustedAssumeUnique(result[0 .. $-1]));
+ else
+ return BigUint(trustedAssumeUnique(result));
+ }
+ }
+
+ // return x << y
+ BigUint opShl(Tulong)(Tulong y) pure nothrow const if (is (Tulong == ulong))
+ {
+ assert(y>0);
+ if (isZero()) return this;
+ 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[words .. words+data.length] = data[];
+ return BigUint(trustedAssumeUnique(result[0 .. words+data.length]));
+ }
+ else
+ {
+ 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));
+ }
+ }
+
+ // If wantSub is false, return x + y, leaving sign unchanged
+ // If wantSub is true, return abs(x - y), negating sign if x < y
+ static BigUint addOrSubInt(Tulong)(const BigUint x, Tulong y,
+ bool wantSub, ref bool sign) pure nothrow if (is(Tulong == ulong))
+ {
+ BigUint r;
+ if (wantSub)
+ { // perform a subtraction
+ if (x.data.length > 2)
+ {
+ r.data = subInt(x.data, y);
+ }
+ else
+ { // could change sign!
+ ulong xx = x.data[0];
+ if (x.data.length > 1)
+ xx += (cast(ulong) x.data[1]) << 32;
+ ulong d;
+ if (xx <= y)
+ {
+ d = y - xx;
+ sign = !sign;
+ }
+ else
+ {
+ d = xx - y;
+ }
+ if (d == 0)
+ {
+ r = 0UL;
+ sign = false;
+ return r;
+ }
+ if (d > uint.max)
+ {
+ r.data = [cast(uint)(d & 0xFFFF_FFFF), cast(uint)(d >> 32)];
+ }
+ else
+ {
+ r.data = [cast(uint)(d & 0xFFFF_FFFF)];
+ }
+ }
+ }
+ else
+ {
+ r.data = addInt(x.data, y);
+ }
+ return r;
+ }
+
+ // If wantSub is false, return x + y, leaving sign unchanged.
+ // If wantSub is true, return abs(x - y), negating sign if x<y
+ static BigUint addOrSub(BigUint x, BigUint y, bool wantSub, bool *sign)
+ pure nothrow
+ {
+ BigUint r;
+ if (wantSub)
+ { // perform a subtraction
+ bool negative;
+ r.data = sub(x.data, y.data, &negative);
+ *sign ^= negative;
+ if (r.isZero())
+ {
+ *sign = false;
+ }
+ }
+ else
+ {
+ r.data = add(x.data, y.data);
+ }
+ return r;
+ }
+
+
+ // return x*y.
+ // y must not be zero.
+ static BigUint mulInt(T = ulong)(BigUint x, T y) pure nothrow
+ {
+ 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)
+ {
+ result[x.data.length+1] = multibyteMulAdd!('+')(result[1 .. x.data.length+1],
+ x.data, hi, 0);
+ }
+ return BigUint(removeLeadingZeros(trustedAssumeUnique(result)));
+ }
+
+ /* return x * y.
+ */
+ static BigUint mul(BigUint x, BigUint y) pure nothrow
+ {
+ if (y == 0 || x == 0)
+ return BigUint(ZERO);
+ auto len = x.data.length + y.data.length;
+ BigDigit [] result = new BigDigit[len];
+ if (y.data.length > x.data.length)
+ {
+ mulInternal(result, y.data, x.data);
+ }
+ else
+ {
+ if (x.data[]==y.data[]) squareInternal(result, x.data);
+ else mulInternal(result, x.data, y.data);
+ }
+ // the highest element could be zero,
+ // in which case we need to reduce the length
+ return BigUint(removeLeadingZeros(trustedAssumeUnique(result)));
+ }
+
+ // return x / y
+ static BigUint divInt(T)(BigUint x, T y_) pure nothrow
+ if ( is(Unqual!T == uint) )
+ {
+ uint y = y_;
+ if (y == 1)
+ return x;
+ uint [] result = new BigDigit[x.data.length];
+ if ((y&(-y))==y)
+ {
+ assert(y != 0, "BigUint division by zero");
+ // perfect power of 2
+ uint b = 0;
+ for (;y != 1; y>>=1)
+ {
+ ++b;
+ }
+ multibyteShr(result, x.data, b);
+ }
+ else
+ {
+ result[] = x.data[];
+ cast(void) multibyteDivAssign(result, y, 0);
+ }
+ return BigUint(removeLeadingZeros(trustedAssumeUnique(result)));
+ }
+
+ static BigUint divInt(T)(BigUint x, T y) pure nothrow
+ if ( is(Unqual!T == ulong) )
+ {
+ if (y <= uint.max)
+ return divInt!uint(x, cast(uint) y);
+ if (x.data.length < 2)
+ return BigUint(ZERO);
+ uint hi = cast(uint)(y >>> 32);
+ uint lo = cast(uint)(y & 0xFFFF_FFFF);
+ immutable uint[2] z = [lo, hi];
+ BigDigit[] result = new BigDigit[x.data.length - z.length + 1];
+ divModInternal(result, null, x.data, z[]);
+ return BigUint(removeLeadingZeros(trustedAssumeUnique(result)));
+ }
+
+ // 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);
+ if ((y&(-y)) == y)
+ { // perfect power of 2
+ return x.data[0] & (y-1);
+ }
+ else
+ {
+ // horribly inefficient - malloc, copy, & store are unnecessary.
+ uint [] wasteful = new BigDigit[x.data.length];
+ wasteful[] = x.data[];
+ immutable rem = multibyteDivAssign(wasteful, y, 0);
+ () @trusted { GC.free(wasteful.ptr); } ();
+ return rem;
+ }
+ }
+
+ // return x / y
+ static BigUint div(BigUint x, BigUint y) pure nothrow
+ {
+ if (y.data.length > x.data.length)
+ return BigUint(ZERO);
+ if (y.data.length == 1)
+ return divInt(x, y.data[0]);
+ BigDigit [] result = new BigDigit[x.data.length - y.data.length + 1];
+ divModInternal(result, null, x.data, y.data);
+ return BigUint(removeLeadingZeros(trustedAssumeUnique(result)));
+ }
+
+ // return x % y
+ static BigUint mod(BigUint x, BigUint y) pure nothrow
+ {
+ if (y.data.length > x.data.length) return x;
+ if (y.data.length == 1)
+ {
+ return BigUint([modInt(x, y.data[0])]);
+ }
+ BigDigit [] result = new BigDigit[x.data.length - y.data.length + 1];
+ BigDigit [] rem = new BigDigit[y.data.length];
+ divModInternal(result, rem, x.data, y.data);
+ return BigUint(removeLeadingZeros(trustedAssumeUnique(rem)));
+ }
+
+ // return x op y
+ static BigUint bitwiseOp(string op)(BigUint x, BigUint y, bool xSign, bool ySign, ref bool resultSign)
+ pure nothrow @safe if (op == "|" || op == "^" || op == "&")
+ {
+ auto d1 = includeSign(x.data, y.uintLength, xSign);
+ auto d2 = includeSign(y.data, x.uintLength, ySign);
+
+ foreach (i; 0 .. d1.length)
+ {
+ mixin("d1[i] " ~ op ~ "= d2[i];");
+ }
+
+ mixin("resultSign = xSign " ~ op ~ " ySign;");
+
+ if (resultSign)
+ {
+ twosComplement(d1, d1);
+ }
+
+ return BigUint(removeLeadingZeros(trustedAssumeUnique(d1)));
+ }
+
+ /**
+ * Return a BigUint which is x raised to the power of y.
+ * Method: Powers of 2 are removed from x, then left-to-right binary
+ * exponentiation is used.
+ * Memory allocation is minimized: at most one temporary BigUint is used.
+ */
+ 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;
+
+ BigUint result;
+
+ // Simplify, step 1: Remove all powers of 2.
+ uint firstnonzero = firstNonZeroDigit(x.data);
+ // Now we know x = x[firstnonzero..$] * (2^^(firstnonzero*BigDigitBits))
+ // where BigDigitBits = BigDigit.sizeof * 8
+
+ // See if x[firstnonzero..$] can now fit into a single digit.
+ bool singledigit = ((x.data.length - firstnonzero) == 1);
+ // 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);
+ // Length of the non-zero portion
+ size_t nonzerolength = x.data.length - firstnonzero;
+ ulong y0;
+ uint evenbits = 0; // number of even bits in the bottom of x
+ while (!(x0 & 1))
+ {
+ x0 >>= 1;
+ ++evenbits;
+ }
+
+ if (x.data.length- firstnonzero == 2)
+ {
+ // Check for a single digit straddling a digit boundary
+ const BigDigit x1 = x.data[firstnonzero+1];
+ if ((x1 >> evenbits) == 0)
+ {
+ x0 |= (x1 << (BigDigit.sizeof * 8 - evenbits));
+ singledigit = true;
+ }
+ }
+ // Now if (singledigit), x^^y = (x0 ^^ y) * 2^^(evenbits * y) * 2^^(firstnonzero*y*BigDigitBits))
+
+ uint evenshiftbits = 0; // Total powers of 2 to shift by, at the end
+
+ // Simplify, step 2: For singledigits, see if we can trivially reduce y
+
+ BigDigit finalMultiplier = 1UL;
+
+ if (singledigit)
+ {
+ // x fits into a single digit. Raise it to the highest power we can
+ // that still fits into a single digit, then reduce the exponent accordingly.
+ // We're quite likely to have a residual multiply at the end.
+ // For example, 10^^100 = (((5^^13)^^7) * 5^^9) * 2^^100.
+ // and 5^^13 still fits into a uint.
+ evenshiftbits = cast(uint)( (evenbits * y) & BIGDIGITSHIFTMASK);
+ if (x0 == 1)
+ { // Perfect power of 2
+ result = 1UL;
+ return result << (evenbits + firstnonzero * 8 * BigDigit.sizeof) * y;
+ }
+ immutable p = highestPowerBelowUintMax(x0);
+ if (y <= p)
+ { // Just do it with pow
+ result = cast(ulong) intpow(x0, y);
+ if (evenbits + firstnonzero == 0)
+ return result;
+ return result << (evenbits + firstnonzero * 8 * BigDigit.sizeof) * y;
+ }
+ y0 = y / p;
+ finalMultiplier = intpow(x0, y - y0*p);
+ x0 = intpow(x0, p);
+ // Result is x0
+ nonzerolength = 1;
+ }
+ // Now if (singledigit), x^^y = finalMultiplier * (x0 ^^ y0) * 2^^(evenbits * y) * 2^^(firstnonzero*y*BigDigitBits))
+
+ // Perform a crude check for overflow and allocate result buffer.
+ // The length required is y * lg2(x) bits.
+ // which will always fit into y*x.length digits. But this is
+ // a gross overestimate if x is small (length 1 or 2) and the highest
+ // digit is nearly empty.
+ // A better estimate is:
+ // y * lg2(x[$-1]/BigDigit.max) + y * (x.length - 1) digits,
+ // and the first term is always between
+ // y * (bsr(x.data[$-1]) + 1) / BIGDIGITBITS and
+ // y * (bsr(x.data[$-1]) + 2) / BIGDIGITBITS
+ // For single digit payloads, we already have
+ // x^^y = finalMultiplier * (x0 ^^ y0) * 2^^(evenbits * y) * 2^^(firstnonzero*y*BigDigitBits))
+ // and x0 is almost a full digit, so it's a tight estimate.
+ // Number of digits is therefore 1 + x0.length*y0 + (evenbits*y)/BIGDIGIT + firstnonzero*y
+ // Note that the divisions must be rounded up.
+
+ // Estimated length in BigDigits
+ 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
+ // (less extreme overflow will result in an out of memory error).
+ if (estimatelength > uint.max/(4*BigDigit.sizeof))
+ assert(0, "Overflow in BigInt.pow");
+
+ // The result buffer includes space for all the trailing zeros
+ 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;
+ BigDigit [] t1 = resultBuffer[result_start..$];
+ BigDigit [] r1;
+
+ if (singledigit)
+ {
+ r1 = t1[0 .. 1];
+ r1[0] = x0;
+ y = y0;
+ }
+ else
+ {
+ // 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[0..$] = x.data[firstnonzero..$];
+ }
+
+ if (y>1)
+ { // Set r1 = r1 ^^ y.
+ // The secondary buffer only needs space for the multiplication results
+ BigDigit [] t2 = new BigDigit[resultBuffer.length - result_start];
+ BigDigit [] r2;
+
+ int shifts = 63; // num bits in a long
+ while (!(y & 0x8000_0000_0000_0000L))
+ {
+ y <<= 1;
+ --shifts;
+ }
+ y <<=1;
+
+ while (y != 0)
+ {
+ // For each bit of y: Set r1 = r1 * r1
+ // If the bit is 1, set r1 = r1 * x
+ // Eg, if y is 0b101, result = ((x^^2)^^2)*x == x^^5.
+ // Optimization opportunity: if more than 2 bits in y are set,
+ // it's usually possible to reduce the number of multiplies
+ // by caching odd powers of x. eg for y = 54,
+ // (0b110110), set u = x^^3, and result is ((u^^8)*u)^^2
+ r2 = t2[0 .. r1.length*2];
+ squareInternal(r2, r1);
+ if (y & 0x8000_0000_0000_0000L)
+ {
+ r1 = t1[0 .. r2.length + nonzerolength];
+ if (singledigit)
+ {
+ r1[$-1] = multibyteMul(r1[0 .. $-1], r2, x0, 0);
+ }
+ else
+ {
+ mulInternal(r1, r2, x.data[firstnonzero..$]);
+ }
+ }
+ else
+ {
+ r1 = t1[0 .. r2.length];
+ r1[] = r2[];
+ }
+ y <<=1;
+ shifts--;
+ }
+ while (shifts>0)
+ {
+ r2 = t2[0 .. r1.length * 2];
+ squareInternal(r2, r1);
+ r1 = t1[0 .. r2.length];
+ r1[] = r2[];
+ --shifts;
+ }
+ }
+
+ if (finalMultiplier != 1)
+ {
+ const BigDigit carry = multibyteMul(r1, r1, finalMultiplier, 0);
+ if (carry)
+ {
+ r1 = t1[0 .. r1.length + 1];
+ r1[$-1] = carry;
+ }
+ }
+ if (evenshiftbits)
+ {
+ const BigDigit carry = multibyteShl(r1, r1, evenshiftbits);
+ if (carry != 0)
+ {
+ r1 = t1[0 .. r1.length + 1];
+ r1[$ - 1] = carry;
+ }
+ }
+ while (r1[$ - 1]==0)
+ {
+ r1=r1[0 .. $ - 1];
+ }
+ return BigUint(trustedAssumeUnique(resultBuffer[0 .. result_start + r1.length]));
+ }
+
+ // Implement toHash so that BigUint works properly as an AA key.
+ size_t toHash() const @trusted nothrow
+ {
+ return typeid(data).getHash(&data);
+ }
+
+} // end BigUint
+
+@safe pure nothrow unittest
+{
+ // ulong comparison test
+ BigUint a = [1];
+ assert(a == 1);
+ assert(a < 0x8000_0000_0000_0000UL); // bug 9548
+
+ // bug 12234
+ BigUint z = [0];
+ assert(z == 0UL);
+ assert(!(z > 0UL));
+ assert(!(z < 0UL));
+}
+
+// Remove leading zeros from x, to restore the BigUint invariant
+inout(BigDigit) [] removeLeadingZeros(inout(BigDigit) [] x) pure nothrow @safe
+{
+ size_t k = x.length;
+ while (k>1 && x[k - 1]==0) --k;
+ return x[0 .. k];
+}
+
+pure @system unittest
+{
+ BigUint r = BigUint([5]);
+ BigUint t = BigUint([7]);
+ BigUint s = BigUint.mod(r, t);
+ assert(s == 5);
+}
+
+
+@safe pure unittest
+{
+ BigUint r;
+ r = 5UL;
+ assert(r.peekUlong(0) == 5UL);
+ assert(r.peekUint(0) == 5U);
+ r = 0x1234_5678_9ABC_DEF0UL;
+ assert(r.peekUlong(0) == 0x1234_5678_9ABC_DEF0UL);
+ assert(r.peekUint(0) == 0x9ABC_DEF0U);
+}
+
+
+// Pow tests
+pure @system unittest
+{
+ BigUint r, s;
+ r.fromHexString("80000000_00000001");
+ s = BigUint.pow(r, 5);
+ r.fromHexString("08000000_00000000_50000000_00000001_40000000_00000002_80000000"
+ ~ "_00000002_80000000_00000001");
+ assert(s == r);
+ s = 10UL;
+ s = BigUint.pow(s, 39);
+ r.fromDecimalString("1000000000000000000000000000000000000000");
+ assert(s == r);
+ r.fromHexString("1_E1178E81_00000000");
+ s = BigUint.pow(r, 15); // Regression test: this used to overflow array bounds
+
+ r.fromDecimalString("000_000_00");
+ assert(r == 0);
+ r.fromDecimalString("0007");
+ assert(r == 7);
+ r.fromDecimalString("0");
+ assert(r == 0);
+}
+
+// Radix conversion tests
+@safe pure unittest
+{
+ BigUint r;
+ r.fromHexString("1_E1178E81_00000000");
+ assert(r.toHexString(0, '_', 0) == "1_E1178E81_00000000");
+ assert(r.toHexString(0, '_', 20) == "0001_E1178E81_00000000");
+ assert(r.toHexString(0, '_', 16+8) == "00000001_E1178E81_00000000");
+ assert(r.toHexString(0, '_', 16+9) == "0_00000001_E1178E81_00000000");
+ assert(r.toHexString(0, '_', 16+8+8) == "00000000_00000001_E1178E81_00000000");
+ assert(r.toHexString(0, '_', 16+8+8+1) == "0_00000000_00000001_E1178E81_00000000");
+ assert(r.toHexString(0, '_', 16+8+8+1, ' ') == " 1_E1178E81_00000000");
+ assert(r.toHexString(0, 0, 16+8+8+1) == "00000000000000001E1178E8100000000");
+ r = 0UL;
+ assert(r.toHexString(0, '_', 0) == "0");
+ assert(r.toHexString(0, '_', 7) == "0000000");
+ 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");
+}
+
+
+private:
+void twosComplement(const(BigDigit) [] x, BigDigit[] result)
+pure nothrow @safe
+{
+ foreach (i; 0 .. x.length)
+ {
+ result[i] = ~x[i];
+ }
+ result[x.length..$] = BigDigit.max;
+
+ foreach (i; 0 .. result.length)
+ {
+ if (result[i] == BigDigit.max)
+ {
+ result[i] = 0;
+ }
+ else
+ {
+ result[i] += 1;
+ break;
+ }
+ }
+}
+
+// Encode BigInt as BigDigit array (sign and 2's complement)
+BigDigit[] includeSign(const(BigDigit) [] x, size_t minSize, bool sign)
+pure nothrow @safe
+{
+ size_t length = (x.length > minSize) ? x.length : minSize;
+ BigDigit [] result = new BigDigit[length];
+ if (sign)
+ {
+ twosComplement(x, result);
+ }
+ else
+ {
+ result[0 .. x.length] = x;
+ }
+ return result;
+}
+
+// works for any type
+T intpow(T)(T x, ulong n) pure nothrow @safe
+{
+ T p;
+
+ switch (n)
+ {
+ case 0:
+ p = 1;
+ break;
+
+ case 1:
+ p = x;
+ break;
+
+ case 2:
+ p = x * x;
+ break;
+
+ default:
+ p = 1;
+ while (1)
+ {
+ if (n & 1)
+ p *= x;
+ n >>= 1;
+ if (!n)
+ break;
+ x *= x;
+ }
+ break;
+ }
+ return p;
+}
+
+
+// returns the maximum power of x that will fit in a uint.
+int highestPowerBelowUintMax(uint x) pure nothrow @safe
+{
+ assert(x>1);
+ static immutable ubyte [22] maxpwr = [ 31, 20, 15, 13, 12, 11, 10, 10, 9, 9,
+ 8, 8, 8, 8, 7, 7, 7, 7, 7, 7, 7, 7];
+ if (x<24) return maxpwr[x-2];
+ if (x<41) return 6;
+ if (x<85) return 5;
+ if (x<256) return 4;
+ if (x<1626) return 3;
+ if (x<65_536) return 2;
+ return 1;
+}
+
+// returns the maximum power of x that will fit in a ulong.
+int highestPowerBelowUlongMax(uint x) pure nothrow @safe
+{
+ assert(x>1);
+ static immutable ubyte [39] maxpwr = [ 63, 40, 31, 27, 24, 22, 21, 20, 19, 18,
+ 17, 17, 16, 16, 15, 15, 15, 15, 14, 14,
+ 14, 14, 13, 13, 13, 13, 13, 13, 13, 12,
+ 12, 12, 12, 12, 12, 12, 12, 12, 12];
+ if (x<41) return maxpwr[x-2];
+ if (x<57) return 11;
+ if (x<85) return 10;
+ if (x<139) return 9;
+ if (x<256) return 8;
+ if (x<566) return 7;
+ if (x<1626) return 6;
+ if (x<7132) return 5;
+ if (x<65_536) return 4;
+ if (x<2_642_246) return 3;
+ return 2;
+}
+
+version (unittest)
+{
+
+int slowHighestPowerBelowUintMax(uint x) pure nothrow @safe
+{
+ int pwr = 1;
+ for (ulong q = x;x*q < cast(ulong) uint.max; )
+ {
+ q*=x; ++pwr;
+ }
+ return pwr;
+}
+
+@safe pure unittest
+{
+ assert(highestPowerBelowUintMax(10)==9);
+ for (int k=82; k<88; ++k)
+ {
+ assert(highestPowerBelowUintMax(k)== slowHighestPowerBelowUintMax(k));
+ }
+}
+
+}
+
+
+/* General unsigned subtraction routine for bigints.
+ * Sets result = x - y. If the result is negative, negative will be true.
+ */
+BigDigit [] sub(const BigDigit [] x, const BigDigit [] y, bool *negative)
+pure nothrow
+{
+ if (x.length == y.length)
+ {
+ // There's a possibility of cancellation, if x and y are almost equal.
+ ptrdiff_t last = highestDifferentDigit(x, y);
+ 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);
+ *negative = true;
+ }
+ else
+ { // positive or zero result
+ multibyteSub(result[0 .. last+1], x[0 .. last+1], y[0 .. last+1], 0);
+ *negative = false;
+ }
+ while (result.length > 1 && result[$-1] == 0)
+ {
+ result = result[0..$-1];
+ }
+// if (result.length >1 && result[$-1]==0) return result[0..$-1];
+ return result;
+ }
+ // Lengths are different
+ const(BigDigit) [] large, small;
+ if (x.length < y.length)
+ {
+ *negative = true;
+ large = y; small = x;
+ }
+ else
+ {
+ *negative = false;
+ large = x; small = y;
+ }
+ // result.length will be equal to larger length, or could decrease by 1.
+
+
+ BigDigit [] result = new BigDigit[large.length];
+ BigDigit carry = multibyteSub(result[0 .. small.length], large[0 .. small.length], small, 0);
+ result[small.length..$] = large[small.length..$];
+ if (carry)
+ {
+ multibyteIncrementAssign!('-')(result[small.length..$], carry);
+ }
+ while (result.length > 1 && result[$-1] == 0)
+ {
+ result = result[0..$-1];
+ }
+ return result;
+}
+
+
+// return a + b
+BigDigit [] add(const BigDigit [] a, const BigDigit [] b) pure nothrow
+{
+ const(BigDigit) [] x, y;
+ if (a.length < b.length)
+ {
+ x = b; y = a;
+ }
+ else
+ {
+ x = a; y = b;
+ }
+ // now we know x.length > y.length
+ // 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);
+ if (x.length != y.length)
+ {
+ result[y.length..$-1]= x[y.length..$];
+ carry = multibyteIncrementAssign!('+')(result[y.length..$-1], carry);
+ }
+ if (carry)
+ {
+ result[$-1] = carry;
+ return result;
+ }
+ else
+ return result[0..$-1];
+}
+
+/** return x + y
+ */
+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;
+ BigDigit [] result = new BigDigit[len+1];
+ 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 (carry)
+ {
+ result[$-1] = carry;
+ return result;
+ }
+ else
+ return result[0..$-1];
+}
+
+/** Return x - y.
+ * x must be greater than y.
+ */
+BigDigit [] subInt(const BigDigit[] x, ulong y) pure nothrow
+{
+ uint hi = cast(uint)(y >>> 32);
+ uint lo = cast(uint)(y & 0xFFFF_FFFF);
+ BigDigit [] result = new BigDigit[x.length];
+ result[] = x[];
+ multibyteIncrementAssign!('-')(result[], lo);
+ if (hi)
+ multibyteIncrementAssign!('-')(result[1..$], hi);
+ if (result[$-1] == 0)
+ return result[0..$-1];
+ else
+ return result;
+}
+
+/** General unsigned multiply routine for bigints.
+ * Sets result = x * y.
+ *
+ * The length of y must not be larger than the length of x.
+ * Different algorithms are used, depending on the lengths of x and y.
+ * TODO: "Modern Computer Arithmetic" suggests the OddEvenKaratsuba algorithm for the
+ * unbalanced case. (But I doubt it would be faster in practice).
+ *
+ */
+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);
+ if (y.length <= KARATSUBALIMIT)
+ {
+ // 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);
+ return;
+ }
+
+ if (x.length + y.length < CACHELIMIT)
+ return mulSimple(result, x, y);
+
+ // If x is so big that it won't fit into the cache, we divide it into chunks
+ // Every chunk must be greater than y.length.
+ // We make the first chunk shorter, if necessary, to ensure this.
+
+ auto chunksize = CACHELIMIT / y.length;
+ 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);
+ auto done = chunksize;
+
+ while (done < x.length)
+ {
+ // 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]);
+ done += chunksize;
+ }
+ return;
+ }
+
+ immutable half = (x.length >> 1) + (x.length & 1);
+ if (2*y.length*y.length <= x.length*x.length)
+ {
+ // UNBALANCED MULTIPLY
+ // Use school multiply to cut into quasi-squares of Karatsuba-size
+ // or larger. The ratio of the two sides of the 'square' must be
+ // between 1.414:1 and 1:1. Use Karatsuba on each chunk.
+ //
+ // For maximum performance, we want the ratio to be as close to
+ // 1:1 as possible. To achieve this, we can either pad x or y.
+ // The best choice depends on the modulus x%y.
+ auto numchunks = x.length / y.length;
+ auto chunksize = y.length;
+ auto extra = x.length % y.length;
+ auto maxchunk = chunksize + extra;
+ bool paddingY; // true = we're padding Y, false = we're padding X.
+ if (extra * extra * 2 < y.length*y.length)
+ {
+ // The leftover bit is small enough that it should be incorporated
+ // in the existing chunks.
+ // Make all the chunks a tiny bit bigger
+ // (We're padding y with zeros)
+ 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.
+ maxchunk = chunksize + 1;
+ paddingY = true;
+ assert(chunksize + extra + chunksize *(numchunks-1) == x.length );
+ }
+ else
+ {
+ // the extra bit is large enough that it's worth making a new chunk.
+ // (This means we're padding x with zeros, when doing the first one).
+ maxchunk = chunksize;
+ ++numchunks;
+ paddingY = false;
+ assert(extra + chunksize *(numchunks-1) == x.length );
+ }
+ // We make the buffer a bit bigger so we have space for the partial sums.
+ 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?
+ if (paddingY)
+ {
+ // If the first chunk is bigger, do it first. We're padding y.
+ mulKaratsuba(result[0 .. y.length + chunksize + (extra > 0 ? 1 : 0 )],
+ x[0 .. chunksize + (extra>0?1:0)], y, scratchbuff);
+ done = chunksize + (extra > 0 ? 1 : 0);
+ if (extra) --extra;
+ }
+ else
+ { // We're padding X. Begin with the extra bit.
+ mulKaratsuba(result[0 .. y.length + extra], y, x[0 .. extra], scratchbuff);
+ done = extra;
+ extra = 0;
+ }
+ immutable basechunksize = chunksize;
+ while (done < x.length)
+ {
+ chunksize = basechunksize + (extra > 0 ? 1 : 0);
+ if (extra) --extra;
+ partial[] = result[done .. done+y.length];
+ mulKaratsuba(result[done .. done + y.length + chunksize],
+ x[done .. done+chunksize], y, scratchbuff);
+ addAssignSimple(result[done .. done + y.length + chunksize], partial);
+ done += chunksize;
+ }
+ () @trusted { GC.free(scratchbuff.ptr); } ();
+ }
+ else
+ {
+ // Balanced. Use Karatsuba directly.
+ BigDigit [] scratchbuff = new BigDigit[karatsubaRequiredBuffSize(x.length)];
+ mulKaratsuba(result, x, y, scratchbuff);
+ () @trusted { GC.free(scratchbuff.ptr); } ();
+ }
+}
+
+/** General unsigned squaring routine for BigInts.
+ * Sets result = x*x.
+ * NOTE: If the highest half-digit of x is zero, the highest digit of result will
+ * also be zero.
+ */
+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)
+ {
+ result[1] = multibyteMul(result[0 .. 1], x, x[0], 0);
+ return;
+ }
+ return squareSimple(result, x);
+ }
+ // The nice thing about squaring is that it always stays balanced
+ BigDigit [] scratchbuff = new BigDigit[karatsubaRequiredBuffSize(x.length)];
+ squareKaratsuba(result, x, scratchbuff);
+ () @trusted { GC.free(scratchbuff.ptr); } ();
+}
+
+
+import core.bitop : bsr;
+
+/// if remainder is null, only calculate quotient.
+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);
+ assert(u.length >= v.length);
+
+ // Normalize by shifting v left just enough so that
+ // its high-order bit is on, and shift u left the
+ // same amount. The highest bit of u will never be set.
+
+ BigDigit [] vn = new BigDigit[v.length];
+ 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)
+ {
+ multibyteShl(vn, v, s);
+ un[$-1] = multibyteShl(un[0..$-1], u, s);
+ }
+ else
+ {
+ vn[] = v[];
+ un[0..$-1] = u[];
+ un[$-1] = 0;
+ }
+ if (quotient.length<FASTDIVLIMIT)
+ {
+ schoolbookDivMod(quotient, un, vn);
+ }
+ else
+ {
+ blockDivMod(quotient, un, vn);
+ }
+
+ // Unnormalize remainder, if required.
+ if (remainder != null)
+ {
+ if (s == 0) remainder[] = un[0 .. vn.length];
+ else multibyteShr(remainder, un[0 .. vn.length+1], s);
+ }
+ () @trusted { GC.free(un.ptr); GC.free(vn.ptr); } ();
+}
+
+pure @system unittest
+{
+ immutable(uint) [] u = [0, 0xFFFF_FFFE, 0x8000_0000];
+ immutable(uint) [] v = [0xFFFF_FFFF, 0x8000_0000];
+ uint [] q = new uint[u.length - v.length + 1];
+ uint [] r = new uint[2];
+ divModInternal(q, r, u, v);
+ assert(q[]==[0xFFFF_FFFFu, 0]);
+ assert(r[]==[0xFFFF_FFFFu, 0x7FFF_FFFF]);
+ u = [0, 0xFFFF_FFFE, 0x8000_0001];
+ v = [0xFFFF_FFFF, 0x8000_0000];
+ divModInternal(q, r, u, v);
+}
+
+
+private:
+// Converts a big uint to a hexadecimal string.
+//
+// Optionally, a separator character (eg, an underscore) may be added between
+// 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,
+ LetterCase letterCase = LetterCase.upper) pure nothrow @safe
+{
+ int x=0;
+ for (ptrdiff_t i=data.length - 1; i >= 0; --i)
+ {
+ toHexZeroPadded(buff[x .. x+8], data[i], letterCase);
+ x+=8;
+ if (separator)
+ {
+ if (i>0) buff[x] = separator;
+ ++x;
+ }
+ }
+ 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:
+ * data The biguint to be converted. Will be destroyed.
+ * buff The destination buffer for the decimal string. Must be
+ * large enough to store the result, including leading zeros.
+ * Will be filled backwards, starting from buff[$-1].
+ *
+ * buff.length must be >= (data.length*32)/log2(10) = 9.63296 * data.length.
+ * Returns:
+ * the lowest index of buff which was used.
+ */
+size_t biguintToDecimal(char [] buff, BigDigit [] data) pure nothrow
+{
+ ptrdiff_t sofar = buff.length;
+ // 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)
+ {
+ uint rem = multibyteDivAssign(data, 10_0000_0000, 0);
+ itoaZeroPadded(buff[sofar-9 .. sofar], rem);
+ sofar -= 9;
+ if (data[$-1] == 0 && data.length > 1)
+ {
+ data.length = data.length - 1;
+ }
+ }
+ itoaZeroPadded(buff[sofar-10 .. sofar], data[0]);
+ sofar -= 10;
+ // and strip off the leading zeros
+ while (sofar != buff.length-1 && buff[sofar] == '0')
+ sofar++;
+ return sofar;
+}
+
+/** Convert a decimal string into a big uint.
+ *
+ * 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
+ *
+ * The required length for the destination buffer is slightly less than
+ * 1 + s.length/log2(10) = 1 + s.length/3.3219.
+ *
+ * Returns:
+ * the highest index of data which was used.
+ */
+int biguintFromDecimal(Range)(BigDigit[] data, Range s)
+if (
+ isInputRange!Range &&
+ isSomeChar!(ElementType!Range) &&
+ !isInfinite!Range)
+in
+{
+ static if (hasLength!Range)
+ assert((data.length >= 2) || (data.length == 1 && s.length == 1));
+}
+body
+{
+ import std.conv : ConvException;
+
+ // Convert to base 1e19 = 10_000_000_000_000_000_000.
+ // (this is the largest power of 10 that will fit into a long).
+ // The length will be less than 1 + s.length/log2(10) = 1 + s.length/3.3219.
+ // 485 bits will only just fit into 146 decimal digits.
+ // As we convert the string, we record the number of digits we've seen in base 19:
+ // hi is the number of digits/19, lo is the extra digits (0 to 18).
+ // 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 x = 0;
+ ulong y = 0;
+ uint hi = 0; // number of base 1e19 digits
+ data[0] = 0; // initially number is 0.
+ if (data.length > 1)
+ data[1] = 0;
+
+ foreach (character; s)
+ {
+ if (character == '_')
+ continue;
+
+ if (character < '0' || character > '9')
+ throw new ConvException("invalid digit");
+ x *= 10;
+ x += character - '0';
+ ++lo;
+ if (lo == 9)
+ {
+ y = x;
+ x = 0;
+ }
+ if (lo == 18)
+ {
+ y *= 10_0000_0000;
+ y += x;
+ x = 0;
+ }
+ if (lo == 19)
+ {
+ y *= 10;
+ y += x;
+ x = 0;
+ // Multiply existing number by 10^19, then add y1.
+ if (hi>0)
+ {
+ 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], 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)
+ {
+ data[hi]=c;
+ ++hi;
+ }
+ y = 0;
+ lo = 0;
+ }
+ }
+ // Now set y = all remaining digits.
+ if (lo >= 18)
+ {
+ }
+ else if (lo >= 9)
+ {
+ for (int k=9; k<lo; ++k) y*=10;
+ y+=x;
+ }
+ else
+ {
+ for (int k=0; k<lo; ++k) y*=10;
+ y+=x;
+ }
+ if (lo != 0)
+ {
+ if (hi == 0)
+ {
+ data[0] = cast(uint) y;
+ if (data.length == 1)
+ {
+ hi = 1;
+ }
+ else
+ {
+ data[1] = cast(uint)(y >>> 32);
+ hi=2;
+ }
+ }
+ else
+ {
+ while (lo>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));
+ if (y > 0xFFFF_FFFFL)
+ {
+ c += multibyteIncrementAssign!('+')(data[1 .. hi], cast(uint)(y >> 32));
+ }
+ if (c != 0)
+ {
+ data[hi]=c;
+ ++hi;
+ }
+ }
+ }
+ while (hi>1 && data[hi-1]==0)
+ --hi;
+ return hi;
+}
+
+
+private:
+// ------------------------
+// These in-place functions are only for internal use; they are incompatible
+// with COW.
+
+// Classic 'schoolbook' multiplication.
+void mulSimple(BigDigit[] result, const(BigDigit) [] left,
+ const(BigDigit)[] right) pure nothrow
+in
+{
+ assert(result.length == left.length + right.length);
+ assert(right.length>1);
+}
+body
+{
+ result[left.length] = multibyteMul(result[0 .. left.length], left, right[0], 0);
+ multibyteMultiplyAccumulate(result[1..$], left, right[1..$]);
+}
+
+// Classic 'schoolbook' squaring
+void squareSimple(BigDigit[] result, const(BigDigit) [] x) pure nothrow
+in
+{
+ assert(result.length == 2*x.length);
+ assert(x.length>1);
+}
+body
+{
+ multibyteSquare(result, x);
+}
+
+
+// add two uints of possibly different lengths. Result must be as long
+// as the larger length.
+// Returns carry (0 or 1).
+uint addSimple(BigDigit[] result, const BigDigit [] left, const BigDigit [] right)
+pure nothrow
+in
+{
+ assert(result.length == left.length);
+ assert(left.length >= right.length);
+ assert(right.length>0);
+}
+body
+{
+ 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 .. $];
+ carry = multibyteIncrementAssign!('+')(result[right.length..$], carry);
+ }
+ return carry;
+}
+
+// result = left - right
+// returns carry (0 or 1)
+BigDigit subSimple(BigDigit [] result,const(BigDigit) [] left,
+ const(BigDigit) [] right) pure nothrow
+in
+{
+ assert(result.length == left.length);
+ assert(left.length >= right.length);
+ assert(right.length>0);
+}
+body
+{
+ 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 .. $];
+ carry = multibyteIncrementAssign!('-')(result[right.length..$], carry);
+ } //else if (result.length == left.length+1) { result[$-1] = carry; carry=0; }
+ return carry;
+}
+
+
+/* result = result - right
+ * Returns carry = 1 if result was less than right.
+*/
+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);
+ if (c && result.length > right.length)
+ c = multibyteIncrementAssign!('-')(result[right.length .. $], c);
+ return c;
+}
+
+/* result = result + right
+*/
+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);
+ if (c && result.length > right.length)
+ c = multibyteIncrementAssign!('+')(result[right.length .. $], c);
+ return c;
+}
+
+/* performs result += wantSub? - right : right;
+*/
+BigDigit addOrSubAssignSimple(BigDigit [] result, const(BigDigit) [] right,
+ bool wantSub) pure nothrow
+{
+ if (wantSub)
+ return subAssignSimple(result, right);
+ else
+ return addAssignSimple(result, right);
+}
+
+
+// return true if x<y, considering leading zeros
+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)
+ --k;
+ 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<y), false if x <= y.
+bool inplaceSub(BigDigit[] result, const(BigDigit)[] x, const(BigDigit)[] y)
+ pure nothrow
+{
+ assert(result.length == (x.length >= y.length) ? x.length : y.length);
+
+ size_t minlen;
+ bool negative;
+ if (x.length >= y.length)
+ {
+ minlen = y.length;
+ negative = less(x, y);
+ }
+ else
+ {
+ minlen = x.length;
+ negative = !less(y, x);
+ }
+ const (BigDigit)[] large, small;
+ if (negative)
+ {
+ large = y; small = x;
+ }
+ else
+ {
+ large = x; small = y;
+ }
+
+ BigDigit carry = multibyteSub(result[0 .. minlen], large[0 .. minlen], small[0 .. minlen], 0);
+ if (x.length != y.length)
+ {
+ result[minlen .. large.length]= large[minlen..$];
+ result[large.length..$] = 0;
+ if (carry)
+ multibyteIncrementAssign!('-')(result[minlen..$], carry);
+ }
+ return negative;
+}
+
+/* Determine how much space is required for the temporaries
+ * when performing a Karatsuba multiplication.
+ */
+size_t karatsubaRequiredBuffSize(size_t xlen) pure nothrow @safe
+{
+ return xlen <= KARATSUBALIMIT ? 0 : 2*xlen; // - KARATSUBALIMIT+2;
+}
+
+/* Sets result = x*y, using Karatsuba multiplication.
+* x must be longer or equal to y.
+* Valid only for balanced multiplies, where x is not shorter than y.
+* It is superior to schoolbook multiplication if and only if
+* sqrt(2)*y.length > x.length > y.length.
+* Karatsuba multiplication is O(n^1.59), whereas schoolbook is O(n^2)
+* The maximum allowable length of x and y is uint.max; but better algorithms
+* should be used far before that length is reached.
+* Params:
+* scratchbuff An array long enough to store all the temporaries. Will be destroyed.
+*/
+void mulKaratsuba(BigDigit [] result, const(BigDigit) [] x,
+ const(BigDigit)[] y, BigDigit [] scratchbuff) pure nothrow
+{
+ assert(x.length >= y.length);
+ assert(result.length < uint.max, "Operands too large");
+ assert(result.length == x.length + y.length);
+ if (x.length <= KARATSUBALIMIT)
+ {
+ return mulSimple(result, x, y);
+ }
+ // Must be almost square (otherwise, a schoolbook iteration is better)
+ assert(2L * y.length * y.length > (x.length-1) * (x.length-1),
+ "Bigint Internal Error: Asymmetric Karatsuba");
+
+ // The subtractive version of Karatsuba multiply uses the following result:
+ // (Nx1 + x0)*(Ny1 + y0) = (N*N)*x1y1 + x0y0 + N * (x0y0 + x1y1 - mid)
+ // where mid = (x0-x1)*(y0-y1)
+ // requiring 3 multiplies of length N, instead of 4.
+ // The advantage of the subtractive over the additive version is that
+ // the mid multiply cannot exceed length N. But there are subtleties:
+ // (x0-x1),(y0-y1) may be negative or zero. To keep it simple, we
+ // retain all of the leading zeros in the subtractions
+
+ // half length, round up.
+ auto half = (x.length >> 1) + (x.length & 1);
+
+ const(BigDigit) [] x0 = x[0 .. half];
+ const(BigDigit) [] x1 = x[half .. $];
+ const(BigDigit) [] y0 = y[0 .. half];
+ const(BigDigit) [] y1 = y[half .. $];
+ BigDigit [] mid = scratchbuff[0 .. half*2];
+ BigDigit [] newscratchbuff = scratchbuff[half*2 .. $];
+ BigDigit [] resultLow = result[0 .. 2*half];
+ BigDigit [] resultHigh = result[2*half .. $];
+ // initially use result to store temporaries
+ BigDigit [] xdiff= result[0 .. half];
+ BigDigit [] ydiff = result[half .. half*2];
+
+ // First, we calculate mid, and sign of mid
+ immutable bool midNegative = inplaceSub(xdiff, x0, x1)
+ ^ inplaceSub(ydiff, y0, y1);
+ mulKaratsuba(mid, xdiff, ydiff, newscratchbuff);
+
+ // Low half of result gets x0 * y0. High half gets x1 * y1
+
+ mulKaratsuba(resultLow, x0, y0, newscratchbuff);
+
+ if (2L * y1.length * y1.length < x1.length * x1.length)
+ {
+ // an asymmetric situation has been created.
+ // Worst case is if x:y = 1.414 : 1, then x1:y1 = 2.41 : 1.
+ // Applying one schoolbook multiply gives us two pieces each 1.2:1
+ if (y1.length <= KARATSUBALIMIT)
+ mulSimple(resultHigh, x1, y1);
+ else
+ {
+ // divide x1 in two, then use schoolbook multiply on the two pieces.
+ auto quarter = (x1.length >> 1) + (x1.length & 1);
+ 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.
+ 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]);
+ }
+ }
+ else
+ mulKaratsuba(resultHigh, x1, y1, newscratchbuff);
+
+ /* We now have result = x0y0 + (N*N)*x1y1
+ Before adding or subtracting mid, we must calculate
+ result += N * (x0y0 + x1y1)
+ We can do this with three half-length additions. With a = x0y0, b = x1y1:
+ aHI aLO
+ + aHI aLO
+ + bHI bLO
+ + bHI bLO
+ = R3 R2 R1 R0
+ R1 = aHI + bLO + aLO
+ R2 = aHI + bLO + aHI + carry_from_R1
+ 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]);
+ */
+ 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 c3 = addAssignSimple(R2, R3); // R2 = R2 + R1 + R3
+ if (c1+c2)
+ multibyteIncrementAssign!('+')(result[half*2..$], c1+c2);
+ if (c1+c3)
+ multibyteIncrementAssign!('+')(R3, c1+c3);
+
+ // And finally we subtract mid
+ addOrSubAssignSimple(result[half..$], mid, !midNegative);
+}
+
+void squareKaratsuba(BigDigit [] result, const BigDigit [] x,
+ BigDigit [] scratchbuff) pure nothrow
+{
+ // See mulKaratsuba for implementation comments.
+ // Squaring is simpler, since it never gets asymmetric.
+ assert(result.length < uint.max, "Operands too large");
+ assert(result.length == 2*x.length);
+ if (x.length <= KARATSUBASQUARELIMIT)
+ {
+ return squareSimple(result, x);
+ }
+ // half length, round up.
+ auto half = (x.length >> 1) + (x.length & 1);
+
+ const(BigDigit)[] x0 = x[0 .. half];
+ const(BigDigit)[] x1 = x[half .. $];
+ BigDigit [] mid = scratchbuff[0 .. half*2];
+ BigDigit [] newscratchbuff = scratchbuff[half*2 .. $];
+ // initially use result to store temporaries
+ BigDigit [] xdiff= result[0 .. half];
+ const BigDigit [] ydiff = result[half .. half*2];
+
+ // First, we calculate mid. We don't need its sign
+ inplaceSub(xdiff, x0, x1);
+ squareKaratsuba(mid, xdiff, newscratchbuff);
+
+ // Set result = x0x0 + (N*N)*x1x1
+ squareKaratsuba(result[0 .. 2*half], x0, newscratchbuff);
+ squareKaratsuba(result[2*half .. $], x1, newscratchbuff);
+
+ /* result += N * (x0x0 + x1x1)
+ Do this with three half-length additions. With a = x0x0, b = x1x1:
+ R1 = aHI + bLO + aLO
+ 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[] 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 c3 = addAssignSimple(R2, R3); // R2 = R2 + R1 + R3
+ if (c1+c2) multibyteIncrementAssign!('+')(result[half*2..$], c1+c2);
+ if (c1+c3) multibyteIncrementAssign!('+')(R3, c1+c3);
+
+ // And finally we subtract mid, which is always positive
+ subAssignSimple(result[half..$], mid);
+}
+
+/* Knuth's Algorithm D, as presented in
+ * H.S. Warren, "Hacker's Delight", Addison-Wesley Professional (2002).
+ * Also described in "Modern Computer Arithmetic" 0.2, Exercise 1.8.18.
+ * 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.
+ */
+void schoolbookDivMod(BigDigit [] quotient, BigDigit [] u, in BigDigit [] v)
+ pure nothrow
+{
+ assert(quotient.length == u.length - v.length);
+ assert(v.length > 1);
+ assert(u.length >= v.length);
+ assert((v[$-1]&0x8000_0000)!=0);
+ assert(u[$-1] < v[$-1]);
+ // BUG: This code only works if BigDigit is uint.
+ uint vhi = v[$-1];
+ uint vlo = v[$-2];
+
+ for (ptrdiff_t j = u.length - v.length - 1; j >= 0; j--)
+ {
+ // Compute estimate of quotient[j],
+ // qhat = (three most significant words of u)/(two most sig words of v).
+ uint qhat;
+ if (u[j + v.length] == vhi)
+ {
+ // uu/vhi could exceed uint.max (it will be 0x8000_0000 or 0x8000_0001)
+ qhat = uint.max;
+ }
+ else
+ {
+ uint ulo = u[j + v.length - 2];
+ version (D_InlineAsm_X86)
+ {
+ // Note: On DMD, this is only ~10% faster than the non-asm code.
+ uint *p = &u[j + v.length - 1];
+ asm pure nothrow
+ {
+ mov EAX, p;
+ mov EDX, [EAX+4];
+ mov EAX, [EAX];
+ div dword ptr [vhi];
+ mov qhat, EAX;
+ mov ECX, EDX;
+div3by2correction:
+ mul dword ptr [vlo]; // EDX:EAX = qhat * vlo
+ sub EAX, ulo;
+ sbb EDX, ECX;
+ jbe div3by2done;
+ mov EAX, qhat;
+ dec EAX;
+ mov qhat, EAX;
+ add ECX, dword ptr [vhi];
+ jnc div3by2correction;
+div3by2done: ;
+ }
+ }
+ else
+ { // version (InlineAsm)
+ ulong uu = (cast(ulong)(u[j + v.length]) << 32) | u[j + v.length - 1];
+ immutable bigqhat = uu / vhi;
+ ulong rhat = uu - bigqhat * vhi;
+ qhat = cast(uint) bigqhat;
+again:
+ if (cast(ulong) qhat * vlo > ((rhat << 32) + ulo))
+ {
+ --qhat;
+ rhat += vhi;
+ if (!(rhat & 0xFFFF_FFFF_0000_0000L))
+ goto again;
+ }
+ } // version (InlineAsm)
+ }
+ // Multiply and subtract.
+ 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);
+ }
+ quotient[j] = qhat;
+ u[j + v.length] = u[j + v.length] - carry;
+ }
+}
+
+private:
+
+// TODO: Replace with a library call
+void itoaZeroPadded(char[] output, uint value)
+ pure nothrow @safe @nogc
+{
+ for (auto i = output.length; i--;)
+ {
+ 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,
+ LetterCase letterCase = LetterCase.upper) pure nothrow @safe
+{
+ ptrdiff_t x = output.length - 1;
+ static immutable string upperHexDigits = "0123456789ABCDEF";
+ static immutable string lowerHexDigits = "0123456789abcdef";
+ for ( ; x >= 0; --x)
+ {
+ if (letterCase == LetterCase.upper)
+ {
+ output[x] = upperHexDigits[value & 0xF];
+ }
+ else
+ {
+ output[x] = lowerHexDigits[value & 0xF];
+ }
+ value >>= 4;
+ }
+}
+
+private:
+
+// Returns the highest value of i for which left[i]!=right[i],
+// or 0 if left[] == right[]
+size_t highestDifferentDigit(const BigDigit [] left, const BigDigit [] right)
+pure nothrow @nogc @safe
+{
+ assert(left.length == right.length);
+ for (ptrdiff_t i = left.length - 1; i>0; --i)
+ {
+ if (left[i] != right[i])
+ return i;
+ }
+ return 0;
+}
+
+// Returns the lowest value of i for which x[i]!=0.
+int firstNonZeroDigit(const BigDigit [] x) pure nothrow @nogc @safe
+{
+ int k = 0;
+ while (x[k]==0)
+ {
+ ++k;
+ assert(k<x.length);
+ }
+ return k;
+}
+
+/*
+ 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.
+ Given u and v, v normalised, calculates quotient = u/v, u = u%v.
+ scratch is temporary storage space, length must be >= quotient + 1.
+
+Returns:
+ 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
+ highest bit of the quotient may be set. This means that in the second
+ recursive call, the 'in' contract would be violated. (This happens only
+ when the top quarter of u is equal to the top half of v. A base 10
+ equivalent example of this situation is 5517/56; the first step gives
+ 55/5 = 11). To maintain the in contract, we pad a zero to the top of both
+ u and the quotient. 'mayOverflow' indicates that that the special case
+ has occurred.
+ (In MCA, a different strategy is used: the in contract is weakened, and
+ schoolbookDivMod is more general: it allows the high bit of u to be set).
+ See also:
+ - C. Burkinel and J. Ziegler, "Fast Recursive Division", MPI-I-98-1-022,
+ Max-Planck Institute fuer Informatik, (Oct 1998).
+*/
+void recursiveDivMod(BigDigit[] quotient, BigDigit[] u, const(BigDigit)[] v,
+ BigDigit[] scratch, bool mayOverflow = false)
+ pure nothrow
+in
+{
+ // v must be normalized
+ assert(v.length > 1);
+ assert((v[$ - 1] & 0x8000_0000) != 0);
+ assert(!(u[$ - 1] & 0x8000_0000));
+ assert(quotient.length == u.length - v.length);
+ if (mayOverflow)
+ {
+ assert(u[$-1] == 0);
+ assert(u[$-2] & 0x8000_0000);
+ }
+
+ // Must be symmetric. Use block schoolbook division if not.
+ assert((mayOverflow ? u.length-1 : u.length) <= 2 * v.length);
+ assert((mayOverflow ? u.length-1 : u.length) >= v.length);
+ assert(scratch.length >= quotient.length + (mayOverflow ? 0 : 1));
+}
+body
+{
+ if (quotient.length < FASTDIVLIMIT)
+ {
+ return schoolbookDivMod(quotient, u, v);
+ }
+
+ // Split quotient into two halves, but keep padding in the top half
+ auto k = (mayOverflow ? quotient.length - 1 : quotient.length) >> 1;
+
+ // RECURSION 1: Calculate the high half of the quotient
+
+ // Note that if u and quotient were padded, they remain padded during
+ // this call, so in contract is satisfied.
+ recursiveDivMod(quotient[k .. $], u[2 * k .. $], v[k .. $],
+ 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.
+
+ // 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.
+
+ 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].
+
+ if (u[k + v.length - 1] & 0x8000_0000)
+ {
+ // Special case. The high quotient is 0x1_00...000 or 0x1_00...001.
+ // This means we need an extra quotient word for the next recursion.
+ // We need to restore the invariant for the recursive calls.
+ // We do this by padding both u and quotient. Extending u is trivial,
+ // because the higher words will not be used again. But for the
+ // quotient, we're clobbering the low word of the high quotient,
+ // so we need save it, and add it back in after the recursive call.
+
+ auto clobberedQuotient = quotient[k];
+ u[k+v.length] = 0;
+
+ recursiveDivMod(quotient[0 .. k+1], u[k .. k + v.length+1],
+ v[k .. $], scratch, true);
+ adjustRemainder(quotient[0 .. k+1], u[0 .. v.length], v, k,
+ scratch[0 .. 2 * k+1], true);
+
+ // Now add the quotient word that got clobbered earlier.
+ multibyteIncrementAssign!('+')(quotient[k..$], clobberedQuotient);
+ }
+ else
+ {
+ // The special case has NOT happened.
+ 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]
+
+ 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.
+// 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,
+ BigDigit[] scratch, bool mayOverflow = false) pure nothrow
+{
+ assert(rem.length == v.length);
+ mulInternal(scratch, quot, v[0 .. k]);
+ uint carry = 0;
+ if (mayOverflow)
+ carry = scratch[$-1] + subAssignSimple(rem, scratch[0..$-1]);
+ else
+ carry = subAssignSimple(rem, scratch);
+ while (carry)
+ {
+ multibyteIncrementAssign!('-')(quot, 1); // quot--
+ carry -= multibyteAdd(rem, rem, v, 0);
+ }
+}
+
+// Cope with unbalanced division by performing block schoolbook division.
+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);
+ assert((v[$-1] & 0x8000_0000)!=0);
+ assert((u[$-1] & 0x8000_0000)==0);
+ BigDigit [] scratch = new BigDigit[v.length + 1];
+
+ // Perform block schoolbook division, with 'v.length' blocks.
+ auto m = u.length - v.length;
+ while (m > v.length)
+ {
+ 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);
+ if (mayOverflow)
+ {
+ assert(quotient[m] == 0);
+ quotient[m] = saveq;
+ }
+ m -= v.length;
+ }
+ recursiveDivMod(quotient[0 .. m], u[0 .. m + v.length], v, scratch);
+ () @trusted { GC.free(scratch.ptr); } ();
+}
+
+@system unittest
+{
+ import core.stdc.stdio;
+
+ void printBiguint(const uint [] data)
+ {
+ char [] buff = biguintToHex(new char[data.length*9], data, '_');
+ printf("%.*s\n", buff.length, buff.ptr);
+ }
+
+ void printDecimalBigUint(BigUint data)
+ {
+ auto str = data.toDecimalString(0);
+ printf("%.*s\n", str.length, str.ptr);
+ }
+
+ uint [] a, b;
+ a = new uint[43];
+ b = new uint[179];
+ for (int i=0; i<a.length; ++i) a[i] = 0x1234_B6E9 + i;
+ for (int i=0; i<b.length; ++i) b[i] = 0x1BCD_8763 - i*546;
+
+ a[$-1] |= 0x8000_0000;
+ uint [] r = new uint[a.length];
+ uint [] q = new uint[b.length-a.length+1];
+
+ divModInternal(q, r, b, a);
+ q = q[0..$-1];
+ uint [] r1 = r.dup;
+ uint [] q1 = q.dup;
+ blockDivMod(q, b, a);
+ 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/libphobos/src/std/internal/math/biguintnoasm.d b/libphobos/src/std/internal/math/biguintnoasm.d
new file mode 100644
index 0000000..aea1d50
--- /dev/null
+++ b/libphobos/src/std/internal/math/biguintnoasm.d
@@ -0,0 +1,370 @@
+/** Arbitrary precision arithmetic ('bignum') for processors with no asm support
+ *
+ * All functions operate on arrays of uints, stored LSB first.
+ * If there is a destination array, it will be the first parameter.
+ * Currently, all of these functions are subject to change, and are
+ * intended for internal use only.
+ * This module is intended only to assist development of high-speed routines
+ * on currently unsupported processors.
+ * The X86 asm version is about 30 times faster than the D version (DMD).
+ */
+
+/* Copyright Don Clugston 2008 - 2010.
+ * 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.internal.math.biguintnoasm;
+
+nothrow:
+@safe:
+
+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
+
+
+/** Multi-byte addition or subtraction
+ * dest[] = src1[] + src2[] + carry (0 or 1).
+ * or dest[] = src1[] - src2[] - carry (0 or 1).
+ * Returns carry or borrow (0 or 1).
+ * Set op == '+' for addition, '-' for subtraction.
+ */
+uint multibyteAddSub(char op)(uint[] dest, const(uint) [] src1,
+ const (uint) [] src2, uint carry) pure @nogc @safe
+{
+ ulong c = carry;
+ 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;
+ c = (c > 0xFFFF_FFFF);
+ }
+ return cast(uint) c;
+}
+
+@safe unittest
+{
+ uint [] a = new uint[40];
+ uint [] b = new uint[40];
+ uint [] c = new uint[40];
+ 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;
+ b[i]= 0x8000_0003;
+ }
+ c[19]=0x3333_3333;
+ 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);
+ for (size_t i = 0; i < a.length; ++i)
+ {
+ a[i] = b[i] = c[i] = 0;
+ }
+ a[8]=0x048D159E;
+ b[8]=0x048D159E;
+ a[10]=0x1D950C84;
+ b[10]=0x1D950C84;
+ a[5] =0x44444444;
+ 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)
+ assert(a[i] == 0);
+
+ for (size_t q = 3; q < 36; ++q)
+ {
+ for (size_t i = 0; i< a.length; ++i)
+ {
+ a[i] = b[i] = c[i] = 0;
+ }
+ a[q-2]=0x040000;
+ b[q-2]=0x040000;
+ carry = multibyteAddSub!('-')(a[0 .. q], a[0 .. q], b[0 .. q], 0);
+ assert(a[q-2]==0);
+ }
+}
+
+
+
+/** dest[] += carry, or dest[] -= carry.
+ * op must be '+' or '-'
+ * Returns final carry or borrow (0 or 1)
+ */
+uint multibyteIncrementAssign(char op)(uint[] dest, uint carry)
+ pure @nogc @safe
+{
+ static if (op=='+')
+ {
+ ulong c = carry;
+ c += dest[0];
+ dest[0] = cast(uint) c;
+ if (c <= 0xFFFF_FFFF)
+ return 0;
+
+ for (size_t i = 1; i < dest.length; ++i)
+ {
+ ++dest[i];
+ if (dest[i] != 0)
+ return 0;
+ }
+ return 1;
+ }
+ else
+ {
+ ulong c = carry;
+ c = dest[0] - c;
+ dest[0] = cast(uint) c;
+ if (c <= 0xFFFF_FFFF)
+ return 0;
+ for (size_t i = 1; i < dest.length; ++i)
+ {
+ --dest[i];
+ if (dest[i] != 0xFFFF_FFFF)
+ return 0;
+ }
+ return 1;
+ }
+}
+
+/** dest[] = src[] << numbits
+ * numbits must be in the range 1 .. 31
+ */
+uint multibyteShl(uint [] dest, const(uint) [] src, uint numbits)
+ pure @nogc @safe
+{
+ ulong c = 0;
+ for (size_t i = 0; i < dest.length; ++i)
+ {
+ c += (cast(ulong)(src[i]) << numbits);
+ dest[i] = cast(uint) c;
+ c >>>= 32;
+ }
+ return cast(uint) c;
+}
+
+
+/** dest[] = src[] >> numbits
+ * 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)
+ {
+ c += (src[i-1] >>numbits) + (cast(ulong)(src[i-1]) << (64 - numbits));
+ dest[i-1] = cast(uint) c;
+ c >>>= 32;
+ }
+}
+
+@safe unittest
+{
+
+ uint [] aa = [0x1222_2223, 0x4555_5556, 0x8999_999A, 0xBCCC_CCCD, 0xEEEE_EEEE];
+ multibyteShr(aa[0..$-2], aa, 4);
+ assert(aa[0] == 0x6122_2222 && aa[1] == 0xA455_5555 && aa[2] == 0x0899_9999);
+ assert(aa[3] == 0xBCCC_CCCD);
+
+ aa = [0x1222_2223, 0x4555_5556, 0x8999_999A, 0xBCCC_CCCD, 0xEEEE_EEEE];
+ multibyteShr(aa[0..$-1], aa, 4);
+ assert(aa[0] == 0x6122_2222 && aa[1] == 0xA455_5555
+ && aa[2] == 0xD899_9999 && aa[3] == 0x0BCC_CCCC);
+
+ aa = [0xF0FF_FFFF, 0x1222_2223, 0x4555_5556, 0x8999_999A, 0xBCCC_CCCD,
+ 0xEEEE_EEEE];
+ 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);
+}
+
+/** dest[] = src[] * multiplier + carry.
+ * Returns carry.
+ */
+uint multibyteMul(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)
+ {
+ c += cast(ulong)(src[i]) * multiplier;
+ dest[i] = cast(uint) c;
+ c>>=32;
+ }
+ return cast(uint) c;
+}
+
+@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);
+ 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).
+ */
+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)
+ {
+ static if (op=='+')
+ {
+ c += cast(ulong)(multiplier) * src[i] + dest[i];
+ 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));
+ }
+ }
+ return cast(uint) c;
+}
+
+@safe unittest
+{
+
+ uint [] aa = [0xF0FF_FFFF, 0x1222_2223, 0x4555_5556, 0x8999_999A,
+ 0xBCCC_CCCD, 0xEEEE_EEEE];
+ uint [] bb = [0x1234_1234, 0xF0F0_F0F0, 0x00C0_C0C0, 0xF0F0_F0F0,
+ 0xC0C0_C0C0];
+ multibyteMulAdd!('+')(bb[1..$-1], aa[1..$-2], 16, 5);
+ assert(bb[0] == 0x1234_1234 && bb[4] == 0xC0C0_C0C0);
+ assert(bb[1] == 0x2222_2230 + 0xF0F0_F0F0 + 5
+ && bb[2] == 0x5555_5561 + 0x00C0_C0C0 + 1
+ && bb[3] == 0x9999_99A4 + 0xF0F0_F0F0 );
+}
+
+
+/**
+ 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],
+ left, right[i], 0);
+ }
+ ----
+ */
+void multibyteMultiplyAccumulate(uint [] dest, const(uint)[] left, const(uint)
+ [] right) pure @nogc @safe
+{
+ for (size_t i = 0; i < right.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.
+ */
+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)
+ {
+ c = (c << 32) + cast(ulong)(dest[i]);
+ uint q = cast(uint)(c/divisor);
+ c -= divisor * q;
+ dest[i] = q;
+ }
+ return cast(uint) c;
+}
+
+@safe unittest
+{
+ uint [] aa = new uint[101];
+ for (uint i = 0; i < aa.length; ++i)
+ aa[i] = 0x8765_4321 * (i+3);
+ uint overflow = multibyteMul(aa, aa, 0x8EFD_FCFB, 0x33FF_7461);
+ uint r = multibyteDivAssign(aa, 0x8EFD_FCFB, overflow);
+ for (uint i=0; i<aa.length; ++i)
+ {
+ assert(aa[i] == 0x8765_4321 * (i+3));
+ }
+ assert(r == 0x33FF_7461);
+
+}
+// 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)
+ {
+ // 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;
+ c = (c>>=32) + dest[2*i+1];
+ dest[2*i+1] = cast(uint) c;
+ c >>= 32;
+ }
+}
+
+// Does half a square multiply. (square = diagonal + 2*triangle)
+void multibyteTriangleAccumulate(uint[] dest, const(uint)[] x)
+ pure @nogc @safe
+{
+ // x[0]*x[1...$] + x[1]*x[2..$] + ... + x[$-2]x[$-1..$]
+ dest[x.length] = multibyteMul(dest[1 .. x.length], x[1..$], x[0], 0);
+ if (x.length < 4)
+ {
+ 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;
+ c >>= 32;
+ dest[2*x.length - 2] = cast(uint) c;
+ }
+ return;
+ }
+ for (size_t i = 2; i < x.length - 2; ++i)
+ {
+ dest[i-1+ x.length] = multibyteMulAdd!('+')(
+ dest[i+i-1 .. i+x.length-1], x[i..$], x[i-1], 0);
+ }
+ // 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;
+ c >>= 32;
+ c += cast(ulong)(x[$-3]) * x[$-1] + dest[2*x.length-4];
+ 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;
+ c >>= 32;
+ dest[2*x.length-2] = cast(uint) c;
+}
+
+void multibyteSquare(BigDigit[] result, const(BigDigit) [] x) pure @nogc @safe
+{
+ multibyteTriangleAccumulate(result, x);
+ result[$-1] = multibyteShl(result[1..$-1], result[1..$-1], 1); // mul by 2
+ result[0] = 0;
+ multibyteAddDiagonalSquares(result, x);
+}
diff --git a/libphobos/src/std/internal/math/biguintx86.d b/libphobos/src/std/internal/math/biguintx86.d
new file mode 100644
index 0000000..bd03d2e
--- /dev/null
+++ b/libphobos/src/std/internal/math/biguintx86.d
@@ -0,0 +1,1353 @@
+/** Optimised asm arbitrary precision arithmetic ('bignum')
+ * routines for X86 processors.
+ *
+ * All functions operate on arrays of uints, stored LSB first.
+ * If there is a destination array, it will be the first parameter.
+ * Currently, all of these functions are subject to change, and are
+ * intended for internal use only.
+ * The symbol [#] indicates an array of machine words which is to be
+ * interpreted as a multi-byte number.
+ */
+
+/* Copyright Don Clugston 2008 - 2010.
+ * 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)
+ */
+/**
+ * In simple terms, there are 3 modern x86 microarchitectures:
+ * (a) the P6 family (Pentium Pro, PII, PIII, PM, Core), produced by Intel;
+ * (b) the K6, Athlon, and AMD64 families, produced by AMD; and
+ * (c) the Pentium 4, produced by Marketing.
+ *
+ * This code has been optimised for the Intel P6 family.
+ * Generally the code remains near-optimal for Intel Core2/Corei7, after
+ * translating EAX-> RAX, etc, since all these CPUs use essentially the same
+ * pipeline, and are typically limited by memory access.
+ * The code uses techniques described in Agner Fog's superb Pentium manuals
+ * available at www.agner.org.
+ * Not optimised for AMD, which can do two memory loads per cycle (Intel
+ * CPUs can only do one). Despite this, performance is superior on AMD.
+ * Performance is dreadful on P4.
+ *
+ * Timing results (cycles per int)
+ * --Intel Pentium-- --AMD--
+ * PM P4 Core2 K7
+ * +,- 2.25 15.6 2.25 1.5
+ * <<,>> 2.0 6.6 2.0 5.0
+ * (<< MMX) 1.7 5.3 1.5 1.2
+ * * 5.0 15.0 4.0 4.3
+ * mulAdd 5.7 19.0 4.9 4.0
+ * div 30.0 32.0 32.0 22.4
+ * mulAcc(32) 6.5 20.0 5.4 4.9
+ *
+ * mulAcc(32) is multiplyAccumulate() for a 32*32 multiply. Thus it includes
+ * function call overhead.
+ * The timing for Div is quite unpredictable, but it's probably too slow
+ * to be useful. On 64-bit processors, these times should
+ * halve if run in 64-bit mode, except for the MMX functions.
+ */
+
+module std.internal.math.biguintx86;
+
+@system:
+pure:
+nothrow:
+
+/*
+ Naked asm is used throughout, because:
+ (a) it frees up the EBP register
+ (b) compiler bugs prevent the use of .ptr when a frame pointer is used.
+*/
+
+version (D_InlineAsm_X86)
+{
+
+private:
+
+/* Duplicate string s, with n times, substituting index for '@'.
+ *
+ * Each instance of '@' in s is replaced by 0,1,...n-1. This is a helper
+ * function for some of the asm routines.
+ */
+string indexedLoopUnroll(int n, string s) pure @safe
+{
+ string u;
+ for (int i = 0; i<n; ++i)
+ {
+ string nstr= (i>9 ? ""~ cast(char)('0'+i/10) : "") ~ cast(char)('0' + i%10);
+
+ int last = 0;
+ for (int j = 0; j<s.length; ++j)
+ {
+ if (s[j]=='@')
+ {
+ u ~= s[last .. j] ~ nstr;
+ last = j+1;
+ }
+ }
+ if (last<s.length) u = u ~ s[last..$];
+
+ }
+ return u;
+}
+@safe unittest
+{
+ assert(indexedLoopUnroll(3, "@*23;")=="0*23;1*23;2*23;");
+}
+
+public:
+
+alias BigDigit = uint; // A Bignum is an array of BigDigits. Usually the machine word size.
+
+// Limits for when to switch between multiplication algorithms.
+enum : int { KARATSUBALIMIT = 18 }; // Minimum value for which Karatsuba is worthwhile.
+enum : int { KARATSUBASQUARELIMIT=26 }; // Minimum value for which square Karatsuba is worthwhile
+
+/** Multi-byte addition or subtraction
+ * dest[#] = src1[#] + src2[#] + carry (0 or 1).
+ * or dest[#] = src1[#] - src2[#] - carry (0 or 1).
+ * Returns carry or borrow (0 or 1).
+ * Set op == '+' for addition, '-' for subtraction.
+ */
+uint multibyteAddSub(char op)(uint[] dest, const uint [] src1, const uint []
+ src2, uint carry) pure
+{
+ // Timing:
+ // Pentium M: 2.25/int
+ // P6 family, Core2 have a partial flags stall when reading the carry flag in
+ // an ADC, SBB operation after an operation such as INC or DEC which
+ // modifies some, but not all, flags. We avoid this by storing carry into
+ // a resister (AL), and restoring it after the branch.
+
+ enum { LASTPARAM = 4*4 } // 3* pushes + return address.
+ asm pure nothrow {
+ naked;
+ push EDI;
+ push EBX;
+ push ESI;
+ mov ECX, [ESP + LASTPARAM + 4*4]; // dest.length;
+ mov EDX, [ESP + LASTPARAM + 3*4]; // src1.ptr
+ mov ESI, [ESP + LASTPARAM + 1*4]; // src2.ptr
+ mov EDI, [ESP + LASTPARAM + 5*4]; // dest.ptr
+ // Carry is in EAX
+ // Count UP to zero (from -len) to minimize loop overhead.
+ lea EDX, [EDX + 4*ECX]; // EDX = end of src1.
+ lea ESI, [ESI + 4*ECX]; // EBP = end of src2.
+ lea EDI, [EDI + 4*ECX]; // EDI = end of dest.
+
+ neg ECX;
+ add ECX, 8;
+ jb L2; // if length < 8 , bypass the unrolled loop.
+L_unrolled:
+ shr AL, 1; // get carry from EAX
+ }
+ mixin(" asm pure nothrow {"
+ ~ indexedLoopUnroll( 8,
+ "mov EAX, [@*4-8*4+EDX+ECX*4];"
+ ~ ( op == '+' ? "adc" : "sbb" ) ~ " EAX, [@*4-8*4+ESI+ECX*4];"
+ ~ "mov [@*4-8*4+EDI+ECX*4], EAX;")
+ ~ "}");
+ asm pure nothrow {
+ setc AL; // save carry
+ add ECX, 8;
+ ja L_unrolled;
+L2: // Do the residual 1 .. 7 ints.
+
+ sub ECX, 8;
+ jz done;
+L_residual:
+ shr AL, 1; // get carry from EAX
+ }
+ mixin(" asm pure nothrow {"
+ ~ indexedLoopUnroll( 1,
+ "mov EAX, [@*4+EDX+ECX*4];"
+ ~ ( op == '+' ? "adc" : "sbb" ) ~ " EAX, [@*4+ESI+ECX*4];"
+ ~ "mov [@*4+EDI+ECX*4], EAX;") ~ "}");
+ asm pure nothrow {
+ setc AL; // save carry
+ add ECX, 1;
+ jnz L_residual;
+done:
+ and EAX, 1; // make it O or 1.
+ pop ESI;
+ pop EBX;
+ pop EDI;
+ ret 6*4;
+ }
+}
+
+@system unittest
+{
+ uint [] a = new uint[40];
+ uint [] b = new uint[40];
+ uint [] c = new uint[40];
+ for (int i=0; i<a.length; ++i)
+ {
+ if (i&1) a[i]=0x8000_0000 + i;
+ else a[i]=i;
+ b[i]= 0x8000_0003;
+ }
+ c[19]=0x3333_3333;
+ uint carry = multibyteAddSub!('+')(c[0 .. 18], a[0 .. 18], b[0 .. 18], 0);
+ assert(carry == 1);
+ assert(c[0]==0x8000_0003);
+ assert(c[1]==4);
+ assert(c[19]==0x3333_3333); // check for overrun
+ for (int i=0; i<a.length; ++i)
+ {
+ a[i]=b[i]=c[i]=0;
+ }
+ a[8]=0x048D159E;
+ b[8]=0x048D159E;
+ a[10]=0x1D950C84;
+ b[10]=0x1D950C84;
+ a[5] =0x44444444;
+ carry = multibyteAddSub!('-')(a[0 .. 12], a[0 .. 12], b[0 .. 12], 0);
+ assert(a[11]==0);
+ for (int i=0; i<10; ++i) if (i != 5) assert(a[i]==0);
+
+ for (int q=3; q<36;++q)
+ {
+ for (int i=0; i<a.length; ++i)
+ {
+ a[i]=b[i]=c[i]=0;
+ }
+ a[q-2]=0x040000;
+ b[q-2]=0x040000;
+ carry = multibyteAddSub!('-')(a[0 .. q], a[0 .. q], b[0 .. q], 0);
+ assert(a[q-2]==0);
+ }
+}
+
+/** dest[#] += carry, or dest[#] -= carry.
+ * op must be '+' or '-'
+ * Returns final carry or borrow (0 or 1)
+ */
+uint multibyteIncrementAssign(char op)(uint[] dest, uint carry) pure
+{
+ enum { LASTPARAM = 1*4 } // 0* pushes + return address.
+ asm pure nothrow {
+ naked;
+ mov ECX, [ESP + LASTPARAM + 0*4]; // dest.length;
+ mov EDX, [ESP + LASTPARAM + 1*4]; // dest.ptr
+ // EAX = carry
+L1: ;
+ }
+ static if (op=='+')
+ asm pure nothrow { add [EDX], EAX; }
+ else
+ asm pure nothrow { sub [EDX], EAX; }
+ asm pure nothrow {
+ mov EAX, 1;
+ jnc L2;
+ add EDX, 4;
+ dec ECX;
+ jnz L1;
+ mov EAX, 2;
+L2: dec EAX;
+ ret 2*4;
+ }
+}
+
+/** dest[#] = src[#] << numbits
+ * numbits must be in the range 1 .. 31
+ * Returns the overflow
+ */
+uint multibyteShlNoMMX(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)
+ // 5.0 cycles/int on Athlon, which has 7 cycles for SHLD!!
+ enum { LASTPARAM = 4*4 } // 3* pushes + return address.
+ asm pure nothrow {
+ naked;
+ push ESI;
+ push EDI;
+ push EBX;
+ mov EDI, [ESP + LASTPARAM + 4*3]; //dest.ptr;
+ mov EBX, [ESP + LASTPARAM + 4*2]; //dest.length;
+ mov ESI, [ESP + LASTPARAM + 4*1]; //src.ptr;
+ mov ECX, EAX; // numbits;
+
+ mov EAX, [-4+ESI + 4*EBX];
+ mov EDX, 0;
+ shld EDX, EAX, CL;
+ push EDX; // Save return value
+ cmp EBX, 1;
+ jz L_last;
+ mov EDX, [-4+ESI + 4*EBX];
+ test EBX, 1;
+ jz L_odd;
+ sub EBX, 1;
+L_even:
+ mov EDX, [-4+ ESI + 4*EBX];
+ shld EAX, EDX, CL;
+ mov [EDI+4*EBX], EAX;
+L_odd:
+ mov EAX, [-8+ESI + 4*EBX];
+ shld EDX, EAX, CL;
+ mov [-4+EDI + 4*EBX], EDX;
+ sub EBX, 2;
+ jg L_even;
+L_last:
+ shl EAX, CL;
+ mov [EDI], EAX;
+ pop EAX; // pop return value
+ pop EBX;
+ pop EDI;
+ pop ESI;
+ ret 4*4;
+ }
+}
+
+/** dest[#] = src[#] >> numbits
+ * numbits must be in the range 1 .. 31
+ * This version uses MMX.
+ */
+uint multibyteShl(uint [] dest, const uint [] src, uint numbits) pure
+{
+ // Timing:
+ // K7 1.2/int. PM 1.7/int P4 5.3/int
+ enum { LASTPARAM = 4*4 } // 3* pushes + return address.
+ asm pure nothrow {
+ naked;
+ push ESI;
+ push EDI;
+ push EBX;
+ mov EDI, [ESP + LASTPARAM + 4*3]; //dest.ptr;
+ mov EBX, [ESP + LASTPARAM + 4*2]; //dest.length;
+ mov ESI, [ESP + LASTPARAM + 4*1]; //src.ptr;
+
+ movd MM3, EAX; // numbits = bits to shift left
+ xor EAX, 63;
+ align 16;
+ inc EAX;
+ movd MM4, EAX ; // 64-numbits = bits to shift right
+
+ // Get the return value into EAX
+ and EAX, 31; // EAX = 32-numbits
+ movd MM2, EAX; // 32-numbits
+ movd MM1, [ESI+4*EBX-4];
+ psrlq MM1, MM2;
+ movd EAX, MM1; // EAX = return value
+ test EBX, 1;
+ jz L_even;
+L_odd:
+ cmp EBX, 1;
+ jz L_length1;
+
+ // deal with odd lengths
+ movq MM1, [ESI+4*EBX-8];
+ psrlq MM1, MM2;
+ movd [EDI +4*EBX-4], MM1;
+ sub EBX, 1;
+L_even: // It's either singly or doubly even
+ movq MM2, [ESI + 4*EBX - 8];
+ psllq MM2, MM3;
+ sub EBX, 2;
+ jle L_last;
+ movq MM1, MM2;
+ add EBX, 2;
+ test EBX, 2;
+ jz L_onceeven;
+ sub EBX, 2;
+
+ // MAIN LOOP -- 128 bytes per iteration
+ L_twiceeven: // here MM2 is the carry
+ movq MM0, [ESI + 4*EBX-8];
+ psrlq MM0, MM4;
+ movq MM1, [ESI + 4*EBX-8];
+ psllq MM1, MM3;
+ por MM2, MM0;
+ movq [EDI +4*EBX], MM2;
+L_onceeven: // here MM1 is the carry
+ movq MM0, [ESI + 4*EBX-16];
+ psrlq MM0, MM4;
+ movq MM2, [ESI + 4*EBX-16];
+ por MM1, MM0;
+ movq [EDI +4*EBX-8], MM1;
+ psllq MM2, MM3;
+ sub EBX, 4;
+ jg L_twiceeven;
+L_last:
+ movq [EDI +4*EBX], MM2;
+L_alldone:
+ emms; // NOTE: costs 6 cycles on Intel CPUs
+ pop EBX;
+ pop EDI;
+ pop ESI;
+ ret 4*4;
+
+L_length1:
+ // length 1 is a special case
+ movd MM1, [ESI];
+ psllq MM1, MM3;
+ movd [EDI], MM1;
+ jmp L_alldone;
+ }
+}
+
+void multibyteShr(uint [] dest, const uint [] src, uint numbits) pure
+{
+ enum { LASTPARAM = 4*4 } // 3* pushes + return address.
+ asm pure nothrow {
+ naked;
+ push ESI;
+ push EDI;
+ push EBX;
+ mov EDI, [ESP + LASTPARAM + 4*3]; //dest.ptr;
+ mov EBX, [ESP + LASTPARAM + 4*2]; //dest.length;
+align 16;
+ mov ESI, [ESP + LASTPARAM + 4*1]; //src.ptr;
+ lea EDI, [EDI + 4*EBX]; // EDI = end of dest
+ lea ESI, [ESI + 4*EBX]; // ESI = end of src
+ neg EBX; // count UP to zero.
+
+ movd MM3, EAX; // numbits = bits to shift right
+ xor EAX, 63;
+ inc EAX;
+ movd MM4, EAX ; // 64-numbits = bits to shift left
+
+ test EBX, 1;
+ jz L_even;
+L_odd:
+ // deal with odd lengths
+ and EAX, 31; // EAX = 32-numbits
+ movd MM2, EAX; // 32-numbits
+ cmp EBX, -1;
+ jz L_length1;
+
+ movq MM0, [ESI+4*EBX];
+ psrlq MM0, MM3;
+ movd [EDI +4*EBX], MM0;
+ add EBX, 1;
+L_even:
+ movq MM2, [ESI + 4*EBX];
+ psrlq MM2, MM3;
+
+ movq MM1, MM2;
+ add EBX, 4;
+ cmp EBX, -2+4;
+ jz L_last;
+ // It's either singly or doubly even
+ sub EBX, 2;
+ test EBX, 2;
+ jnz L_onceeven;
+ add EBX, 2;
+
+ // MAIN LOOP -- 128 bytes per iteration
+ L_twiceeven: // here MM2 is the carry
+ movq MM0, [ESI + 4*EBX-8];
+ psllq MM0, MM4;
+ movq MM1, [ESI + 4*EBX-8];
+ psrlq MM1, MM3;
+ por MM2, MM0;
+ movq [EDI +4*EBX-16], MM2;
+L_onceeven: // here MM1 is the carry
+ movq MM0, [ESI + 4*EBX];
+ psllq MM0, MM4;
+ movq MM2, [ESI + 4*EBX];
+ por MM1, MM0;
+ movq [EDI +4*EBX-8], MM1;
+ psrlq MM2, MM3;
+ add EBX, 4;
+ jl L_twiceeven;
+L_last:
+ movq [EDI +4*EBX-16], MM2;
+L_alldone:
+ emms; // NOTE: costs 6 cycles on Intel CPUs
+ pop EBX;
+ pop EDI;
+ pop ESI;
+ ret 4*4;
+
+L_length1:
+ // length 1 is a special case
+ movd MM1, [ESI+4*EBX];
+ psrlq MM1, MM3;
+ movd [EDI +4*EBX], MM1;
+ jmp L_alldone;
+
+ }
+}
+
+/** dest[#] = src[#] >> numbits
+ * 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)
+ // Terrible performance on AMD64, which has 7 cycles for SHRD!!
+ enum { LASTPARAM = 4*4 } // 3* pushes + return address.
+ asm pure nothrow {
+ naked;
+ push ESI;
+ push EDI;
+ push EBX;
+ mov EDI, [ESP + LASTPARAM + 4*3]; //dest.ptr;
+ mov EBX, [ESP + LASTPARAM + 4*2]; //dest.length;
+ mov ESI, [ESP + LASTPARAM + 4*1]; //src.ptr;
+ mov ECX, EAX; // numbits;
+
+ lea EDI, [EDI + 4*EBX]; // EDI = end of dest
+ lea ESI, [ESI + 4*EBX]; // ESI = end of src
+ neg EBX; // count UP to zero.
+ mov EAX, [ESI + 4*EBX];
+ cmp EBX, -1;
+ jz L_last;
+ mov EDX, [ESI + 4*EBX];
+ test EBX, 1;
+ jz L_odd;
+ add EBX, 1;
+L_even:
+ mov EDX, [ ESI + 4*EBX];
+ shrd EAX, EDX, CL;
+ mov [-4 + EDI+4*EBX], EAX;
+L_odd:
+ mov EAX, [4 + ESI + 4*EBX];
+ shrd EDX, EAX, CL;
+ mov [EDI + 4*EBX], EDX;
+ add EBX, 2;
+ jl L_even;
+L_last:
+ shr EAX, CL;
+ mov [-4 + EDI], EAX;
+
+ pop EBX;
+ pop EDI;
+ pop ESI;
+ ret 4*4;
+ }
+}
+
+@system unittest
+{
+
+ uint [] aa = [0x1222_2223, 0x4555_5556, 0x8999_999A, 0xBCCC_CCCD, 0xEEEE_EEEE];
+ multibyteShr(aa[0..$-1], aa, 4);
+ assert(aa[0] == 0x6122_2222 && aa[1]==0xA455_5555
+ && aa[2]==0xD899_9999 && aa[3]==0x0BCC_CCCC);
+
+ aa = [0x1222_2223, 0x4555_5556, 0x8999_999A, 0xBCCC_CCCD, 0xEEEE_EEEE];
+ multibyteShr(aa[2..$-1], aa[2..$-1], 4);
+ assert(aa[0] == 0x1222_2223 && aa[1]==0x4555_5556
+ && aa[2]==0xD899_9999 && aa[3]==0x0BCC_CCCC);
+
+ aa = [0x1222_2223, 0x4555_5556, 0x8999_999A, 0xBCCC_CCCD, 0xEEEE_EEEE];
+ multibyteShr(aa[0..$-2], aa, 4);
+ assert(aa[1]==0xA455_5555 && aa[2]==0x0899_9999);
+ assert(aa[0]==0x6122_2222);
+ assert(aa[3]==0xBCCC_CCCD);
+
+
+ aa = [0xF0FF_FFFF, 0x1222_2223, 0x4555_5556, 0x8999_999A, 0xBCCC_CCCD, 0xEEEE_EEEE];
+ 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);
+
+ aa = [0xF0FF_FFFF, 0x1222_2223, 0x4555_5556, 0x8999_999A, 0xBCCC_CCCD, 0xEEEE_EEEE];
+ 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(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);
+}
+
+/** dest[#] = src[#] * multiplier + carry.
+ * Returns carry.
+ */
+uint multibyteMul(uint[] dest, const uint[] src, uint multiplier, uint carry)
+ pure
+{
+ // Timing: definitely not optimal.
+ // Pentium M: 5.0 cycles/operation, has 3 resource stalls/iteration
+ // Fastest implementation found was 4.6 cycles/op, but not worth the complexity.
+
+ enum { LASTPARAM = 4*4 } // 4* pushes + return address.
+ // We'll use p2 (load unit) instead of the overworked p0 or p1 (ALU units)
+ // when initializing variables to zero.
+ version (D_PIC)
+ {
+ enum { zero = 0 }
+ }
+ else
+ {
+ __gshared int zero = 0;
+ }
+ asm pure nothrow {
+ naked;
+ push ESI;
+ push EDI;
+ push EBX;
+
+ mov EDI, [ESP + LASTPARAM + 4*4]; // dest.ptr
+ mov EBX, [ESP + LASTPARAM + 4*3]; // dest.length
+ mov ESI, [ESP + LASTPARAM + 4*2]; // src.ptr
+ align 16;
+ lea EDI, [EDI + 4*EBX]; // EDI = end of dest
+ lea ESI, [ESI + 4*EBX]; // ESI = end of src
+ mov ECX, EAX; // [carry]; -- last param is in EAX.
+ neg EBX; // count UP to zero.
+ test EBX, 1;
+ jnz L_odd;
+ add EBX, 1;
+ L1:
+ mov EAX, [-4 + ESI + 4*EBX];
+ mul int ptr [ESP+LASTPARAM]; //[multiplier];
+ add EAX, ECX;
+ mov ECX, zero;
+ mov [-4+EDI + 4*EBX], EAX;
+ adc ECX, EDX;
+L_odd:
+ mov EAX, [ESI + 4*EBX]; // p2
+ mul int ptr [ESP+LASTPARAM]; //[multiplier]; // p0*3,
+ add EAX, ECX;
+ mov ECX, zero;
+ adc ECX, EDX;
+ mov [EDI + 4*EBX], EAX;
+ add EBX, 2;
+ jl L1;
+
+ mov EAX, ECX; // get final carry
+
+ pop EBX;
+ pop EDI;
+ pop ESI;
+ ret 5*4;
+ }
+}
+
+@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);
+}
+
+// The inner multiply-and-add loop, together with the Even entry point.
+// Multiples by M_ADDRESS which should be "ESP+LASTPARAM" or "ESP". OP must be "add" or "sub"
+// This is the most time-critical code in the BigInt library.
+// It is used by both MulAdd, multiplyAccumulate, and triangleAccumulate
+string asmMulAdd_innerloop(string OP, string M_ADDRESS) pure {
+ // The bottlenecks in this code are extremely complicated. The MUL, ADD, and ADC
+ // need 4 cycles on each of the ALUs units p0 and p1. So we use memory load
+ // (unit p2) for initializing registers to zero.
+ // There are also dependencies between the instructions, and we run up against the
+ // ROB-read limit (can only read 2 registers per cycle).
+ // We also need the number of uops in the loop to be a multiple of 3.
+ // The only available execution unit for this is p3 (memory write). Unfortunately we can't do that
+ // if Position-Independent Code is required.
+
+ // Register usage
+ // ESI = end of src
+ // EDI = end of dest
+ // EBX = index. Counts up to zero (in steps of 2).
+ // EDX:EAX = scratch, used in multiply.
+ // ECX = carry1.
+ // EBP = carry2.
+ // ESP = points to the multiplier.
+
+ // The first member of 'dest' which will be modified is [EDI+4*EBX].
+ // EAX must already contain the first member of 'src', [ESI+4*EBX].
+
+ version (D_PIC) { bool using_PIC = true; } else { bool using_PIC = false; }
+ return "
+ // Entry point for even length
+ add EBX, 1;
+ mov EBP, ECX; // carry
+
+ mul int ptr [" ~ M_ADDRESS ~ "]; // M
+ mov ECX, 0;
+
+ add EBP, EAX;
+ mov EAX, [ESI+4*EBX];
+ adc ECX, EDX;
+
+ mul int ptr [" ~ M_ADDRESS ~ "]; // M
+ " ~ OP ~ " [-4+EDI+4*EBX], EBP;
+ mov EBP, zero;
+
+ adc ECX, EAX;
+ mov EAX, [4+ESI+4*EBX];
+
+ adc EBP, EDX;
+ add EBX, 2;
+ jnl L_done;
+L1:
+ mul int ptr [" ~ M_ADDRESS ~ "];
+ " ~ OP ~ " [-8+EDI+4*EBX], ECX;
+ adc EBP, EAX;
+ mov ECX, zero;
+ mov EAX, [ESI+4*EBX];
+ adc ECX, EDX;
+" ~
+ (using_PIC ? "" : " mov storagenop, EDX; ") // make #uops in loop a multiple of 3, can't do this in PIC mode.
+~ "
+ mul int ptr [" ~ M_ADDRESS ~ "];
+ " ~ OP ~ " [-4+EDI+4*EBX], EBP;
+ mov EBP, zero;
+
+ adc ECX, EAX;
+ mov EAX, [4+ESI+4*EBX];
+
+ adc EBP, EDX;
+ add EBX, 2;
+ jl L1;
+L_done: " ~ OP ~ " [-8+EDI+4*EBX], ECX;
+ adc EBP, 0;
+";
+ // final carry is now in EBP
+}
+
+string asmMulAdd_enter_odd(string OP, string M_ADDRESS) pure
+{
+ return "
+ mul int ptr [" ~M_ADDRESS ~"];
+ mov EBP, zero;
+ add ECX, EAX;
+ mov EAX, [4+ESI+4*EBX];
+
+ adc EBP, EDX;
+ add EBX, 2;
+ jl L1;
+ jmp L_done;
+";
+}
+
+
+
+/**
+ * dest[#] += src[#] * multiplier OP carry(0 .. FFFF_FFFF).
+ * where op == '+' or '-'
+ * Returns carry out of MSB (0 .. FFFF_FFFF).
+ */
+uint multibyteMulAdd(char op)(uint [] dest, const uint [] src, uint
+ multiplier, uint carry) pure {
+ // Timing: This is the most time-critical bignum function.
+ // Pentium M: 5.4 cycles/operation, still has 2 resource stalls + 1load block/iteration
+
+ // The main loop is pipelined and unrolled by 2,
+ // so entry to the loop is also complicated.
+
+ // Register usage
+ // EDX:EAX = multiply
+ // EBX = counter
+ // ECX = carry1
+ // EBP = carry2
+ // EDI = dest
+ // ESI = src
+
+ enum string OP = (op=='+')? "add" : "sub";
+ version (D_PIC)
+ {
+ enum { zero = 0 }
+ }
+ else
+ {
+ // use p2 (load unit) instead of the overworked p0 or p1 (ALU units)
+ // when initializing registers to zero.
+ __gshared int zero = 0;
+ // use p3/p4 units
+ __gshared int storagenop; // write-only
+ }
+
+ enum { LASTPARAM = 5*4 } // 4* pushes + return address.
+ asm pure nothrow {
+ naked;
+
+ push ESI;
+ push EDI;
+ push EBX;
+ push EBP;
+ mov EDI, [ESP + LASTPARAM + 4*4]; // dest.ptr
+ mov EBX, [ESP + LASTPARAM + 4*3]; // dest.length
+ align 16;
+ nop;
+ mov ESI, [ESP + LASTPARAM + 4*2]; // src.ptr
+ lea EDI, [EDI + 4*EBX]; // EDI = end of dest
+ lea ESI, [ESI + 4*EBX]; // ESI = end of src
+ mov EBP, 0;
+ mov ECX, EAX; // ECX = input carry.
+ neg EBX; // count UP to zero.
+ mov EAX, [ESI+4*EBX];
+ test EBX, 1;
+ jnz L_enter_odd;
+ }
+ // Main loop, with entry point for even length
+ mixin("asm pure nothrow {" ~ asmMulAdd_innerloop(OP, "ESP+LASTPARAM") ~ "}");
+ asm pure nothrow {
+ mov EAX, EBP; // get final carry
+ pop EBP;
+ pop EBX;
+ pop EDI;
+ pop ESI;
+ ret 5*4;
+ }
+L_enter_odd:
+ mixin("asm pure nothrow {" ~ asmMulAdd_enter_odd(OP, "ESP+LASTPARAM") ~ "}");
+}
+
+@system unittest
+{
+
+ uint [] aa = [0xF0FF_FFFF, 0x1222_2223, 0x4555_5556, 0x8999_999A, 0xBCCC_CCCD, 0xEEEE_EEEE];
+ uint [] bb = [0x1234_1234, 0xF0F0_F0F0, 0x00C0_C0C0, 0xF0F0_F0F0, 0xC0C0_C0C0];
+ multibyteMulAdd!('+')(bb[1..$-1], aa[1..$-2], 16, 5);
+ assert(bb[0] == 0x1234_1234 && bb[4] == 0xC0C0_C0C0);
+ assert(bb[1] == 0x2222_2230 + 0xF0F0_F0F0+5 && bb[2] == 0x5555_5561+0x00C0_C0C0+1
+ && bb[3] == 0x9999_99A4+0xF0F0_F0F0 );
+}
+
+/**
+ 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],
+ left, right[i], 0);
+ }
+ ----
+ */
+void multibyteMultiplyAccumulate(uint [] dest, const uint[] left,
+ const uint [] right) pure {
+ // Register usage
+ // EDX:EAX = used in multiply
+ // EBX = index
+ // ECX = carry1
+ // EBP = carry2
+ // EDI = end of dest for this pass through the loop. Index for outer loop.
+ // 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)
+ {
+ enum { zero = 0 }
+ }
+ else
+ {
+ // use p2 (load unit) instead of the overworked p0 or p1 (ALU units)
+ // when initializing registers to zero.
+ __gshared int zero = 0;
+ // use p3/p4 units
+ __gshared int storagenop; // write-only
+ }
+
+ enum { LASTPARAM = 6*4 } // 4* pushes + local + return address.
+ asm pure nothrow {
+ naked;
+
+ push ESI;
+ push EDI;
+ align 16;
+ push EBX;
+ push EBP;
+ push EAX; // local variable M
+ mov EDI, [ESP + LASTPARAM + 4*5]; // dest.ptr
+ mov EBX, [ESP + LASTPARAM + 4*2]; // left.length
+ mov ESI, [ESP + LASTPARAM + 4*3]; // left.ptr
+ lea EDI, [EDI + 4*EBX]; // EDI = end of dest for first pass
+
+ mov EAX, [ESP + LASTPARAM + 4*0]; // right.length
+ lea EAX, [EDI + 4*EAX];
+ mov [ESP + LASTPARAM + 4*0], EAX; // last value for EDI
+
+ lea ESI, [ESI + 4*EBX]; // ESI = end of left
+ mov EAX, [ESP + LASTPARAM + 4*1]; // right.ptr
+ mov EAX, [EAX];
+ mov [ESP], EAX; // M
+outer_loop:
+ mov EBP, 0;
+ mov ECX, 0; // ECX = input carry.
+ neg EBX; // count UP to zero.
+ mov EAX, [ESI+4*EBX];
+ test EBX, 1;
+ jnz L_enter_odd;
+ }
+ // -- Inner loop, with even entry point
+ mixin("asm pure nothrow { " ~ asmMulAdd_innerloop("add", "ESP") ~ "}");
+ asm pure nothrow {
+ mov [-4+EDI+4*EBX], EBP;
+ add EDI, 4;
+ cmp EDI, [ESP + LASTPARAM + 4*0]; // is EDI = &dest[$]?
+ jz outer_done;
+ mov EAX, [ESP + LASTPARAM + 4*1]; // right.ptr
+ mov EAX, [EAX+4]; // get new M
+ mov [ESP], EAX; // save new M
+ add int ptr [ESP + LASTPARAM + 4*1], 4; // right.ptr
+ mov EBX, [ESP + LASTPARAM + 4*2]; // left.length
+ jmp outer_loop;
+outer_done:
+ pop EAX;
+ pop EBP;
+ pop EBX;
+ pop EDI;
+ pop ESI;
+ ret 6*4;
+ }
+L_enter_odd:
+ mixin("asm pure nothrow {" ~ asmMulAdd_enter_odd("add", "ESP") ~ "}");
+}
+
+/** dest[#] /= divisor.
+ * 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
+ *
+ * Based on public domain code by Eric Bainville.
+ * (http://www.bealto.com/) Used with permission.
+ */
+uint multibyteDivAssign(uint [] dest, uint divisor, uint overflow) pure
+{
+ // Timing: limited by a horrible dependency chain.
+ // Pentium M: 18 cycles/op, 8 resource stalls/op.
+ // EAX, EDX = scratch, used by MUL
+ // EDI = dest
+ // CL = shift
+ // ESI = quotient
+ // EBX = remainderhi
+ // EBP = remainderlo
+ // [ESP-4] = mask
+ // [ESP] = kinv (2^64 /divisor)
+ enum { LASTPARAM = 5*4 } // 4* pushes + return address.
+ enum { LOCALS = 2*4} // MASK, KINV
+ asm pure nothrow {
+ naked;
+
+ push ESI;
+ push EDI;
+ push EBX;
+ push EBP;
+
+ mov EDI, [ESP + LASTPARAM + 4*2]; // dest.ptr
+ mov EBX, [ESP + LASTPARAM + 4*1]; // dest.length
+
+ // Loop from msb to lsb
+ lea EDI, [EDI + 4*EBX];
+ 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;
+ mov EAX, [ESP + LASTPARAM]; //divisor;
+ mov EDX, 1;
+kinv1:
+ inc ECX;
+ ror EDX, 1;
+ shl EAX, 1;
+ jnc kinv1;
+ dec ECX;
+ // Here, ecx is a left shift moving the msb of k to bit 32
+
+ mov EAX, 1;
+ shl EAX, CL;
+ dec EAX;
+ ror EAX, CL ; //ecx bits at msb
+ push EAX; // MASK
+
+ // Then divide 2^(32+cx) by divisor (edx already ok)
+ xor EAX, EAX;
+ div int ptr [ESP + LASTPARAM + LOCALS-4*1]; //divisor;
+ push EAX; // kinv
+ align 16;
+L2:
+ // Get 32 bits of quotient approx, multiplying
+ // most significant word of (rem*2^32+input)
+ mov EAX, [ESP+4]; //MASK;
+ and EAX, [EDI - 4];
+ or EAX, EBP;
+ rol EAX, CL;
+ mov EBX, EBP;
+ mov EBP, [EDI - 4];
+ mul int ptr [ESP]; //KINV;
+
+ shl EAX, 1;
+ rcl EDX, 1;
+
+ // Multiply by k and subtract to get remainder
+ // Subtraction must be done on two words
+ mov EAX, EDX;
+ mov ESI, EDX; // quot = high word
+ mul int ptr [ESP + LASTPARAM+LOCALS]; //divisor;
+ sub EBP, EAX;
+ sbb EBX, EDX;
+ jz Lb; // high word is 0, goto adjust on single word
+
+ // Adjust quotient and remainder on two words
+Ld: inc ESI;
+ sub EBP, [ESP + LASTPARAM+LOCALS]; //divisor;
+ sbb EBX, 0;
+ jnz Ld;
+
+ // Adjust quotient and remainder on single word
+Lb: cmp EBP, [ESP + LASTPARAM+LOCALS]; //divisor;
+ jc Lc; // rem in 0 .. divisor-1, OK
+ sub EBP, [ESP + LASTPARAM+LOCALS]; //divisor;
+ inc ESI;
+ jmp Lb;
+
+ // Store result
+Lc:
+ mov [EDI - 4], ESI;
+ lea EDI, [EDI - 4];
+ dec int ptr [ESP + LASTPARAM + 4*1+LOCALS]; // len
+ jnz L2;
+
+ pop EAX; // discard kinv
+ pop EAX; // discard mask
+
+ mov EAX, EBP; // return final remainder
+ pop EBP;
+ pop EBX;
+ pop EDI;
+ pop ESI;
+ ret 3*4;
+ }
+}
+
+@system unittest
+{
+ uint [] aa = new uint[101];
+ for (int i=0; i<aa.length; ++i) aa[i] = 0x8765_4321 * (i+3);
+ uint overflow = multibyteMul(aa, aa, 0x8EFD_FCFB, 0x33FF_7461);
+ uint r = multibyteDivAssign(aa, 0x8EFD_FCFB, overflow);
+ for (int i=0; i<aa.length-1; ++i) assert(aa[i] == 0x8765_4321 * (i+3));
+ assert(r == 0x33FF_7461);
+}
+
+// Set dest[2*i .. 2*i+1]+=src[i]*src[i]
+void multibyteAddDiagonalSquares(uint [] dest, const uint [] src) pure
+{
+ /* Unlike mulAdd, the carry is only 1 bit,
+ since FFFF*FFFF+FFFF_FFFF = 1_0000_0000.
+ Note also that on the last iteration, no carry can occur.
+ As for multibyteAdd, we save & restore carry flag through the loop.
+
+ The timing is entirely dictated by the dependency chain. We could
+ improve it by moving the mov EAX after the adc [EDI], EAX. Probably not worthwhile.
+ */
+ enum { LASTPARAM = 4*5 } // 4* pushes + return address.
+ asm pure nothrow {
+ naked;
+ push ESI;
+ push EDI;
+ push EBX;
+ push ECX;
+ mov EDI, [ESP + LASTPARAM + 4*3]; //dest.ptr;
+ mov EBX, [ESP + LASTPARAM + 4*0]; //src.length;
+ mov ESI, [ESP + LASTPARAM + 4*1]; //src.ptr;
+ lea EDI, [EDI + 8*EBX]; // EDI = end of dest
+ lea ESI, [ESI + 4*EBX]; // ESI = end of src
+ neg EBX; // count UP to zero.
+ xor ECX, ECX; // initial carry = 0.
+L1:
+ mov EAX, [ESI + 4*EBX];
+ mul EAX, EAX;
+ shr CL, 1; // get carry
+ adc [EDI + 8*EBX], EAX;
+ adc [EDI + 8*EBX + 4], EDX;
+ setc CL; // save carry
+ inc EBX;
+ jnz L1;
+
+ pop ECX;
+ pop EBX;
+ pop EDI;
+ pop ESI;
+ ret 4*4;
+ }
+}
+
+@system unittest
+{
+ uint [] aa = new uint[13];
+ uint [] bb = new uint[6];
+ for (int i=0; i<aa.length; ++i) aa[i] = 0x8000_0000;
+ for (int i=0; i<bb.length; ++i) bb[i] = i;
+ aa[$-1]= 7;
+ multibyteAddDiagonalSquares(aa[0..$-1], bb);
+ assert(aa[$-1]==7);
+ for (int i=0; i<bb.length; ++i) { assert(aa[2*i]==0x8000_0000+i*i); assert(aa[2*i+1]==0x8000_0000); }
+}
+
+void multibyteTriangleAccumulateD(uint[] dest, uint[] x) pure
+{
+ for (int i = 0; i < x.length-3; ++i)
+ {
+ dest[i+x.length] = multibyteMulAdd!('+')(
+ dest[i+i+1 .. i+x.length], x[i+1..$], x[i], 0);
+ }
+ ulong c = cast(ulong)(x[$-3]) * x[$-2] + dest[$-5];
+ dest[$-5] = cast(uint) c;
+ c >>= 32;
+ c += cast(ulong)(x[$-3]) * x[$-1] + dest[$-4];
+ dest[$-4] = cast(uint) c;
+ c >>= 32;
+length2:
+ c += cast(ulong)(x[$-2]) * x[$-1];
+ dest[$-3] = cast(uint) c;
+ c >>= 32;
+ dest[$-2] = cast(uint) c;
+}
+
+//dest += src[0]*src[1...$] + src[1]*src[2..$] + ... + src[$-3]*src[$-2..$]+ src[$-2]*src[$-1]
+// assert(dest.length = src.length*2);
+// assert(src.length >= 3);
+void multibyteTriangleAccumulateAsm(uint[] dest, const uint[] src) pure
+{
+ // Register usage
+ // EDX:EAX = used in multiply
+ // EBX = index
+ // ECX = carry1
+ // EBP = carry2
+ // EDI = end of dest for this pass through the loop. Index for outer loop.
+ // 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)
+ {
+ enum { zero = 0 }
+ }
+ else
+ {
+ // use p2 (load unit) instead of the overworked p0 or p1 (ALU units)
+ // when initializing registers to zero.
+ __gshared int zero = 0;
+ // use p3/p4 units
+ __gshared int storagenop; // write-only
+ }
+
+ enum { LASTPARAM = 6*4 } // 4* pushes + local + return address.
+ asm pure nothrow {
+ naked;
+
+ push ESI;
+ push EDI;
+ align 16;
+ push EBX;
+ push EBP;
+ push EAX; // local variable M= src[i]
+ mov EDI, [ESP + LASTPARAM + 4*3]; // dest.ptr
+ mov EBX, [ESP + LASTPARAM + 4*0]; // src.length
+ mov ESI, [ESP + LASTPARAM + 4*1]; // src.ptr
+
+ lea ESI, [ESI + 4*EBX]; // ESI = end of left
+ add int ptr [ESP + LASTPARAM + 4*1], 4; // src.ptr, used for getting M
+
+ // local variable [ESP + LASTPARAM + 4*2] = last value for EDI
+ lea EDI, [EDI + 4*EBX]; // EDI = end of dest for first pass
+
+ lea EAX, [EDI + 4*EBX-3*4]; // up to src.length - 3
+ mov [ESP + LASTPARAM + 4*2], EAX; // last value for EDI = &dest[src.length*2 -3]
+
+ cmp EBX, 3;
+ jz length_is_3;
+
+ // We start at src[1], not src[0].
+ dec EBX;
+ mov [ESP + LASTPARAM + 4*0], EBX;
+
+outer_loop:
+ mov EBX, [ESP + LASTPARAM + 4*0]; // src.length
+ mov EBP, 0;
+ mov ECX, 0; // ECX = input carry.
+ dec [ESP + LASTPARAM + 4*0]; // Next time, the length will be shorter by 1.
+ neg EBX; // count UP to zero.
+
+ mov EAX, [ESI + 4*EBX - 4*1]; // get new M
+ mov [ESP], EAX; // save new M
+
+ mov EAX, [ESI+4*EBX];
+ test EBX, 1;
+ jnz L_enter_odd;
+ }
+ // -- Inner loop, with even entry point
+ mixin("asm pure nothrow { " ~ asmMulAdd_innerloop("add", "ESP") ~ "}");
+ asm pure nothrow {
+ mov [-4+EDI+4*EBX], EBP;
+ add EDI, 4;
+ cmp EDI, [ESP + LASTPARAM + 4*2]; // is EDI = &dest[$-3]?
+ jnz outer_loop;
+length_is_3:
+ mov EAX, [ESI - 4*3];
+ mul EAX, [ESI - 4*2];
+ mov ECX, 0;
+ add [EDI-2*4], EAX; // ECX:dest[$-5] += x[$-3] * x[$-2]
+ adc ECX, EDX;
+
+ mov EAX, [ESI - 4*3];
+ mul EAX, [ESI - 4*1]; // x[$-3] * x[$-1]
+ add EAX, ECX;
+ mov ECX, 0;
+ adc EDX, 0;
+ // 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
+ mov EAX, [ESI - 4*2];
+ mul EAX, [ESI - 4*1];
+ add ECX, EAX;
+ adc EDX, 0;
+ mov [EDI - 0*4], ECX; // dest[$-2:$-3] = c + x[$-2] * x[$-1];
+ mov [EDI + 1*4], EDX;
+
+ pop EAX;
+ pop EBP;
+ pop EBX;
+ pop EDI;
+ pop ESI;
+ ret 4*4;
+ }
+L_enter_odd:
+ mixin("asm pure nothrow {" ~ asmMulAdd_enter_odd("add", "ESP") ~ "}");
+}
+
+@system unittest
+{
+ uint [] aa = new uint[200];
+ uint [] a = aa[0 .. 100];
+ uint [] b = new uint [100];
+ aa[] = 761;
+ a[] = 0;
+ b[] = 0;
+ a[3] = 6;
+ b[0]=1;
+ b[1] = 17;
+ b[50 .. 100]=78;
+ multibyteTriangleAccumulateAsm(a, b[0 .. 50]);
+ uint [] c = new uint[100];
+ c[] = 0;
+ c[1] = 17;
+ c[3] = 6;
+ assert(a[]==c[]);
+ assert(a[0]==0);
+ aa[] = 0xFFFF_FFFF;
+ a[] = 0;
+ b[] = 0;
+ b[0]= 0xbf6a1f01;
+ b[1]= 0x6e38ed64;
+ b[2]= 0xdaa797ed;
+ b[3] = 0;
+
+ multibyteTriangleAccumulateAsm(a[0 .. 8], b[0 .. 4]);
+ assert(a[1]==0x3a600964);
+ assert(a[2]==0x339974f6);
+ assert(a[3]==0x46736fce);
+ assert(a[4]==0x5e24a2b4);
+
+ b[3] = 0xe93ff9f4;
+ b[4] = 0x184f03;
+ a[]=0;
+ multibyteTriangleAccumulateAsm(a[0 .. 14], b[0 .. 7]);
+ assert(a[3]==0x79fff5c2);
+ assert(a[4]==0xcf384241);
+ assert(a[5]== 0x4a17fc8);
+ assert(a[6]==0x4d549025);
+}
+
+
+void multibyteSquare(BigDigit[] result, const BigDigit [] x) pure
+{
+ if (x.length < 4)
+ {
+ // Special cases, not worth doing triangular.
+ result[x.length] = multibyteMul(result[0 .. x.length], x, x[0], 0);
+ multibyteMultiplyAccumulate(result[1..$], x, x[1..$]);
+ return;
+ }
+ // Do half a square multiply.
+ // dest += src[0]*src[1...$] + src[1]*src[2..$] + ... + src[$-3]*src[$-2..$]+ src[$-2]*src[$-1]
+ result[x.length] = multibyteMul(result[1 .. x.length], x[1..$], x[0], 0);
+ multibyteTriangleAccumulateAsm(result[2..$], x[1..$]);
+ // Multiply by 2
+ result[$-1] = multibyteShlNoMMX(result[1..$-1], result[1..$-1], 1);
+ // And add the diagonal elements
+ result[0] = 0;
+ multibyteAddDiagonalSquares(result, x);
+}
+
+version (BignumPerformanceTest)
+{
+import core.stdc.stdio;
+int clock() { asm { push EBX; xor EAX, EAX; cpuid; pop EBX; rdtsc; } }
+
+__gshared uint [2200] X1;
+__gshared uint [2200] Y1;
+__gshared uint [4000] Z1;
+
+void testPerformance() pure
+{
+ // The performance results at the top of this file were obtained using
+ // a Windows device driver to access the CPU performance counters.
+ // The code below is less accurate but more widely usable.
+ // The value for division is quite inconsistent.
+ for (int i=0; i<X1.length; ++i) { X1[i]=i; Y1[i]=i; Z1[i]=i; }
+ int t, t0;
+ multibyteShl(Z1[0 .. 2000], X1[0 .. 2000], 7);
+ t0 = clock();
+ multibyteShl(Z1[0 .. 1000], X1[0 .. 1000], 7);
+ t = clock();
+ multibyteShl(Z1[0 .. 2000], X1[0 .. 2000], 7);
+ auto shltime = (clock() - t) - (t - t0);
+ t0 = clock();
+ multibyteShr(Z1[2 .. 1002], X1[4 .. 1004], 13);
+ t = clock();
+ multibyteShr(Z1[2 .. 2002], X1[4 .. 2004], 13);
+ auto shrtime = (clock() - t) - (t - t0);
+ t0 = clock();
+ multibyteAddSub!('+')(Z1[0 .. 1000], X1[0 .. 1000], Y1[0 .. 1000], 0);
+ t = clock();
+ multibyteAddSub!('+')(Z1[0 .. 2000], X1[0 .. 2000], Y1[0 .. 2000], 0);
+ auto addtime = (clock() - t) - (t-t0);
+ t0 = clock();
+ multibyteMul(Z1[0 .. 1000], X1[0 .. 1000], 7, 0);
+ t = clock();
+ multibyteMul(Z1[0 .. 2000], X1[0 .. 2000], 7, 0);
+ auto multime = (clock() - t) - (t - t0);
+ multibyteMulAdd!('+')(Z1[0 .. 2000], X1[0 .. 2000], 217, 0);
+ t0 = clock();
+ multibyteMulAdd!('+')(Z1[0 .. 1000], X1[0 .. 1000], 217, 0);
+ t = clock();
+ multibyteMulAdd!('+')(Z1[0 .. 2000], X1[0 .. 2000], 217, 0);
+ auto muladdtime = (clock() - t) - (t - t0);
+ multibyteMultiplyAccumulate(Z1[0 .. 64], X1[0 .. 32], Y1[0 .. 32]);
+ t = clock();
+ multibyteMultiplyAccumulate(Z1[0 .. 64], X1[0 .. 32], Y1[0 .. 32]);
+ auto accumtime = clock() - t;
+ t0 = clock();
+ multibyteDivAssign(Z1[0 .. 2000], 217, 0);
+ t = clock();
+ multibyteDivAssign(Z1[0 .. 1000], 37, 0);
+ auto divtime = (t - t0) - (clock() - t);
+ t= clock();
+ multibyteSquare(Z1[0 .. 64], X1[0 .. 32]);
+ auto squaretime = clock() - t;
+
+ printf("-- BigInt asm performance (cycles/int) --\n");
+ printf("Add: %.2f\n", addtime/1000.0);
+ printf("Shl: %.2f\n", shltime/1000.0);
+ printf("Shr: %.2f\n", shrtime/1000.0);
+ printf("Mul: %.2f\n", multime/1000.0);
+ printf("MulAdd: %.2f\n", muladdtime/1000.0);
+ printf("Div: %.2f\n", divtime/1000.0);
+ printf("MulAccum32: %.2f*n*n (total %d)\n", accumtime/(32.0*32.0), accumtime);
+ printf("Square32: %.2f*n*n (total %d)\n\n", squaretime/(32.0*32.0), squaretime);
+}
+
+static this()
+{
+ testPerformance();
+}
+}
+
+} // version (D_InlineAsm_X86)
diff --git a/libphobos/src/std/internal/math/errorfunction.d b/libphobos/src/std/internal/math/errorfunction.d
new file mode 100644
index 0000000..4012e64
--- /dev/null
+++ b/libphobos/src/std/internal/math/errorfunction.d
@@ -0,0 +1,1145 @@
+/**
+ * Error Functions and Normal Distribution.
+ *
+ * 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, ported to D by Don Clugston and David Nadlinger
+ */
+/**
+ * Macros:
+ * NAN = $(RED NAN)
+ * SUP = <span style="vertical-align:super;font-size:smaller">$0</span>
+ * GAMMA = &#915;
+ * INTEGRAL = &#8747;
+ * INTEGRATE = $(BIG &#8747;<sub>$(SMALL $1)</sub><sup>$2</sup>)
+ * POWER = $1<sup>$2</sup>
+ * BIGSUM = $(BIG &Sigma; <sup>$2</sup><sub>$(SMALL $1)</sub>)
+ * CHOOSE = $(BIG &#40;) <sup>$(SMALL $1)</sup><sub>$(SMALL $2)</sub> $(BIG &#41;)
+ * TABLE_SV = <table border="1" cellpadding="4" cellspacing="0">
+ * <caption>Special Values</caption>
+ * $0</table>
+ * SVH = $(TR $(TH $1) $(TH $2))
+ * SV = $(TR $(TD $1) $(TD $2))
+ */
+module std.internal.math.errorfunction;
+import std.math;
+
+pure:
+nothrow:
+@safe:
+@nogc:
+
+private {
+immutable real EXP_2 = 0.135335283236612691893999494972484403L; /* exp(-2) */
+enum real SQRT2PI = 2.50662827463100050241576528481104525L; // sqrt(2pi)
+
+
+enum real MAXLOG = 0x1.62e42fefa39ef358p+13L; // log(real.max)
+enum real MINLOG = -0x1.6436716d5406e6d8p+13L; // log(real.min*real.epsilon) = log(smallest denormal)
+}
+
+T rationalPoly(T)(T x, const(T) [] numerator, const(T) [] denominator) pure nothrow
+{
+ return poly(x, numerator)/poly(x, denominator);
+}
+
+
+private {
+
+/* erfc(x) = exp(-x^2) P(1/x)/Q(1/x)
+ 1/8 <= 1/x <= 1
+ Peak relative error 5.8e-21 */
+immutable real[10] P = [ -0x1.30dfa809b3cc6676p-17, 0x1.38637cd0913c0288p+18,
+ 0x1.2f015e047b4476bp+22, 0x1.24726f46aa9ab08p+25, 0x1.64b13c6395dc9c26p+27,
+ 0x1.294c93046ad55b5p+29, 0x1.5962a82f92576dap+30, 0x1.11a709299faba04ap+31,
+ 0x1.11028065b087be46p+31, 0x1.0d8ef40735b097ep+30
+];
+
+immutable real[11] Q = [ 0x1.14d8e2a72dec49f4p+19, 0x1.0c880ff467626e1p+23,
+ 0x1.04417ef060b58996p+26, 0x1.404e61ba86df4ebap+28, 0x1.0f81887bc82b873ap+30,
+ 0x1.4552a5e39fb49322p+31, 0x1.11779a0ceb2a01cep+32, 0x1.3544dd691b5b1d5cp+32,
+ 0x1.a91781f12251f02ep+31, 0x1.0d8ef3da605a1c86p+30, 1.0
+];
+
+// For 128 bit quadruple-precision floats, we use a higher-precision implementation
+// with more polynomial segments.
+enum isIEEEQuadruple = floatTraits!real.realFormat == RealFormat.ieeeQuadruple;
+static if (isIEEEQuadruple)
+{
+ // erfc(x + 0.25) = erfc(0.25) + x R(x)
+ // 0 <= x < 0.125
+ // Peak relative error 1.4e-35
+ immutable real[9] RNr13 = [
+ -2.353707097641280550282633036456457014829E3L,
+ 3.871159656228743599994116143079870279866E2L,
+ -3.888105134258266192210485617504098426679E2L,
+ -2.129998539120061668038806696199343094971E1L,
+ -8.125462263594034672468446317145384108734E1L,
+ 8.151549093983505810118308635926270319660E0L,
+ -5.033362032729207310462422357772568553670E0L,
+ -4.253956621135136090295893547735851168471E-2L,
+ -8.098602878463854789780108161581050357814E-2L
+ ];
+ immutable real[9] RDr13 = [
+ 2.220448796306693503549505450626652881752E3L,
+ 1.899133258779578688791041599040951431383E2L,
+ 1.061906712284961110196427571557149268454E3L,
+ 7.497086072306967965180978101974566760042E1L,
+ 2.146796115662672795876463568170441327274E2L,
+ 1.120156008362573736664338015952284925592E1L,
+ 2.211014952075052616409845051695042741074E1L,
+ 6.469655675326150785692908453094054988938E-1L,
+ 1.0
+ ];
+
+ // erfc(0.25) = C13a + C13b to extra precision.
+ immutable real C13a = 0.723663330078125L;
+ immutable real C13b = 1.0279753638067014931732235184287934646022E-5L;
+
+ // erfc(x + 0.375) = erfc(0.375) + x R(x)
+ // 0 <= x < 0.125
+ // Peak relative error 1.2e-35
+ immutable real[9] RNr14 = [
+ -2.446164016404426277577283038988918202456E3L,
+ 6.718753324496563913392217011618096698140E2L,
+ -4.581631138049836157425391886957389240794E2L,
+ -2.382844088987092233033215402335026078208E1L,
+ -7.119237852400600507927038680970936336458E1L,
+ 1.313609646108420136332418282286454287146E1L,
+ -6.188608702082264389155862490056401365834E0L,
+ -2.787116601106678287277373011101132659279E-2L,
+ -2.230395570574153963203348263549700967918E-2L
+ ];
+ immutable real[9] RDr14 = [
+ 2.495187439241869732696223349840963702875E3L,
+ 2.503549449872925580011284635695738412162E2L,
+ 1.159033560988895481698051531263861842461E3L,
+ 9.493751466542304491261487998684383688622E1L,
+ 2.276214929562354328261422263078480321204E2L,
+ 1.367697521219069280358984081407807931847E1L,
+ 2.276988395995528495055594829206582732682E1L,
+ 7.647745753648996559837591812375456641163E-1L,
+ 1.0
+ ];
+
+ // erfc(0.375) = C14a + C14b to extra precision.
+ immutable real C14a = 0.5958709716796875L;
+ immutable real C14b = 1.2118885490201676174914080878232469565953E-5L;
+
+ // erfc(x + 0.5) = erfc(0.5) + x R(x)
+ // 0 <= x < 0.125
+ // Peak relative error 4.7e-36
+ immutable real[9] RNr15 = [
+ -2.624212418011181487924855581955853461925E3L,
+ 8.473828904647825181073831556439301342756E2L,
+ -5.286207458628380765099405359607331669027E2L,
+ -3.895781234155315729088407259045269652318E1L,
+ -6.200857908065163618041240848728398496256E1L,
+ 1.469324610346924001393137895116129204737E1L,
+ -6.961356525370658572800674953305625578903E0L,
+ 5.145724386641163809595512876629030548495E-3L,
+ 1.990253655948179713415957791776180406812E-2L
+ ];
+ immutable real[9] RDr15 = [
+ 2.986190760847974943034021764693341524962E3L,
+ 5.288262758961073066335410218650047725985E2L,
+ 1.363649178071006978355113026427856008978E3L,
+ 1.921707975649915894241864988942255320833E2L,
+ 2.588651100651029023069013885900085533226E2L,
+ 2.628752920321455606558942309396855629459E1L,
+ 2.455649035885114308978333741080991380610E1L,
+ 1.378826653595128464383127836412100939126E0L,
+ 1.0
+ ];
+ // erfc(0.5) = C15a + C15b to extra precision.
+ immutable real C15a = 0.4794921875L;
+ immutable real C15b = 7.9346869534623172533461080354712635484242E-6L;
+
+ // erfc(x + 0.625) = erfc(0.625) + x R(x)
+ // 0 <= x < 0.125
+ // Peak relative error 5.1e-36
+ immutable real[9] RNr16 = [
+ -2.347887943200680563784690094002722906820E3L,
+ 8.008590660692105004780722726421020136482E2L,
+ -5.257363310384119728760181252132311447963E2L,
+ -4.471737717857801230450290232600243795637E1L,
+ -4.849540386452573306708795324759300320304E1L,
+ 1.140885264677134679275986782978655952843E1L,
+ -6.731591085460269447926746876983786152300E0L,
+ 1.370831653033047440345050025876085121231E-1L,
+ 2.022958279982138755020825717073966576670E-2L,
+ ];
+ immutable real[9] RDr16 = [
+ 3.075166170024837215399323264868308087281E3L,
+ 8.730468942160798031608053127270430036627E2L,
+ 1.458472799166340479742581949088453244767E3L,
+ 3.230423687568019709453130785873540386217E2L,
+ 2.804009872719893612081109617983169474655E2L,
+ 4.465334221323222943418085830026979293091E1L,
+ 2.612723259683205928103787842214809134746E1L,
+ 2.341526751185244109722204018543276124997E0L,
+ 1.0
+ ];
+ // erfc(0.625) = C16a + C16b to extra precision.
+ immutable real C16a = 0.3767547607421875L;
+ immutable real C16b = 4.3570693945275513594941232097252997287766E-6L;
+
+ // erfc(x + 0.75) = erfc(0.75) + x R(x)
+ // 0 <= x < 0.125
+ // Peak relative error 1.7e-35
+ immutable real[9] RNr17 = [
+ -1.767068734220277728233364375724380366826E3L,
+ 6.693746645665242832426891888805363898707E2L,
+ -4.746224241837275958126060307406616817753E2L,
+ -2.274160637728782675145666064841883803196E1L,
+ -3.541232266140939050094370552538987982637E1L,
+ 6.988950514747052676394491563585179503865E0L,
+ -5.807687216836540830881352383529281215100E0L,
+ 3.631915988567346438830283503729569443642E-1L,
+ -1.488945487149634820537348176770282391202E-2L
+ ];
+ immutable real[9] RDr17 = [
+ 2.748457523498150741964464942246913394647E3L,
+ 1.020213390713477686776037331757871252652E3L,
+ 1.388857635935432621972601695296561952738E3L,
+ 3.903363681143817750895999579637315491087E2L,
+ 2.784568344378139499217928969529219886578E2L,
+ 5.555800830216764702779238020065345401144E1L,
+ 2.646215470959050279430447295801291168941E1L,
+ 2.984905282103517497081766758550112011265E0L,
+ 1.0
+ ];
+ // erfc(0.75) = C17a + C17b to extra precision.
+ immutable real C17a = 0.2888336181640625L;
+ immutable real C17b = 1.0748182422368401062165408589222625794046E-5L;
+
+
+ // erfc(x + 0.875) = erfc(0.875) + x R(x)
+ // 0 <= x < 0.125
+ // Peak relative error 2.2e-35
+ immutable real[9] RNr18 = [
+ -1.342044899087593397419622771847219619588E3L,
+ 6.127221294229172997509252330961641850598E2L,
+ -4.519821356522291185621206350470820610727E2L,
+ 1.223275177825128732497510264197915160235E1L,
+ -2.730789571382971355625020710543532867692E1L,
+ 4.045181204921538886880171727755445395862E0L,
+ -4.925146477876592723401384464691452700539E0L,
+ 5.933878036611279244654299924101068088582E-1L,
+ -5.557645435858916025452563379795159124753E-2L
+ ];
+ immutable real[9] RDr18 = [
+ 2.557518000661700588758505116291983092951E3L,
+ 1.070171433382888994954602511991940418588E3L,
+ 1.344842834423493081054489613250688918709E3L,
+ 4.161144478449381901208660598266288188426E2L,
+ 2.763670252219855198052378138756906980422E2L,
+ 5.998153487868943708236273854747564557632E1L,
+ 2.657695108438628847733050476209037025318E1L,
+ 3.252140524394421868923289114410336976512E0L,
+ 1.0
+ ];
+
+ // erfc(0.875) = C18a + C18b to extra precision.
+ immutable real C18a = 0.215911865234375L;
+ immutable real C18b = 1.3073705765341685464282101150637224028267E-5L;
+
+ // erfc(x + 1.0) = erfc(1.0) + x R(x)
+ // 0 <= x < 0.125
+ // Peak relative error 1.6e-35
+ immutable real[9] RNr19 = [
+ -1.139180936454157193495882956565663294826E3L,
+ 6.134903129086899737514712477207945973616E2L,
+ -4.628909024715329562325555164720732868263E2L,
+ 4.165702387210732352564932347500364010833E1L,
+ -2.286979913515229747204101330405771801610E1L,
+ 1.870695256449872743066783202326943667722E0L,
+ -4.177486601273105752879868187237000032364E0L,
+ 7.533980372789646140112424811291782526263E-1L,
+ -8.629945436917752003058064731308767664446E-2L
+ ];
+ immutable real[9] RDr19 = [
+ 2.744303447981132701432716278363418643778E3L,
+ 1.266396359526187065222528050591302171471E3L,
+ 1.466739461422073351497972255511919814273E3L,
+ 4.868710570759693955597496520298058147162E2L,
+ 2.993694301559756046478189634131722579643E2L,
+ 6.868976819510254139741559102693828237440E1L,
+ 2.801505816247677193480190483913753613630E1L,
+ 3.604439909194350263552750347742663954481E0L,
+ 1.0
+ ];
+
+ // erfc(1.0) = C19a + C19b to extra precision.
+ immutable real C19a = 0.15728759765625L;
+ immutable real C19b = 1.1609394035130658779364917390740703933002E-5L;
+
+ // erfc(x + 1.125) = erfc(1.125) + x R(x)
+ // 0 <= x < 0.125
+ // Peak relative error 3.6e-36
+ immutable real[9] RNr20 = [
+ -9.652706916457973956366721379612508047640E2L,
+ 5.577066396050932776683469951773643880634E2L,
+ -4.406335508848496713572223098693575485978E2L,
+ 5.202893466490242733570232680736966655434E1L,
+ -1.931311847665757913322495948705563937159E1L,
+ -9.364318268748287664267341457164918090611E-2L,
+ -3.306390351286352764891355375882586201069E0L,
+ 7.573806045289044647727613003096916516475E-1L,
+ -9.611744011489092894027478899545635991213E-2L
+ ];
+ immutable real[9] RDr20 = [
+ 3.032829629520142564106649167182428189014E3L,
+ 1.659648470721967719961167083684972196891E3L,
+ 1.703545128657284619402511356932569292535E3L,
+ 6.393465677731598872500200253155257708763E2L,
+ 3.489131397281030947405287112726059221934E2L,
+ 8.848641738570783406484348434387611713070E1L,
+ 3.132269062552392974833215844236160958502E1L,
+ 4.430131663290563523933419966185230513168E0L,
+ 1.0
+ ];
+
+ // erfc(1.125) = C20a + C20b to extra precision.
+ immutable real C20a = 0.111602783203125L;
+ immutable real C20b = 8.9850951672359304215530728365232161564636E-6L;
+
+ // erfc(x) = exp(-x^2) 1/x R(1/x^2) / S(1/x^2)
+ // 7/8 <= 1/x < 1
+ // Peak relative error 1.4e-35
+ immutable real[10] RNr8 = [
+ 3.587451489255356250759834295199296936784E1L,
+ 5.406249749087340431871378009874875889602E2L,
+ 2.931301290625250886238822286506381194157E3L,
+ 7.359254185241795584113047248898753470923E3L,
+ 9.201031849810636104112101947312492532314E3L,
+ 5.749697096193191467751650366613289284777E3L,
+ 1.710415234419860825710780802678697889231E3L,
+ 2.150753982543378580859546706243022719599E2L,
+ 8.740953582272147335100537849981160931197E0L,
+ 4.876422978828717219629814794707963640913E-2L
+ ];
+ immutable real[10] RDr8 = [
+ 6.358593134096908350929496535931630140282E1L,
+ 9.900253816552450073757174323424051765523E2L,
+ 5.642928777856801020545245437089490805186E3L,
+ 1.524195375199570868195152698617273739609E4L,
+ 2.113829644500006749947332935305800887345E4L,
+ 1.526438562626465706267943737310282977138E4L,
+ 5.561370922149241457131421914140039411782E3L,
+ 9.394035530179705051609070428036834496942E2L,
+ 6.147019596150394577984175188032707343615E1L,
+ 1.0L
+ ];
+
+ // erfc(x) = exp(-x^2) 1/x R(1/x^2) / S(1/x^2)
+ // 3/4 <= 1/x < 7/8
+ // Peak relative error 1.7e-36
+ immutable real[10] RNr7 = [
+ 1.293515364043117601705535485785956592493E2L,
+ 2.474534371269189867053251150063459055230E3L,
+ 1.756537563959875738809491329250457486510E4L,
+ 5.977479535376639763773153344676726091607E4L,
+ 1.054313816139671870123172936972055385049E5L,
+ 9.754699773487726957401038094714603033904E4L,
+ 4.579131242577671038339922925213209214880E4L,
+ 1.000710322164510887997115157797717324370E4L,
+ 8.496863250712471449526805271633794700452E2L,
+ 1.797349831386892396933210199236530557333E1L
+ ];
+ immutable real[11] RDr7 = [
+ 2.292696320307033494820399866075534515002E2L,
+ 4.500632608295626968062258401895610053116E3L,
+ 3.321218723485498111535866988511716659339E4L,
+ 1.196084512221845156596781258490840961462E5L,
+ 2.287033883912529843927983406878910939930E5L,
+ 2.370223495794642027268482075021298394425E5L,
+ 1.305173734022437154610938308944995159199E5L,
+ 3.589386258485887630236490009835928559621E4L,
+ 4.339996864041074149726360516336773136101E3L,
+ 1.753135522665469574605384979152863899099E2L,
+ 1.0L
+ ];
+
+ // erfc(x) = exp(-x^2) 1/x R(1/x^2) / S(1/x^2)
+ // 5/8 <= 1/x < 3/4
+ // Peak relative error 1.6e-35
+ immutable real[10] RNr6 = [
+ 1.423313561367394923305025174137639124533E1L,
+ 3.244462503273003837514629113846075327206E2L,
+ 2.784937282403293364911673341412846781934E3L,
+ 1.163060685810874867196849890286455473382E4L,
+ 2.554141394931962276102205517358731053756E4L,
+ 2.982733782500729530503336931258698708782E4L,
+ 1.789683564523810605328169719436374742840E4L,
+ 5.056032142227470121262177112822018882754E3L,
+ 5.605349942234782054561269306895707034586E2L,
+ 1.561652599080729507274832243665726064881E1L
+ ];
+ immutable real[11] RDr6 = [
+ 2.522757606611479946069351519410222913326E1L,
+ 5.876797910931896554014229647006604017806E2L,
+ 5.211092128250480712011248211246144751074E3L,
+ 2.282679910855404599271496827409168580797E4L,
+ 5.371245819205596609986320599133109262447E4L,
+ 6.926186102106400355114925675028888924445E4L,
+ 4.794366033363621432575096487724913414473E4L,
+ 1.673190682734065914573814938835674963896E4L,
+ 2.589544846151313120096957014256536236242E3L,
+ 1.349438432583208276883323156200117027433E2L,
+ 1.0L
+ ];
+
+ // erfc(x) = exp(-x^2) 1/x R(1/x^2) / S(1/x^2)
+ // 1/2 <= 1/x < 5/8
+ // Peak relative error 4.3e-36
+ immutable real[11] RNr5 = [
+ 6.743447478279267339131211137241149796763E-2L,
+ 2.031339015932962998168472743355874796350E0L,
+ 2.369234815713196480221800940201367484379E1L,
+ 1.387819614016107433603101545594790875922E2L,
+ 4.435600256936515839937720907171966121786E2L,
+ 7.881577949936817507981170892417739733047E2L,
+ 7.615749099291545976179905281851765734680E2L,
+ 3.752484528663442467089606663006771157777E2L,
+ 8.279644286027145214308303292537009564726E1L,
+ 6.201462983413738162709722770960040042647E0L,
+ 6.649631608720062333043506249503378282697E-2L
+ ];
+ immutable real[11] RDr5 = [
+ 1.195244945161889822018178270706903972343E-1L,
+ 3.660216908153253021384862427197665991311E0L,
+ 4.373405883243078019655721779021995159854E1L,
+ 2.653305963056235008916733402786877121865E2L,
+ 8.921329790491152046318422124415895506335E2L,
+ 1.705552231555600759729260640562363304312E3L,
+ 1.832711109606893446763174603477244625325E3L,
+ 1.056823953275835649973998168744261083316E3L,
+ 2.975561981792909722126456997074344895584E2L,
+ 3.393149095158232521894537008472203487436E1L,
+ 1.0L
+ ];
+
+ // erfc(x) = exp(-x^2) 1/x R(1/x^2) / S(1/x^2)
+ // 3/8 <= 1/x < 1/2
+ // Peak relative error 1.8e-36
+ immutable real[11] RNr4 = [
+ 3.558685919236420073872459554885612994007E-2L,
+ 1.460223642496950651561817195253277924528E0L,
+ 2.379856746555189546876720308841066577268E1L,
+ 2.005205521246554860334064698817220160117E2L,
+ 9.533374928664989955629120027419490517596E2L,
+ 2.623024576994438336130421711314560425373E3L,
+ 4.126446434603735586340585027628851620886E3L,
+ 3.540675861596687801829655387867654030013E3L,
+ 1.506037084891064572653273761987617394259E3L,
+ 2.630715699182706745867272452228891752353E2L,
+ 1.202476629652900619635409242749750364878E1L
+ ];
+ immutable real[12] RDr4 = [
+ 6.307606561714590590399683184410336583739E-2L,
+ 2.619717051134271249293056836082721776665E0L,
+ 4.344441402681725017630451522968410844608E1L,
+ 3.752891116408399440953195184301023399176E2L,
+ 1.849305988804654653921972804388006355502E3L,
+ 5.358505261991675891835885654499883449403E3L,
+ 9.091890995405251314631428721090705475825E3L,
+ 8.731418313949291797856351745278287516416E3L,
+ 4.420211285043270337492325400764271868740E3L,
+ 1.031487363021856106882306509107923200832E3L,
+ 8.387036084846046121805145056040429461783E1L,
+ 1.0L
+ ];
+
+ // erfc(x) = exp(-x^2) 1/x R(1/x^2) / S(1/x^2)
+ // 1/4 <= 1/x < 3/8
+ // Peak relative error 8.1e-37
+ immutable real[12] RNr3 = [
+ 4.584481542956275354582319313040418316755E-5L,
+ 2.674923158288848442110883948437930349128E-3L,
+ 6.344232532055212248017211243740466847311E-2L,
+ 7.985145965992002744933550450451513513963E-1L,
+ 5.845078061888281665064746347663123946270E0L,
+ 2.566625318816866587482759497608029522596E1L,
+ 6.736225182343446605268837827950856640948E1L,
+ 1.021796460139598089409347761712730512053E2L,
+ 8.344336615515430530929955615400706619764E1L,
+ 3.207749011528249356283897356277376306967E1L,
+ 4.386185123863412086856423971695142026036E0L,
+ 8.971576448581208351826868348023528863856E-2L
+ ];
+ immutable real[12] RDr3 = [
+ 8.125781965218112303281657065320409661370E-5L,
+ 4.781806762611504685247817818428945295520E-3L,
+ 1.147785538413798317790357996845767614561E-1L,
+ 1.469285552007088106614218996464752307606E0L,
+ 1.101712261349880339221039938999124077650E1L,
+ 5.008507527095093413713171655268276861071E1L,
+ 1.383058691613468970486425146336829447433E2L,
+ 2.264114250278912520501010108736339599752E2L,
+ 2.081377197698598680576330179979996940039E2L,
+ 9.724438129690013609440151781601781137944E1L,
+ 1.907905050771832372089975877589291760121E1L,
+ 1.0L
+ ];
+
+ // erfc(x) = exp(-x^2) 1/x R(1/x^2) / S(1/x^2)
+ // 1/8 <= 1/x < 1/4
+ // Peak relative error 1.5e-36
+ immutable real[12] RNr2 = [
+ 6.928615158005256885698045840589513728399E-7L,
+ 5.616245938942075826026382337922413007879E-5L,
+ 1.871624980715261794832358438894219696113E-3L,
+ 3.349922063795792371642023765253747563009E-2L,
+ 3.531865233974349943956345502463135695834E-1L,
+ 2.264714157625072773976468825160906342360E0L,
+ 8.810720294489253776747319730638214883026E0L,
+ 2.014056685571655833019183248931442888437E1L,
+ 2.524586947657190747039554310814128743320E1L,
+ 1.520656940937208886246188940244581671609E1L,
+ 3.334145500790963675035841482334493680498E0L,
+ 1.122108380007109245896534245151140632457E-1L
+ ];
+ immutable real[12] RDr2 = [
+ 1.228065061824874795984937092427781089256E-6L,
+ 1.001593999520159167559129042893802235969E-4L,
+ 3.366527555699367241421450749821030974446E-3L,
+ 6.098626947195865254152265585991861150369E-2L,
+ 6.541547922508613985813189387198804660235E-1L,
+ 4.301130233305371976727117480925676583204E0L,
+ 1.737155892350891711527711121692994762909E1L,
+ 4.206892112110558214680649401236873828801E1L,
+ 5.787487996025016843403524261574779631219E1L,
+ 4.094047148590822715163181507813774861621E1L,
+ 1.230603930056944875836549716515643997094E1L,
+ 1.0L
+ ];
+
+ // erfc(x) = exp(-x^2) 1/x R(1/x^2) / S(1/x^2)
+ // 1/128 <= 1/x < 1/8
+ // Peak relative error 2.2e-36
+ immutable real[10] RNr1 = [
+ 1.293111801138199795250035229383033322539E-6L,
+ 9.785224720880980456171438759161161816706E-5L,
+ 2.932474396233212166056331430621176065943E-3L,
+ 4.496429595719847083917435337780697436921E-2L,
+ 3.805989855927720844877478869846718877846E-1L,
+ 1.789745532222426292126781724570152590071E0L,
+ 4.465737379634389318903237306594171764628E0L,
+ 5.268535822258082278401240171488850433767E0L,
+ 2.258357766807433839494276681092713991651E0L,
+ 1.504459334078750002966538036652860809497E-1L
+ ];
+ immutable real[10] RDr1 = [
+ 2.291980991578770070179177302906728371406E-6L,
+ 1.745845828808028552029674694534934620384E-4L,
+ 5.283248841982102317072923869576785278019E-3L,
+ 8.221212297078141470944454807434634848018E-2L,
+ 7.120500674861902950423510939060230945621E-1L,
+ 3.475435367560809622183983439133664598155E0L,
+ 9.243253391989233533874386043611304387113E0L,
+ 1.227894792475280941511758877318903197188E1L,
+ 6.789361410398841316638617624392719077724E0L,
+ 1.0L
+ ];
+
+ // erf(z+1) = erfConst + P(z)/Q(z)
+ // -.125 <= z <= 0
+ // Peak relative error 7.3e-36
+ immutable real erfConst = 0.845062911510467529296875L;
+ immutable real[9] TN2 = [
+ -4.088889697077485301010486931817357000235E1L,
+ 7.157046430681808553842307502826960051036E3L,
+ -2.191561912574409865550015485451373731780E3L,
+ 2.180174916555316874988981177654057337219E3L,
+ 2.848578658049670668231333682379720943455E2L,
+ 1.630362490952512836762810462174798925274E2L,
+ 6.317712353961866974143739396865293596895E0L,
+ 2.450441034183492434655586496522857578066E1L,
+ 5.127662277706787664956025545897050896203E-1L
+ ];
+ immutable real[10] TD2 = [
+ 1.731026445926834008273768924015161048885E4L,
+ 1.209682239007990370796112604286048173750E4L,
+ 1.160950290217993641320602282462976163857E4L,
+ 5.394294645127126577825507169061355698157E3L,
+ 2.791239340533632669442158497532521776093E3L,
+ 8.989365571337319032943005387378993827684E2L,
+ 2.974016493766349409725385710897298069677E2L,
+ 6.148192754590376378740261072533527271947E1L,
+ 1.178502892490738445655468927408440847480E1L,
+ 1.0L
+ ];
+
+ // erf(x) = x + x P(x^2)/Q(x^2)
+ // 0 <= x <= 7/8
+ // Peak relative error 1.8e-35
+ immutable real[9] TN1 = [
+ -3.858252324254637124543172907442106422373E10L,
+ 9.580319248590464682316366876952214879858E10L,
+ 1.302170519734879977595901236693040544854E10L,
+ 2.922956950426397417800321486727032845006E9L,
+ 1.764317520783319397868923218385468729799E8L,
+ 1.573436014601118630105796794840834145120E7L,
+ 4.028077380105721388745632295157816229289E5L,
+ 1.644056806467289066852135096352853491530E4L,
+ 3.390868480059991640235675479463287886081E1L
+ ];
+ immutable real[10] TD1 = [
+ -3.005357030696532927149885530689529032152E11L,
+ -1.342602283126282827411658673839982164042E11L,
+ -2.777153893355340961288511024443668743399E10L,
+ -3.483826391033531996955620074072768276974E9L,
+ -2.906321047071299585682722511260895227921E8L,
+ -1.653347985722154162439387878512427542691E7L,
+ -6.245520581562848778466500301865173123136E5L,
+ -1.402124304177498828590239373389110545142E4L,
+ -1.209368072473510674493129989468348633579E2L,
+ 1.0L
+ ];
+}
+else
+{
+ /* erfc(x) = exp(-x^2) 1/x R(1/x^2) / S(1/x^2)
+ 1/128 <= 1/x < 1/8
+ Peak relative error 1.9e-21 */
+ immutable real[5] R = [ 0x1.b9f6d8b78e22459ep-6, 0x1.1b84686b0a4ea43ap-1,
+ 0x1.b8f6aebe96000c2ap+1, 0x1.cb1dbedac27c8ec2p+2, 0x1.cf885f8f572a4c14p+1
+ ];
+
+ immutable real[6] S = [
+ 0x1.87ae3cae5f65eb5ep-5, 0x1.01616f266f306d08p+0, 0x1.a4abe0411eed6c22p+2,
+ 0x1.eac9ce3da600abaap+3, 0x1.5752a9ac2faebbccp+3, 1.0
+ ];
+
+ /* erf(x) = x P(x^2)/Q(x^2)
+ 0 <= x <= 1
+ Peak relative error 7.6e-23 */
+ immutable real[7] T = [ 0x1.0da01654d757888cp+20, 0x1.2eb7497bc8b4f4acp+17,
+ 0x1.79078c19530f72a8p+15, 0x1.4eaf2126c0b2c23p+11, 0x1.1f2ea81c9d272a2ep+8,
+ 0x1.59ca6e2d866e625p+2, 0x1.c188e0b67435faf4p-4
+ ];
+
+ immutable real[7] U = [ 0x1.dde6025c395ae34ep+19, 0x1.c4bc8b6235df35aap+18,
+ 0x1.8465900e88b6903ap+16, 0x1.855877093959ffdp+13, 0x1.e5c44395625ee358p+9,
+ 0x1.6a0fed103f1c68a6p+5, 1.0
+ ];
+}
+}
+
+/**
+ * Complementary error function
+ *
+ * erfc(x) = 1 - erf(x), and has high relative accuracy for
+ * values of x far from zero. (For values near zero, use erf(x)).
+ *
+ * 1 - erf(x) = 2/ $(SQRT)(&pi;)
+ * $(INTEGRAL x, $(INFINITY)) exp( - $(POWER t, 2)) dt
+ *
+ *
+ * For small x, erfc(x) = 1 - erf(x); otherwise rational
+ * approximations are computed.
+ *
+ * A special function expx2(x) is used to suppress error amplification
+ * in computing exp(-x^2).
+ */
+real erfc(real a)
+{
+ if (a == real.infinity)
+ return 0.0;
+ if (a == -real.infinity)
+ return 2.0;
+
+ immutable x = (a < 0.0) ? -a : a;
+
+ if (x < (isIEEEQuadruple ? 0.25 : 1.0))
+ return 1.0 - erf(a);
+
+ static if (isIEEEQuadruple)
+ {
+ if (x < 1.25)
+ {
+ real y;
+ final switch (cast(int)(8.0 * x))
+ {
+ case 2:
+ const z = x - 0.25;
+ y = C13b + z * rationalPoly(z, RNr13, RDr13);
+ y += C13a;
+ break;
+ case 3:
+ const z = x - 0.375;
+ y = C14b + z * rationalPoly(z, RNr14, RDr14);
+ y += C14a;
+ break;
+ case 4:
+ const z = x - 0.5;
+ y = C15b + z * rationalPoly(z, RNr15, RDr15);
+ y += C15a;
+ break;
+ case 5:
+ const z = x - 0.625;
+ y = C16b + z * rationalPoly(z, RNr16, RDr16);
+ y += C16a;
+ break;
+ case 6:
+ const z = x - 0.75;
+ y = C17b + z * rationalPoly(z, RNr17, RDr17);
+ y += C17a;
+ break;
+ case 7:
+ const z = x - 0.875;
+ y = C18b + z * rationalPoly(z, RNr18, RDr18);
+ y += C18a;
+ break;
+ case 8:
+ const z = x - 1.0;
+ y = C19b + z * rationalPoly(z, RNr19, RDr19);
+ y += C19a;
+ break;
+ case 9:
+ const z = x - 1.125;
+ y = C20b + z * rationalPoly(z, RNr20, RDr20);
+ y += C20a;
+ break;
+ }
+ if (a < 0.0)
+ y = 2.0 - y;
+ return y;
+ }
+ }
+
+ if (-a * a < -MAXLOG)
+ {
+ // underflow
+ if (a < 0.0) return 2.0;
+ else return 0.0;
+ }
+
+ real y;
+ immutable z = expx2(a, -1);
+
+ static if (isIEEEQuadruple)
+ {
+ y = z * erfce(x);
+ }
+ else
+ {
+ y = 1.0 / x;
+ if (x < 8.0)
+ y = z * rationalPoly(y, P, Q);
+ else
+ y = z * y * rationalPoly(y * y, R, S);
+ }
+
+ if (a < 0.0)
+ y = 2.0 - y;
+
+ if (y == 0.0)
+ {
+ // underflow
+ if (a < 0.0) return 2.0;
+ else return 0.0;
+ }
+
+ return y;
+}
+
+
+private {
+/* Exponentially scaled erfc function
+ exp(x^2) erfc(x)
+ valid for x > 1.
+ Use with normalDistribution and expx2. */
+static if (isIEEEQuadruple)
+{
+ real erfce(real x)
+ {
+ immutable z = 1.0L / (x * x);
+
+ real p;
+ switch (cast(int)(8.0 / x))
+ {
+ default:
+ case 0:
+ p = rationalPoly(z, RNr1, RDr1);
+ break;
+ case 1:
+ p = rationalPoly(z, RNr2, RDr2);
+ break;
+ case 2:
+ p = rationalPoly(z, RNr3, RDr3);
+ break;
+ case 3:
+ p = rationalPoly(z, RNr4, RDr4);
+ break;
+ case 4:
+ p = rationalPoly(z, RNr5, RDr5);
+ break;
+ case 5:
+ p = rationalPoly(z, RNr6, RDr6);
+ break;
+ case 6:
+ p = rationalPoly(z, RNr7, RDr7);
+ break;
+ case 7:
+ p = rationalPoly(z, RNr8, RDr8);
+ break;
+ }
+ return p / x;
+ }
+}
+else
+{
+ real erfce(real x)
+ {
+ real y = 1.0/x;
+
+ if (x < 8.0)
+ {
+ return rationalPoly(y, P, Q);
+ }
+ else
+ {
+ return y * rationalPoly(y * y, R, S);
+ }
+ }
+}
+}
+
+/**
+ * Error function
+ *
+ * The integral is
+ *
+ * erf(x) = 2/ $(SQRT)(&pi;)
+ * $(INTEGRAL 0, x) exp( - $(POWER t, 2)) dt
+ *
+ * The magnitude of x is limited to about 106.56 for IEEE 80-bit
+ * arithmetic; 1 or -1 is returned outside this range.
+ *
+ * For 0 <= |x| < 1, a rational polynomials are used; otherwise
+ * erf(x) = 1 - erfc(x).
+ *
+ * ACCURACY:
+ * Relative error:
+ * arithmetic domain # trials peak rms
+ * IEEE 0,1 50000 2.0e-19 5.7e-20
+ */
+real erf(real x)
+{
+ if (x == 0.0)
+ return x; // deal with negative zero
+ if (x == -real.infinity)
+ return -1.0;
+ if (x == real.infinity)
+ return 1.0;
+ immutable ax = abs(x);
+ if (ax > 1.0L)
+ return 1.0L - erfc(x);
+
+ static if (isIEEEQuadruple)
+ {
+ immutable z = x * x;
+
+ real y;
+ if (ax < 0.875)
+ {
+ y = ax + ax * rationalPoly(x * x, TN1, TD1);
+ }
+ else
+ {
+ y = erfConst + rationalPoly(ax - 1.0L, TN2, TD2);
+ }
+
+ if (x < 0)
+ y = -y;
+ return y;
+ }
+ else
+ {
+ real z = x * x;
+ return x * rationalPoly(x * x, T, U);
+ }
+}
+
+@safe unittest
+{
+ // High resolution test points.
+ enum real erfc0_250 = 0.723663330078125 + 1.0279753638067014931732235184287934646022E-5;
+ enum real erfc0_375 = 0.5958709716796875 + 1.2118885490201676174914080878232469565953E-5;
+ enum real erfc0_500 = 0.4794921875 + 7.9346869534623172533461080354712635484242E-6;
+ enum real erfc0_625 = 0.3767547607421875 + 4.3570693945275513594941232097252997287766E-6;
+ enum real erfc0_750 = 0.2888336181640625 + 1.0748182422368401062165408589222625794046E-5;
+ enum real erfc0_875 = 0.215911865234375 + 1.3073705765341685464282101150637224028267E-5;
+ enum real erfc1_000 = 0.15728759765625 + 1.1609394035130658779364917390740703933002E-5;
+ enum real erfc1_125 = 0.111602783203125 + 8.9850951672359304215530728365232161564636E-6;
+
+ 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);
+ assert(feqrel(erfc(0.500L), erfc0_500 )>=real.mant_dig-2);
+ assert(feqrel(erfc(0.625L), erfc0_625 )>=real.mant_dig-1);
+ assert(feqrel(erfc(0.750L), erfc0_750 )>=real.mant_dig-1);
+ assert(feqrel(erfc(0.875L), erfc0_875 )>=real.mant_dig-4);
+ assert(feqrel(erfc(1.000L), erfc1_000 )>=real.mant_dig-2);
+ assert(feqrel(erfc(1.125L), erfc1_125 )>=real.mant_dig-2);
+ assert(feqrel(erf(0.875L), erf0_875 )>=real.mant_dig-1);
+ // The DMC implementation of erfc() fails this next test (just).
+ // Test point from Mathematica 11.0.
+ assert(feqrel(erfc(4.1L), 6.70002765408489837272673380763418472e-9L) >= real.mant_dig-5);
+
+ assert(isIdentical(erf(0.0),0.0));
+ assert(isIdentical(erf(-0.0),-0.0));
+ assert(erf(real.infinity) == 1.0);
+ assert(erf(-real.infinity) == -1.0);
+ 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);
+}
+
+/*
+ * Exponential of squared argument
+ *
+ * Computes y = exp(x*x) while suppressing error amplification
+ * that would ordinarily arise from the inexactness of the
+ * exponential argument x*x.
+ *
+ * If sign < 0, the result is inverted; i.e., y = exp(-x*x) .
+ *
+ * ACCURACY:
+ * Relative error:
+ * arithmetic domain # trials peak rms
+ * IEEE -106.566, 106.566 10^5 1.6e-19 4.4e-20
+ */
+
+real expx2(real x, int sign)
+{
+ /*
+ Cephes Math Library Release 2.9: June, 2000
+ Copyright 2000 by Stephen L. Moshier
+ */
+ const real M = 32_768.0;
+ const real MINV = 3.0517578125e-5L;
+
+ x = abs(x);
+ if (sign < 0)
+ x = -x;
+
+ /* Represent x as an exact multiple of M plus a residual.
+ M is a power of 2 chosen so that exp(m * m) does not overflow
+ or underflow and so that |x - m| is small. */
+ real m = MINV * floor(M * x + 0.5L);
+ real f = x - m;
+
+ /* x^2 = m^2 + 2mf + f^2 */
+ real u = m * m;
+ real u1 = 2 * m * f + f * f;
+
+ if (sign < 0)
+ {
+ u = -u;
+ u1 = -u1;
+ }
+
+ if ((u+u1) > MAXLOG)
+ return real.infinity;
+
+ /* u is exact, u1 is small. */
+ return exp(u) * exp(u1);
+}
+
+
+/*
+Computes the normal distribution function.
+
+The normal (or Gaussian, or bell-shaped) distribution is
+defined as:
+
+normalDist(x) = 1/$(SQRT) &pi; $(INTEGRAL -$(INFINITY), x) exp( - $(POWER t, 2)/2) dt
+ = 0.5 + 0.5 * erf(x/sqrt(2))
+ = 0.5 * erfc(- x/sqrt(2))
+
+To maintain accuracy at high values of x, use
+normalDistribution(x) = 1 - normalDistribution(-x).
+
+Accuracy:
+Within a few bits of machine resolution over the entire
+range.
+
+References:
+$(LINK http://www.netlib.org/cephes/ldoubdoc.html),
+G. Marsaglia, "Evaluating the Normal Distribution",
+Journal of Statistical Software <b>11</b>, (July 2004).
+*/
+real normalDistributionImpl(real a)
+{
+ real x = a * SQRT1_2;
+ real z = abs(x);
+
+ if ( z < 1.0 )
+ return 0.5L + 0.5L * erf(x);
+ 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 )
+ y = 1.0L - y;
+ return y;
+ }
+}
+
+@safe unittest
+{
+assert(fabs(normalDistributionImpl(1L) - (0.841344746068543))< 0.0000000000000005);
+assert(isIdentical(normalDistributionImpl(NaN(0x325)), NaN(0x325)));
+}
+
+/*
+ * Inverse of Normal distribution function
+ *
+ * Returns the argument, x, for which the area under the
+ * Normal probability density function (integrated from
+ * minus infinity to x) is equal to p.
+ *
+ * For small arguments 0 < p < exp(-2), the program computes
+ * z = sqrt( -2 log(p) ); then the approximation is
+ * x = z - log(z)/z - (1/z) P(1/z) / Q(1/z) .
+ * For larger arguments, x/sqrt(2 pi) = w + w^3 R(w^2)/S(w^2)) ,
+ * where w = p - 0.5.
+ */
+// TODO: isIEEEQuadruple (128 bit) real implementation; not available from CEPHES.
+real normalDistributionInvImpl(real p)
+in {
+ assert(p >= 0.0L && p <= 1.0L, "Domain error");
+}
+body
+{
+static immutable real[8] P0 =
+[ -0x1.758f4d969484bfdcp-7, 0x1.53cee17a59259dd2p-3,
+ -0x1.ea01e4400a9427a2p-1, 0x1.61f7504a0105341ap+1, -0x1.09475a594d0399f6p+2,
+ 0x1.7c59e7a0df99e3e2p+1, -0x1.87a81da52edcdf14p-1, 0x1.1fb149fd3f83600cp-7
+];
+
+static immutable real[8] Q0 =
+[ -0x1.64b92ae791e64bb2p-7, 0x1.7585c7d597298286p-3,
+ -0x1.40011be4f7591ce6p+0, 0x1.1fc067d8430a425ep+2, -0x1.21008ffb1e7ccdf2p+3,
+ 0x1.3d1581cf9bc12fccp+3, -0x1.53723a89fd8f083cp+2, 1.0
+];
+
+static immutable real[10] P1 =
+[ 0x1.20ceea49ea142f12p-13, 0x1.cbe8a7267aea80bp-7,
+ 0x1.79fea765aa787c48p-2, 0x1.d1f59faa1f4c4864p+1, 0x1.1c22e426a013bb96p+4,
+ 0x1.a8675a0c51ef3202p+5, 0x1.75782c4f83614164p+6, 0x1.7a2f3d90948f1666p+6,
+ 0x1.5cd116ee4c088c3ap+5, 0x1.1361e3eb6e3cc20ap+2
+];
+
+static immutable real[10] Q1 =
+[ 0x1.3a4ce1406cea98fap-13, 0x1.f45332623335cda2p-7,
+ 0x1.98f28bbd4b98db1p-2, 0x1.ec3b24f9c698091cp+1, 0x1.1cc56ecda7cf58e4p+4,
+ 0x1.92c6f7376bf8c058p+5, 0x1.4154c25aa47519b4p+6, 0x1.1b321d3b927849eap+6,
+ 0x1.403a5f5a4ce7b202p+4, 1.0
+];
+
+static immutable real[8] P2 =
+[ 0x1.8c124a850116a6d8p-21, 0x1.534abda3c2fb90bap-13,
+ 0x1.29a055ec93a4718cp-7, 0x1.6468e98aad6dd474p-3, 0x1.3dab2ef4c67a601cp+0,
+ 0x1.e1fb3a1e70c67464p+1, 0x1.b6cce8035ff57b02p+2, 0x1.9f4c9e749ff35f62p+1
+];
+
+static immutable real[8] Q2 =
+[ 0x1.af03f4fc0655e006p-21, 0x1.713192048d11fb2p-13,
+ 0x1.4357e5bbf5fef536p-7, 0x1.7fdac8749985d43cp-3, 0x1.4a080c813a2d8e84p+0,
+ 0x1.c3a4b423cdb41bdap+1, 0x1.8160694e24b5557ap+2, 1.0
+];
+
+static immutable real[8] P3 =
+[ -0x1.55da447ae3806168p-34, -0x1.145635641f8778a6p-24,
+ -0x1.abf46d6b48040128p-17, -0x1.7da550945da790fcp-11, -0x1.aa0b2a31157775fap-8,
+ 0x1.b11d97522eed26bcp-3, 0x1.1106d22f9ae89238p+1, 0x1.029a358e1e630f64p+1
+];
+
+static immutable real[8] Q3 =
+[ -0x1.74022dd5523e6f84p-34, -0x1.2cb60d61e29ee836p-24,
+ -0x1.d19e6ec03a85e556p-17, -0x1.9ea2a7b4422f6502p-11, -0x1.c54b1e852f107162p-8,
+ 0x1.e05268dd3c07989ep-3, 0x1.239c6aff14afbf82p+1, 1.0
+];
+
+ if (p <= 0.0L || p >= 1.0L)
+ {
+ if (p == 0.0L)
+ return -real.infinity;
+ if ( p == 1.0L )
+ return real.infinity;
+ return real.nan; // domain error
+ }
+ int code = 1;
+ real y = p;
+ if ( y > (1.0L - EXP_2) )
+ {
+ y = 1.0L - y;
+ code = 0;
+ }
+
+ real x, z, y2, x0, x1;
+
+ if ( y > EXP_2 )
+ {
+ y = y - 0.5L;
+ y2 = y * y;
+ x = y + y * (y2 * rationalPoly( y2, P0, Q0));
+ return x * SQRT2PI;
+ }
+
+ x = sqrt( -2.0L * log(y) );
+ x0 = x - log(x)/x;
+ z = 1.0L/x;
+ if ( x < 8.0L )
+ {
+ x1 = z * rationalPoly( z, P1, Q1);
+ }
+ else if ( x < 32.0L )
+ {
+ x1 = z * rationalPoly( z, P2, Q2);
+ }
+ else
+ {
+ x1 = z * rationalPoly( z, P3, Q3);
+ }
+ x = x0 - x1;
+ if ( code != 0 )
+ {
+ x = -x;
+ }
+ return x;
+}
+
+
+@safe unittest
+{
+ // TODO: Use verified test points.
+ // The values below are from Excel 2003.
+ assert(fabs(normalDistributionInvImpl(0.001) - (-3.09023230616779))< 0.00000000000005);
+ assert(fabs(normalDistributionInvImpl(1e-50) - (-14.9333375347885))< 0.00000000000005);
+ assert(feqrel(normalDistributionInvImpl(0.999), -normalDistributionInvImpl(0.001)) > real.mant_dig-6);
+
+ // Excel 2003 gets all the following values wrong!
+ assert(normalDistributionInvImpl(0.0) == -real.infinity);
+ assert(normalDistributionInvImpl(1.0) == real.infinity);
+ assert(normalDistributionInvImpl(0.5) == 0);
+ // (Excel 2003 returns norminv(p) = -30 for all p < 1e-200).
+ // The value tested here is the one the function returned in Jan 2006.
+ real unknown1 = normalDistributionInvImpl(1e-250L);
+ assert( fabs(unknown1 -(-33.79958617269L) ) < 0.00000005);
+}
diff --git a/libphobos/src/std/internal/math/gammafunction.d b/libphobos/src/std/internal/math/gammafunction.d
new file mode 100644
index 0000000..dd20691
--- /dev/null
+++ b/libphobos/src/std/internal/math/gammafunction.d
@@ -0,0 +1,1834 @@
+/**
+ * Implementation of the gamma and beta functions, and their integrals.
+ *
+ * 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 border="1" cellpadding="4" cellspacing="0">
+ * <caption>Special Values</caption>
+ * $0</table>
+ * SVH = $(TR $(TH $1) $(TH $2))
+ * SV = $(TR $(TD $1) $(TD $2))
+ * GAMMA = &#915;
+ * INTEGRATE = $(BIG &#8747;<sub>$(SMALL $1)</sub><sup>$2</sup>)
+ * POWER = $1<sup>$2</sup>
+ * NAN = $(RED NAN)
+ */
+module std.internal.math.gammafunction;
+import std.internal.math.errorfunction;
+import std.math;
+
+pure:
+nothrow:
+@safe:
+@nogc:
+
+private {
+
+enum real SQRT2PI = 2.50662827463100050242E0L; // sqrt(2pi)
+immutable real EULERGAMMA = 0.57721_56649_01532_86060_65120_90082_40243_10421_59335_93992L; /** Euler-Mascheroni constant 0.57721566.. */
+
+// Polynomial approximations for gamma and loggamma.
+
+immutable real[8] GammaNumeratorCoeffs = [ 1.0,
+ 0x1.acf42d903366539ep-1, 0x1.73a991c8475f1aeap-2, 0x1.c7e918751d6b2a92p-4,
+ 0x1.86d162cca32cfe86p-6, 0x1.0c378e2e6eaf7cd8p-8, 0x1.dc5c66b7d05feb54p-12,
+ 0x1.616457b47e448694p-15
+];
+
+immutable real[9] GammaDenominatorCoeffs = [ 1.0,
+ 0x1.a8f9faae5d8fc8bp-2, -0x1.cb7895a6756eebdep-3, -0x1.7b9bab006d30652ap-5,
+ 0x1.c671af78f312082ep-6, -0x1.a11ebbfaf96252dcp-11, -0x1.447b4d2230a77ddap-10,
+ 0x1.ec1d45bb85e06696p-13,-0x1.d4ce24d05bd0a8e6p-17
+];
+
+immutable real[9] GammaSmallCoeffs = [ 1.0,
+ 0x1.2788cfc6fb618f52p-1, -0x1.4fcf4026afa2f7ecp-1, -0x1.5815e8fa24d7e306p-5,
+ 0x1.5512320aea2ad71ap-3, -0x1.59af0fb9d82e216p-5, -0x1.3b4b61d3bfdf244ap-7,
+ 0x1.d9358e9d9d69fd34p-8, -0x1.38fc4bcbada775d6p-10
+];
+
+immutable real[9] GammaSmallNegCoeffs = [ -1.0,
+ 0x1.2788cfc6fb618f54p-1, 0x1.4fcf4026afa2bc4cp-1, -0x1.5815e8fa2468fec8p-5,
+ -0x1.5512320baedaf4b6p-3, -0x1.59af0fa283baf07ep-5, 0x1.3b4a70de31e05942p-7,
+ 0x1.d9398be3bad13136p-8, 0x1.291b73ee05bcbba2p-10
+];
+
+immutable real[7] logGammaStirlingCoeffs = [
+ 0x1.5555555555553f98p-4, -0x1.6c16c16c07509b1p-9, 0x1.a01a012461cbf1e4p-11,
+ -0x1.3813089d3f9d164p-11, 0x1.b911a92555a277b8p-11, -0x1.ed0a7b4206087b22p-10,
+ 0x1.402523859811b308p-8
+];
+
+immutable real[7] logGammaNumerator = [
+ -0x1.0edd25913aaa40a2p+23, -0x1.31c6ce2e58842d1ep+24, -0x1.f015814039477c3p+23,
+ -0x1.74ffe40c4b184b34p+22, -0x1.0d9c6d08f9eab55p+20, -0x1.54c6b71935f1fc88p+16,
+ -0x1.0e761b42932b2aaep+11
+];
+
+immutable real[8] logGammaDenominator = [
+ -0x1.4055572d75d08c56p+24, -0x1.deeb6013998e4d76p+24, -0x1.106f7cded5dcc79ep+24,
+ -0x1.25e17184848c66d2p+22, -0x1.301303b99a614a0ap+19, -0x1.09e76ab41ae965p+15,
+ -0x1.00f95ced9e5f54eep+9, 1.0
+];
+
+/*
+ * Helper function: Gamma function computed by Stirling's formula.
+ *
+ * Stirling's formula for the gamma function is:
+ *
+ * $(GAMMA)(x) = sqrt(2 &pi;) x<sup>x-0.5</sup> exp(-x) (1 + 1/x P(1/x))
+ *
+ */
+real gammaStirling(real x)
+{
+ // CEPHES code Copyright 1994 by Stephen L. Moshier
+
+ static immutable real[9] SmallStirlingCoeffs = [
+ 0x1.55555555555543aap-4, 0x1.c71c71c720dd8792p-9, -0x1.5f7268f0b5907438p-9,
+ -0x1.e13cd410e0477de6p-13, 0x1.9b0f31643442616ep-11, 0x1.2527623a3472ae08p-14,
+ -0x1.37f6bc8ef8b374dep-11,-0x1.8c968886052b872ap-16, 0x1.76baa9c6d3eeddbcp-11
+ ];
+
+ static immutable real[7] LargeStirlingCoeffs = [ 1.0L,
+ 8.33333333333333333333E-2L, 3.47222222222222222222E-3L,
+ -2.68132716049382716049E-3L, -2.29472093621399176955E-4L,
+ 7.84039221720066627474E-4L, 6.97281375836585777429E-5L
+ ];
+
+ real w = 1.0L/x;
+ real y = exp(x);
+ 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
+ {
+ w = 1.0L + w * poly( w, SmallStirlingCoeffs);
+ 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;
+}
+
+/*
+ * Helper function: Incomplete gamma function computed by Temme's expansion.
+ *
+ * This is a port of igamma_temme_large from Boost.
+ *
+ */
+real igammaTemmeLarge(real a, real x)
+{
+ static immutable real[][13] coef = [
+ [ -0.333333333333333333333, 0.0833333333333333333333,
+ -0.0148148148148148148148, 0.00115740740740740740741,
+ 0.000352733686067019400353, -0.0001787551440329218107,
+ 0.39192631785224377817e-4, -0.218544851067999216147e-5,
+ -0.18540622107151599607e-5, 0.829671134095308600502e-6,
+ -0.176659527368260793044e-6, 0.670785354340149858037e-8,
+ 0.102618097842403080426e-7, -0.438203601845335318655e-8,
+ 0.914769958223679023418e-9, -0.255141939949462497669e-10,
+ -0.583077213255042506746e-10, 0.243619480206674162437e-10,
+ -0.502766928011417558909e-11 ],
+ [ -0.00185185185185185185185, -0.00347222222222222222222,
+ 0.00264550264550264550265, -0.000990226337448559670782,
+ 0.000205761316872427983539, -0.40187757201646090535e-6,
+ -0.18098550334489977837e-4, 0.764916091608111008464e-5,
+ -0.161209008945634460038e-5, 0.464712780280743434226e-8,
+ 0.137863344691572095931e-6, -0.575254560351770496402e-7,
+ 0.119516285997781473243e-7, -0.175432417197476476238e-10,
+ -0.100915437106004126275e-8, 0.416279299184258263623e-9,
+ -0.856390702649298063807e-10 ],
+ [ 0.00413359788359788359788, -0.00268132716049382716049,
+ 0.000771604938271604938272, 0.200938786008230452675e-5,
+ -0.000107366532263651605215, 0.529234488291201254164e-4,
+ -0.127606351886187277134e-4, 0.342357873409613807419e-7,
+ 0.137219573090629332056e-5, -0.629899213838005502291e-6,
+ 0.142806142060642417916e-6, -0.204770984219908660149e-9,
+ -0.140925299108675210533e-7, 0.622897408492202203356e-8,
+ -0.136704883966171134993e-8 ],
+ [ 0.000649434156378600823045, 0.000229472093621399176955,
+ -0.000469189494395255712128, 0.000267720632062838852962,
+ -0.756180167188397641073e-4, -0.239650511386729665193e-6,
+ 0.110826541153473023615e-4, -0.56749528269915965675e-5,
+ 0.142309007324358839146e-5, -0.278610802915281422406e-10,
+ -0.169584040919302772899e-6, 0.809946490538808236335e-7,
+ -0.191111684859736540607e-7 ],
+ [ -0.000861888290916711698605, 0.000784039221720066627474,
+ -0.000299072480303190179733, -0.146384525788434181781e-5,
+ 0.664149821546512218666e-4, -0.396836504717943466443e-4,
+ 0.113757269706784190981e-4, 0.250749722623753280165e-9,
+ -0.169541495365583060147e-5, 0.890750753220530968883e-6,
+ -0.229293483400080487057e-6],
+ [ -0.000336798553366358150309, -0.697281375836585777429e-4,
+ 0.000277275324495939207873, -0.000199325705161888477003,
+ 0.679778047793720783882e-4, 0.141906292064396701483e-6,
+ -0.135940481897686932785e-4, 0.801847025633420153972e-5,
+ -0.229148117650809517038e-5 ],
+ [ 0.000531307936463992223166, -0.000592166437353693882865,
+ 0.000270878209671804482771, 0.790235323266032787212e-6,
+ -0.815396936756196875093e-4, 0.561168275310624965004e-4,
+ -0.183291165828433755673e-4, -0.307961345060330478256e-8,
+ 0.346515536880360908674e-5, -0.20291327396058603727e-5,
+ 0.57887928631490037089e-6 ],
+ [ 0.000344367606892377671254, 0.517179090826059219337e-4,
+ -0.000334931610811422363117, 0.000281269515476323702274,
+ -0.000109765822446847310235, -0.127410090954844853795e-6,
+ 0.277444515115636441571e-4, -0.182634888057113326614e-4,
+ 0.578769494973505239894e-5 ],
+ [ -0.000652623918595309418922, 0.000839498720672087279993,
+ -0.000438297098541721005061, -0.696909145842055197137e-6,
+ 0.000166448466420675478374, -0.000127835176797692185853,
+ 0.462995326369130429061e-4 ],
+ [ -0.000596761290192746250124, -0.720489541602001055909e-4,
+ 0.000678230883766732836162, -0.0006401475260262758451,
+ 0.000277501076343287044992 ],
+ [ 0.00133244544948006563713, -0.0019144384985654775265,
+ 0.00110893691345966373396 ],
+ [ 0.00157972766073083495909, 0.000162516262783915816899,
+ -0.00206334210355432762645, 0.00213896861856890981541,
+ -0.00101085593912630031708 ],
+ [ -0.00407251211951401664727, 0.00640336283380806979482,
+ -0.00404101610816766177474 ]
+ ];
+
+ // avoid nans when one of the arguments is inf:
+ if (x == real.infinity && a != real.infinity)
+ return 0;
+
+ if (x != real.infinity && a == real.infinity)
+ return 1;
+
+ real sigma = (x - a) / a;
+ real phi = sigma - log(sigma + 1);
+
+ real y = a * phi;
+ real z = sqrt(2 * phi);
+ if (x < a)
+ z = -z;
+
+ real[13] workspace;
+ 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)
+ result = -result;
+
+ result += erfc(sqrt(y)) / 2;
+
+ return result;
+}
+
+} // private
+
+public:
+/// The maximum value of x for which gamma(x) < real.infinity.
+static if (floatTraits!(real).realFormat == RealFormat.ieeeQuadruple)
+ enum real MAXGAMMA = 1755.5483429L;
+else 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");
+
+
+/*****************************************************
+ * The Gamma function, $(GAMMA)(x)
+ *
+ * $(GAMMA)(x) is a generalisation of the factorial function
+ * to real and complex numbers.
+ * Like x!, $(GAMMA)(x+1) = x*$(GAMMA)(x).
+ *
+ * Mathematically, if z.re > 0 then
+ * $(GAMMA)(z) = $(INTEGRATE 0, &infin;) $(POWER t, z-1)$(POWER e, -t) dt
+ *
+ * $(TABLE_SV
+ * $(SVH x, $(GAMMA)(x) )
+ * $(SV $(NAN), $(NAN) )
+ * $(SV &plusmn;0.0, &plusmn;&infin;)
+ * $(SV integer > 0, (x-1)! )
+ * $(SV integer < 0, $(NAN) )
+ * $(SV +&infin;, +&infin; )
+ * $(SV -&infin;, $(NAN) )
+ * )
+ */
+real gamma(real x)
+{
+/* Based on code from the CEPHES library.
+ * CEPHES code Copyright 1994 by Stephen L. Moshier
+ *
+ * Arguments |x| <= 13 are reduced by recurrence and the function
+ * approximated by a rational function of degree 7/8 in the
+ * interval (2,3). Large arguments are handled by Stirling's
+ * formula. Large negative arguments are made positive using
+ * a reflection formula.
+ */
+
+ real q, z;
+ 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.
+
+ q = fabs(x);
+
+ 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 < -1/real.epsilon)
+ {
+ // Large negatives lose all precision
+ return real.nan;
+ }
+ int sgngam = 1; // sign of gamma.
+ long intpart = cast(long)(q);
+ if (q == intpart)
+ return real.nan; // poles for all integers <0.
+ real p = intpart;
+ if ( (intpart & 1) == 0 )
+ sgngam = -1;
+ z = q - p;
+ if ( z > 0.5L )
+ {
+ p += 1.0L;
+ z = q - p;
+ }
+ z = q * sin( PI * z );
+ z = fabs(z) * gammaStirling(q);
+ if ( z <= PI/real.max ) return sgngam * real.infinity;
+ return sgngam * PI/z;
+ }
+ else
+ {
+ return gammaStirling(x);
+ }
+ }
+
+ // Arguments |x| <= 13 are reduced by recurrence and the function
+ // approximated by a rational function of degree 7/8 in the
+ // interval (2,3).
+
+ z = 1.0L;
+ while ( x >= 3.0L )
+ {
+ x -= 1.0L;
+ z *= x;
+ }
+
+ while ( x < -0.03125L )
+ {
+ z /= x;
+ x += 1.0L;
+ }
+
+ if ( x <= 0.03125L )
+ {
+ if ( x == 0.0L )
+ return real.nan;
+ else
+ {
+ if ( x < 0.0L )
+ {
+ x = -x;
+ return z / (x * poly( x, GammaSmallNegCoeffs ));
+ }
+ else
+ {
+ return z / (x * poly( x, GammaSmallCoeffs ));
+ }
+ }
+ }
+
+ while ( x < 2.0L )
+ {
+ z /= x;
+ x += 1.0L;
+ }
+ if ( x == 2.0L ) return z;
+
+ x -= 2.0L;
+ return z * poly( x, GammaNumeratorCoeffs ) / poly( x, GammaDenominatorCoeffs );
+}
+
+@safe unittest
+{
+ // gamma(n) = factorial(n-1) if n is an integer.
+ real fact = 1.0L;
+ for (int i=1; fact<real.max; ++i)
+ {
+ // Require exact equality for small factorials
+ if (i<14) assert(gamma(i*1.0L) == fact);
+ assert(feqrel(gamma(i*1.0L), fact) >= real.mant_dig-15);
+ fact *= (i*1.0L);
+ }
+ assert(gamma(0.0) == real.infinity);
+ assert(gamma(-0.0) == -real.infinity);
+ assert(isNaN(gamma(-1.0)));
+ assert(isNaN(gamma(-15.0)));
+ assert(isIdentical(gamma(NaN(0xABC)), NaN(0xABC)));
+ assert(gamma(real.infinity) == real.infinity);
+ assert(gamma(real.max) == real.infinity);
+ assert(isNaN(gamma(-real.infinity)));
+ assert(gamma(real.min_normal*real.epsilon) == real.infinity);
+ assert(gamma(MAXGAMMA)< real.infinity);
+ assert(gamma(MAXGAMMA*2) == real.infinity);
+
+ // Test some high-precision values (50 decimal digits)
+ real SQRT_PI = 1.77245385090551602729816748334114518279754945612238L;
+
+
+ assert(feqrel(gamma(0.5L), SQRT_PI) >= real.mant_dig-1);
+ assert(feqrel(gamma(17.25L), 4.224986665692703551570937158682064589938e13L) >= real.mant_dig-4);
+
+ assert(feqrel(gamma(1.0 / 3.0L), 2.67893853470774763365569294097467764412868937795730L) >= real.mant_dig-2);
+ assert(feqrel(gamma(0.25L),
+ 3.62560990822190831193068515586767200299516768288006L) >= real.mant_dig-1);
+ assert(feqrel(gamma(1.0 / 5.0L),
+ 4.59084371199880305320475827592915200343410999829340L) >= real.mant_dig-1);
+}
+
+/*****************************************************
+ * Natural logarithm of gamma function.
+ *
+ * Returns the base e (2.718...) logarithm of the absolute
+ * value of the gamma function of the argument.
+ *
+ * For reals, logGamma is equivalent to log(fabs(gamma(x))).
+ *
+ * $(TABLE_SV
+ * $(SVH x, logGamma(x) )
+ * $(SV $(NAN), $(NAN) )
+ * $(SV integer <= 0, +&infin; )
+ * $(SV &plusmn;&infin;, +&infin; )
+ * )
+ */
+real logGamma(real x)
+{
+ /* Based on code from the CEPHES library.
+ * CEPHES code Copyright 1994 by Stephen L. Moshier
+ *
+ * For arguments greater than 33, the logarithm of the gamma
+ * function is approximated by the logarithmic version of
+ * Stirling's formula using a polynomial approximation of
+ * degree 4. Arguments between -33 and +33 are reduced by
+ * recurrence to the interval [2,3] of a rational approximation.
+ * The cosecant reflection formula is employed for arguments
+ * less than -33.
+ */
+ real q, w, z, f, nx;
+
+ if (isNaN(x)) return x;
+ if (fabs(x) == x.infinity) return x.infinity;
+
+ if ( x < -34.0L )
+ {
+ q = -x;
+ w = logGamma(q);
+ real p = floor(q);
+ if ( p == q )
+ return real.infinity;
+ int intpart = cast(int)(p);
+ real sgngam = 1;
+ if ( (intpart & 1) == 0 )
+ sgngam = -1;
+ z = q - p;
+ if ( z > 0.5L )
+ {
+ p += 1.0L;
+ z = p - q;
+ }
+ z = q * sin( PI * z );
+ if ( z == 0.0L )
+ return sgngam * real.infinity;
+ /* z = LOGPI - logl( z ) - w; */
+ z = log( PI/z ) - w;
+ return z;
+ }
+
+ if ( x < 13.0L )
+ {
+ z = 1.0L;
+ nx = floor( x + 0.5L );
+ f = x - nx;
+ while ( x >= 3.0L )
+ {
+ nx -= 1.0L;
+ x = nx + f;
+ z *= x;
+ }
+ while ( x < 2.0L )
+ {
+ if ( fabs(x) <= 0.03125 )
+ {
+ if ( x == 0.0L )
+ return real.infinity;
+ if ( x < 0.0L )
+ {
+ x = -x;
+ q = z / (x * poly( x, GammaSmallNegCoeffs));
+ } else
+ q = z / (x * poly( x, GammaSmallCoeffs));
+ return log( fabs(q) );
+ }
+ z /= nx + f;
+ nx += 1.0L;
+ x = nx + f;
+ }
+ z = fabs(z);
+ if ( x == 2.0L )
+ return log(z);
+ x = (nx - 2.0L) + f;
+ real p = x * rationalPoly( x, logGammaNumerator, logGammaDenominator);
+ return log(z) + p;
+ }
+
+ // const real MAXLGM = 1.04848146839019521116e+4928L;
+ // if ( x > MAXLGM ) return sgngaml * real.infinity;
+
+ const real LOGSQRT2PI = 0.91893853320467274178L; // log( sqrt( 2*pi ) )
+
+ q = ( x - 0.5L ) * log(x) - x + LOGSQRT2PI;
+ if (x > 1.0e10L) return q;
+ real p = 1.0L / (x*x);
+ q += poly( p, logGammaStirlingCoeffs ) / x;
+ return q ;
+}
+
+@safe unittest
+{
+ assert(isIdentical(logGamma(NaN(0xDEF)), NaN(0xDEF)));
+ assert(logGamma(real.infinity) == real.infinity);
+ assert(logGamma(-1.0) == real.infinity);
+ assert(logGamma(0.0) == real.infinity);
+ assert(logGamma(-50.0) == real.infinity);
+ assert(isIdentical(0.0L, logGamma(1.0L)));
+ assert(isIdentical(0.0L, logGamma(2.0L)));
+ assert(logGamma(real.min_normal*real.epsilon) == real.infinity);
+ assert(logGamma(-real.min_normal*real.epsilon) == real.infinity);
+
+ // x, correct loggamma(x), correct d/dx loggamma(x).
+ immutable static real[] testpoints = [
+ 8.0L, 8.525146484375L + 1.48766904143001655310E-5, 2.01564147795560999654E0L,
+ 8.99993896484375e-1L, 6.6375732421875e-2L + 5.11505711292524166220E-6L, -7.54938684259372234258E-1,
+ 7.31597900390625e-1L, 2.2369384765625e-1 + 5.21506341809849792422E-6L, -1.13355566660398608343E0L,
+ 2.31639862060546875e-1L, 1.3686676025390625L + 1.12609441752996145670E-5L, -4.56670961813812679012E0,
+ 1.73162841796875L, -8.88214111328125e-2L + 3.36207740803753034508E-6L, 2.33339034686200586920E-1L,
+ 1.23162841796875L, -9.3902587890625e-2L + 1.28765089229009648104E-5L, -2.49677345775751390414E-1L,
+ 7.3786976294838206464e19L, 3.301798506038663053312e21L - 1.656137564136932662487046269677E5L,
+ 4.57477139169563904215E1L,
+ 1.08420217248550443401E-19L, 4.36682586669921875e1L + 1.37082843669932230418E-5L,
+ -9.22337203685477580858E18L,
+ 1.0L, 0.0L, -5.77215664901532860607E-1L,
+ 2.0L, 0.0L, 4.22784335098467139393E-1L,
+ -0.5L, 1.2655029296875L + 9.19379714539648894580E-6L, 3.64899739785765205590E-2L,
+ -1.5L, 8.6004638671875e-1L + 6.28657731014510932682E-7L, 7.03156640645243187226E-1L,
+ -2.5L, -5.6243896484375E-2L + 1.79986700949327405470E-7, 1.10315664064524318723E0L,
+ -3.5L, -1.30902099609375L + 1.43111007079536392848E-5L, 1.38887092635952890151E0L
+ ];
+ // TODO: test derivatives as well.
+ for (int i=0; i<testpoints.length; i+=3)
+ {
+ assert( feqrel(logGamma(testpoints[i]), testpoints[i+1]) > real.mant_dig-5);
+ if (testpoints[i]<MAXGAMMA)
+ {
+ assert( feqrel(log(fabs(gamma(testpoints[i]))), testpoints[i+1]) > 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);
+ 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 {
+/*
+ * These value can be calculated like this:
+ * 1) Get exact real.max/min_normal/epsilon from compiler:
+ * writefln!"%a"(real.max/min_normal_epsilon)
+ * 2) Convert for Wolfram Alpha
+ * 0xf.fffffffffffffffp+16380 ==> (f.fffffffffffffff base 16) * 2^16380
+ * 3) Calculate result on wofram alpha:
+ * http://www.wolframalpha.com/input/?i=ln((1.ffffffffffffffffffffffffffff+base+16)+*+2%5E16383)+in+base+2
+ * 4) Convert to proper format:
+ * string mantissa = "1.011...";
+ * write(mantissa[0 .. 2]); mantissa = mantissa[2 .. $];
+ * for (size_t i = 0; i < mantissa.length/4; i++)
+ * {
+ * writef!"%x"(to!ubyte(mantissa[0 .. 4], 2)); mantissa = mantissa[4 .. $];
+ * }
+ */
+static if (floatTraits!(real).realFormat == RealFormat.ieeeQuadruple)
+{
+ enum real MAXLOG = 0x1.62e42fefa39ef35793c7673007e6p+13; // log(real.max)
+ enum real MINLOG = -0x1.6546282207802c89d24d65e96274p+13; // log(real.min_normal*real.epsilon) = log(smallest denormal)
+}
+else 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;
+}
+
+/** Incomplete beta integral
+ *
+ * Returns incomplete beta integral of the arguments, evaluated
+ * from zero to x. The regularized incomplete beta function is defined as
+ *
+ * betaIncomplete(a, b, x) = &Gamma;(a+b)/(&Gamma;(a) &Gamma;(b)) *
+ * $(INTEGRATE 0, x) $(POWER t, a-1)$(POWER (1-t),b-1) dt
+ *
+ * and is the same as the the cumulative distribution function.
+ *
+ * The domain of definition is 0 <= x <= 1. In this
+ * implementation a and b are restricted to positive values.
+ * The integral from x to 1 may be obtained by the symmetry
+ * relation
+ *
+ * betaIncompleteCompl(a, b, x ) = betaIncomplete( b, a, 1-x )
+ *
+ * The integral is evaluated by a continued fraction expansion
+ * or, when b*x is small, by a power series.
+ */
+real betaIncomplete(real aa, real bb, real xx )
+{
+ if ( !(aa>0 && bb>0) )
+ {
+ if ( isNaN(aa) ) return aa;
+ if ( isNaN(bb) ) return bb;
+ return real.nan; // domain error
+ }
+ 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)
+ {
+ return betaDistPowerSeries(aa, bb, xx);
+ }
+ real x;
+ real xc; // = 1 - x
+
+ real a, b;
+ int flag = 0;
+
+ /* Reverse a and b if x is greater than the mean. */
+ 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
+ {
+ a = aa;
+ b = bb;
+ xc = 1.0L - xx;
+ x = xx;
+ }
+
+ 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
+ }
+
+ real w;
+ // Choose expansion for optimal convergence
+ // 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 )
+ {
+ w = betaDistExpansion1( a, b, x );
+ }
+ else
+ {
+ w = betaDistExpansion2( a, b, x ) / xc;
+ }
+
+ /* Multiply w by the factor
+ a b
+ x (1-x) Gamma(a+b) / ( a Gamma(a) Gamma(b) ) . */
+
+ y = a * log(x);
+ real t = b * log(xc);
+ 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
+ {
+ /* Resort to logarithms. */
+ y += t + logGamma(a+b) - logGamma(a) - logGamma(b);
+ y += log(w/a);
+
+ t = exp(y);
+/+
+ // There seems to be a bug in Cephes at this point.
+ // Problems occur for y > MAXLOG, not y < MINLOG.
+ if ( y < MINLOG )
+ {
+ t = 0.0L;
+ }
+ else
+ {
+ t = exp(y);
+ }
++/
+ }
+ if ( flag == 1 )
+ {
+/+ // CEPHES includes this code, but I think it is erroneous.
+ if ( t <= real.epsilon )
+ {
+ t = 1.0L - real.epsilon;
+ } else
++/
+ t = 1.0L - t;
+ }
+ return t;
+}
+
+/** Inverse of incomplete beta integral
+ *
+ * Given y, the function finds x such that
+ *
+ * betaIncomplete(a, b, x) == y
+ *
+ * Newton iterations or interval halving is used.
+ */
+real betaIncompleteInv(real aa, real bb, real yy0 )
+{
+ real a, b, y0, d, y, x, x0, x1, lgm, yp, di, dithresh, yl, yh, xt;
+ int i, rflg, dir, nflg;
+
+ if (isNaN(yy0)) return yy0;
+ if (isNaN(aa)) return aa;
+ if (isNaN(bb)) return bb;
+ if ( yy0 <= 0.0L )
+ return 0.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 )
+ {
+ dithresh = 1.0e-7L;
+ rflg = 0;
+ a = aa;
+ b = bb;
+ y0 = yy0;
+ x = a/(a+b);
+ y = betaIncomplete( a, b, x );
+ nflg = 0;
+ goto ihalve;
+ }
+ else
+ {
+ nflg = 0;
+ dithresh = 1.0e-4L;
+ }
+
+ // approximation to inverse function
+
+ yp = -normalDistributionInvImpl( yy0 );
+
+ if ( yy0 > 0.5L )
+ {
+ rflg = 1;
+ a = bb;
+ b = aa;
+ y0 = 1.0L - yy0;
+ yp = -yp;
+ }
+ else
+ {
+ rflg = 0;
+ a = aa;
+ b = bb;
+ y0 = yy0;
+ }
+
+ lgm = (yp * yp - 3.0L)/6.0L;
+ x = 2.0L/( 1.0L/(2.0L * a-1.0L) + 1.0L/(2.0L * b - 1.0L) );
+ d = yp * sqrt( x + lgm ) / x
+ - ( 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 )
+ {
+ 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 )
+ goto newt;
+
+ /* Resort to interval halving if not close enough. */
+ihalve:
+
+ dir = 0;
+ di = 0.5L;
+ for ( i=0; i<400; i++ )
+ {
+ if ( i != 0 )
+ {
+ x = x0 + di * (x1 - x0);
+ if ( x == 1.0L )
+ {
+ x = 1.0L - real.epsilon;
+ }
+ if ( x == 0.0L )
+ {
+ di = 0.5;
+ x = x0 + di * (x1 - x0);
+ if ( x == 0.0 )
+ goto under;
+ }
+ y = betaIncomplete( a, b, x );
+ yp = (x1 - x0)/(x1 + x0);
+ if ( fabs(yp) < dithresh )
+ goto newt;
+ yp = (y-y0)/y0;
+ if ( fabs(yp) < dithresh )
+ goto newt;
+ }
+ if ( y < y0 )
+ {
+ x0 = x;
+ yl = y;
+ if ( dir < 0 )
+ {
+ dir = 0;
+ di = 0.5L;
+ } else if ( dir > 3 )
+ di = 1.0L - (1.0L - di) * (1.0L - di);
+ 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 )
+ {
+ rflg = 0;
+ a = aa;
+ b = bb;
+ y0 = yy0;
+ }
+ else
+ {
+ rflg = 1;
+ a = bb;
+ b = aa;
+ y0 = 1.0 - yy0;
+ }
+ x = 1.0L - x;
+ y = betaIncomplete( a, b, x );
+ x0 = 0.0;
+ yl = 0.0;
+ x1 = 1.0;
+ yh = 1.0;
+ goto ihalve;
+ }
+ }
+ else
+ {
+ x1 = x;
+ if ( rflg == 1 && x1 < real.epsilon )
+ {
+ x = 0.0L;
+ goto done;
+ }
+ yh = y;
+ if ( dir > 0 )
+ {
+ dir = 0;
+ di = 0.5L;
+ }
+ else if ( dir < -3 )
+ di = di * di;
+ else if ( dir < -1 )
+ di = 0.5L * di;
+ else
+ di = (y - y0)/(yh - yl);
+ dir -= 1;
+ }
+ }
+ if ( x0 >= 1.0L )
+ {
+ // partial loss of precision
+ x = 1.0L - real.epsilon;
+ goto done;
+ }
+ if ( x <= 0.0L )
+ {
+under:
+ // underflow has occurred
+ x = real.min_normal * real.min_normal;
+ goto done;
+ }
+
+newt:
+
+ if ( nflg )
+ {
+ goto done;
+ }
+ nflg = 1;
+ lgm = logGamma(a+b) - logGamma(a) - logGamma(b);
+
+ for ( i=0; i<15; i++ )
+ {
+ /* Compute the function at this point. */
+ if ( i != 0 )
+ y = betaIncomplete(a,b,x);
+ if ( y < yl )
+ {
+ x = x0;
+ y = yl;
+ }
+ else if ( y > yh )
+ {
+ x = x1;
+ y = yh;
+ }
+ else if ( y < y0 )
+ {
+ x0 = x;
+ yl = y;
+ }
+ else
+ {
+ x1 = x;
+ yh = y;
+ }
+ 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 )
+ {
+ goto done;
+ }
+ 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 )
+ {
+ y = (x - x0) / (x1 - x0);
+ xt = x0 + 0.5L * y * (x - x0);
+ if ( xt <= 0.0L )
+ break;
+ }
+ if ( xt >= x1 )
+ {
+ y = (x1 - x) / (x1 - x0);
+ xt = x1 - 0.5L * y * (x1 - x);
+ if ( xt >= 1.0L )
+ break;
+ }
+ x = xt;
+ if ( fabs(d/x) < (128.0L * real.epsilon) )
+ goto done;
+ }
+ /* Did not converge. */
+ dithresh = 256.0L * real.epsilon;
+ goto ihalve;
+
+done:
+ if ( rflg )
+ {
+ if ( x <= real.epsilon )
+ x = 1.0L - real.epsilon;
+ else
+ x = 1.0L - x;
+ }
+ return x;
+}
+
+@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)));
+ assert(isIdentical(betaIncomplete(7,15,NaN(0xABC)), NaN(0xABC)));
+ assert(isIdentical(betaIncompleteInv(NaN(0xABC),1,17), NaN(0xABC)));
+ assert(isIdentical(betaIncompleteInv(2,NaN(0xABC),8), NaN(0xABC)));
+ assert(isIdentical(betaIncompleteInv(2,3, NaN(0xABC)), NaN(0xABC)));
+
+ assert(isNaN(betaIncomplete(-1, 2, 3)));
+
+ assert(betaIncomplete(1, 2, 0)==0);
+ assert(betaIncomplete(1, 2, 1)==1);
+ assert(isNaN(betaIncomplete(1, 2, 3)));
+ assert(betaIncompleteInv(1, 1, 0)==0);
+ assert(betaIncompleteInv(1, 1, 1)==1);
+
+ // 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);
+ 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(betaIncompleteInv(5, 10, 0.2000002972865658842), 0.229121208190918L) >= real.mant_dig - 3);
+ assert(feqrel(betaIncompleteInv(4, 7, 0.8000002209179505L), 0.483657360076904L) >= real.mant_dig - 3);
+
+ // Coverage tests. I don't have correct values for these tests, but
+ // these values cover most of the code, so they are useful for
+ // regression testing.
+ // 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.
+ 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);
+ }
+}
+
+
+private {
+// Implementation functions
+
+// Continued fraction expansion #1 for incomplete beta integral
+// Use when x < (a+1)/(a+b+2)
+real betaDistExpansion1(real a, real b, real x )
+{
+ real xk, pk, pkm1, pkm2, qk, qkm1, qkm2;
+ real k1, k2, k3, k4, k5, k6, k7, k8;
+ real r, t, ans;
+ int n;
+
+ k1 = a;
+ k2 = a + b;
+ k3 = a;
+ k4 = a + 1.0L;
+ k5 = 1.0L;
+ k6 = b - 1.0L;
+ k7 = k4;
+ k8 = a + 2.0L;
+
+ pkm2 = 0.0L;
+ qkm2 = 1.0L;
+ pkm1 = 1.0L;
+ qkm1 = 1.0L;
+ ans = 1.0L;
+ r = 1.0L;
+ n = 0;
+ const real thresh = 3.0L * real.epsilon;
+ do
+ {
+ xk = -( x * k1 * k2 )/( k3 * k4 );
+ pk = pkm1 + pkm2 * xk;
+ qk = qkm1 + qkm2 * xk;
+ pkm2 = pkm1;
+ pkm1 = pk;
+ qkm2 = qkm1;
+ qkm1 = qk;
+
+ xk = ( x * k5 * k6 )/( k7 * k8 );
+ pk = pkm1 + pkm2 * xk;
+ qk = qkm1 + qkm2 * xk;
+ pkm2 = pkm1;
+ pkm1 = pk;
+ qkm2 = qkm1;
+ qkm1 = qk;
+
+ if ( qk != 0.0L )
+ r = pk/qk;
+ if ( r != 0.0L )
+ {
+ t = fabs( (ans - r)/r );
+ ans = r;
+ }
+ else
+ {
+ t = 1.0L;
+ }
+
+ if ( t < thresh )
+ return ans;
+
+ k1 += 1.0L;
+ k2 += 1.0L;
+ k3 += 2.0L;
+ k4 += 2.0L;
+ k5 += 1.0L;
+ k6 -= 1.0L;
+ k7 += 2.0L;
+ k8 += 2.0L;
+
+ 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) )
+ {
+ pkm2 *= BETA_BIG;
+ pkm1 *= BETA_BIG;
+ qkm2 *= BETA_BIG;
+ qkm1 *= BETA_BIG;
+ }
+ }
+ while ( ++n < 400 );
+// loss of precision has occurred
+// mtherr( "incbetl", PLOSS );
+ return ans;
+}
+
+// Continued fraction expansion #2 for incomplete beta integral
+// Use when x > (a+1)/(a+b+2)
+real betaDistExpansion2(real a, real b, real x )
+{
+ real xk, pk, pkm1, pkm2, qk, qkm1, qkm2;
+ real k1, k2, k3, k4, k5, k6, k7, k8;
+ real r, t, ans, z;
+
+ k1 = a;
+ k2 = b - 1.0L;
+ k3 = a;
+ k4 = a + 1.0L;
+ k5 = 1.0L;
+ k6 = a + b;
+ k7 = a + 1.0L;
+ k8 = a + 2.0L;
+
+ pkm2 = 0.0L;
+ qkm2 = 1.0L;
+ pkm1 = 1.0L;
+ qkm1 = 1.0L;
+ z = x / (1.0L-x);
+ ans = 1.0L;
+ r = 1.0L;
+ int n = 0;
+ const real thresh = 3.0L * real.epsilon;
+ do
+ {
+ xk = -( z * k1 * k2 )/( k3 * k4 );
+ pk = pkm1 + pkm2 * xk;
+ qk = qkm1 + qkm2 * xk;
+ pkm2 = pkm1;
+ pkm1 = pk;
+ qkm2 = qkm1;
+ qkm1 = qk;
+
+ xk = ( z * k5 * k6 )/( k7 * k8 );
+ pk = pkm1 + pkm2 * xk;
+ qk = qkm1 + qkm2 * xk;
+ pkm2 = pkm1;
+ pkm1 = pk;
+ qkm2 = qkm1;
+ qkm1 = qk;
+
+ if ( qk != 0.0L )
+ r = pk/qk;
+ if ( r != 0.0L )
+ {
+ t = fabs( (ans - r)/r );
+ ans = r;
+ } else
+ t = 1.0L;
+
+ if ( t < thresh )
+ return ans;
+ k1 += 1.0L;
+ k2 -= 1.0L;
+ k3 += 2.0L;
+ k4 += 2.0L;
+ k5 += 1.0L;
+ k6 += 1.0L;
+ k7 += 2.0L;
+ k8 += 2.0L;
+
+ 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) )
+ {
+ pkm2 *= BETA_BIG;
+ pkm1 *= BETA_BIG;
+ qkm2 *= BETA_BIG;
+ qkm1 *= BETA_BIG;
+ }
+ } while ( ++n < 400 );
+// loss of precision has occurred
+//mtherr( "incbetl", PLOSS );
+ return ans;
+}
+
+/* Power series for incomplete gamma integral.
+ Use when b*x is small. */
+real betaDistPowerSeries(real a, real b, real x )
+{
+ real ai = 1.0L / a;
+ real u = (1.0L - b) * x;
+ real v = u / (a + 1.0L);
+ real t1 = v;
+ real t = u;
+ real n = 2.0L;
+ real s = 0.0L;
+ real z = real.epsilon * ai;
+ while ( fabs(v) > z )
+ {
+ u = (n - b) * x / n;
+ t *= u;
+ v = t / (a + n);
+ s += v;
+ n += 1.0L;
+ }
+ s += t1;
+ s += ai;
+
+ u = a * log(x);
+ if ( (a+b) < MAXGAMMA && fabs(u) < MAXLOG )
+ {
+ t = gamma(a+b)/(gamma(a)*gamma(b));
+ s = s * t * pow(x,a);
+ }
+ else
+ {
+ t = logGamma(a+b) - logGamma(a) - logGamma(b) + u + log(s);
+
+ if ( t < MINLOG )
+ {
+ s = 0.0L;
+ } else
+ s = exp(t);
+ }
+ return s;
+}
+
+}
+
+/***************************************
+ * Incomplete gamma integral and its complement
+ *
+ * These functions are defined by
+ *
+ * gammaIncomplete = ( $(INTEGRATE 0, x) $(POWER e, -t) $(POWER t, a-1) dt )/ $(GAMMA)(a)
+ *
+ * gammaIncompleteCompl(a,x) = 1 - gammaIncomplete(a,x)
+ * = ($(INTEGRATE x, &infin;) $(POWER e, -t) $(POWER t, a-1) dt )/ $(GAMMA)(a)
+ *
+ * In this implementation both arguments must be positive.
+ * The integral is evaluated by either a power series or
+ * continued fraction expansion, depending on the relative
+ * values of a and x.
+ */
+real gammaIncomplete(real a, real x )
+in {
+ assert(x >= 0);
+ assert(a > 0);
+}
+body {
+ /* left tail of incomplete gamma function:
+ *
+ * inf. k
+ * a -x - x
+ * x e > ----------
+ * - -
+ * k=0 | (a+k+1)
+ *
+ */
+ if (x == 0)
+ return 0.0L;
+
+ if ( (x > 1.0L) && (x > a ) )
+ return 1.0L - gammaIncompleteCompl(a,x);
+
+ real ax = a * log(x) - x - logGamma(a);
+/+
+ if ( ax < MINLOGL ) return 0; // underflow
+ // { mtherr( "igaml", UNDERFLOW ); return( 0.0L ); }
++/
+ ax = exp(ax);
+
+ /* power series */
+ real r = a;
+ real c = 1.0L;
+ real ans = 1.0L;
+
+ do
+ {
+ r += 1.0L;
+ c *= x/r;
+ ans += c;
+ } while ( c/ans > real.epsilon );
+
+ return ans * ax/a;
+}
+
+/** ditto */
+real gammaIncompleteCompl(real a, real x )
+in {
+ assert(x >= 0);
+ assert(a > 0);
+}
+body {
+ if (x == 0)
+ return 1.0L;
+ if ( (x < 1.0L) || (x < a) )
+ return 1.0L - gammaIncomplete(a,x);
+
+ // DAC (Cephes bug fix): This is necessary to avoid
+ // spurious nans, eg
+ // log(x)-x = NaN when x = real.infinity
+ const real MAXLOGL = 1.1356523406294143949492E4L;
+ if (x > MAXLOGL)
+ return igammaTemmeLarge(a, x);
+
+ real ax = a * log(x) - x - logGamma(a);
+//const real MINLOGL = -1.1355137111933024058873E4L;
+// if ( ax < MINLOGL ) return 0; // underflow;
+ ax = exp(ax);
+
+
+ /* continued fraction */
+ real y = 1.0L - a;
+ real z = x + y + 1.0L;
+ real c = 0.0L;
+
+ real pk, qk, t;
+
+ real pkm2 = 1.0L;
+ real qkm2 = x;
+ real pkm1 = x + 1.0L;
+ real qkm1 = z * x;
+ real ans = pkm1/qkm1;
+
+ 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 )
+ {
+ real r = pk/qk;
+ t = fabs( (ans - r)/r );
+ ans = r;
+ }
+ else
+ {
+ t = 1.0L;
+ }
+ pkm2 = pkm1;
+ pkm1 = pk;
+ qkm2 = qkm1;
+ qkm1 = qk;
+
+ const real BIG = 9.223372036854775808e18L;
+
+ if ( fabs(pk) > BIG )
+ {
+ pkm2 /= BIG;
+ pkm1 /= BIG;
+ qkm2 /= BIG;
+ qkm1 /= BIG;
+ }
+ } while ( t > real.epsilon );
+
+ return ans * ax;
+}
+
+/** Inverse of complemented incomplete gamma integral
+ *
+ * Given a and p, the function finds x such that
+ *
+ * gammaIncompleteCompl( a, x ) = p.
+ *
+ * Starting with the approximate value x = a $(POWER t, 3), where
+ * t = 1 - d - normalDistributionInv(p) sqrt(d),
+ * and d = 1/9a,
+ * the routine performs up to 10 Newton iterations to find the
+ * root of incompleteGammaCompl(a,x) - p = 0.
+ */
+real gammaIncompleteComplInv(real a, real p)
+in {
+ assert(p >= 0 && p <= 1);
+ assert(a>0);
+}
+body {
+ if (p == 0) return real.infinity;
+
+ real y0 = p;
+ const real MAXLOGL = 1.1356523406294143949492E4L;
+ real x0, x1, x, yl, yh, y, d, lgm, dithresh;
+ int i, dir;
+
+ /* bound the solution */
+ x0 = real.max;
+ yl = 0.0L;
+ x1 = 0.0L;
+ yh = 1.0L;
+ dithresh = 4.0 * real.epsilon;
+
+ /* approximation to inverse function */
+ d = 1.0L/(9.0L*a);
+ y = 1.0L - d - normalDistributionInvImpl(y0) * sqrt(d);
+ x = a * y * y * y;
+
+ lgm = logGamma(a);
+
+ 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 )
+ {
+ x0 = x;
+ yl = y;
+ }
+ else
+ {
+ x1 = x;
+ yh = y;
+ }
+ /* compute the derivative of the function at this point */
+ d = (a - 1.0L) * log(x0) - x0 - lgm;
+ if ( d < -MAXLOGL )
+ goto ihalve;
+ d = -exp(d);
+ /* compute the step to the next approximation of x */
+ d = (y - y0)/d;
+ x = x - d;
+ if ( i < 3 ) continue;
+ if ( fabs(d/x) < dithresh ) return x;
+ }
+
+ /* Resort to interval halving if Newton iteration did not converge. */
+ihalve:
+ d = 0.0625L;
+ if ( x0 == real.max )
+ {
+ if ( x <= 0.0L )
+ x = 1.0L;
+ while ( x0 == real.max )
+ {
+ x = (1.0L + d) * x;
+ y = gammaIncompleteCompl( a, x );
+ if ( y < y0 )
+ {
+ x0 = x;
+ yl = y;
+ break;
+ }
+ d = d + d;
+ }
+ }
+ d = 0.5L;
+ dir = 0;
+
+ for ( i=0; i<400; i++ )
+ {
+ x = x1 + d * (x0 - x1);
+ y = gammaIncompleteCompl( a, x );
+ lgm = (x0 - x1)/(x1 + x0);
+ if ( fabs(lgm) < dithresh )
+ break;
+ lgm = (y - y0)/y0;
+ if ( fabs(lgm) < dithresh )
+ break;
+ if ( x <= 0.0L )
+ break;
+ if ( y > y0 )
+ {
+ x1 = x;
+ yh = y;
+ if ( dir < 0 )
+ {
+ dir = 0;
+ d = 0.5L;
+ } else if ( dir > 1 )
+ d = 0.5L * d + 0.5L;
+ else
+ d = (y0 - yl)/(yh - yl);
+ dir += 1;
+ }
+ else
+ {
+ x0 = x;
+ yl = y;
+ if ( dir > 0 )
+ {
+ dir = 0;
+ d = 0.5L;
+ } else if ( dir < -1 )
+ d = 0.5L * d;
+ else
+ d = (y0 - yl)/(yh - yl);
+ dir -= 1;
+ }
+ }
+ /+
+ if ( x == 0.0L )
+ mtherr( "igamil", UNDERFLOW );
+ +/
+ return x;
+}
+
+@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);
+assert(fabs(gammaIncompleteComplInv(100, 0.8) - 91.5013985848288L) < 0.000005);
+assert(gammaIncomplete(1, 0)==0);
+assert(gammaIncompleteCompl(1, 0)==1);
+assert(gammaIncomplete(4545, real.infinity)==1);
+
+// Values from Excel's (1-GammaDist(x, alpha, 1, TRUE))
+
+assert(fabs(1.0L-gammaIncompleteCompl(0.5, 2) - 0.954499729507309L) < 0.00000005);
+assert(fabs(gammaIncomplete(0.5, 2) - 0.954499729507309L) < 0.00000005);
+// Fixed Cephes bug:
+assert(gammaIncompleteCompl(384, real.infinity)==0);
+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])
+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);
+}
+
+
+// DAC: These values are Bn / n for n=2,4,6,8,10,12,14.
+immutable real [7] Bn_n = [
+ 1.0L/(6*2), -1.0L/(30*4), 1.0L/(42*6), -1.0L/(30*8),
+ 5.0L/(66*10), -691.0L/(2730*12), 7.0L/(6*14) ];
+
+/** Digamma function
+*
+* The digamma function is the logarithmic derivative of the gamma function.
+*
+* digamma(x) = d/dx logGamma(x)
+*
+* References:
+* 1. Abramowitz, M., and Stegun, I. A. (1970).
+* Handbook of mathematical functions. Dover, New York,
+* pages 258-259, equations 6.3.6 and 6.3.18.
+*/
+real digamma(real x)
+{
+ // Based on CEPHES, Stephen L. Moshier.
+
+ real p, q, nz, s, w, y, z;
+ long i, n;
+ int negative;
+
+ negative = 0;
+ nz = 0.0;
+
+ if ( x <= 0.0 )
+ {
+ negative = 1;
+ q = x;
+ p = floor(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 )
+ {
+ p += 1.0;
+ nz = q - p;
+ }
+ nz = PI/tan(PI*nz);
+ }
+ else
+ {
+ nz = 0.0;
+ }
+ x = 1.0 - x;
+ }
+
+ // check for small positive integer
+ 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)
+ {
+ y+=1.0L/i;
+ }
+ y -= EULERGAMMA;
+ goto done;
+ }
+
+ s = x;
+ w = 0.0;
+ while ( s < 10.0 )
+ {
+ w += 1.0/s;
+ s += 1.0;
+ }
+
+ if ( s < 1.0e17 )
+ {
+ z = 1.0/(s * s);
+ y = z * poly(z, Bn_n);
+ } else
+ y = 0.0;
+
+ y = log(s) - 0.5L/s - y - w;
+
+done:
+ if ( negative )
+ {
+ y -= nz;
+ }
+ return y;
+}
+
+@safe unittest
+{
+ // Exact values
+ assert(digamma(1.0)== -EULERGAMMA);
+ assert(feqrel(digamma(0.25), -PI/2 - 3* LN2 - EULERGAMMA) >= real.mant_dig-7);
+ assert(feqrel(digamma(1.0L/6), -PI/2 *sqrt(3.0L) - 2* LN2 -1.5*log(3.0L) - EULERGAMMA) >= real.mant_dig-7);
+ assert(digamma(-5.0).isNaN());
+ 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)
+ {
+ real y=0;
+ for (int u=k; u >= 1; --u)
+ {
+ y += 1.0L/u;
+ }
+ assert(feqrel(digamma(k+1.0), -EULERGAMMA + y) >= real.mant_dig-2);
+ }
+}
+
+/** Log Minus Digamma function
+*
+* logmdigamma(x) = log(x) - digamma(x)
+*
+* References:
+* 1. Abramowitz, M., and Stegun, I. A. (1970).
+* Handbook of mathematical functions. Dover, New York,
+* pages 258-259, equations 6.3.6 and 6.3.18.
+*/
+real logmdigamma(real x)
+{
+ if (x <= 0.0)
+ {
+ if (x == 0.0)
+ {
+ return real.infinity;
+ }
+ return real.nan;
+ }
+
+ real s = x;
+ real w = 0.0;
+ while ( s < 10.0 )
+ {
+ w += 1.0/s;
+ s += 1.0;
+ }
+
+ real y;
+ if ( s < 1.0e17 )
+ {
+ immutable real z = 1.0/(s * s);
+ y = z * poly(z, Bn_n);
+ } else
+ y = 0.0;
+
+ return x == s ? y + 0.5L/s : (log(x/s) + 0.5L/s + y + w);
+}
+
+@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)
+ assert(approxEqual(digamma(x), log(x) - logmdigamma(x)));
+ 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;
+ // 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)
+ {
+ //lim x->0 (log(x)-digamma(x))*x == 1
+ return 1 / y;
+ }
+ if (y < 0)
+ {
+ return real.nan;
+ }
+ if (y < real.min_normal)
+ {
+ //6.3.18
+ return 0.5 / y;
+ }
+ if (y > 0)
+ {
+ // x/2 <= logmdigamma(1 / x) <= x, x > 0
+ // calls logmdigamma ~6 times
+ return 1 / findRoot((real x) => logmdigamma(1 / x) - y, y, 2*y);
+ }
+ return y; //NaN
+}
+
+@safe unittest
+{
+ import std.typecons;
+ //WolframAlpha, 22.02.2015
+ immutable Tuple!(real, real)[5] testData = [
+ tuple(1.0L, 0.615556766479594378978099158335549201923L),
+ tuple(1.0L/8, 4.15937801516894947161054974029150730555L),
+ tuple(1.0L/1024, 512.166612384991507850643277924243523243L),
+ tuple(0.000500083333325000003968249801594877323784632117L, 1000.0L),
+ tuple(1017.644138623741168814449776695062817947092468536L, 1.0L/1024),
+ ];
+ foreach (test; testData)
+ 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));
+ assert(approxEqual(logmdigamma(logmdigammaInverse(real.max/2)), real.max/2, 1e-15, 0));
+ assert(approxEqual(logmdigammaInverse(logmdigamma(1)), 1, 1e-15, 0));
+ assert(approxEqual(logmdigammaInverse(logmdigamma(real.min_normal)), real.min_normal, 1e-15, 0));
+ assert(approxEqual(logmdigammaInverse(logmdigamma(real.max/2)), real.max/2, 1e-15, 0));
+}
diff --git a/libphobos/src/std/internal/scopebuffer.d b/libphobos/src/std/internal/scopebuffer.d
new file mode 100644
index 0000000..70a7c8d
--- /dev/null
+++ b/libphobos/src/std/internal/scopebuffer.d
@@ -0,0 +1,398 @@
+/*
+ * Copyright: 2014 by Digital Mars
+ * License: $(LINK2 http://boost.org/LICENSE_1_0.txt, Boost License 1.0).
+ * Authors: Walter Bright
+ * Source: $(PHOBOSSRC std/internal/_scopebuffer.d)
+ */
+
+module std.internal.scopebuffer;
+
+
+//debug=ScopeBuffer;
+
+import core.stdc.stdlib : realloc;
+import std.traits;
+
+/**************************************
+ * ScopeBuffer encapsulates using a local array as a temporary buffer.
+ * 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 is an Output Range.
+ *
+ * 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, except when a `realloc` function is provided which supports this.
+ *
+ * Example:
+---
+import core.stdc.stdio;
+import std.internal.scopebuffer;
+void main()
+{
+ char[2] buf = void;
+ auto textbuf = ScopeBuffer!char(buf);
+ scope(exit) textbuf.free(); // necessary for cleanup
+
+ // Put characters and strings into textbuf, verify they got there
+ textbuf.put('a');
+ textbuf.put('x');
+ textbuf.put("abc");
+ assert(textbuf.length == 5);
+ assert(textbuf[1 .. 3] == "xa");
+ assert(textbuf[3] == 'b');
+
+ // Can shrink it
+ textbuf.length = 3;
+ assert(textbuf[0 .. textbuf.length] == "axa");
+ assert(textbuf[textbuf.length - 1] == 'a');
+ assert(textbuf[1 .. 3] == "xa");
+
+ textbuf.put('z');
+ assert(textbuf[] == "axaz");
+
+ // Can shrink it to 0 size, and reuse same memory
+ textbuf.length = 0;
+}
+---
+ * It is invalid to access ScopeBuffer's contents when ScopeBuffer goes out of scope.
+ * Hence, copying the contents are necessary to keep them around:
+---
+import std.internal.scopebuffer;
+string cat(string s1, string s2)
+{
+ char[10] tmpbuf = void;
+ auto textbuf = ScopeBuffer!char(tmpbuf);
+ scope(exit) textbuf.free();
+ textbuf.put(s1);
+ textbuf.put(s2);
+ textbuf.put("even more");
+ return textbuf[].idup;
+}
+---
+ * ScopeBuffer is intended for high performance usages in $(D @system) and $(D @trusted) code.
+ * It is designed to fit into two 64 bit registers, again for high performance use.
+ * If used incorrectly, memory leaks and corruption can result. Be sure to use
+ * $(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.
+ *
+ * ScopeBuffer instances may be copied, as in:
+---
+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.
+ */
+
+@system
+struct ScopeBuffer(T, alias realloc = /*core.stdc.stdlib*/.realloc)
+if (isAssignable!T &&
+ !hasElaborateDestructor!T &&
+ !hasElaborateCopyConstructor!T &&
+ !hasElaborateAssign!T)
+{
+ import core.exception : onOutOfMemoryError;
+ import core.stdc.string : memcpy;
+
+
+ /**************************
+ * Initialize with buf to use as scratch buffer space.
+ * Params:
+ * buf = Scratch buffer space, must have length that is even
+ * Example:
+ * ---
+ * ubyte[10] tmpbuf = void;
+ * auto sbuf = ScopeBuffer!ubyte(tmpbuf);
+ * ---
+ * 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
+ {
+ assert(!(buf.length & wasResized)); // assure even length of scratch buffer space
+ assert(buf.length <= uint.max); // because we cast to uint later
+ }
+ body
+ {
+ this.buf = buf.ptr;
+ this.bufLen = cast(uint) buf.length;
+ }
+
+ @system unittest
+ {
+ ubyte[10] tmpbuf = void;
+ auto sbuf = ScopeBuffer!ubyte(tmpbuf);
+ }
+
+ /**************************
+ * Releases any memory used.
+ * 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.
+ */
+ void free()
+ {
+ debug(ScopeBuffer) buf[0 .. bufLen] = 0;
+ if (bufLen & wasResized)
+ realloc(buf, 0);
+ buf = null;
+ bufLen = 0;
+ used = 0;
+ }
+
+ /************************
+ * Append element c to the buffer.
+ * This member function makes `ScopeBuffer` an Output Range.
+ */
+ void put(T c)
+ {
+ /* j will get enregistered, while used will not because resize() may change used
+ */
+ const j = used;
+ if (j == bufLen)
+ {
+ assert(j <= (uint.max - 16) / 2);
+ resize(j * 2 + 16);
+ }
+ buf[j] = c;
+ used = j + 1;
+ }
+
+ /************************
+ * Append array s to the buffer.
+ *
+ * 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.
+ */
+ 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);
+ const len = bufLen;
+ if (newlen > len)
+ {
+ assert(len <= uint.max / 2);
+ resize(newlen <= len * 2 ? len * 2 : newlen);
+ }
+ buf[used .. newlen] = s[];
+ used = cast(uint) newlen;
+ }
+
+ /******
+ * Returns:
+ * 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
+ {
+ assert(lower <= bufLen);
+ assert(upper <= bufLen);
+ assert(lower <= upper);
+ }
+ body
+ {
+ return buf[lower .. upper];
+ }
+
+ /// ditto
+ @system inout(T)[] opSlice() inout
+ {
+ assert(used <= bufLen);
+ return buf[0 .. used];
+ }
+
+ /*******
+ * Returns:
+ * The element at index i.
+ */
+ ref inout(T) opIndex(size_t i) inout
+ {
+ assert(i < bufLen);
+ return buf[i];
+ }
+
+ /***
+ * Returns:
+ * The number of elements in the `ScopeBuffer`.
+ */
+ @property size_t length() const
+ {
+ return used;
+ }
+
+ /***
+ * Used to shrink the length of the buffer,
+ * 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)
+ in
+ {
+ assert(i <= this.used);
+ }
+ body
+ {
+ this.used = cast(uint) i;
+ }
+
+ alias opDollar = length;
+
+ private:
+ T* buf;
+ // Using uint instead of size_t so the struct fits in 2 registers in 64 bit code
+ uint bufLen;
+ enum wasResized = 1; // this bit is set in bufLen if we control the memory
+ uint used;
+
+ void resize(size_t newsize)
+ in
+ {
+ assert(newsize <= uint.max);
+ }
+ body
+ {
+ //writefln("%s: oldsize %s newsize %s", id, buf.length, newsize);
+ newsize |= wasResized;
+ void *newBuf = realloc((bufLen & wasResized) ? buf : null, newsize * T.sizeof);
+ if (!newBuf)
+ onOutOfMemoryError();
+ if (!(bufLen & wasResized))
+ {
+ memcpy(newBuf, buf, used * T.sizeof);
+ debug(ScopeBuffer) buf[0 .. bufLen] = 0;
+ }
+ buf = cast(T*) newBuf;
+ bufLen = cast(uint) newsize;
+
+ /* This function is called only rarely,
+ * inlining results in poorer register allocation.
+ */
+ version (DigitalMars)
+ /* With dmd, a fake loop will prevent inlining.
+ * Using a hack until a language enhancement is implemented.
+ */
+ while (1) { break; }
+ }
+}
+
+@system unittest
+{
+ import core.stdc.stdio;
+ import std.range;
+
+ char[2] tmpbuf = void;
+ {
+ // Exercise all the lines of code except for assert(0)'s
+ auto textbuf = ScopeBuffer!char(tmpbuf);
+ scope(exit) textbuf.free();
+
+ static assert(isOutputRange!(ScopeBuffer!char, char));
+
+ textbuf.put('a');
+ textbuf.put('x');
+ textbuf.put("abc"); // tickle put([])'s resize
+ assert(textbuf.length == 5);
+ assert(textbuf[1 .. 3] == "xa");
+ assert(textbuf[3] == 'b');
+
+ textbuf.length = textbuf.length - 1;
+ assert(textbuf[0 .. textbuf.length] == "axab");
+
+ textbuf.length = 3;
+ assert(textbuf[0 .. textbuf.length] == "axa");
+ assert(textbuf[textbuf.length - 1] == 'a');
+ assert(textbuf[1 .. 3] == "xa");
+
+ textbuf.put(cast(dchar)'z');
+ assert(textbuf[] == "axaz");
+
+ textbuf.length = 0; // reset for reuse
+ assert(textbuf.length == 0);
+
+ foreach (char c; "asdf;lasdlfaklsdjfalksdjfa;lksdjflkajsfdasdfkja;sdlfj")
+ {
+ textbuf.put(c); // tickle put(c)'s resize
+ }
+ assert(textbuf[] == "asdf;lasdlfaklsdjfalksdjfa;lksdjflkajsfdasdfkja;sdlfj");
+ } // run destructor on textbuf here
+
+}
+
+@system unittest
+{
+ string cat(string s1, string s2)
+ {
+ char[10] tmpbuf = void;
+ auto textbuf = ScopeBuffer!char(tmpbuf);
+ scope(exit) textbuf.free();
+ textbuf.put(s1);
+ textbuf.put(s2);
+ textbuf.put("even more");
+ return textbuf[].idup;
+ }
+
+ auto s = cat("hello", "betty");
+ assert(s == "hellobettyeven more");
+}
+
+// const
+@system unittest
+{
+ char[10] tmpbuf = void;
+ auto textbuf = ScopeBuffer!char(tmpbuf);
+ scope(exit) textbuf.free();
+ foreach (i; 0 .. 10) textbuf.put('w');
+ const csb = textbuf;
+ const elem = csb[3];
+ const slice0 = csb[0 .. 5];
+ const slice1 = csb[];
+}
+
+/*********************************
+ * 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`.
+ */
+
+auto scopeBuffer(T)(T[] tmpbuf)
+{
+ return ScopeBuffer!T(tmpbuf);
+}
+
+///
+@system unittest
+{
+ ubyte[10] tmpbuf = void;
+ auto sb = scopeBuffer(tmpbuf);
+ scope(exit) sb.free();
+}
+
+@system unittest
+{
+ ScopeBuffer!(int*) b;
+ int*[] s;
+ b.put(s);
+
+ ScopeBuffer!char c;
+ string s1;
+ char[] s2;
+ c.put(s1);
+ c.put(s2);
+}
diff --git a/libphobos/src/std/internal/test/dummyrange.d b/libphobos/src/std/internal/test/dummyrange.d
new file mode 100644
index 0000000..a6bce0a
--- /dev/null
+++ b/libphobos/src/std/internal/test/dummyrange.d
@@ -0,0 +1,565 @@
+/**
+For testing only.
+Used with the dummy ranges for testing higher order ranges.
+*/
+module std.internal.test.dummyrange;
+
+import std.meta;
+import std.range.primitives;
+import std.typecons;
+
+enum RangeType
+{
+ Input,
+ Forward,
+ Bidirectional,
+ Random
+}
+
+enum Length
+{
+ Yes,
+ No
+}
+
+enum ReturnBy
+{
+ Reference,
+ 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, 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;
+
+ 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
+ static if (is(T == uint[]))
+ {
+ arr = uinttestData.dup;
+ }
+ }
+
+ void popFront()
+ {
+ arr = arr[1..$];
+ }
+
+ @property bool empty() const
+ {
+ return arr.length == 0;
+ }
+
+ static if (r == ReturnBy.Reference)
+ {
+ @property ref inout(RetType) front() inout
+ {
+ return arr[0];
+ }
+ }
+ else
+ {
+ @property RetType front() const
+ {
+ return arr[0];
+ }
+
+ @property void front(RetTypeNoAutoDecoding val)
+ {
+ arr[0] = val;
+ }
+ }
+
+ static if (rt >= RangeType.Forward)
+ {
+ @property typeof(this) save()
+ {
+ return this;
+ }
+ }
+
+ static if (rt >= RangeType.Bidirectional)
+ {
+ void popBack()
+ {
+ arr = arr[0..$ - 1];
+ }
+
+ static if (r == ReturnBy.Reference)
+ {
+ @property ref inout(RetType) back() inout
+ {
+ return arr[$ - 1];
+ }
+ }
+ else
+ {
+ @property RetType back() const
+ {
+ return arr[$ - 1];
+ }
+
+ @property void back(RetTypeNoAutoDecoding val)
+ {
+ arr[$ - 1] = val;
+ }
+ }
+ }
+
+ static if (rt >= RangeType.Random)
+ {
+ static if (r == ReturnBy.Reference)
+ {
+ ref inout(RetType) opIndex(size_t index) inout
+ {
+ return arr[index];
+ }
+ }
+ else
+ {
+ 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];
+ return ret;
+ }
+
+ typeof(this) opSlice()
+ {
+ return this;
+ }
+ }
+
+ static if (l == Length.Yes)
+ {
+ @property size_t length() const
+ {
+ return arr.length;
+ }
+
+ alias opDollar = length;
+ }
+}
+
+enum dummyLength = 10;
+
+alias AllDummyRanges = 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)
+);
+
+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
+H. Useful in combination with DummyRange for testing several higher
+order ranges.
+*/
+template propagatesRangeType(H, R...)
+{
+ static if (allSatisfy!(isRandomAccessRange, R))
+ enum bool propagatesRangeType = isRandomAccessRange!H;
+ else static if (allSatisfy!(isBidirectionalRange, R))
+ enum bool propagatesRangeType = isBidirectionalRange!H;
+ else static if (allSatisfy!(isForwardRange, R))
+ enum bool propagatesRangeType = isForwardRange!H;
+ else
+ enum bool propagatesRangeType = isInputRange!H;
+}
+
+template propagatesLength(H, R...)
+{
+ static if (allSatisfy!(hasLength, R))
+ enum bool propagatesLength = hasLength!H;
+ else
+ enum bool propagatesLength = !hasLength!H;
+}
+
+/**
+Reference type input range
+*/
+class ReferenceInputRange(T)
+{
+ import std.array : array;
+
+ this(Range)(Range r) if (isInputRange!Range) {_payload = array(r);}
+ final @property ref T front(){return _payload.front;}
+ final void popFront(){_payload.popFront();}
+ final @property bool empty(){return _payload.empty;}
+ protected T[] _payload;
+}
+
+/**
+Infinite input range
+*/
+class ReferenceInfiniteInputRange(T)
+{
+ this(T first = T.init) {_val = first;}
+ final @property T front(){return _val;}
+ final void popFront(){++_val;}
+ enum bool empty = false;
+ protected T _val;
+}
+
+/**
+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.algorithm.iteration : map;
+ import std.range : iota;
+ 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/libphobos/src/std/internal/test/range.d b/libphobos/src/std/internal/test/range.d
new file mode 100644
index 0000000..6aa9676
--- /dev/null
+++ b/libphobos/src/std/internal/test/range.d
@@ -0,0 +1,25 @@
+/**
+For testing only.
+Contains tests related to member privacy that cannot be verified inside
+std.range itself.
+*/
+module std.internal.test.range;
+
+// Note: currently can't be @safe because RefCounted, which is used by chunks,
+// isn't.
+@system /*@safe*/ unittest
+{
+ import std.algorithm.comparison : equal;
+ import std.range : chunks;
+
+ struct R
+ {
+ int state = 0;
+ @property bool empty() { return state >= 5; }
+ @property int front() { return state; }
+ void popFront() { state++; }
+ }
+
+ auto r = R().chunks(3);
+ assert(r.equal!equal([[ 0, 1, 2 ], [ 3, 4 ]]));
+}
diff --git a/libphobos/src/std/internal/test/uda.d b/libphobos/src/std/internal/test/uda.d
new file mode 100644
index 0000000..88e3a1b
--- /dev/null
+++ b/libphobos/src/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/libphobos/src/std/internal/unicode_comp.d b/libphobos/src/std/internal/unicode_comp.d
new file mode 100644
index 0000000..fa0fcb5
--- /dev/null
+++ b/libphobos/src/std/internal/unicode_comp.d
@@ -0,0 +1,2984 @@
+module std.internal.unicode_comp;
+import std.internal.unicode_tables;
+
+@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()
+ {
+ 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()
+ {
+ 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/libphobos/src/std/internal/unicode_decomp.d b/libphobos/src/std/internal/unicode_decomp.d
new file mode 100644
index 0000000..736965d
--- /dev/null
+++ b/libphobos/src/std/internal/unicode_decomp.d
@@ -0,0 +1,5301 @@
+module std.internal.unicode_decomp;
+import std.internal.unicode_tables;
+
+@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
+ {
+ 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
+ {
+ 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/libphobos/src/std/internal/unicode_grapheme.d b/libphobos/src/std/internal/unicode_grapheme.d
new file mode 100644
index 0000000..b4befc9
--- /dev/null
+++ b/libphobos/src/std/internal/unicode_grapheme.d
@@ -0,0 +1,293 @@
+module std.internal.unicode_grapheme;
+import std.internal.unicode_tables;
+
+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]);
+
+}
diff --git a/libphobos/src/std/internal/unicode_norm.d b/libphobos/src/std/internal/unicode_norm.d
new file mode 100644
index 0000000..16059cd
--- /dev/null
+++ b/libphobos/src/std/internal/unicode_norm.d
@@ -0,0 +1,548 @@
+module std.internal.unicode_norm;
+import std.internal.unicode_tables;
+
+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]);
+
+}
diff --git a/libphobos/src/std/internal/unicode_tables.d b/libphobos/src/std/internal/unicode_tables.d
new file mode 100644
index 0000000..00b94bd
--- /dev/null
+++ b/libphobos/src/std/internal/unicode_tables.d
@@ -0,0 +1,11081 @@
+//Written in the D programming language
+/**
+ * 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 package(std):
+
+
+struct SimpleCaseEntry
+{
+ uint ch;
+ ubyte n, bucket;// n - number in bucket
+
+pure nothrow @nogc:
+
+ @property ubyte size() const
+ {
+ return bucket & 0x3F;
+ }
+ @property auto isLower() const
+ {
+ return bucket & 0x40;
+ }
+ @property auto isUpper() const
+ {
+ return bucket & 0x80;
+ }
+}
+
+struct FullCaseEntry
+{
+ dchar[3] seq;
+ ubyte n, size;// n number in batch, size - size of batch
+ ubyte entry_len;
+
+ @property auto value() const @trusted pure nothrow @nogc return
+ {
+ return seq[0 .. entry_len];
+ }
+}
+
+struct CompEntry
+{
+ dchar rhs, composed;
+}
+
+struct UnicodeProperty
+{
+ string name;
+ ubyte[] compressed;
+}
+
+struct TrieEntry(T...)
+{
+ size_t[] offsets;
+ size_t[] sizes;
+ size_t[] data;
+}
+
+@property immutable(SimpleCaseEntry[]) simpleCaseTable()
+{
+alias SCE = SimpleCaseEntry;
+static immutable SCE[] t = [
+SCE(0x2c00, 0, 0x82),
+SCE(0x2c30, 1, 0x42),SCE(0x24c3, 0, 0x82),SCE(0x24dd, 1, 0x42),SCE(0x2c01, 0, 0x82),
+SCE(0x2c31, 1, 0x42),SCE(0x2c1d, 0, 0x82),SCE(0x2c4d, 1, 0x42),SCE(0x2c02, 0, 0x82),
+SCE(0x2c32, 1, 0x42),SCE(0x2c03, 0, 0x82),SCE(0x2c33, 1, 0x42),SCE(0x2c04, 0, 0x82),
+SCE(0x2c34, 1, 0x42),SCE(0x2c05, 0, 0x82),SCE(0x2c35, 1, 0x42),SCE(0x2c06, 0, 0x82),
+SCE(0x2c36, 1, 0x42),SCE(0x10400, 0, 0x82),SCE(0x10428, 1, 0x42),SCE(0x2cc2, 0, 0x82),
+SCE(0x2cc3, 1, 0x42),SCE(0x2c07, 0, 0x82),SCE(0x2c37, 1, 0x42),SCE(0x2c08, 0, 0x82),
+SCE(0x2c38, 1, 0x42),SCE(0x2c09, 0, 0x82),SCE(0x2c39, 1, 0x42),SCE(0x2c0a, 0, 0x82),
+SCE(0x2c3a, 1, 0x42),SCE(0xa68c, 0, 0x82),SCE(0xa68d, 1, 0x42),SCE(0x0041, 0, 0x82),
+SCE(0x0061, 1, 0x42),SCE(0x0042, 0, 0x82),SCE(0x0062, 1, 0x42),SCE(0x0043, 0, 0x82),
+SCE(0x0063, 1, 0x42),SCE(0x0044, 0, 0x82),SCE(0x0064, 1, 0x42),SCE(0x0045, 0, 0x82),
+SCE(0x0065, 1, 0x42),SCE(0x0046, 0, 0x82),SCE(0x0066, 1, 0x42),SCE(0x0047, 0, 0x82),
+SCE(0x0067, 1, 0x42),SCE(0x0048, 0, 0x82),SCE(0x0068, 1, 0x42),SCE(0x0049, 0, 0x82),
+SCE(0x0069, 1, 0x42),SCE(0x004a, 0, 0x82),SCE(0x006a, 1, 0x42),SCE(0x004b, 0, 0x83),
+SCE(0x006b, 1, 0x43),SCE(0x212a, 2, 0x83),SCE(0x004c, 0, 0x82),SCE(0x006c, 1, 0x42),
+SCE(0x004d, 0, 0x82),SCE(0x006d, 1, 0x42),SCE(0x004e, 0, 0x82),SCE(0x006e, 1, 0x42),
+SCE(0x004f, 0, 0x82),SCE(0x006f, 1, 0x42),SCE(0x0050, 0, 0x82),SCE(0x0070, 1, 0x42),
+SCE(0x0051, 0, 0x82),SCE(0x0071, 1, 0x42),SCE(0x0052, 0, 0x82),SCE(0x0072, 1, 0x42),
+SCE(0x0053, 0, 0x83),SCE(0x0073, 1, 0x43),SCE(0x017f, 2, 0x43),SCE(0x0054, 0, 0x82),
+SCE(0x0074, 1, 0x42),SCE(0x0055, 0, 0x82),SCE(0x0075, 1, 0x42),SCE(0x0056, 0, 0x82),
+SCE(0x0076, 1, 0x42),SCE(0x0057, 0, 0x82),SCE(0x0077, 1, 0x42),SCE(0x0058, 0, 0x82),
+SCE(0x0078, 1, 0x42),SCE(0x0059, 0, 0x82),SCE(0x0079, 1, 0x42),SCE(0x005a, 0, 0x82),
+SCE(0x007a, 1, 0x42),SCE(0x2c0f, 0, 0x82),SCE(0x2c3f, 1, 0x42),SCE(0x2c10, 0, 0x82),
+SCE(0x2c40, 1, 0x42),SCE(0x10402, 0, 0x82),SCE(0x1042a, 1, 0x42),SCE(0x2cc4, 0, 0x82),
+SCE(0x2cc5, 1, 0x42),SCE(0x2166, 0, 0x82),SCE(0x2176, 1, 0x42),SCE(0x2c11, 0, 0x82),
+SCE(0x2c41, 1, 0x42),SCE(0x2c12, 0, 0x82),SCE(0x2c42, 1, 0x42),SCE(0x2168, 0, 0x82),
+SCE(0x2178, 1, 0x42),SCE(0x2c13, 0, 0x82),SCE(0x2c43, 1, 0x42),SCE(0xa682, 0, 0x82),
+SCE(0xa683, 1, 0x42),SCE(0x2c14, 0, 0x82),SCE(0x2c44, 1, 0x42),SCE(0x216a, 0, 0x82),
+SCE(0x217a, 1, 0x42),SCE(0x24c7, 0, 0x82),SCE(0x24e1, 1, 0x42),SCE(0x2c15, 0, 0x82),
+SCE(0x2c45, 1, 0x42),SCE(0x10403, 0, 0x82),SCE(0x1042b, 1, 0x42),SCE(0x2c16, 0, 0x82),
+SCE(0x2c46, 1, 0x42),SCE(0x216c, 0, 0x82),SCE(0x217c, 1, 0x42),SCE(0x2c17, 0, 0x82),
+SCE(0x2c47, 1, 0x42),SCE(0xff38, 0, 0x82),SCE(0xff58, 1, 0x42),SCE(0x2c18, 0, 0x82),
+SCE(0x2c48, 1, 0x42),SCE(0x216e, 0, 0x82),SCE(0x217e, 1, 0x42),SCE(0x2c19, 0, 0x82),
+SCE(0x2c49, 1, 0x42),SCE(0x2c1a, 0, 0x82),SCE(0x2c4a, 1, 0x42),SCE(0x2c1e, 0, 0x82),
+SCE(0x2c4e, 1, 0x42),SCE(0x10a0, 0, 0x82),SCE(0x2d00, 1, 0x42),SCE(0x10a1, 0, 0x82),
+SCE(0x2d01, 1, 0x42),SCE(0x10a2, 0, 0x82),SCE(0x2d02, 1, 0x42),SCE(0x10a3, 0, 0x82),
+SCE(0x2d03, 1, 0x42),SCE(0x10a4, 0, 0x82),SCE(0x2d04, 1, 0x42),SCE(0x10a5, 0, 0x82),
+SCE(0x2d05, 1, 0x42),SCE(0x10a6, 0, 0x82),SCE(0x2d06, 1, 0x42),SCE(0x10a7, 0, 0x82),
+SCE(0x2d07, 1, 0x42),SCE(0x10a8, 0, 0x82),SCE(0x2d08, 1, 0x42),SCE(0x10a9, 0, 0x82),
+SCE(0x2d09, 1, 0x42),SCE(0x10aa, 0, 0x82),SCE(0x2d0a, 1, 0x42),SCE(0x10ab, 0, 0x82),
+SCE(0x2d0b, 1, 0x42),SCE(0x10ac, 0, 0x82),SCE(0x2d0c, 1, 0x42),SCE(0x10ad, 0, 0x82),
+SCE(0x2d0d, 1, 0x42),SCE(0x10ae, 0, 0x82),SCE(0x2d0e, 1, 0x42),SCE(0x10af, 0, 0x82),
+SCE(0x2d0f, 1, 0x42),SCE(0x10b0, 0, 0x82),SCE(0x2d10, 1, 0x42),SCE(0x10b1, 0, 0x82),
+SCE(0x2d11, 1, 0x42),SCE(0x10b2, 0, 0x82),SCE(0x2d12, 1, 0x42),SCE(0x10b3, 0, 0x82),
+SCE(0x2d13, 1, 0x42),SCE(0x10b4, 0, 0x82),SCE(0x2d14, 1, 0x42),SCE(0x10b5, 0, 0x82),
+SCE(0x2d15, 1, 0x42),SCE(0x10b6, 0, 0x82),SCE(0x2d16, 1, 0x42),SCE(0x10b7, 0, 0x82),
+SCE(0x2d17, 1, 0x42),SCE(0x10b8, 0, 0x82),SCE(0x2d18, 1, 0x42),SCE(0x10b9, 0, 0x82),
+SCE(0x2d19, 1, 0x42),SCE(0x10ba, 0, 0x82),SCE(0x2d1a, 1, 0x42),SCE(0x10bb, 0, 0x82),
+SCE(0x2d1b, 1, 0x42),SCE(0x10bc, 0, 0x82),SCE(0x2d1c, 1, 0x42),SCE(0x10bd, 0, 0x82),
+SCE(0x2d1d, 1, 0x42),SCE(0x10be, 0, 0x82),SCE(0x2d1e, 1, 0x42),SCE(0x10bf, 0, 0x82),
+SCE(0x2d1f, 1, 0x42),SCE(0x00c0, 0, 0x82),SCE(0x00e0, 1, 0x42),SCE(0x00c1, 0, 0x82),
+SCE(0x00e1, 1, 0x42),SCE(0x10c2, 0, 0x82),SCE(0x2d22, 1, 0x42),SCE(0x00c3, 0, 0x82),
+SCE(0x00e3, 1, 0x42),SCE(0x10c4, 0, 0x82),SCE(0x2d24, 1, 0x42),SCE(0x00c5, 0, 0x83),
+SCE(0x00e5, 1, 0x43),SCE(0x212b, 2, 0x83),SCE(0x00c6, 0, 0x82),SCE(0x00e6, 1, 0x42),
+SCE(0x00c7, 0, 0x82),SCE(0x00e7, 1, 0x42),SCE(0x00c8, 0, 0x82),SCE(0x00e8, 1, 0x42),
+SCE(0x00c9, 0, 0x82),SCE(0x00e9, 1, 0x42),SCE(0x00ca, 0, 0x82),SCE(0x00ea, 1, 0x42),
+SCE(0x00cb, 0, 0x82),SCE(0x00eb, 1, 0x42),SCE(0x00cc, 0, 0x82),SCE(0x00ec, 1, 0x42),
+SCE(0x00cd, 0, 0x82),SCE(0x00ed, 1, 0x42),SCE(0x00ce, 0, 0x82),SCE(0x00ee, 1, 0x42),
+SCE(0x00cf, 0, 0x82),SCE(0x00ef, 1, 0x42),SCE(0x00d0, 0, 0x82),SCE(0x00f0, 1, 0x42),
+SCE(0x00d1, 0, 0x82),SCE(0x00f1, 1, 0x42),SCE(0x00d2, 0, 0x82),SCE(0x00f2, 1, 0x42),
+SCE(0x00d3, 0, 0x82),SCE(0x00f3, 1, 0x42),SCE(0x00d4, 0, 0x82),SCE(0x00f4, 1, 0x42),
+SCE(0x00d5, 0, 0x82),SCE(0x00f5, 1, 0x42),SCE(0x00d6, 0, 0x82),SCE(0x00f6, 1, 0x42),
+SCE(0x00d8, 0, 0x82),SCE(0x00f8, 1, 0x42),SCE(0x00d9, 0, 0x82),SCE(0x00f9, 1, 0x42),
+SCE(0x00da, 0, 0x82),SCE(0x00fa, 1, 0x42),SCE(0x00db, 0, 0x82),SCE(0x00fb, 1, 0x42),
+SCE(0x00dc, 0, 0x82),SCE(0x00fc, 1, 0x42),SCE(0x00dd, 0, 0x82),SCE(0x00fd, 1, 0x42),
+SCE(0x00de, 0, 0x82),SCE(0x00fe, 1, 0x42),SCE(0x2c25, 0, 0x82),SCE(0x2c55, 1, 0x42),
+SCE(0x2c26, 0, 0x82),SCE(0x2c56, 1, 0x42),SCE(0x2c27, 0, 0x82),SCE(0x2c57, 1, 0x42),
+SCE(0x2c28, 0, 0x82),SCE(0x2c58, 1, 0x42),SCE(0x1040f, 0, 0x82),SCE(0x10437, 1, 0x42),
+SCE(0x24cb, 0, 0x82),SCE(0x24e5, 1, 0x42),SCE(0x2c29, 0, 0x82),SCE(0x2c59, 1, 0x42),
+SCE(0x10407, 0, 0x82),SCE(0x1042f, 1, 0x42),SCE(0x2c2a, 0, 0x82),SCE(0x2c5a, 1, 0x42),
+SCE(0x0100, 0, 0x82),SCE(0x0101, 1, 0x42),SCE(0x0102, 0, 0x82),SCE(0x0103, 1, 0x42),
+SCE(0x2c2b, 0, 0x82),SCE(0x2c5b, 1, 0x42),SCE(0x0104, 0, 0x82),SCE(0x0105, 1, 0x42),
+SCE(0x0106, 0, 0x82),SCE(0x0107, 1, 0x42),SCE(0x0108, 0, 0x82),SCE(0x0109, 1, 0x42),
+SCE(0x2c2c, 0, 0x82),SCE(0x2c5c, 1, 0x42),SCE(0x010a, 0, 0x82),SCE(0x010b, 1, 0x42),
+SCE(0x010c, 0, 0x82),SCE(0x010d, 1, 0x42),SCE(0x010e, 0, 0x82),SCE(0x010f, 1, 0x42),
+SCE(0x2c2d, 0, 0x82),SCE(0x2c5d, 1, 0x42),SCE(0x0110, 0, 0x82),SCE(0x0111, 1, 0x42),
+SCE(0x0112, 0, 0x82),SCE(0x0113, 1, 0x42),SCE(0x0114, 0, 0x82),SCE(0x0115, 1, 0x42),
+SCE(0x2c2e, 0, 0x82),SCE(0x2c5e, 1, 0x42),SCE(0x0116, 0, 0x82),SCE(0x0117, 1, 0x42),
+SCE(0x0118, 0, 0x82),SCE(0x0119, 1, 0x42),SCE(0x011a, 0, 0x82),SCE(0x011b, 1, 0x42),
+SCE(0x011c, 0, 0x82),SCE(0x011d, 1, 0x42),SCE(0x011e, 0, 0x82),SCE(0x011f, 1, 0x42),
+SCE(0x0120, 0, 0x82),SCE(0x0121, 1, 0x42),SCE(0x0122, 0, 0x82),SCE(0x0123, 1, 0x42),
+SCE(0x0124, 0, 0x82),SCE(0x0125, 1, 0x42),SCE(0x0126, 0, 0x82),SCE(0x0127, 1, 0x42),
+SCE(0x0128, 0, 0x82),SCE(0x0129, 1, 0x42),SCE(0x012a, 0, 0x82),SCE(0x012b, 1, 0x42),
+SCE(0x00c5, 0, 0x83),SCE(0x00e5, 1, 0x43),SCE(0x212b, 2, 0x83),SCE(0x012c, 0, 0x82),
+SCE(0x012d, 1, 0x42),SCE(0x012e, 0, 0x82),SCE(0x012f, 1, 0x42),SCE(0x0132, 0, 0x82),
+SCE(0x0133, 1, 0x42),SCE(0x0134, 0, 0x82),SCE(0x0135, 1, 0x42),SCE(0x0136, 0, 0x82),
+SCE(0x0137, 1, 0x42),SCE(0x0139, 0, 0x82),SCE(0x013a, 1, 0x42),SCE(0x013b, 0, 0x82),
+SCE(0x013c, 1, 0x42),SCE(0x2cde, 0, 0x82),SCE(0x2cdf, 1, 0x42),SCE(0x013d, 0, 0x82),
+SCE(0x013e, 1, 0x42),SCE(0x013f, 0, 0x82),SCE(0x0140, 1, 0x42),SCE(0x0141, 0, 0x82),
+SCE(0x0142, 1, 0x42),SCE(0x0143, 0, 0x82),SCE(0x0144, 1, 0x42),SCE(0x0145, 0, 0x82),
+SCE(0x0146, 1, 0x42),SCE(0x0147, 0, 0x82),SCE(0x0148, 1, 0x42),SCE(0x014a, 0, 0x82),
+SCE(0x014b, 1, 0x42),SCE(0x014c, 0, 0x82),SCE(0x014d, 1, 0x42),SCE(0x014e, 0, 0x82),
+SCE(0x014f, 1, 0x42),SCE(0x0150, 0, 0x82),SCE(0x0151, 1, 0x42),SCE(0x0152, 0, 0x82),
+SCE(0x0153, 1, 0x42),SCE(0x0154, 0, 0x82),SCE(0x0155, 1, 0x42),SCE(0x0156, 0, 0x82),
+SCE(0x0157, 1, 0x42),SCE(0x0158, 0, 0x82),SCE(0x0159, 1, 0x42),SCE(0x015a, 0, 0x82),
+SCE(0x015b, 1, 0x42),SCE(0x015c, 0, 0x82),SCE(0x015d, 1, 0x42),SCE(0x015e, 0, 0x82),
+SCE(0x015f, 1, 0x42),SCE(0x0160, 0, 0x82),SCE(0x0161, 1, 0x42),SCE(0x2161, 0, 0x82),
+SCE(0x2171, 1, 0x42),SCE(0x0162, 0, 0x82),SCE(0x0163, 1, 0x42),SCE(0x2163, 0, 0x82),
+SCE(0x2173, 1, 0x42),SCE(0x0164, 0, 0x82),SCE(0x0165, 1, 0x42),SCE(0x2165, 0, 0x82),
+SCE(0x2175, 1, 0x42),SCE(0x0166, 0, 0x82),SCE(0x0167, 1, 0x42),SCE(0x2167, 0, 0x82),
+SCE(0x2177, 1, 0x42),SCE(0x0168, 0, 0x82),SCE(0x0169, 1, 0x42),SCE(0x2169, 0, 0x82),
+SCE(0x2179, 1, 0x42),SCE(0x016a, 0, 0x82),SCE(0x016b, 1, 0x42),SCE(0x216b, 0, 0x82),
+SCE(0x217b, 1, 0x42),SCE(0x016c, 0, 0x82),SCE(0x016d, 1, 0x42),SCE(0x216d, 0, 0x82),
+SCE(0x217d, 1, 0x42),SCE(0x016e, 0, 0x82),SCE(0x016f, 1, 0x42),SCE(0x216f, 0, 0x82),
+SCE(0x217f, 1, 0x42),SCE(0x0170, 0, 0x82),SCE(0x0171, 1, 0x42),SCE(0x2ccc, 0, 0x82),
+SCE(0x2ccd, 1, 0x42),SCE(0x0172, 0, 0x82),SCE(0x0173, 1, 0x42),SCE(0x0174, 0, 0x82),
+SCE(0x0175, 1, 0x42),SCE(0x0176, 0, 0x82),SCE(0x0177, 1, 0x42),SCE(0x00ff, 0, 0x42),
+SCE(0x0178, 1, 0x82),SCE(0x0179, 0, 0x82),SCE(0x017a, 1, 0x42),SCE(0x017b, 0, 0x82),
+SCE(0x017c, 1, 0x42),SCE(0x017d, 0, 0x82),SCE(0x017e, 1, 0x42),SCE(0x0053, 0, 0x83),
+SCE(0x0073, 1, 0x43),SCE(0x017f, 2, 0x43),SCE(0x0181, 0, 0x82),SCE(0x0253, 1, 0x42),
+SCE(0x0182, 0, 0x82),SCE(0x0183, 1, 0x42),SCE(0x2183, 0, 0x82),SCE(0x2184, 1, 0x42),
+SCE(0x0184, 0, 0x82),SCE(0x0185, 1, 0x42),SCE(0x0186, 0, 0x82),SCE(0x0254, 1, 0x42),
+SCE(0x0187, 0, 0x82),SCE(0x0188, 1, 0x42),SCE(0x0189, 0, 0x82),SCE(0x0256, 1, 0x42),
+SCE(0x018a, 0, 0x82),SCE(0x0257, 1, 0x42),SCE(0x018b, 0, 0x82),SCE(0x018c, 1, 0x42),
+SCE(0x018e, 0, 0x82),SCE(0x01dd, 1, 0x42),SCE(0x018f, 0, 0x82),SCE(0x0259, 1, 0x42),
+SCE(0x0190, 0, 0x82),SCE(0x025b, 1, 0x42),SCE(0x0191, 0, 0x82),SCE(0x0192, 1, 0x42),
+SCE(0x0193, 0, 0x82),SCE(0x0260, 1, 0x42),SCE(0x0194, 0, 0x82),SCE(0x0263, 1, 0x42),
+SCE(0x0196, 0, 0x82),SCE(0x0269, 1, 0x42),SCE(0x0197, 0, 0x82),SCE(0x0268, 1, 0x42),
+SCE(0x0198, 0, 0x82),SCE(0x0199, 1, 0x42),SCE(0x019c, 0, 0x82),SCE(0x026f, 1, 0x42),
+SCE(0x019d, 0, 0x82),SCE(0x0272, 1, 0x42),SCE(0x019f, 0, 0x82),SCE(0x0275, 1, 0x42),
+SCE(0x01a0, 0, 0x82),SCE(0x01a1, 1, 0x42),SCE(0x01a2, 0, 0x82),SCE(0x01a3, 1, 0x42),
+SCE(0x01a4, 0, 0x82),SCE(0x01a5, 1, 0x42),SCE(0x01a6, 0, 0x82),SCE(0x0280, 1, 0x42),
+SCE(0x01a7, 0, 0x82),SCE(0x01a8, 1, 0x42),SCE(0x01a9, 0, 0x82),SCE(0x0283, 1, 0x42),
+SCE(0x01ac, 0, 0x82),SCE(0x01ad, 1, 0x42),SCE(0x01ae, 0, 0x82),SCE(0x0288, 1, 0x42),
+SCE(0x01af, 0, 0x82),SCE(0x01b0, 1, 0x42),SCE(0x01b1, 0, 0x82),SCE(0x028a, 1, 0x42),
+SCE(0x01b2, 0, 0x82),SCE(0x028b, 1, 0x42),SCE(0x01b3, 0, 0x82),SCE(0x01b4, 1, 0x42),
+SCE(0x01b5, 0, 0x82),SCE(0x01b6, 1, 0x42),SCE(0x01b7, 0, 0x82),SCE(0x0292, 1, 0x42),
+SCE(0x01b8, 0, 0x82),SCE(0x01b9, 1, 0x42),SCE(0x01bc, 0, 0x82),SCE(0x01bd, 1, 0x42),
+SCE(0x01c4, 0, 0x83),SCE(0x01c5, 1, 0x3),SCE(0x01c6, 2, 0x43),SCE(0x01c4, 0, 0x83),
+SCE(0x01c5, 1, 0x3),SCE(0x01c6, 2, 0x43),SCE(0x01c7, 0, 0x83),SCE(0x01c8, 1, 0x3),
+SCE(0x01c9, 2, 0x43),SCE(0x01c7, 0, 0x83),SCE(0x01c8, 1, 0x3),SCE(0x01c9, 2, 0x43),
+SCE(0x01ca, 0, 0x83),SCE(0x01cb, 1, 0x3),SCE(0x01cc, 2, 0x43),SCE(0x01ca, 0, 0x83),
+SCE(0x01cb, 1, 0x3),SCE(0x01cc, 2, 0x43),SCE(0x01cd, 0, 0x82),SCE(0x01ce, 1, 0x42),
+SCE(0x01cf, 0, 0x82),SCE(0x01d0, 1, 0x42),SCE(0x01d1, 0, 0x82),SCE(0x01d2, 1, 0x42),
+SCE(0x01d3, 0, 0x82),SCE(0x01d4, 1, 0x42),SCE(0x01d5, 0, 0x82),SCE(0x01d6, 1, 0x42),
+SCE(0x01d7, 0, 0x82),SCE(0x01d8, 1, 0x42),SCE(0x01d9, 0, 0x82),SCE(0x01da, 1, 0x42),
+SCE(0x01db, 0, 0x82),SCE(0x01dc, 1, 0x42),SCE(0x01de, 0, 0x82),SCE(0x01df, 1, 0x42),
+SCE(0xff36, 0, 0x82),SCE(0xff56, 1, 0x42),SCE(0x01e0, 0, 0x82),SCE(0x01e1, 1, 0x42),
+SCE(0x01e2, 0, 0x82),SCE(0x01e3, 1, 0x42),SCE(0x01e4, 0, 0x82),SCE(0x01e5, 1, 0x42),
+SCE(0x01e6, 0, 0x82),SCE(0x01e7, 1, 0x42),SCE(0x01e8, 0, 0x82),SCE(0x01e9, 1, 0x42),
+SCE(0x01ea, 0, 0x82),SCE(0x01eb, 1, 0x42),SCE(0x01ec, 0, 0x82),SCE(0x01ed, 1, 0x42),
+SCE(0x01ee, 0, 0x82),SCE(0x01ef, 1, 0x42),SCE(0x01f1, 0, 0x83),SCE(0x01f2, 1, 0x3),
+SCE(0x01f3, 2, 0x43),SCE(0x01f1, 0, 0x83),SCE(0x01f2, 1, 0x3),SCE(0x01f3, 2, 0x43),
+SCE(0x01f4, 0, 0x82),SCE(0x01f5, 1, 0x42),SCE(0x0195, 0, 0x42),SCE(0x01f6, 1, 0x82),
+SCE(0x01bf, 0, 0x42),SCE(0x01f7, 1, 0x82),SCE(0x01f8, 0, 0x82),SCE(0x01f9, 1, 0x42),
+SCE(0x1041d, 0, 0x82),SCE(0x10445, 1, 0x42),SCE(0x01fa, 0, 0x82),SCE(0x01fb, 1, 0x42),
+SCE(0x01fc, 0, 0x82),SCE(0x01fd, 1, 0x42),SCE(0x01fe, 0, 0x82),SCE(0x01ff, 1, 0x42),
+SCE(0x0200, 0, 0x82),SCE(0x0201, 1, 0x42),SCE(0x0202, 0, 0x82),SCE(0x0203, 1, 0x42),
+SCE(0x0204, 0, 0x82),SCE(0x0205, 1, 0x42),SCE(0x0206, 0, 0x82),SCE(0x0207, 1, 0x42),
+SCE(0x0208, 0, 0x82),SCE(0x0209, 1, 0x42),SCE(0x020a, 0, 0x82),SCE(0x020b, 1, 0x42),
+SCE(0x020c, 0, 0x82),SCE(0x020d, 1, 0x42),SCE(0x020e, 0, 0x82),SCE(0x020f, 1, 0x42),
+SCE(0x0210, 0, 0x82),SCE(0x0211, 1, 0x42),SCE(0x0212, 0, 0x82),SCE(0x0213, 1, 0x42),
+SCE(0x0214, 0, 0x82),SCE(0x0215, 1, 0x42),SCE(0x0216, 0, 0x82),SCE(0x0217, 1, 0x42),
+SCE(0x0218, 0, 0x82),SCE(0x0219, 1, 0x42),SCE(0x021a, 0, 0x82),SCE(0x021b, 1, 0x42),
+SCE(0x021c, 0, 0x82),SCE(0x021d, 1, 0x42),SCE(0x021e, 0, 0x82),SCE(0x021f, 1, 0x42),
+SCE(0x019e, 0, 0x42),SCE(0x0220, 1, 0x82),SCE(0x0222, 0, 0x82),SCE(0x0223, 1, 0x42),
+SCE(0x0224, 0, 0x82),SCE(0x0225, 1, 0x42),SCE(0x0226, 0, 0x82),SCE(0x0227, 1, 0x42),
+SCE(0x0228, 0, 0x82),SCE(0x0229, 1, 0x42),SCE(0x022a, 0, 0x82),SCE(0x022b, 1, 0x42),
+SCE(0x022c, 0, 0x82),SCE(0x022d, 1, 0x42),SCE(0x022e, 0, 0x82),SCE(0x022f, 1, 0x42),
+SCE(0x0230, 0, 0x82),SCE(0x0231, 1, 0x42),SCE(0x0232, 0, 0x82),SCE(0x0233, 1, 0x42),
+SCE(0xa684, 0, 0x82),SCE(0xa685, 1, 0x42),SCE(0x023a, 0, 0x82),SCE(0x2c65, 1, 0x42),
+SCE(0x023b, 0, 0x82),SCE(0x023c, 1, 0x42),SCE(0x019a, 0, 0x42),SCE(0x023d, 1, 0x82),
+SCE(0x023e, 0, 0x82),SCE(0x2c66, 1, 0x42),SCE(0x0241, 0, 0x82),SCE(0x0242, 1, 0x42),
+SCE(0x10412, 0, 0x82),SCE(0x1043a, 1, 0x42),SCE(0x0180, 0, 0x42),SCE(0x0243, 1, 0x82),
+SCE(0x0244, 0, 0x82),SCE(0x0289, 1, 0x42),SCE(0x0245, 0, 0x82),SCE(0x028c, 1, 0x42),
+SCE(0x0246, 0, 0x82),SCE(0x0247, 1, 0x42),SCE(0x0248, 0, 0x82),SCE(0x0249, 1, 0x42),
+SCE(0x024a, 0, 0x82),SCE(0x024b, 1, 0x42),SCE(0x024c, 0, 0x82),SCE(0x024d, 1, 0x42),
+SCE(0x2c1b, 0, 0x82),SCE(0x2c4b, 1, 0x42),SCE(0x024e, 0, 0x82),SCE(0x024f, 1, 0x42),
+SCE(0x1040a, 0, 0x82),SCE(0x10432, 1, 0x42),SCE(0x2160, 0, 0x82),SCE(0x2170, 1, 0x42),
+SCE(0xa692, 0, 0x82),SCE(0xa693, 1, 0x42),SCE(0x027d, 0, 0x42),SCE(0x2c64, 1, 0x82),
+SCE(0x10410, 0, 0x82),SCE(0x10438, 1, 0x42),SCE(0x2c21, 0, 0x82),SCE(0x2c51, 1, 0x42),
+SCE(0x2c69, 0, 0x82),SCE(0x2c6a, 1, 0x42),SCE(0x10409, 0, 0x82),SCE(0x10431, 1, 0x42),
+SCE(0x10414, 0, 0x82),SCE(0x1043c, 1, 0x42),SCE(0x2162, 0, 0x82),SCE(0x2172, 1, 0x42),
+SCE(0x1041e, 0, 0x82),SCE(0x10446, 1, 0x42),SCE(0x0271, 0, 0x42),SCE(0x2c6e, 1, 0x82),
+SCE(0x10415, 0, 0x82),SCE(0x1043d, 1, 0x42),SCE(0x0252, 0, 0x42),SCE(0x2c70, 1, 0x82),
+SCE(0x2c72, 0, 0x82),SCE(0x2c73, 1, 0x42),SCE(0x2c0b, 0, 0x82),SCE(0x2c3b, 1, 0x42),
+SCE(0x10416, 0, 0x82),SCE(0x1043e, 1, 0x42),SCE(0x2c75, 0, 0x82),SCE(0x2c76, 1, 0x42),
+SCE(0x2164, 0, 0x82),SCE(0x2174, 1, 0x42),SCE(0xa640, 0, 0x82),SCE(0xa641, 1, 0x42),
+SCE(0xff22, 0, 0x82),SCE(0xff42, 1, 0x42),SCE(0x2c0c, 0, 0x82),SCE(0x2c3c, 1, 0x42),
+SCE(0x10417, 0, 0x82),SCE(0x1043f, 1, 0x42),SCE(0xff24, 0, 0x82),SCE(0xff44, 1, 0x42),
+SCE(0xff25, 0, 0x82),SCE(0xff45, 1, 0x42),SCE(0xff26, 0, 0x82),SCE(0xff46, 1, 0x42),
+SCE(0x2c0d, 0, 0x82),SCE(0x2c3d, 1, 0x42),SCE(0x24c1, 0, 0x82),SCE(0x24db, 1, 0x42),
+SCE(0xa728, 0, 0x82),SCE(0xa729, 1, 0x42),SCE(0x023f, 0, 0x42),SCE(0x2c7e, 1, 0x82),
+SCE(0x10411, 0, 0x82),SCE(0x10439, 1, 0x42),SCE(0xff29, 0, 0x82),SCE(0xff49, 1, 0x42),
+SCE(0x1040b, 0, 0x82),SCE(0x10433, 1, 0x42),SCE(0xa72a, 0, 0x82),SCE(0xa72b, 1, 0x42),
+SCE(0x2c80, 0, 0x82),SCE(0x2c81, 1, 0x42),SCE(0xff2b, 0, 0x82),SCE(0xff4b, 1, 0x42),
+SCE(0xa72c, 0, 0x82),SCE(0xa72d, 1, 0x42),SCE(0x2c0e, 0, 0x82),SCE(0x2c3e, 1, 0x42),
+SCE(0xff2d, 0, 0x82),SCE(0xff4d, 1, 0x42),SCE(0x10419, 0, 0x82),SCE(0x10441, 1, 0x42),
+SCE(0xa72e, 0, 0x82),SCE(0xa72f, 1, 0x42),SCE(0x1040d, 0, 0x82),SCE(0x10435, 1, 0x42),
+SCE(0xff2f, 0, 0x82),SCE(0xff4f, 1, 0x42),SCE(0xff31, 0, 0x82),SCE(0xff51, 1, 0x42),
+SCE(0xff32, 0, 0x82),SCE(0xff52, 1, 0x42),SCE(0x1041a, 0, 0x82),SCE(0x10442, 1, 0x42),
+SCE(0xff34, 0, 0x82),SCE(0xff54, 1, 0x42),SCE(0x2c98, 0, 0x82),SCE(0x2c99, 1, 0x42),
+SCE(0x2c8a, 0, 0x82),SCE(0x2c8b, 1, 0x42),SCE(0x0345, 0, 0x44),SCE(0x0399, 1, 0x84),
+SCE(0x03b9, 2, 0x44),SCE(0x1fbe, 3, 0x44),SCE(0x2c8c, 0, 0x82),SCE(0x2c8d, 1, 0x42),
+SCE(0xff37, 0, 0x82),SCE(0xff57, 1, 0x42),SCE(0xa656, 0, 0x82),SCE(0xa657, 1, 0x42),
+SCE(0x1041b, 0, 0x82),SCE(0x10443, 1, 0x42),SCE(0xa738, 0, 0x82),SCE(0xa739, 1, 0x42),
+SCE(0x2c8e, 0, 0x82),SCE(0x2c8f, 1, 0x42),SCE(0xff39, 0, 0x82),SCE(0xff59, 1, 0x42),
+SCE(0x10404, 0, 0x82),SCE(0x1042c, 1, 0x42),SCE(0xa73a, 0, 0x82),SCE(0xa73b, 1, 0x42),
+SCE(0x2c90, 0, 0x82),SCE(0x2c91, 1, 0x42),SCE(0xa73c, 0, 0x82),SCE(0xa73d, 1, 0x42),
+SCE(0x2c92, 0, 0x82),SCE(0x2c93, 1, 0x42),SCE(0x1041c, 0, 0x82),SCE(0x10444, 1, 0x42),
+SCE(0x0370, 0, 0x82),SCE(0x0371, 1, 0x42),SCE(0x0372, 0, 0x82),SCE(0x0373, 1, 0x42),
+SCE(0xa73e, 0, 0x82),SCE(0xa73f, 1, 0x42),SCE(0x0376, 0, 0x82),SCE(0x0377, 1, 0x42),
+SCE(0x2c94, 0, 0x82),SCE(0x2c95, 1, 0x42),SCE(0x2c96, 0, 0x82),SCE(0x2c97, 1, 0x42),
+SCE(0x0386, 0, 0x82),SCE(0x03ac, 1, 0x42),SCE(0x10405, 0, 0x82),SCE(0x1042d, 1, 0x42),
+SCE(0x0388, 0, 0x82),SCE(0x03ad, 1, 0x42),SCE(0x0389, 0, 0x82),SCE(0x03ae, 1, 0x42),
+SCE(0x038a, 0, 0x82),SCE(0x03af, 1, 0x42),SCE(0x038c, 0, 0x82),SCE(0x03cc, 1, 0x42),
+SCE(0x038e, 0, 0x82),SCE(0x03cd, 1, 0x42),SCE(0x038f, 0, 0x82),SCE(0x03ce, 1, 0x42),
+SCE(0x0391, 0, 0x82),SCE(0x03b1, 1, 0x42),SCE(0x0392, 0, 0x83),SCE(0x03b2, 1, 0x43),
+SCE(0x03d0, 2, 0x43),SCE(0x0393, 0, 0x82),SCE(0x03b3, 1, 0x42),SCE(0x0394, 0, 0x82),
+SCE(0x03b4, 1, 0x42),SCE(0x0395, 0, 0x83),SCE(0x03b5, 1, 0x43),SCE(0x03f5, 2, 0x43),
+SCE(0x0396, 0, 0x82),SCE(0x03b6, 1, 0x42),SCE(0x0397, 0, 0x82),SCE(0x03b7, 1, 0x42),
+SCE(0x0398, 0, 0x84),SCE(0x03b8, 1, 0x44),SCE(0x03d1, 2, 0x44),SCE(0x03f4, 3, 0x84),
+SCE(0x0345, 0, 0x44),SCE(0x0399, 1, 0x84),SCE(0x03b9, 2, 0x44),SCE(0x1fbe, 3, 0x44),
+SCE(0x039a, 0, 0x83),SCE(0x03ba, 1, 0x43),SCE(0x03f0, 2, 0x43),SCE(0x039b, 0, 0x82),
+SCE(0x03bb, 1, 0x42),SCE(0x00b5, 0, 0x43),SCE(0x039c, 1, 0x83),SCE(0x03bc, 2, 0x43),
+SCE(0x039d, 0, 0x82),SCE(0x03bd, 1, 0x42),SCE(0x039e, 0, 0x82),SCE(0x03be, 1, 0x42),
+SCE(0x039f, 0, 0x82),SCE(0x03bf, 1, 0x42),SCE(0x03a0, 0, 0x83),SCE(0x03c0, 1, 0x43),
+SCE(0x03d6, 2, 0x43),SCE(0x03a1, 0, 0x83),SCE(0x03c1, 1, 0x43),SCE(0x03f1, 2, 0x43),
+SCE(0x03a3, 0, 0x83),SCE(0x03c2, 1, 0x43),SCE(0x03c3, 2, 0x43),SCE(0x03a4, 0, 0x82),
+SCE(0x03c4, 1, 0x42),SCE(0x03a5, 0, 0x82),SCE(0x03c5, 1, 0x42),SCE(0x03a6, 0, 0x83),
+SCE(0x03c6, 1, 0x43),SCE(0x03d5, 2, 0x43),SCE(0x03a7, 0, 0x82),SCE(0x03c7, 1, 0x42),
+SCE(0x03a8, 0, 0x82),SCE(0x03c8, 1, 0x42),SCE(0x03a9, 0, 0x83),SCE(0x03c9, 1, 0x43),
+SCE(0x2126, 2, 0x83),SCE(0x03aa, 0, 0x82),SCE(0x03ca, 1, 0x42),SCE(0x03ab, 0, 0x82),
+SCE(0x03cb, 1, 0x42),SCE(0x24c9, 0, 0x82),SCE(0x24e3, 1, 0x42),SCE(0x2ce0, 0, 0x82),
+SCE(0x2ce1, 1, 0x42),SCE(0xa748, 0, 0x82),SCE(0xa749, 1, 0x42),SCE(0x2c9c, 0, 0x82),
+SCE(0x2c9d, 1, 0x42),SCE(0x2c9e, 0, 0x82),SCE(0x2c9f, 1, 0x42),SCE(0xa74a, 0, 0x82),
+SCE(0xa74b, 1, 0x42),SCE(0x2ca0, 0, 0x82),SCE(0x2ca1, 1, 0x42),SCE(0x03a3, 0, 0x83),
+SCE(0x03c2, 1, 0x43),SCE(0x03c3, 2, 0x43),SCE(0x1041f, 0, 0x82),SCE(0x10447, 1, 0x42),
+SCE(0xa74c, 0, 0x82),SCE(0xa74d, 1, 0x42),SCE(0xa68a, 0, 0x82),SCE(0xa68b, 1, 0x42),
+SCE(0x2ca2, 0, 0x82),SCE(0x2ca3, 1, 0x42),SCE(0x03cf, 0, 0x82),SCE(0x03d7, 1, 0x42),
+SCE(0x0392, 0, 0x83),SCE(0x03b2, 1, 0x43),SCE(0x03d0, 2, 0x43),SCE(0x0398, 0, 0x84),
+SCE(0x03b8, 1, 0x44),SCE(0x03d1, 2, 0x44),SCE(0x03f4, 3, 0x84),SCE(0x03a6, 0, 0x83),
+SCE(0x03c6, 1, 0x43),SCE(0x03d5, 2, 0x43),SCE(0x03a0, 0, 0x83),SCE(0x03c0, 1, 0x43),
+SCE(0x03d6, 2, 0x43),SCE(0x03d8, 0, 0x82),SCE(0x03d9, 1, 0x42),SCE(0x2ca4, 0, 0x82),
+SCE(0x2ca5, 1, 0x42),SCE(0x03da, 0, 0x82),SCE(0x03db, 1, 0x42),SCE(0x03dc, 0, 0x82),
+SCE(0x03dd, 1, 0x42),SCE(0x03de, 0, 0x82),SCE(0x03df, 1, 0x42),SCE(0x03e0, 0, 0x82),
+SCE(0x03e1, 1, 0x42),SCE(0x03e2, 0, 0x82),SCE(0x03e3, 1, 0x42),SCE(0x03e4, 0, 0x82),
+SCE(0x03e5, 1, 0x42),SCE(0x2ca6, 0, 0x82),SCE(0x2ca7, 1, 0x42),SCE(0x03e6, 0, 0x82),
+SCE(0x03e7, 1, 0x42),SCE(0x10420, 0, 0x82),SCE(0x10448, 1, 0x42),SCE(0x03e8, 0, 0x82),
+SCE(0x03e9, 1, 0x42),SCE(0x2ce2, 0, 0x82),SCE(0x2ce3, 1, 0x42),SCE(0x03ea, 0, 0x82),
+SCE(0x03eb, 1, 0x42),SCE(0x03ec, 0, 0x82),SCE(0x03ed, 1, 0x42),SCE(0x03ee, 0, 0x82),
+SCE(0x03ef, 1, 0x42),SCE(0x039a, 0, 0x83),SCE(0x03ba, 1, 0x43),SCE(0x03f0, 2, 0x43),
+SCE(0x03a1, 0, 0x83),SCE(0x03c1, 1, 0x43),SCE(0x03f1, 2, 0x43),SCE(0x0398, 0, 0x84),
+SCE(0x03b8, 1, 0x44),SCE(0x03d1, 2, 0x44),SCE(0x03f4, 3, 0x84),SCE(0x0395, 0, 0x83),
+SCE(0x03b5, 1, 0x43),SCE(0x03f5, 2, 0x43),SCE(0x03f7, 0, 0x82),SCE(0x03f8, 1, 0x42),
+SCE(0x03f2, 0, 0x42),SCE(0x03f9, 1, 0x82),SCE(0x03fa, 0, 0x82),SCE(0x03fb, 1, 0x42),
+SCE(0x037b, 0, 0x42),SCE(0x03fd, 1, 0x82),SCE(0x037c, 0, 0x42),SCE(0x03fe, 1, 0x82),
+SCE(0x037d, 0, 0x42),SCE(0x03ff, 1, 0x82),SCE(0x0400, 0, 0x82),SCE(0x0450, 1, 0x42),
+SCE(0x0401, 0, 0x82),SCE(0x0451, 1, 0x42),SCE(0x0402, 0, 0x82),SCE(0x0452, 1, 0x42),
+SCE(0x0403, 0, 0x82),SCE(0x0453, 1, 0x42),SCE(0x0404, 0, 0x82),SCE(0x0454, 1, 0x42),
+SCE(0x0405, 0, 0x82),SCE(0x0455, 1, 0x42),SCE(0x0406, 0, 0x82),SCE(0x0456, 1, 0x42),
+SCE(0x0407, 0, 0x82),SCE(0x0457, 1, 0x42),SCE(0x0408, 0, 0x82),SCE(0x0458, 1, 0x42),
+SCE(0x0409, 0, 0x82),SCE(0x0459, 1, 0x42),SCE(0x040a, 0, 0x82),SCE(0x045a, 1, 0x42),
+SCE(0x040b, 0, 0x82),SCE(0x045b, 1, 0x42),SCE(0x040c, 0, 0x82),SCE(0x045c, 1, 0x42),
+SCE(0x040d, 0, 0x82),SCE(0x045d, 1, 0x42),SCE(0x040e, 0, 0x82),SCE(0x045e, 1, 0x42),
+SCE(0x040f, 0, 0x82),SCE(0x045f, 1, 0x42),SCE(0x0410, 0, 0x82),SCE(0x0430, 1, 0x42),
+SCE(0x0411, 0, 0x82),SCE(0x0431, 1, 0x42),SCE(0x0412, 0, 0x82),SCE(0x0432, 1, 0x42),
+SCE(0x0413, 0, 0x82),SCE(0x0433, 1, 0x42),SCE(0x0414, 0, 0x82),SCE(0x0434, 1, 0x42),
+SCE(0x0415, 0, 0x82),SCE(0x0435, 1, 0x42),SCE(0x0416, 0, 0x82),SCE(0x0436, 1, 0x42),
+SCE(0x0417, 0, 0x82),SCE(0x0437, 1, 0x42),SCE(0x0418, 0, 0x82),SCE(0x0438, 1, 0x42),
+SCE(0x0419, 0, 0x82),SCE(0x0439, 1, 0x42),SCE(0x041a, 0, 0x82),SCE(0x043a, 1, 0x42),
+SCE(0x041b, 0, 0x82),SCE(0x043b, 1, 0x42),SCE(0x041c, 0, 0x82),SCE(0x043c, 1, 0x42),
+SCE(0x041d, 0, 0x82),SCE(0x043d, 1, 0x42),SCE(0x041e, 0, 0x82),SCE(0x043e, 1, 0x42),
+SCE(0x041f, 0, 0x82),SCE(0x043f, 1, 0x42),SCE(0x0420, 0, 0x82),SCE(0x0440, 1, 0x42),
+SCE(0x0421, 0, 0x82),SCE(0x0441, 1, 0x42),SCE(0x0422, 0, 0x82),SCE(0x0442, 1, 0x42),
+SCE(0x0423, 0, 0x82),SCE(0x0443, 1, 0x42),SCE(0x0424, 0, 0x82),SCE(0x0444, 1, 0x42),
+SCE(0x0425, 0, 0x82),SCE(0x0445, 1, 0x42),SCE(0x0426, 0, 0x82),SCE(0x0446, 1, 0x42),
+SCE(0x0427, 0, 0x82),SCE(0x0447, 1, 0x42),SCE(0x0428, 0, 0x82),SCE(0x0448, 1, 0x42),
+SCE(0x0429, 0, 0x82),SCE(0x0449, 1, 0x42),SCE(0x042a, 0, 0x82),SCE(0x044a, 1, 0x42),
+SCE(0x042b, 0, 0x82),SCE(0x044b, 1, 0x42),SCE(0x042c, 0, 0x82),SCE(0x044c, 1, 0x42),
+SCE(0x042d, 0, 0x82),SCE(0x044d, 1, 0x42),SCE(0x042e, 0, 0x82),SCE(0x044e, 1, 0x42),
+SCE(0x042f, 0, 0x82),SCE(0x044f, 1, 0x42),SCE(0xff3a, 0, 0x82),SCE(0xff5a, 1, 0x42),
+SCE(0x2cb4, 0, 0x82),SCE(0x2cb5, 1, 0x42),SCE(0x00b5, 0, 0x43),SCE(0x039c, 1, 0x83),
+SCE(0x03bc, 2, 0x43),SCE(0x10423, 0, 0x82),SCE(0x1044b, 1, 0x42),SCE(0x24b6, 0, 0x82),
+SCE(0x24d0, 1, 0x42),SCE(0x24b8, 0, 0x82),SCE(0x24d2, 1, 0x42),SCE(0xff2c, 0, 0x82),
+SCE(0xff4c, 1, 0x42),SCE(0x10421, 0, 0x82),SCE(0x10449, 1, 0x42),SCE(0x24ba, 0, 0x82),
+SCE(0x24d4, 1, 0x42),SCE(0x10424, 0, 0x82),SCE(0x1044c, 1, 0x42),SCE(0x0460, 0, 0x82),
+SCE(0x0461, 1, 0x42),SCE(0x0462, 0, 0x82),SCE(0x0463, 1, 0x42),SCE(0x1d7d, 0, 0x42),
+SCE(0x2c63, 1, 0x82),SCE(0x0464, 0, 0x82),SCE(0x0465, 1, 0x42),SCE(0x0466, 0, 0x82),
+SCE(0x0467, 1, 0x42),SCE(0x2c67, 0, 0x82),SCE(0x2c68, 1, 0x42),SCE(0x0468, 0, 0x82),
+SCE(0x0469, 1, 0x42),SCE(0x24bc, 0, 0x82),SCE(0x24d6, 1, 0x42),SCE(0x046a, 0, 0x82),
+SCE(0x046b, 1, 0x42),SCE(0x2c6b, 0, 0x82),SCE(0x2c6c, 1, 0x42),SCE(0x046c, 0, 0x82),
+SCE(0x046d, 1, 0x42),SCE(0x0251, 0, 0x42),SCE(0x2c6d, 1, 0x82),SCE(0x046e, 0, 0x82),
+SCE(0x046f, 1, 0x42),SCE(0x0250, 0, 0x42),SCE(0x2c6f, 1, 0x82),SCE(0x0470, 0, 0x82),
+SCE(0x0471, 1, 0x42),SCE(0xa768, 0, 0x82),SCE(0xa769, 1, 0x42),SCE(0x0472, 0, 0x82),
+SCE(0x0473, 1, 0x42),SCE(0x0474, 0, 0x82),SCE(0x0475, 1, 0x42),SCE(0x24be, 0, 0x82),
+SCE(0x24d8, 1, 0x42),SCE(0x0476, 0, 0x82),SCE(0x0477, 1, 0x42),SCE(0x0478, 0, 0x82),
+SCE(0x0479, 1, 0x42),SCE(0x047a, 0, 0x82),SCE(0x047b, 1, 0x42),SCE(0x047c, 0, 0x82),
+SCE(0x047d, 1, 0x42),SCE(0xa76a, 0, 0x82),SCE(0xa76b, 1, 0x42),SCE(0x047e, 0, 0x82),
+SCE(0x047f, 1, 0x42),SCE(0x0240, 0, 0x42),SCE(0x2c7f, 1, 0x82),SCE(0x0480, 0, 0x82),
+SCE(0x0481, 1, 0x42),SCE(0x10c0, 0, 0x82),SCE(0x2d20, 1, 0x42),SCE(0x2c82, 0, 0x82),
+SCE(0x2c83, 1, 0x42),SCE(0x2c84, 0, 0x82),SCE(0x2c85, 1, 0x42),SCE(0x2c86, 0, 0x82),
+SCE(0x2c87, 1, 0x42),SCE(0x10c1, 0, 0x82),SCE(0x2d21, 1, 0x42),SCE(0x2c88, 0, 0x82),
+SCE(0x2c89, 1, 0x42),SCE(0xa76c, 0, 0x82),SCE(0xa76d, 1, 0x42),SCE(0x048a, 0, 0x82),
+SCE(0x048b, 1, 0x42),SCE(0x048c, 0, 0x82),SCE(0x048d, 1, 0x42),SCE(0x00c2, 0, 0x82),
+SCE(0x00e2, 1, 0x42),SCE(0x048e, 0, 0x82),SCE(0x048f, 1, 0x42),SCE(0x0490, 0, 0x82),
+SCE(0x0491, 1, 0x42),SCE(0x0492, 0, 0x82),SCE(0x0493, 1, 0x42),SCE(0x10c3, 0, 0x82),
+SCE(0x2d23, 1, 0x42),SCE(0x0494, 0, 0x82),SCE(0x0495, 1, 0x42),SCE(0xa76e, 0, 0x82),
+SCE(0xa76f, 1, 0x42),SCE(0x0496, 0, 0x82),SCE(0x0497, 1, 0x42),SCE(0x0498, 0, 0x82),
+SCE(0x0499, 1, 0x42),SCE(0x00c4, 0, 0x82),SCE(0x00e4, 1, 0x42),SCE(0x049a, 0, 0x82),
+SCE(0x049b, 1, 0x42),SCE(0x10426, 0, 0x82),SCE(0x1044e, 1, 0x42),SCE(0x049c, 0, 0x82),
+SCE(0x049d, 1, 0x42),SCE(0x049e, 0, 0x82),SCE(0x049f, 1, 0x42),SCE(0x10c5, 0, 0x82),
+SCE(0x2d25, 1, 0x42),SCE(0x04a0, 0, 0x82),SCE(0x04a1, 1, 0x42),SCE(0x04a2, 0, 0x82),
+SCE(0x04a3, 1, 0x42),SCE(0x04a4, 0, 0x82),SCE(0x04a5, 1, 0x42),SCE(0x2cc6, 0, 0x82),
+SCE(0x2cc7, 1, 0x42),SCE(0x04a6, 0, 0x82),SCE(0x04a7, 1, 0x42),SCE(0x04a8, 0, 0x82),
+SCE(0x04a9, 1, 0x42),SCE(0x2c60, 0, 0x82),SCE(0x2c61, 1, 0x42),SCE(0x04aa, 0, 0x82),
+SCE(0x04ab, 1, 0x42),SCE(0x10c7, 0, 0x82),SCE(0x2d27, 1, 0x42),SCE(0x04ac, 0, 0x82),
+SCE(0x04ad, 1, 0x42),SCE(0x10413, 0, 0x82),SCE(0x1043b, 1, 0x42),SCE(0x04ae, 0, 0x82),
+SCE(0x04af, 1, 0x42),SCE(0x04b0, 0, 0x82),SCE(0x04b1, 1, 0x42),SCE(0x2cc8, 0, 0x82),
+SCE(0x2cc9, 1, 0x42),SCE(0x04b2, 0, 0x82),SCE(0x04b3, 1, 0x42),SCE(0x04b4, 0, 0x82),
+SCE(0x04b5, 1, 0x42),SCE(0x04b6, 0, 0x82),SCE(0x04b7, 1, 0x42),SCE(0x24b7, 0, 0x82),
+SCE(0x24d1, 1, 0x42),SCE(0x04b8, 0, 0x82),SCE(0x04b9, 1, 0x42),SCE(0x24b9, 0, 0x82),
+SCE(0x24d3, 1, 0x42),SCE(0x04ba, 0, 0x82),SCE(0x04bb, 1, 0x42),SCE(0x24bb, 0, 0x82),
+SCE(0x24d5, 1, 0x42),SCE(0x04bc, 0, 0x82),SCE(0x04bd, 1, 0x42),SCE(0x24bd, 0, 0x82),
+SCE(0x24d7, 1, 0x42),SCE(0x04be, 0, 0x82),SCE(0x04bf, 1, 0x42),SCE(0x24bf, 0, 0x82),
+SCE(0x24d9, 1, 0x42),SCE(0x04c0, 0, 0x82),SCE(0x04cf, 1, 0x42),SCE(0x04c1, 0, 0x82),
+SCE(0x04c2, 1, 0x42),SCE(0x24c2, 0, 0x82),SCE(0x24dc, 1, 0x42),SCE(0x04c3, 0, 0x82),
+SCE(0x04c4, 1, 0x42),SCE(0x24c4, 0, 0x82),SCE(0x24de, 1, 0x42),SCE(0x04c5, 0, 0x82),
+SCE(0x04c6, 1, 0x42),SCE(0x24c6, 0, 0x82),SCE(0x24e0, 1, 0x42),SCE(0x04c7, 0, 0x82),
+SCE(0x04c8, 1, 0x42),SCE(0x24c8, 0, 0x82),SCE(0x24e2, 1, 0x42),SCE(0x04c9, 0, 0x82),
+SCE(0x04ca, 1, 0x42),SCE(0x24ca, 0, 0x82),SCE(0x24e4, 1, 0x42),SCE(0x04cb, 0, 0x82),
+SCE(0x04cc, 1, 0x42),SCE(0x24cc, 0, 0x82),SCE(0x24e6, 1, 0x42),SCE(0x04cd, 0, 0x82),
+SCE(0x04ce, 1, 0x42),SCE(0x24ce, 0, 0x82),SCE(0x24e8, 1, 0x42),SCE(0x10cd, 0, 0x82),
+SCE(0x2d2d, 1, 0x42),SCE(0x04d0, 0, 0x82),SCE(0x04d1, 1, 0x42),SCE(0x04d2, 0, 0x82),
+SCE(0x04d3, 1, 0x42),SCE(0x04d4, 0, 0x82),SCE(0x04d5, 1, 0x42),SCE(0x2cce, 0, 0x82),
+SCE(0x2ccf, 1, 0x42),SCE(0x04d6, 0, 0x82),SCE(0x04d7, 1, 0x42),SCE(0xa779, 0, 0x82),
+SCE(0xa77a, 1, 0x42),SCE(0x04d8, 0, 0x82),SCE(0x04d9, 1, 0x42),SCE(0x04da, 0, 0x82),
+SCE(0x04db, 1, 0x42),SCE(0x24cf, 0, 0x82),SCE(0x24e9, 1, 0x42),SCE(0x04dc, 0, 0x82),
+SCE(0x04dd, 1, 0x42),SCE(0x04de, 0, 0x82),SCE(0x04df, 1, 0x42),SCE(0x04e0, 0, 0x82),
+SCE(0x04e1, 1, 0x42),SCE(0x2cd0, 0, 0x82),SCE(0x2cd1, 1, 0x42),SCE(0x04e2, 0, 0x82),
+SCE(0x04e3, 1, 0x42),SCE(0x04e4, 0, 0x82),SCE(0x04e5, 1, 0x42),SCE(0x026b, 0, 0x42),
+SCE(0x2c62, 1, 0x82),SCE(0x04e6, 0, 0x82),SCE(0x04e7, 1, 0x42),SCE(0x04e8, 0, 0x82),
+SCE(0x04e9, 1, 0x42),SCE(0x04ea, 0, 0x82),SCE(0x04eb, 1, 0x42),SCE(0x2132, 0, 0x82),
+SCE(0x214e, 1, 0x42),SCE(0x04ec, 0, 0x82),SCE(0x04ed, 1, 0x42),SCE(0x2cd2, 0, 0x82),
+SCE(0x2cd3, 1, 0x42),SCE(0x04ee, 0, 0x82),SCE(0x04ef, 1, 0x42),SCE(0x04f0, 0, 0x82),
+SCE(0x04f1, 1, 0x42),SCE(0x10422, 0, 0x82),SCE(0x1044a, 1, 0x42),SCE(0x04f2, 0, 0x82),
+SCE(0x04f3, 1, 0x42),SCE(0x04f4, 0, 0x82),SCE(0x04f5, 1, 0x42),SCE(0x04f6, 0, 0x82),
+SCE(0x04f7, 1, 0x42),SCE(0x04f8, 0, 0x82),SCE(0x04f9, 1, 0x42),SCE(0x2cd4, 0, 0x82),
+SCE(0x2cd5, 1, 0x42),SCE(0x04fa, 0, 0x82),SCE(0x04fb, 1, 0x42),SCE(0x04fc, 0, 0x82),
+SCE(0x04fd, 1, 0x42),SCE(0x04fe, 0, 0x82),SCE(0x04ff, 1, 0x42),SCE(0x0500, 0, 0x82),
+SCE(0x0501, 1, 0x42),SCE(0x0502, 0, 0x82),SCE(0x0503, 1, 0x42),SCE(0x0504, 0, 0x82),
+SCE(0x0505, 1, 0x42),SCE(0x2cd6, 0, 0x82),SCE(0x2cd7, 1, 0x42),SCE(0x0506, 0, 0x82),
+SCE(0x0507, 1, 0x42),SCE(0x0508, 0, 0x82),SCE(0x0509, 1, 0x42),SCE(0x050a, 0, 0x82),
+SCE(0x050b, 1, 0x42),SCE(0x050c, 0, 0x82),SCE(0x050d, 1, 0x42),SCE(0x050e, 0, 0x82),
+SCE(0x050f, 1, 0x42),SCE(0x0510, 0, 0x82),SCE(0x0511, 1, 0x42),SCE(0x2cd8, 0, 0x82),
+SCE(0x2cd9, 1, 0x42),SCE(0x0512, 0, 0x82),SCE(0x0513, 1, 0x42),SCE(0x0514, 0, 0x82),
+SCE(0x0515, 1, 0x42),SCE(0x0516, 0, 0x82),SCE(0x0517, 1, 0x42),SCE(0x0518, 0, 0x82),
+SCE(0x0519, 1, 0x42),SCE(0x051a, 0, 0x82),SCE(0x051b, 1, 0x42),SCE(0x2ca8, 0, 0x82),
+SCE(0x2ca9, 1, 0x42),SCE(0x051c, 0, 0x82),SCE(0x051d, 1, 0x42),SCE(0x2cda, 0, 0x82),
+SCE(0x2cdb, 1, 0x42),SCE(0x051e, 0, 0x82),SCE(0x051f, 1, 0x42),SCE(0x0520, 0, 0x82),
+SCE(0x0521, 1, 0x42),SCE(0x0522, 0, 0x82),SCE(0x0523, 1, 0x42),SCE(0x0524, 0, 0x82),
+SCE(0x0525, 1, 0x42),SCE(0x0526, 0, 0x82),SCE(0x0527, 1, 0x42),SCE(0x2c20, 0, 0x82),
+SCE(0x2c50, 1, 0x42),SCE(0x2cdc, 0, 0x82),SCE(0x2cdd, 1, 0x42),SCE(0x0531, 0, 0x82),
+SCE(0x0561, 1, 0x42),SCE(0x0532, 0, 0x82),SCE(0x0562, 1, 0x42),SCE(0x0533, 0, 0x82),
+SCE(0x0563, 1, 0x42),SCE(0x0534, 0, 0x82),SCE(0x0564, 1, 0x42),SCE(0x0535, 0, 0x82),
+SCE(0x0565, 1, 0x42),SCE(0x0536, 0, 0x82),SCE(0x0566, 1, 0x42),SCE(0x0537, 0, 0x82),
+SCE(0x0567, 1, 0x42),SCE(0x0538, 0, 0x82),SCE(0x0568, 1, 0x42),SCE(0x0539, 0, 0x82),
+SCE(0x0569, 1, 0x42),SCE(0x053a, 0, 0x82),SCE(0x056a, 1, 0x42),SCE(0x053b, 0, 0x82),
+SCE(0x056b, 1, 0x42),SCE(0x053c, 0, 0x82),SCE(0x056c, 1, 0x42),SCE(0x053d, 0, 0x82),
+SCE(0x056d, 1, 0x42),SCE(0x053e, 0, 0x82),SCE(0x056e, 1, 0x42),SCE(0x053f, 0, 0x82),
+SCE(0x056f, 1, 0x42),SCE(0x0540, 0, 0x82),SCE(0x0570, 1, 0x42),SCE(0x0541, 0, 0x82),
+SCE(0x0571, 1, 0x42),SCE(0x0542, 0, 0x82),SCE(0x0572, 1, 0x42),SCE(0x0543, 0, 0x82),
+SCE(0x0573, 1, 0x42),SCE(0x0544, 0, 0x82),SCE(0x0574, 1, 0x42),SCE(0x0545, 0, 0x82),
+SCE(0x0575, 1, 0x42),SCE(0x0546, 0, 0x82),SCE(0x0576, 1, 0x42),SCE(0x0547, 0, 0x82),
+SCE(0x0577, 1, 0x42),SCE(0x0548, 0, 0x82),SCE(0x0578, 1, 0x42),SCE(0x0549, 0, 0x82),
+SCE(0x0579, 1, 0x42),SCE(0x054a, 0, 0x82),SCE(0x057a, 1, 0x42),SCE(0x054b, 0, 0x82),
+SCE(0x057b, 1, 0x42),SCE(0x054c, 0, 0x82),SCE(0x057c, 1, 0x42),SCE(0x054d, 0, 0x82),
+SCE(0x057d, 1, 0x42),SCE(0x054e, 0, 0x82),SCE(0x057e, 1, 0x42),SCE(0x054f, 0, 0x82),
+SCE(0x057f, 1, 0x42),SCE(0x0550, 0, 0x82),SCE(0x0580, 1, 0x42),SCE(0x0551, 0, 0x82),
+SCE(0x0581, 1, 0x42),SCE(0x0552, 0, 0x82),SCE(0x0582, 1, 0x42),SCE(0x0553, 0, 0x82),
+SCE(0x0583, 1, 0x42),SCE(0x0554, 0, 0x82),SCE(0x0584, 1, 0x42),SCE(0x0555, 0, 0x82),
+SCE(0x0585, 1, 0x42),SCE(0x0556, 0, 0x82),SCE(0x0586, 1, 0x42),SCE(0x2caa, 0, 0x82),
+SCE(0x2cab, 1, 0x42),SCE(0x2c22, 0, 0x82),SCE(0x2c52, 1, 0x42),SCE(0x2c23, 0, 0x82),
+SCE(0x2c53, 1, 0x42),SCE(0x2ceb, 0, 0x82),SCE(0x2cec, 1, 0x42),SCE(0x2cca, 0, 0x82),
+SCE(0x2ccb, 1, 0x42),SCE(0xa642, 0, 0x82),SCE(0xa643, 1, 0x42),SCE(0x2ced, 0, 0x82),
+SCE(0x2cee, 1, 0x42),SCE(0x2cac, 0, 0x82),SCE(0x2cad, 1, 0x42),SCE(0xa644, 0, 0x82),
+SCE(0xa645, 1, 0x42),SCE(0x2c24, 0, 0x82),SCE(0x2c54, 1, 0x42),SCE(0xa646, 0, 0x82),
+SCE(0xa647, 1, 0x42),SCE(0x2cf2, 0, 0x82),SCE(0x2cf3, 1, 0x42),SCE(0x10408, 0, 0x82),
+SCE(0x10430, 1, 0x42),SCE(0xa648, 0, 0x82),SCE(0xa649, 1, 0x42),SCE(0xa64a, 0, 0x82),
+SCE(0xa64b, 1, 0x42),SCE(0xa64c, 0, 0x82),SCE(0xa64d, 1, 0x42),SCE(0x2cae, 0, 0x82),
+SCE(0x2caf, 1, 0x42),SCE(0xa64e, 0, 0x82),SCE(0xa64f, 1, 0x42),SCE(0xa650, 0, 0x82),
+SCE(0xa651, 1, 0x42),SCE(0xa78b, 0, 0x82),SCE(0xa78c, 1, 0x42),SCE(0xa652, 0, 0x82),
+SCE(0xa653, 1, 0x42),SCE(0xa7a8, 0, 0x82),SCE(0xa7a9, 1, 0x42),SCE(0xa654, 0, 0x82),
+SCE(0xa655, 1, 0x42),SCE(0x0266, 0, 0x42),SCE(0xa7aa, 1, 0x82),SCE(0x1e00, 0, 0x82),
+SCE(0x1e01, 1, 0x42),SCE(0x1e02, 0, 0x82),SCE(0x1e03, 1, 0x42),SCE(0x1e04, 0, 0x82),
+SCE(0x1e05, 1, 0x42),SCE(0x2c1f, 0, 0x82),SCE(0x2c4f, 1, 0x42),SCE(0x1e06, 0, 0x82),
+SCE(0x1e07, 1, 0x42),SCE(0x1040e, 0, 0x82),SCE(0x10436, 1, 0x42),SCE(0x1e08, 0, 0x82),
+SCE(0x1e09, 1, 0x42),SCE(0x1e0a, 0, 0x82),SCE(0x1e0b, 1, 0x42),SCE(0x2cb0, 0, 0x82),
+SCE(0x2cb1, 1, 0x42),SCE(0x1e0c, 0, 0x82),SCE(0x1e0d, 1, 0x42),SCE(0x1e0e, 0, 0x82),
+SCE(0x1e0f, 1, 0x42),SCE(0x1e10, 0, 0x82),SCE(0x1e11, 1, 0x42),SCE(0xa658, 0, 0x82),
+SCE(0xa659, 1, 0x42),SCE(0x1e12, 0, 0x82),SCE(0x1e13, 1, 0x42),SCE(0x1e14, 0, 0x82),
+SCE(0x1e15, 1, 0x42),SCE(0x24cd, 0, 0x82),SCE(0x24e7, 1, 0x42),SCE(0x1e16, 0, 0x82),
+SCE(0x1e17, 1, 0x42),SCE(0x1e18, 0, 0x82),SCE(0x1e19, 1, 0x42),SCE(0x1e1a, 0, 0x82),
+SCE(0x1e1b, 1, 0x42),SCE(0x1e1c, 0, 0x82),SCE(0x1e1d, 1, 0x42),SCE(0xa65a, 0, 0x82),
+SCE(0xa65b, 1, 0x42),SCE(0x1e1e, 0, 0x82),SCE(0x1e1f, 1, 0x42),SCE(0x1e20, 0, 0x82),
+SCE(0x1e21, 1, 0x42),SCE(0x1e22, 0, 0x82),SCE(0x1e23, 1, 0x42),SCE(0x1e24, 0, 0x82),
+SCE(0x1e25, 1, 0x42),SCE(0x1e26, 0, 0x82),SCE(0x1e27, 1, 0x42),SCE(0x1e28, 0, 0x82),
+SCE(0x1e29, 1, 0x42),SCE(0xa65c, 0, 0x82),SCE(0xa65d, 1, 0x42),SCE(0x1e2a, 0, 0x82),
+SCE(0x1e2b, 1, 0x42),SCE(0x1e2c, 0, 0x82),SCE(0x1e2d, 1, 0x42),SCE(0x1e2e, 0, 0x82),
+SCE(0x1e2f, 1, 0x42),SCE(0x1e30, 0, 0x82),SCE(0x1e31, 1, 0x42),SCE(0x1e32, 0, 0x82),
+SCE(0x1e33, 1, 0x42),SCE(0x1e34, 0, 0x82),SCE(0x1e35, 1, 0x42),SCE(0xa65e, 0, 0x82),
+SCE(0xa65f, 1, 0x42),SCE(0x1e36, 0, 0x82),SCE(0x1e37, 1, 0x42),SCE(0x1e38, 0, 0x82),
+SCE(0x1e39, 1, 0x42),SCE(0x1e3a, 0, 0x82),SCE(0x1e3b, 1, 0x42),SCE(0x1e3c, 0, 0x82),
+SCE(0x1e3d, 1, 0x42),SCE(0x1e3e, 0, 0x82),SCE(0x1e3f, 1, 0x42),SCE(0x1e40, 0, 0x82),
+SCE(0x1e41, 1, 0x42),SCE(0xa660, 0, 0x82),SCE(0xa661, 1, 0x42),SCE(0x1e42, 0, 0x82),
+SCE(0x1e43, 1, 0x42),SCE(0x1e44, 0, 0x82),SCE(0x1e45, 1, 0x42),SCE(0x1e46, 0, 0x82),
+SCE(0x1e47, 1, 0x42),SCE(0x2cb2, 0, 0x82),SCE(0x2cb3, 1, 0x42),SCE(0x1e48, 0, 0x82),
+SCE(0x1e49, 1, 0x42),SCE(0x2cc0, 0, 0x82),SCE(0x2cc1, 1, 0x42),SCE(0x1e4a, 0, 0x82),
+SCE(0x1e4b, 1, 0x42),SCE(0x1e4c, 0, 0x82),SCE(0x1e4d, 1, 0x42),SCE(0xa662, 0, 0x82),
+SCE(0xa663, 1, 0x42),SCE(0x1e4e, 0, 0x82),SCE(0x1e4f, 1, 0x42),SCE(0x1e50, 0, 0x82),
+SCE(0x1e51, 1, 0x42),SCE(0x1e52, 0, 0x82),SCE(0x1e53, 1, 0x42),SCE(0x1e54, 0, 0x82),
+SCE(0x1e55, 1, 0x42),SCE(0x1e56, 0, 0x82),SCE(0x1e57, 1, 0x42),SCE(0x1e58, 0, 0x82),
+SCE(0x1e59, 1, 0x42),SCE(0xa664, 0, 0x82),SCE(0xa665, 1, 0x42),SCE(0x1e5a, 0, 0x82),
+SCE(0x1e5b, 1, 0x42),SCE(0x1e5c, 0, 0x82),SCE(0x1e5d, 1, 0x42),SCE(0x1e5e, 0, 0x82),
+SCE(0x1e5f, 1, 0x42),SCE(0x1e60, 0, 0x83),SCE(0x1e61, 1, 0x43),SCE(0x1e9b, 2, 0x43),
+SCE(0x1e62, 0, 0x82),SCE(0x1e63, 1, 0x42),SCE(0x1e64, 0, 0x82),SCE(0x1e65, 1, 0x42),
+SCE(0xa666, 0, 0x82),SCE(0xa667, 1, 0x42),SCE(0x1e66, 0, 0x82),SCE(0x1e67, 1, 0x42),
+SCE(0x1e68, 0, 0x82),SCE(0x1e69, 1, 0x42),SCE(0x1e6a, 0, 0x82),SCE(0x1e6b, 1, 0x42),
+SCE(0x1e6c, 0, 0x82),SCE(0x1e6d, 1, 0x42),SCE(0x1e6e, 0, 0x82),SCE(0x1e6f, 1, 0x42),
+SCE(0x1e70, 0, 0x82),SCE(0x1e71, 1, 0x42),SCE(0xa668, 0, 0x82),SCE(0xa669, 1, 0x42),
+SCE(0x1e72, 0, 0x82),SCE(0x1e73, 1, 0x42),SCE(0x1e74, 0, 0x82),SCE(0x1e75, 1, 0x42),
+SCE(0x1e76, 0, 0x82),SCE(0x1e77, 1, 0x42),SCE(0x2cbe, 0, 0x82),SCE(0x2cbf, 1, 0x42),
+SCE(0x1e78, 0, 0x82),SCE(0x1e79, 1, 0x42),SCE(0x1e7a, 0, 0x82),SCE(0x1e7b, 1, 0x42),
+SCE(0x1e7c, 0, 0x82),SCE(0x1e7d, 1, 0x42),SCE(0xa66a, 0, 0x82),SCE(0xa66b, 1, 0x42),
+SCE(0x1e7e, 0, 0x82),SCE(0x1e7f, 1, 0x42),SCE(0x1e80, 0, 0x82),SCE(0x1e81, 1, 0x42),
+SCE(0x1e82, 0, 0x82),SCE(0x1e83, 1, 0x42),SCE(0x1e84, 0, 0x82),SCE(0x1e85, 1, 0x42),
+SCE(0x1e86, 0, 0x82),SCE(0x1e87, 1, 0x42),SCE(0x1e88, 0, 0x82),SCE(0x1e89, 1, 0x42),
+SCE(0xa66c, 0, 0x82),SCE(0xa66d, 1, 0x42),SCE(0x1e8a, 0, 0x82),SCE(0x1e8b, 1, 0x42),
+SCE(0x1e8c, 0, 0x82),SCE(0x1e8d, 1, 0x42),SCE(0x1e8e, 0, 0x82),SCE(0x1e8f, 1, 0x42),
+SCE(0x1e90, 0, 0x82),SCE(0x1e91, 1, 0x42),SCE(0x1e92, 0, 0x82),SCE(0x1e93, 1, 0x42),
+SCE(0x1e94, 0, 0x82),SCE(0x1e95, 1, 0x42),SCE(0xa696, 0, 0x82),SCE(0xa697, 1, 0x42),
+SCE(0x10406, 0, 0x82),SCE(0x1042e, 1, 0x42),SCE(0x1e60, 0, 0x83),SCE(0x1e61, 1, 0x43),
+SCE(0x1e9b, 2, 0x43),SCE(0x00df, 0, 0x42),SCE(0x1e9e, 1, 0x82),SCE(0x1ea0, 0, 0x82),
+SCE(0x1ea1, 1, 0x42),SCE(0x1ea2, 0, 0x82),SCE(0x1ea3, 1, 0x42),SCE(0x1ea4, 0, 0x82),
+SCE(0x1ea5, 1, 0x42),SCE(0x24c5, 0, 0x82),SCE(0x24df, 1, 0x42),SCE(0x1ea6, 0, 0x82),
+SCE(0x1ea7, 1, 0x42),SCE(0x1ea8, 0, 0x82),SCE(0x1ea9, 1, 0x42),SCE(0x1eaa, 0, 0x82),
+SCE(0x1eab, 1, 0x42),SCE(0x1eac, 0, 0x82),SCE(0x1ead, 1, 0x42),SCE(0x1eae, 0, 0x82),
+SCE(0x1eaf, 1, 0x42),SCE(0xff28, 0, 0x82),SCE(0xff48, 1, 0x42),SCE(0x1eb0, 0, 0x82),
+SCE(0x1eb1, 1, 0x42),SCE(0x1eb2, 0, 0x82),SCE(0x1eb3, 1, 0x42),SCE(0x10425, 0, 0x82),
+SCE(0x1044d, 1, 0x42),SCE(0x1eb4, 0, 0x82),SCE(0x1eb5, 1, 0x42),SCE(0x1eb6, 0, 0x82),
+SCE(0x1eb7, 1, 0x42),SCE(0x1eb8, 0, 0x82),SCE(0x1eb9, 1, 0x42),SCE(0x1eba, 0, 0x82),
+SCE(0x1ebb, 1, 0x42),SCE(0x1ebc, 0, 0x82),SCE(0x1ebd, 1, 0x42),SCE(0x1ebe, 0, 0x82),
+SCE(0x1ebf, 1, 0x42),SCE(0x2cb6, 0, 0x82),SCE(0x2cb7, 1, 0x42),SCE(0x1ec0, 0, 0x82),
+SCE(0x1ec1, 1, 0x42),SCE(0x1ec2, 0, 0x82),SCE(0x1ec3, 1, 0x42),SCE(0x2c9a, 0, 0x82),
+SCE(0x2c9b, 1, 0x42),SCE(0x1ec4, 0, 0x82),SCE(0x1ec5, 1, 0x42),SCE(0x1ec6, 0, 0x82),
+SCE(0x1ec7, 1, 0x42),SCE(0x1ec8, 0, 0x82),SCE(0x1ec9, 1, 0x42),SCE(0x1eca, 0, 0x82),
+SCE(0x1ecb, 1, 0x42),SCE(0x1ecc, 0, 0x82),SCE(0x1ecd, 1, 0x42),SCE(0x1ece, 0, 0x82),
+SCE(0x1ecf, 1, 0x42),SCE(0x1ed0, 0, 0x82),SCE(0x1ed1, 1, 0x42),SCE(0x1ed2, 0, 0x82),
+SCE(0x1ed3, 1, 0x42),SCE(0x1ed4, 0, 0x82),SCE(0x1ed5, 1, 0x42),SCE(0x1ed6, 0, 0x82),
+SCE(0x1ed7, 1, 0x42),SCE(0x1ed8, 0, 0x82),SCE(0x1ed9, 1, 0x42),SCE(0x1eda, 0, 0x82),
+SCE(0x1edb, 1, 0x42),SCE(0x1edc, 0, 0x82),SCE(0x1edd, 1, 0x42),SCE(0x1ede, 0, 0x82),
+SCE(0x1edf, 1, 0x42),SCE(0x1ee0, 0, 0x82),SCE(0x1ee1, 1, 0x42),SCE(0x1ee2, 0, 0x82),
+SCE(0x1ee3, 1, 0x42),SCE(0x1ee4, 0, 0x82),SCE(0x1ee5, 1, 0x42),SCE(0x03a9, 0, 0x83),
+SCE(0x03c9, 1, 0x43),SCE(0x2126, 2, 0x83),SCE(0x1ee6, 0, 0x82),SCE(0x1ee7, 1, 0x42),
+SCE(0x1ee8, 0, 0x82),SCE(0x1ee9, 1, 0x42),SCE(0x1eea, 0, 0x82),SCE(0x1eeb, 1, 0x42),
+SCE(0xff2a, 0, 0x82),SCE(0xff4a, 1, 0x42),SCE(0x1eec, 0, 0x82),SCE(0x1eed, 1, 0x42),
+SCE(0x1eee, 0, 0x82),SCE(0x1eef, 1, 0x42),SCE(0x1ef0, 0, 0x82),SCE(0x1ef1, 1, 0x42),
+SCE(0x1ef2, 0, 0x82),SCE(0x1ef3, 1, 0x42),SCE(0x1ef4, 0, 0x82),SCE(0x1ef5, 1, 0x42),
+SCE(0x1ef6, 0, 0x82),SCE(0x1ef7, 1, 0x42),SCE(0x1ef8, 0, 0x82),SCE(0x1ef9, 1, 0x42),
+SCE(0x1efa, 0, 0x82),SCE(0x1efb, 1, 0x42),SCE(0x2cb8, 0, 0x82),SCE(0x2cb9, 1, 0x42),
+SCE(0x1efc, 0, 0x82),SCE(0x1efd, 1, 0x42),SCE(0x004b, 0, 0x83),SCE(0x006b, 1, 0x43),
+SCE(0x212a, 2, 0x83),SCE(0x1efe, 0, 0x82),SCE(0x1eff, 1, 0x42),SCE(0xa680, 0, 0x82),
+SCE(0xa681, 1, 0x42),SCE(0x1f00, 0, 0x42),SCE(0x1f08, 1, 0x82),SCE(0x1f01, 0, 0x42),
+SCE(0x1f09, 1, 0x82),SCE(0x1f02, 0, 0x42),SCE(0x1f0a, 1, 0x82),SCE(0x1f03, 0, 0x42),
+SCE(0x1f0b, 1, 0x82),SCE(0x1f04, 0, 0x42),SCE(0x1f0c, 1, 0x82),SCE(0x1f05, 0, 0x42),
+SCE(0x1f0d, 1, 0x82),SCE(0x1f06, 0, 0x42),SCE(0x1f0e, 1, 0x82),SCE(0x1f07, 0, 0x42),
+SCE(0x1f0f, 1, 0x82),SCE(0x10418, 0, 0x82),SCE(0x10440, 1, 0x42),SCE(0x0265, 0, 0x42),
+SCE(0xa78d, 1, 0x82),SCE(0x1f10, 0, 0x42),SCE(0x1f18, 1, 0x82),SCE(0x1f11, 0, 0x42),
+SCE(0x1f19, 1, 0x82),SCE(0x1f12, 0, 0x42),SCE(0x1f1a, 1, 0x82),SCE(0x1f13, 0, 0x42),
+SCE(0x1f1b, 1, 0x82),SCE(0x1f14, 0, 0x42),SCE(0x1f1c, 1, 0x82),SCE(0x1f15, 0, 0x42),
+SCE(0x1f1d, 1, 0x82),SCE(0xff21, 0, 0x82),SCE(0xff41, 1, 0x42),SCE(0xa722, 0, 0x82),
+SCE(0xa723, 1, 0x42),SCE(0xff23, 0, 0x82),SCE(0xff43, 1, 0x42),SCE(0xa724, 0, 0x82),
+SCE(0xa725, 1, 0x42),SCE(0xa686, 0, 0x82),SCE(0xa687, 1, 0x42),SCE(0xa726, 0, 0x82),
+SCE(0xa727, 1, 0x42),SCE(0xff27, 0, 0x82),SCE(0xff47, 1, 0x42),SCE(0x1f20, 0, 0x42),
+SCE(0x1f28, 1, 0x82),SCE(0x1f21, 0, 0x42),SCE(0x1f29, 1, 0x82),SCE(0x1f22, 0, 0x42),
+SCE(0x1f2a, 1, 0x82),SCE(0x1f23, 0, 0x42),SCE(0x1f2b, 1, 0x82),SCE(0x1f24, 0, 0x42),
+SCE(0x1f2c, 1, 0x82),SCE(0x1f25, 0, 0x42),SCE(0x1f2d, 1, 0x82),SCE(0x1f26, 0, 0x42),
+SCE(0x1f2e, 1, 0x82),SCE(0x1f27, 0, 0x42),SCE(0x1f2f, 1, 0x82),SCE(0xff30, 0, 0x82),
+SCE(0xff50, 1, 0x42),SCE(0xa688, 0, 0x82),SCE(0xa689, 1, 0x42),SCE(0xa732, 0, 0x82),
+SCE(0xa733, 1, 0x42),SCE(0xff33, 0, 0x82),SCE(0xff53, 1, 0x42),SCE(0xa734, 0, 0x82),
+SCE(0xa735, 1, 0x42),SCE(0xff35, 0, 0x82),SCE(0xff55, 1, 0x42),SCE(0xa736, 0, 0x82),
+SCE(0xa737, 1, 0x42),SCE(0x2cba, 0, 0x82),SCE(0x2cbb, 1, 0x42),SCE(0x1f30, 0, 0x42),
+SCE(0x1f38, 1, 0x82),SCE(0x1f31, 0, 0x42),SCE(0x1f39, 1, 0x82),SCE(0x1f32, 0, 0x42),
+SCE(0x1f3a, 1, 0x82),SCE(0x1f33, 0, 0x42),SCE(0x1f3b, 1, 0x82),SCE(0x1f34, 0, 0x42),
+SCE(0x1f3c, 1, 0x82),SCE(0x1f35, 0, 0x42),SCE(0x1f3d, 1, 0x82),SCE(0x1f36, 0, 0x42),
+SCE(0x1f3e, 1, 0x82),SCE(0x1f37, 0, 0x42),SCE(0x1f3f, 1, 0x82),SCE(0xa740, 0, 0x82),
+SCE(0xa741, 1, 0x42),SCE(0xa742, 0, 0x82),SCE(0xa743, 1, 0x42),SCE(0xa744, 0, 0x82),
+SCE(0xa745, 1, 0x42),SCE(0xa746, 0, 0x82),SCE(0xa747, 1, 0x42),SCE(0x1f40, 0, 0x42),
+SCE(0x1f48, 1, 0x82),SCE(0x1f41, 0, 0x42),SCE(0x1f49, 1, 0x82),SCE(0x1f42, 0, 0x42),
+SCE(0x1f4a, 1, 0x82),SCE(0x1f43, 0, 0x42),SCE(0x1f4b, 1, 0x82),SCE(0x1f44, 0, 0x42),
+SCE(0x1f4c, 1, 0x82),SCE(0x1f45, 0, 0x42),SCE(0x1f4d, 1, 0x82),SCE(0xa74e, 0, 0x82),
+SCE(0xa74f, 1, 0x42),SCE(0xa750, 0, 0x82),SCE(0xa751, 1, 0x42),SCE(0xa752, 0, 0x82),
+SCE(0xa753, 1, 0x42),SCE(0xa754, 0, 0x82),SCE(0xa755, 1, 0x42),SCE(0xa68e, 0, 0x82),
+SCE(0xa68f, 1, 0x42),SCE(0xa756, 0, 0x82),SCE(0xa757, 1, 0x42),SCE(0xa758, 0, 0x82),
+SCE(0xa759, 1, 0x42),SCE(0x1f51, 0, 0x42),SCE(0x1f59, 1, 0x82),SCE(0xa75a, 0, 0x82),
+SCE(0xa75b, 1, 0x42),SCE(0x1f53, 0, 0x42),SCE(0x1f5b, 1, 0x82),SCE(0xa75c, 0, 0x82),
+SCE(0xa75d, 1, 0x42),SCE(0x1f55, 0, 0x42),SCE(0x1f5d, 1, 0x82),SCE(0xa75e, 0, 0x82),
+SCE(0xa75f, 1, 0x42),SCE(0x1f57, 0, 0x42),SCE(0x1f5f, 1, 0x82),SCE(0xa760, 0, 0x82),
+SCE(0xa761, 1, 0x42),SCE(0xa690, 0, 0x82),SCE(0xa691, 1, 0x42),SCE(0xa762, 0, 0x82),
+SCE(0xa763, 1, 0x42),SCE(0xff2e, 0, 0x82),SCE(0xff4e, 1, 0x42),SCE(0xa764, 0, 0x82),
+SCE(0xa765, 1, 0x42),SCE(0xa766, 0, 0x82),SCE(0xa767, 1, 0x42),SCE(0x1f60, 0, 0x42),
+SCE(0x1f68, 1, 0x82),SCE(0x1f61, 0, 0x42),SCE(0x1f69, 1, 0x82),SCE(0x1f62, 0, 0x42),
+SCE(0x1f6a, 1, 0x82),SCE(0x1f63, 0, 0x42),SCE(0x1f6b, 1, 0x82),SCE(0x1f64, 0, 0x42),
+SCE(0x1f6c, 1, 0x82),SCE(0x1f65, 0, 0x42),SCE(0x1f6d, 1, 0x82),SCE(0x1f66, 0, 0x42),
+SCE(0x1f6e, 1, 0x82),SCE(0x1f67, 0, 0x42),SCE(0x1f6f, 1, 0x82),SCE(0x2c1c, 0, 0x82),
+SCE(0x2c4c, 1, 0x42),SCE(0x2cbc, 0, 0x82),SCE(0x2cbd, 1, 0x42),SCE(0xa694, 0, 0x82),
+SCE(0xa695, 1, 0x42),SCE(0xa77b, 0, 0x82),SCE(0xa77c, 1, 0x42),SCE(0x1d79, 0, 0x42),
+SCE(0xa77d, 1, 0x82),SCE(0xa77e, 0, 0x82),SCE(0xa77f, 1, 0x42),SCE(0xa780, 0, 0x82),
+SCE(0xa781, 1, 0x42),SCE(0xa782, 0, 0x82),SCE(0xa783, 1, 0x42),SCE(0xa784, 0, 0x82),
+SCE(0xa785, 1, 0x42),SCE(0xa786, 0, 0x82),SCE(0xa787, 1, 0x42),SCE(0x1f80, 0, 0x42),
+SCE(0x1f88, 1, 0x2),SCE(0x1f81, 0, 0x42),SCE(0x1f89, 1, 0x2),SCE(0x1f82, 0, 0x42),
+SCE(0x1f8a, 1, 0x2),SCE(0x1f83, 0, 0x42),SCE(0x1f8b, 1, 0x2),SCE(0x1f84, 0, 0x42),
+SCE(0x1f8c, 1, 0x2),SCE(0x1f85, 0, 0x42),SCE(0x1f8d, 1, 0x2),SCE(0x1f86, 0, 0x42),
+SCE(0x1f8e, 1, 0x2),SCE(0x1f87, 0, 0x42),SCE(0x1f8f, 1, 0x2),SCE(0xa790, 0, 0x82),
+SCE(0xa791, 1, 0x42),SCE(0xa792, 0, 0x82),SCE(0xa793, 1, 0x42),SCE(0x1f90, 0, 0x42),
+SCE(0x1f98, 1, 0x2),SCE(0x1f91, 0, 0x42),SCE(0x1f99, 1, 0x2),SCE(0x1f92, 0, 0x42),
+SCE(0x1f9a, 1, 0x2),SCE(0x1f93, 0, 0x42),SCE(0x1f9b, 1, 0x2),SCE(0x1f94, 0, 0x42),
+SCE(0x1f9c, 1, 0x2),SCE(0x1f95, 0, 0x42),SCE(0x1f9d, 1, 0x2),SCE(0x1f96, 0, 0x42),
+SCE(0x1f9e, 1, 0x2),SCE(0x1f97, 0, 0x42),SCE(0x1f9f, 1, 0x2),SCE(0xa7a0, 0, 0x82),
+SCE(0xa7a1, 1, 0x42),SCE(0xa7a2, 0, 0x82),SCE(0xa7a3, 1, 0x42),SCE(0xa7a4, 0, 0x82),
+SCE(0xa7a5, 1, 0x42),SCE(0xa7a6, 0, 0x82),SCE(0xa7a7, 1, 0x42),SCE(0x1fa0, 0, 0x42),
+SCE(0x1fa8, 1, 0x2),SCE(0x1fa1, 0, 0x42),SCE(0x1fa9, 1, 0x2),SCE(0x1fa2, 0, 0x42),
+SCE(0x1faa, 1, 0x2),SCE(0x1fa3, 0, 0x42),SCE(0x1fab, 1, 0x2),SCE(0x1fa4, 0, 0x42),
+SCE(0x1fac, 1, 0x2),SCE(0x1fa5, 0, 0x42),SCE(0x1fad, 1, 0x2),SCE(0x1fa6, 0, 0x42),
+SCE(0x1fae, 1, 0x2),SCE(0x1fa7, 0, 0x42),SCE(0x1faf, 1, 0x2),SCE(0x1fb0, 0, 0x42),
+SCE(0x1fb8, 1, 0x82),SCE(0x1fb1, 0, 0x42),SCE(0x1fb9, 1, 0x82),SCE(0x1f70, 0, 0x42),
+SCE(0x1fba, 1, 0x82),SCE(0x1f71, 0, 0x42),SCE(0x1fbb, 1, 0x82),SCE(0x1fb3, 0, 0x42),
+SCE(0x1fbc, 1, 0x2),SCE(0x0345, 0, 0x44),SCE(0x0399, 1, 0x84),SCE(0x03b9, 2, 0x44),
+SCE(0x1fbe, 3, 0x44),SCE(0x1f72, 0, 0x42),SCE(0x1fc8, 1, 0x82),SCE(0x1f73, 0, 0x42),
+SCE(0x1fc9, 1, 0x82),SCE(0x1f74, 0, 0x42),SCE(0x1fca, 1, 0x82),SCE(0x1f75, 0, 0x42),
+SCE(0x1fcb, 1, 0x82),SCE(0x1fc3, 0, 0x42),SCE(0x1fcc, 1, 0x2),SCE(0x1fd0, 0, 0x42),
+SCE(0x1fd8, 1, 0x82),SCE(0x1fd1, 0, 0x42),SCE(0x1fd9, 1, 0x82),SCE(0x1f76, 0, 0x42),
+SCE(0x1fda, 1, 0x82),SCE(0x1f77, 0, 0x42),SCE(0x1fdb, 1, 0x82),SCE(0x10427, 0, 0x82),
+SCE(0x1044f, 1, 0x42),SCE(0x1fe0, 0, 0x42),SCE(0x1fe8, 1, 0x82),SCE(0x1fe1, 0, 0x42),
+SCE(0x1fe9, 1, 0x82),SCE(0x1f7a, 0, 0x42),SCE(0x1fea, 1, 0x82),SCE(0x1f7b, 0, 0x42),
+SCE(0x1feb, 1, 0x82),SCE(0x1fe5, 0, 0x42),SCE(0x1fec, 1, 0x82),SCE(0x10401, 0, 0x82),
+SCE(0x10429, 1, 0x42),SCE(0x1040c, 0, 0x82),SCE(0x10434, 1, 0x42),SCE(0x1f78, 0, 0x42),
+SCE(0x1ff8, 1, 0x82),SCE(0x1f79, 0, 0x42),SCE(0x1ff9, 1, 0x82),SCE(0x1f7c, 0, 0x42),
+SCE(0x1ffa, 1, 0x82),SCE(0x1f7d, 0, 0x42),SCE(0x1ffb, 1, 0x82),SCE(0x1ff3, 0, 0x42),
+SCE(0x1ffc, 1, 0x2),SCE(0x24c0, 0, 0x82),SCE(0x24da, 1, 0x42),];
+return t;
+}
+@property immutable(FullCaseEntry[]) fullCaseTable()
+{
+alias FCE = FullCaseEntry;
+static immutable FCE[] t = [
+FCE("Ⰰ", 0, 2, 1),
+FCE("ⰰ", 1, 2, 1),FCE("Ⓝ", 0, 2, 1),FCE("ⓝ", 1, 2, 1),FCE("Ⰱ", 0, 2, 1),
+FCE("ⰱ", 1, 2, 1),FCE("Ⱍ", 0, 2, 1),FCE("ⱍ", 1, 2, 1),FCE("Ⰲ", 0, 2, 1),
+FCE("ⰲ", 1, 2, 1),FCE("Ⰳ", 0, 2, 1),FCE("ⰳ", 1, 2, 1),FCE("Ⰴ", 0, 2, 1),
+FCE("ⰴ", 1, 2, 1),FCE("Ⰵ", 0, 2, 1),FCE("ⰵ", 1, 2, 1),FCE("Ⰶ", 0, 2, 1),
+FCE("ⰶ", 1, 2, 1),FCE("𐐀", 0, 2, 1),FCE("𐐨", 1, 2, 1),FCE("Ⳃ", 0, 2, 1),
+FCE("ⳃ", 1, 2, 1),FCE("Ⰷ", 0, 2, 1),FCE("ⰷ", 1, 2, 1),FCE("Ⰸ", 0, 2, 1),
+FCE("ⰸ", 1, 2, 1),FCE("Ⰹ", 0, 2, 1),FCE("ⰹ", 1, 2, 1),FCE("Ⰺ", 0, 2, 1),
+FCE("ⰺ", 1, 2, 1),FCE("Ꚍ", 0, 2, 1),FCE("ꚍ", 1, 2, 1),FCE("A", 0, 2, 1),
+FCE("a", 1, 2, 1),FCE("B", 0, 2, 1),FCE("b", 1, 2, 1),FCE("C", 0, 2, 1),
+FCE("c", 1, 2, 1),FCE("D", 0, 2, 1),FCE("d", 1, 2, 1),FCE("E", 0, 2, 1),
+FCE("e", 1, 2, 1),FCE("F", 0, 2, 1),FCE("f", 1, 2, 1),FCE("G", 0, 2, 1),
+FCE("g", 1, 2, 1),FCE("H", 0, 2, 1),FCE("h", 1, 2, 1),FCE("I", 0, 2, 1),
+FCE("i", 1, 2, 1),FCE("J", 0, 2, 1),FCE("j", 1, 2, 1),FCE("K", 0, 3, 1),
+FCE("k", 1, 3, 1),FCE("K", 2, 3, 1),FCE("L", 0, 2, 1),FCE("l", 1, 2, 1),
+FCE("M", 0, 2, 1),FCE("m", 1, 2, 1),FCE("N", 0, 2, 1),FCE("n", 1, 2, 1),
+FCE("O", 0, 2, 1),FCE("o", 1, 2, 1),FCE("P", 0, 2, 1),FCE("p", 1, 2, 1),
+FCE("Q", 0, 2, 1),FCE("q", 1, 2, 1),FCE("R", 0, 2, 1),FCE("r", 1, 2, 1),
+FCE("S", 0, 3, 1),FCE("s", 1, 3, 1),FCE("ſ", 2, 3, 1),FCE("T", 0, 2, 1),
+FCE("t", 1, 2, 1),FCE("U", 0, 2, 1),FCE("u", 1, 2, 1),FCE("V", 0, 2, 1),
+FCE("v", 1, 2, 1),FCE("W", 0, 2, 1),FCE("w", 1, 2, 1),FCE("X", 0, 2, 1),
+FCE("x", 1, 2, 1),FCE("Y", 0, 2, 1),FCE("y", 1, 2, 1),FCE("Z", 0, 2, 1),
+FCE("z", 1, 2, 1),FCE("Ⰿ", 0, 2, 1),FCE("ⰿ", 1, 2, 1),FCE("Ⱀ", 0, 2, 1),
+FCE("ⱀ", 1, 2, 1),FCE("𐐂", 0, 2, 1),FCE("𐐪", 1, 2, 1),FCE("Ⳅ", 0, 2, 1),
+FCE("ⳅ", 1, 2, 1),FCE("Ⅶ", 0, 2, 1),FCE("ⅶ", 1, 2, 1),FCE("Ⱁ", 0, 2, 1),
+FCE("ⱁ", 1, 2, 1),FCE("Ⱂ", 0, 2, 1),FCE("ⱂ", 1, 2, 1),FCE("Ⅸ", 0, 2, 1),
+FCE("ⅸ", 1, 2, 1),FCE("Ⱃ", 0, 2, 1),FCE("ⱃ", 1, 2, 1),FCE("Ꚃ", 0, 2, 1),
+FCE("ꚃ", 1, 2, 1),FCE("Ⱄ", 0, 2, 1),FCE("ⱄ", 1, 2, 1),FCE("Ⅺ", 0, 2, 1),
+FCE("ⅺ", 1, 2, 1),FCE("Ⓡ", 0, 2, 1),FCE("ⓡ", 1, 2, 1),FCE("Ⱅ", 0, 2, 1),
+FCE("ⱅ", 1, 2, 1),FCE("𐐃", 0, 2, 1),FCE("𐐫", 1, 2, 1),FCE("Ⱆ", 0, 2, 1),
+FCE("ⱆ", 1, 2, 1),FCE("Ⅼ", 0, 2, 1),FCE("ⅼ", 1, 2, 1),FCE("Ⱇ", 0, 2, 1),
+FCE("ⱇ", 1, 2, 1),FCE("X", 0, 2, 1),FCE("x", 1, 2, 1),FCE("Ⱈ", 0, 2, 1),
+FCE("ⱈ", 1, 2, 1),FCE("Ⅾ", 0, 2, 1),FCE("ⅾ", 1, 2, 1),FCE("Ⱉ", 0, 2, 1),
+FCE("ⱉ", 1, 2, 1),FCE("Ⱊ", 0, 2, 1),FCE("ⱊ", 1, 2, 1),FCE("Ⱎ", 0, 2, 1),
+FCE("ⱎ", 1, 2, 1),FCE("Ⴀ", 0, 2, 1),FCE("ⴀ", 1, 2, 1),FCE("Ⴁ", 0, 2, 1),
+FCE("ⴁ", 1, 2, 1),FCE("Ⴂ", 0, 2, 1),FCE("ⴂ", 1, 2, 1),FCE("Ⴃ", 0, 2, 1),
+FCE("ⴃ", 1, 2, 1),FCE("Ⴄ", 0, 2, 1),FCE("ⴄ", 1, 2, 1),FCE("Ⴅ", 0, 2, 1),
+FCE("ⴅ", 1, 2, 1),FCE("Ⴆ", 0, 2, 1),FCE("ⴆ", 1, 2, 1),FCE("Ⴇ", 0, 2, 1),
+FCE("ⴇ", 1, 2, 1),FCE("Ⴈ", 0, 2, 1),FCE("ⴈ", 1, 2, 1),FCE("Ⴉ", 0, 2, 1),
+FCE("ⴉ", 1, 2, 1),FCE("Ⴊ", 0, 2, 1),FCE("ⴊ", 1, 2, 1),FCE("Ⴋ", 0, 2, 1),
+FCE("ⴋ", 1, 2, 1),FCE("Ⴌ", 0, 2, 1),FCE("ⴌ", 1, 2, 1),FCE("Ⴍ", 0, 2, 1),
+FCE("ⴍ", 1, 2, 1),FCE("Ⴎ", 0, 2, 1),FCE("ⴎ", 1, 2, 1),FCE("Ⴏ", 0, 2, 1),
+FCE("ⴏ", 1, 2, 1),FCE("Ⴐ", 0, 2, 1),FCE("ⴐ", 1, 2, 1),FCE("Ⴑ", 0, 2, 1),
+FCE("ⴑ", 1, 2, 1),FCE("Ⴒ", 0, 2, 1),FCE("ⴒ", 1, 2, 1),FCE("Ⴓ", 0, 2, 1),
+FCE("ⴓ", 1, 2, 1),FCE("Ⴔ", 0, 2, 1),FCE("ⴔ", 1, 2, 1),FCE("Ⴕ", 0, 2, 1),
+FCE("ⴕ", 1, 2, 1),FCE("Ⴖ", 0, 2, 1),FCE("ⴖ", 1, 2, 1),FCE("Ⴗ", 0, 2, 1),
+FCE("ⴗ", 1, 2, 1),FCE("Ⴘ", 0, 2, 1),FCE("ⴘ", 1, 2, 1),FCE("Ⴙ", 0, 2, 1),
+FCE("ⴙ", 1, 2, 1),FCE("Ⴚ", 0, 2, 1),FCE("ⴚ", 1, 2, 1),FCE("Ⴛ", 0, 2, 1),
+FCE("ⴛ", 1, 2, 1),FCE("Ⴜ", 0, 2, 1),FCE("ⴜ", 1, 2, 1),FCE("Ⴝ", 0, 2, 1),
+FCE("ⴝ", 1, 2, 1),FCE("Ⴞ", 0, 2, 1),FCE("ⴞ", 1, 2, 1),FCE("Ⴟ", 0, 2, 1),
+FCE("ⴟ", 1, 2, 1),FCE("À", 0, 2, 1),FCE("à", 1, 2, 1),FCE("Á", 0, 2, 1),
+FCE("á", 1, 2, 1),FCE("Ⴢ", 0, 2, 1),FCE("ⴢ", 1, 2, 1),FCE("Ã", 0, 2, 1),
+FCE("ã", 1, 2, 1),FCE("Ⴤ", 0, 2, 1),FCE("ⴤ", 1, 2, 1),FCE("Å", 0, 3, 1),
+FCE("å", 1, 3, 1),FCE("Å", 2, 3, 1),FCE("Æ", 0, 2, 1),FCE("æ", 1, 2, 1),
+FCE("Ç", 0, 2, 1),FCE("ç", 1, 2, 1),FCE("È", 0, 2, 1),FCE("è", 1, 2, 1),
+FCE("É", 0, 2, 1),FCE("é", 1, 2, 1),FCE("Ê", 0, 2, 1),FCE("ê", 1, 2, 1),
+FCE("Ë", 0, 2, 1),FCE("ë", 1, 2, 1),FCE("Ì", 0, 2, 1),FCE("ì", 1, 2, 1),
+FCE("Í", 0, 2, 1),FCE("í", 1, 2, 1),FCE("Î", 0, 2, 1),FCE("î", 1, 2, 1),
+FCE("Ï", 0, 2, 1),FCE("ï", 1, 2, 1),FCE("Ð", 0, 2, 1),FCE("ð", 1, 2, 1),
+FCE("Ñ", 0, 2, 1),FCE("ñ", 1, 2, 1),FCE("Ò", 0, 2, 1),FCE("ò", 1, 2, 1),
+FCE("Ó", 0, 2, 1),FCE("ó", 1, 2, 1),FCE("Ô", 0, 2, 1),FCE("ô", 1, 2, 1),
+FCE("Õ", 0, 2, 1),FCE("õ", 1, 2, 1),FCE("Ö", 0, 2, 1),FCE("ö", 1, 2, 1),
+FCE("Ø", 0, 2, 1),FCE("ø", 1, 2, 1),FCE("Ù", 0, 2, 1),FCE("ù", 1, 2, 1),
+FCE("Ú", 0, 2, 1),FCE("ú", 1, 2, 1),FCE("Û", 0, 2, 1),FCE("û", 1, 2, 1),
+FCE("Ü", 0, 2, 1),FCE("ü", 1, 2, 1),FCE("Ý", 0, 2, 1),FCE("ý", 1, 2, 1),
+FCE("Þ", 0, 2, 1),FCE("þ", 1, 2, 1),FCE("ß", 0, 3, 1),FCE("ẞ", 1, 3, 1),
+FCE("ss", 2, 3, 2),FCE("Ⱖ", 0, 2, 1),FCE("ⱖ", 1, 2, 1),FCE("Ⱗ", 0, 2, 1),
+FCE("ⱗ", 1, 2, 1),FCE("Ⱘ", 0, 2, 1),FCE("ⱘ", 1, 2, 1),FCE("𐐆", 0, 2, 1),
+FCE("𐐮", 1, 2, 1),FCE("𐐏", 0, 2, 1),FCE("𐐷", 1, 2, 1),FCE("Ⓥ", 0, 2, 1),
+FCE("ⓥ", 1, 2, 1),FCE("Ⱙ", 0, 2, 1),FCE("ⱙ", 1, 2, 1),FCE("𐐇", 0, 2, 1),
+FCE("𐐯", 1, 2, 1),FCE("Ⱚ", 0, 2, 1),FCE("ⱚ", 1, 2, 1),FCE("Ā", 0, 2, 1),
+FCE("ā", 1, 2, 1),FCE("Ă", 0, 2, 1),FCE("ă", 1, 2, 1),FCE("Ⱛ", 0, 2, 1),
+FCE("ⱛ", 1, 2, 1),FCE("Ą", 0, 2, 1),FCE("ą", 1, 2, 1),FCE("Ć", 0, 2, 1),
+FCE("ć", 1, 2, 1),FCE("Ĉ", 0, 2, 1),FCE("ĉ", 1, 2, 1),FCE("Ⱜ", 0, 2, 1),
+FCE("ⱜ", 1, 2, 1),FCE("Ċ", 0, 2, 1),FCE("ċ", 1, 2, 1),FCE("Č", 0, 2, 1),
+FCE("č", 1, 2, 1),FCE("Ď", 0, 2, 1),FCE("ď", 1, 2, 1),FCE("Ⱝ", 0, 2, 1),
+FCE("ⱝ", 1, 2, 1),FCE("Đ", 0, 2, 1),FCE("đ", 1, 2, 1),FCE("Ē", 0, 2, 1),
+FCE("ē", 1, 2, 1),FCE("Ĕ", 0, 2, 1),FCE("ĕ", 1, 2, 1),FCE("Ⱞ", 0, 2, 1),
+FCE("ⱞ", 1, 2, 1),FCE("Ė", 0, 2, 1),FCE("ė", 1, 2, 1),FCE("Ę", 0, 2, 1),
+FCE("ę", 1, 2, 1),FCE("Ě", 0, 2, 1),FCE("ě", 1, 2, 1),FCE("Ĝ", 0, 2, 1),
+FCE("ĝ", 1, 2, 1),FCE("Ğ", 0, 2, 1),FCE("ğ", 1, 2, 1),FCE("Ġ", 0, 2, 1),
+FCE("ġ", 1, 2, 1),FCE("Ģ", 0, 2, 1),FCE("ģ", 1, 2, 1),FCE("K", 0, 2, 1),
+FCE("k", 1, 2, 1),FCE("Ĥ", 0, 2, 1),FCE("ĥ", 1, 2, 1),FCE("Ħ", 0, 2, 1),
+FCE("ħ", 1, 2, 1),FCE("Ĩ", 0, 2, 1),FCE("ĩ", 1, 2, 1),FCE("Ī", 0, 2, 1),
+FCE("ī", 1, 2, 1),FCE("Å", 0, 3, 1),FCE("å", 1, 3, 1),FCE("Å", 2, 3, 1),
+FCE("Ĭ", 0, 2, 1),FCE("ĭ", 1, 2, 1),FCE("Į", 0, 2, 1),FCE("į", 1, 2, 1),
+FCE("İ", 0, 2, 1),FCE("i̇", 1, 2, 2),FCE("IJ", 0, 2, 1),FCE("ij", 1, 2, 1),
+FCE("Ĵ", 0, 2, 1),FCE("ĵ", 1, 2, 1),FCE("Ķ", 0, 2, 1),FCE("ķ", 1, 2, 1),
+FCE("Ĺ", 0, 2, 1),FCE("ĺ", 1, 2, 1),FCE("Ļ", 0, 2, 1),FCE("ļ", 1, 2, 1),
+FCE("Ⳟ", 0, 2, 1),FCE("ⳟ", 1, 2, 1),FCE("Ľ", 0, 2, 1),FCE("ľ", 1, 2, 1),
+FCE("Ŀ", 0, 2, 1),FCE("ŀ", 1, 2, 1),FCE("Ł", 0, 2, 1),FCE("ł", 1, 2, 1),
+FCE("Ń", 0, 2, 1),FCE("ń", 1, 2, 1),FCE("Ņ", 0, 2, 1),FCE("ņ", 1, 2, 1),
+FCE("Ň", 0, 2, 1),FCE("ň", 1, 2, 1),FCE("ʼn", 0, 2, 1),FCE("ʼn", 1, 2, 2),
+FCE("Ŋ", 0, 2, 1),FCE("ŋ", 1, 2, 1),FCE("Ō", 0, 2, 1),FCE("ō", 1, 2, 1),
+FCE("Ŏ", 0, 2, 1),FCE("ŏ", 1, 2, 1),FCE("Ő", 0, 2, 1),FCE("ő", 1, 2, 1),
+FCE("Œ", 0, 2, 1),FCE("œ", 1, 2, 1),FCE("Ŕ", 0, 2, 1),FCE("ŕ", 1, 2, 1),
+FCE("Ŗ", 0, 2, 1),FCE("ŗ", 1, 2, 1),FCE("Ř", 0, 2, 1),FCE("ř", 1, 2, 1),
+FCE("Ś", 0, 2, 1),FCE("ś", 1, 2, 1),FCE("Ŝ", 0, 2, 1),FCE("ŝ", 1, 2, 1),
+FCE("Ş", 0, 2, 1),FCE("ş", 1, 2, 1),FCE("Š", 0, 2, 1),FCE("š", 1, 2, 1),
+FCE("Ⅱ", 0, 2, 1),FCE("ⅱ", 1, 2, 1),FCE("Ţ", 0, 2, 1),FCE("ţ", 1, 2, 1),
+FCE("Ⅳ", 0, 2, 1),FCE("ⅳ", 1, 2, 1),FCE("Ť", 0, 2, 1),FCE("ť", 1, 2, 1),
+FCE("Ⅵ", 0, 2, 1),FCE("ⅵ", 1, 2, 1),FCE("Ŧ", 0, 2, 1),FCE("ŧ", 1, 2, 1),
+FCE("Ⅷ", 0, 2, 1),FCE("ⅷ", 1, 2, 1),FCE("Ũ", 0, 2, 1),FCE("ũ", 1, 2, 1),
+FCE("Ⅹ", 0, 2, 1),FCE("ⅹ", 1, 2, 1),FCE("Ū", 0, 2, 1),FCE("ū", 1, 2, 1),
+FCE("Ⅻ", 0, 2, 1),FCE("ⅻ", 1, 2, 1),FCE("Ŭ", 0, 2, 1),FCE("ŭ", 1, 2, 1),
+FCE("Ⅽ", 0, 2, 1),FCE("ⅽ", 1, 2, 1),FCE("Ů", 0, 2, 1),FCE("ů", 1, 2, 1),
+FCE("Ⅿ", 0, 2, 1),FCE("ⅿ", 1, 2, 1),FCE("Ű", 0, 2, 1),FCE("ű", 1, 2, 1),
+FCE("Ⳍ", 0, 2, 1),FCE("ⳍ", 1, 2, 1),FCE("Ų", 0, 2, 1),FCE("ų", 1, 2, 1),
+FCE("Ŵ", 0, 2, 1),FCE("ŵ", 1, 2, 1),FCE("Ŷ", 0, 2, 1),FCE("ŷ", 1, 2, 1),
+FCE("ÿ", 0, 2, 1),FCE("Ÿ", 1, 2, 1),FCE("Ź", 0, 2, 1),FCE("ź", 1, 2, 1),
+FCE("Ż", 0, 2, 1),FCE("ż", 1, 2, 1),FCE("Ž", 0, 2, 1),FCE("ž", 1, 2, 1),
+FCE("S", 0, 3, 1),FCE("s", 1, 3, 1),FCE("ſ", 2, 3, 1),FCE("Ɓ", 0, 2, 1),
+FCE("ɓ", 1, 2, 1),FCE("Ƃ", 0, 2, 1),FCE("ƃ", 1, 2, 1),FCE("Ↄ", 0, 2, 1),
+FCE("ↄ", 1, 2, 1),FCE("Ƅ", 0, 2, 1),FCE("ƅ", 1, 2, 1),FCE("Ɔ", 0, 2, 1),
+FCE("ɔ", 1, 2, 1),FCE("Ƈ", 0, 2, 1),FCE("ƈ", 1, 2, 1),FCE("Ɖ", 0, 2, 1),
+FCE("ɖ", 1, 2, 1),FCE("Ɗ", 0, 2, 1),FCE("ɗ", 1, 2, 1),FCE("Ƌ", 0, 2, 1),
+FCE("ƌ", 1, 2, 1),FCE("Ǝ", 0, 2, 1),FCE("ǝ", 1, 2, 1),FCE("Ə", 0, 2, 1),
+FCE("ə", 1, 2, 1),FCE("Ɛ", 0, 2, 1),FCE("ɛ", 1, 2, 1),FCE("Ƒ", 0, 2, 1),
+FCE("ƒ", 1, 2, 1),FCE("Ɠ", 0, 2, 1),FCE("ɠ", 1, 2, 1),FCE("Ɣ", 0, 2, 1),
+FCE("ɣ", 1, 2, 1),FCE("Ɩ", 0, 2, 1),FCE("ɩ", 1, 2, 1),FCE("Ɨ", 0, 2, 1),
+FCE("ɨ", 1, 2, 1),FCE("Ƙ", 0, 2, 1),FCE("ƙ", 1, 2, 1),FCE("Ɯ", 0, 2, 1),
+FCE("ɯ", 1, 2, 1),FCE("Ɲ", 0, 2, 1),FCE("ɲ", 1, 2, 1),FCE("Ꙋ", 0, 2, 1),
+FCE("ꙋ", 1, 2, 1),FCE("Ɵ", 0, 2, 1),FCE("ɵ", 1, 2, 1),FCE("Ơ", 0, 2, 1),
+FCE("ơ", 1, 2, 1),FCE("Ƣ", 0, 2, 1),FCE("ƣ", 1, 2, 1),FCE("Ƥ", 0, 2, 1),
+FCE("ƥ", 1, 2, 1),FCE("Ʀ", 0, 2, 1),FCE("ʀ", 1, 2, 1),FCE("Ƨ", 0, 2, 1),
+FCE("ƨ", 1, 2, 1),FCE("Ʃ", 0, 2, 1),FCE("ʃ", 1, 2, 1),FCE("Ƭ", 0, 2, 1),
+FCE("ƭ", 1, 2, 1),FCE("Ʈ", 0, 2, 1),FCE("ʈ", 1, 2, 1),FCE("Ư", 0, 2, 1),
+FCE("ư", 1, 2, 1),FCE("Ʊ", 0, 2, 1),FCE("ʊ", 1, 2, 1),FCE("Ʋ", 0, 2, 1),
+FCE("ʋ", 1, 2, 1),FCE("Ƴ", 0, 2, 1),FCE("ƴ", 1, 2, 1),FCE("Ƶ", 0, 2, 1),
+FCE("ƶ", 1, 2, 1),FCE("Ʒ", 0, 2, 1),FCE("ʒ", 1, 2, 1),FCE("Ƹ", 0, 2, 1),
+FCE("ƹ", 1, 2, 1),FCE("Ƽ", 0, 2, 1),FCE("ƽ", 1, 2, 1),FCE("DŽ", 0, 3, 1),
+FCE("Dž", 1, 3, 1),FCE("dž", 2, 3, 1),FCE("DŽ", 0, 3, 1),FCE("Dž", 1, 3, 1),
+FCE("dž", 2, 3, 1),FCE("LJ", 0, 3, 1),FCE("Lj", 1, 3, 1),FCE("lj", 2, 3, 1),
+FCE("LJ", 0, 3, 1),FCE("Lj", 1, 3, 1),FCE("lj", 2, 3, 1),FCE("NJ", 0, 3, 1),
+FCE("Nj", 1, 3, 1),FCE("nj", 2, 3, 1),FCE("NJ", 0, 3, 1),FCE("Nj", 1, 3, 1),
+FCE("nj", 2, 3, 1),FCE("Ǎ", 0, 2, 1),FCE("ǎ", 1, 2, 1),FCE("Ǐ", 0, 2, 1),
+FCE("ǐ", 1, 2, 1),FCE("Ǒ", 0, 2, 1),FCE("ǒ", 1, 2, 1),FCE("Ǔ", 0, 2, 1),
+FCE("ǔ", 1, 2, 1),FCE("Ǖ", 0, 2, 1),FCE("ǖ", 1, 2, 1),FCE("Ǘ", 0, 2, 1),
+FCE("ǘ", 1, 2, 1),FCE("Ǚ", 0, 2, 1),FCE("ǚ", 1, 2, 1),FCE("Ǜ", 0, 2, 1),
+FCE("ǜ", 1, 2, 1),FCE("Ǟ", 0, 2, 1),FCE("ǟ", 1, 2, 1),FCE("V", 0, 2, 1),
+FCE("v", 1, 2, 1),FCE("Ǡ", 0, 2, 1),FCE("ǡ", 1, 2, 1),FCE("Ǣ", 0, 2, 1),
+FCE("ǣ", 1, 2, 1),FCE("Ǥ", 0, 2, 1),FCE("ǥ", 1, 2, 1),FCE("Ǧ", 0, 2, 1),
+FCE("ǧ", 1, 2, 1),FCE("Ǩ", 0, 2, 1),FCE("ǩ", 1, 2, 1),FCE("Ǫ", 0, 2, 1),
+FCE("ǫ", 1, 2, 1),FCE("Ǭ", 0, 2, 1),FCE("ǭ", 1, 2, 1),FCE("Ǯ", 0, 2, 1),
+FCE("ǯ", 1, 2, 1),FCE("ǰ", 0, 2, 1),FCE("ǰ", 1, 2, 2),FCE("DZ", 0, 3, 1),
+FCE("Dz", 1, 3, 1),FCE("dz", 2, 3, 1),FCE("DZ", 0, 3, 1),FCE("Dz", 1, 3, 1),
+FCE("dz", 2, 3, 1),FCE("Ǵ", 0, 2, 1),FCE("ǵ", 1, 2, 1),FCE("ƕ", 0, 2, 1),
+FCE("Ƕ", 1, 2, 1),FCE("ƿ", 0, 2, 1),FCE("Ƿ", 1, 2, 1),FCE("Ǹ", 0, 2, 1),
+FCE("ǹ", 1, 2, 1),FCE("𐐝", 0, 2, 1),FCE("𐑅", 1, 2, 1),FCE("Ǻ", 0, 2, 1),
+FCE("ǻ", 1, 2, 1),FCE("Ǽ", 0, 2, 1),FCE("ǽ", 1, 2, 1),FCE("Ǿ", 0, 2, 1),
+FCE("ǿ", 1, 2, 1),FCE("Ȁ", 0, 2, 1),FCE("ȁ", 1, 2, 1),FCE("Ȃ", 0, 2, 1),
+FCE("ȃ", 1, 2, 1),FCE("Ȅ", 0, 2, 1),FCE("ȅ", 1, 2, 1),FCE("Ȇ", 0, 2, 1),
+FCE("ȇ", 1, 2, 1),FCE("fi", 0, 2, 1),FCE("fi", 1, 2, 2),FCE("Ȉ", 0, 2, 1),
+FCE("ȉ", 1, 2, 1),FCE("Ȋ", 0, 2, 1),FCE("ȋ", 1, 2, 1),FCE("Ȍ", 0, 2, 1),
+FCE("ȍ", 1, 2, 1),FCE("Ȏ", 0, 2, 1),FCE("ȏ", 1, 2, 1),FCE("Ȑ", 0, 2, 1),
+FCE("ȑ", 1, 2, 1),FCE("Ȓ", 0, 2, 1),FCE("ȓ", 1, 2, 1),FCE("Ȕ", 0, 2, 1),
+FCE("ȕ", 1, 2, 1),FCE("Ȗ", 0, 2, 1),FCE("ȗ", 1, 2, 1),FCE("Ș", 0, 2, 1),
+FCE("ș", 1, 2, 1),FCE("Ț", 0, 2, 1),FCE("ț", 1, 2, 1),FCE("Ȝ", 0, 2, 1),
+FCE("ȝ", 1, 2, 1),FCE("Ȟ", 0, 2, 1),FCE("ȟ", 1, 2, 1),FCE("ƞ", 0, 2, 1),
+FCE("Ƞ", 1, 2, 1),FCE("Ȣ", 0, 2, 1),FCE("ȣ", 1, 2, 1),FCE("Ȥ", 0, 2, 1),
+FCE("ȥ", 1, 2, 1),FCE("Ȧ", 0, 2, 1),FCE("ȧ", 1, 2, 1),FCE("Ȩ", 0, 2, 1),
+FCE("ȩ", 1, 2, 1),FCE("Ꝗ", 0, 2, 1),FCE("ꝗ", 1, 2, 1),FCE("Ȫ", 0, 2, 1),
+FCE("ȫ", 1, 2, 1),FCE("Ȭ", 0, 2, 1),FCE("ȭ", 1, 2, 1),FCE("Ȯ", 0, 2, 1),
+FCE("ȯ", 1, 2, 1),FCE("Ȱ", 0, 2, 1),FCE("ȱ", 1, 2, 1),FCE("Ȳ", 0, 2, 1),
+FCE("ȳ", 1, 2, 1),FCE("Ꚅ", 0, 2, 1),FCE("ꚅ", 1, 2, 1),FCE("Ⱥ", 0, 2, 1),
+FCE("ⱥ", 1, 2, 1),FCE("Ȼ", 0, 2, 1),FCE("ȼ", 1, 2, 1),FCE("ƚ", 0, 2, 1),
+FCE("Ƚ", 1, 2, 1),FCE("Ⱦ", 0, 2, 1),FCE("ⱦ", 1, 2, 1),FCE("Ɂ", 0, 2, 1),
+FCE("ɂ", 1, 2, 1),FCE("𐐒", 0, 2, 1),FCE("𐐺", 1, 2, 1),FCE("ƀ", 0, 2, 1),
+FCE("Ƀ", 1, 2, 1),FCE("Ʉ", 0, 2, 1),FCE("ʉ", 1, 2, 1),FCE("Ʌ", 0, 2, 1),
+FCE("ʌ", 1, 2, 1),FCE("Ɇ", 0, 2, 1),FCE("ɇ", 1, 2, 1),FCE("Ɉ", 0, 2, 1),
+FCE("ɉ", 1, 2, 1),FCE("Ɋ", 0, 2, 1),FCE("ɋ", 1, 2, 1),FCE("Ɍ", 0, 2, 1),
+FCE("ɍ", 1, 2, 1),FCE("Ⱋ", 0, 2, 1),FCE("ⱋ", 1, 2, 1),FCE("Ɏ", 0, 2, 1),
+FCE("ɏ", 1, 2, 1),FCE("𐐊", 0, 2, 1),FCE("𐐲", 1, 2, 1),FCE("Ⅰ", 0, 2, 1),
+FCE("ⅰ", 1, 2, 1),FCE("Ꚓ", 0, 2, 1),FCE("ꚓ", 1, 2, 1),FCE("ɽ", 0, 2, 1),
+FCE("Ɽ", 1, 2, 1),FCE("𐐐", 0, 2, 1),FCE("𐐸", 1, 2, 1),FCE("Ⱑ", 0, 2, 1),
+FCE("ⱑ", 1, 2, 1),FCE("Ⱪ", 0, 2, 1),FCE("ⱪ", 1, 2, 1),FCE("𐐉", 0, 2, 1),
+FCE("𐐱", 1, 2, 1),FCE("𐐔", 0, 2, 1),FCE("𐐼", 1, 2, 1),FCE("ﬕ", 0, 2, 1),
+FCE("մի", 1, 2, 2),FCE("Ⅲ", 0, 2, 1),FCE("ⅲ", 1, 2, 1),FCE("𐐞", 0, 2, 1),
+FCE("𐑆", 1, 2, 1),FCE("ɱ", 0, 2, 1),FCE("Ɱ", 1, 2, 1),FCE("𐐕", 0, 2, 1),
+FCE("𐐽", 1, 2, 1),FCE("ɒ", 0, 2, 1),FCE("Ɒ", 1, 2, 1),FCE("Ⱳ", 0, 2, 1),
+FCE("ⱳ", 1, 2, 1),FCE("Ⰻ", 0, 2, 1),FCE("ⰻ", 1, 2, 1),FCE("𐐖", 0, 2, 1),
+FCE("𐐾", 1, 2, 1),FCE("Ꚗ", 0, 2, 1),FCE("ꚗ", 1, 2, 1),FCE("Ⱶ", 0, 2, 1),
+FCE("ⱶ", 1, 2, 1),FCE("Ⅴ", 0, 2, 1),FCE("ⅴ", 1, 2, 1),FCE("Ꙁ", 0, 2, 1),
+FCE("ꙁ", 1, 2, 1),FCE("B", 0, 2, 1),FCE("b", 1, 2, 1),FCE("Ⰼ", 0, 2, 1),
+FCE("ⰼ", 1, 2, 1),FCE("𐐗", 0, 2, 1),FCE("𐐿", 1, 2, 1),FCE("D", 0, 2, 1),
+FCE("d", 1, 2, 1),FCE("E", 0, 2, 1),FCE("e", 1, 2, 1),FCE("F", 0, 2, 1),
+FCE("f", 1, 2, 1),FCE("Ⰽ", 0, 2, 1),FCE("ⰽ", 1, 2, 1),FCE("Ⓛ", 0, 2, 1),
+FCE("ⓛ", 1, 2, 1),FCE("Ꜩ", 0, 2, 1),FCE("ꜩ", 1, 2, 1),FCE("ȿ", 0, 2, 1),
+FCE("Ȿ", 1, 2, 1),FCE("𐐑", 0, 2, 1),FCE("𐐹", 1, 2, 1),FCE("I", 0, 2, 1),
+FCE("i", 1, 2, 1),FCE("𐐋", 0, 2, 1),FCE("𐐳", 1, 2, 1),FCE("Ꜫ", 0, 2, 1),
+FCE("ꜫ", 1, 2, 1),FCE("ff", 0, 2, 1),FCE("ff", 1, 2, 2),FCE("Ⲁ", 0, 2, 1),
+FCE("ⲁ", 1, 2, 1),FCE("fl", 0, 2, 1),FCE("fl", 1, 2, 2),FCE("ffi", 0, 2, 1),
+FCE("ffi", 1, 2, 3),FCE("ffl", 0, 2, 1),FCE("ffl", 1, 2, 3),FCE("ſt", 0, 3, 1),
+FCE("st", 1, 3, 1),FCE("st", 2, 3, 2),FCE("ſt", 0, 3, 1),FCE("st", 1, 3, 1),
+FCE("st", 2, 3, 2),FCE("Ꜭ", 0, 2, 1),FCE("ꜭ", 1, 2, 1),FCE("Ⰾ", 0, 2, 1),
+FCE("ⰾ", 1, 2, 1),FCE("M", 0, 2, 1),FCE("m", 1, 2, 1),FCE("ﬓ", 0, 2, 1),
+FCE("մն", 1, 2, 2),FCE("ﬔ", 0, 2, 1),FCE("մե", 1, 2, 2),FCE("Ꜯ", 0, 2, 1),
+FCE("ꜯ", 1, 2, 1),FCE("ﬖ", 0, 2, 1),FCE("վն", 1, 2, 2),FCE("ﬗ", 0, 2, 1),
+FCE("մխ", 1, 2, 2),FCE("𐐍", 0, 2, 1),FCE("𐐵", 1, 2, 1),FCE("O", 0, 2, 1),
+FCE("o", 1, 2, 1),FCE("Q", 0, 2, 1),FCE("q", 1, 2, 1),FCE("R", 0, 2, 1),
+FCE("r", 1, 2, 1),FCE("𐐚", 0, 2, 1),FCE("𐑂", 1, 2, 1),FCE("T", 0, 2, 1),
+FCE("t", 1, 2, 1),FCE("Ⲙ", 0, 2, 1),FCE("ⲙ", 1, 2, 1),FCE("Ⲋ", 0, 2, 1),
+FCE("ⲋ", 1, 2, 1),FCE("ͅ", 0, 4, 1),FCE("Ι", 1, 4, 1),FCE("ι", 2, 4, 1),
+FCE("ι", 3, 4, 1),FCE("Ⲍ", 0, 2, 1),FCE("ⲍ", 1, 2, 1),FCE("W", 0, 2, 1),
+FCE("w", 1, 2, 1),FCE("Ꙗ", 0, 2, 1),FCE("ꙗ", 1, 2, 1),FCE("𐐛", 0, 2, 1),
+FCE("𐑃", 1, 2, 1),FCE("Ꜹ", 0, 2, 1),FCE("ꜹ", 1, 2, 1),FCE("Ⲏ", 0, 2, 1),
+FCE("ⲏ", 1, 2, 1),FCE("Y", 0, 2, 1),FCE("y", 1, 2, 1),FCE("𐐄", 0, 2, 1),
+FCE("𐐬", 1, 2, 1),FCE("Ꜻ", 0, 2, 1),FCE("ꜻ", 1, 2, 1),FCE("Ⲑ", 0, 2, 1),
+FCE("ⲑ", 1, 2, 1),FCE("Ꜽ", 0, 2, 1),FCE("ꜽ", 1, 2, 1),FCE("Ⲓ", 0, 2, 1),
+FCE("ⲓ", 1, 2, 1),FCE("𐐜", 0, 2, 1),FCE("𐑄", 1, 2, 1),FCE("Ͱ", 0, 2, 1),
+FCE("ͱ", 1, 2, 1),FCE("Ͳ", 0, 2, 1),FCE("ͳ", 1, 2, 1),FCE("Ꜿ", 0, 2, 1),
+FCE("ꜿ", 1, 2, 1),FCE("Ͷ", 0, 2, 1),FCE("ͷ", 1, 2, 1),FCE("Ⲕ", 0, 2, 1),
+FCE("ⲕ", 1, 2, 1),FCE("Ⲗ", 0, 2, 1),FCE("ⲗ", 1, 2, 1),FCE("Ά", 0, 2, 1),
+FCE("ά", 1, 2, 1),FCE("𐐅", 0, 2, 1),FCE("𐐭", 1, 2, 1),FCE("Έ", 0, 2, 1),
+FCE("έ", 1, 2, 1),FCE("Ή", 0, 2, 1),FCE("ή", 1, 2, 1),FCE("Ί", 0, 2, 1),
+FCE("ί", 1, 2, 1),FCE("Ό", 0, 2, 1),FCE("ό", 1, 2, 1),FCE("Ύ", 0, 2, 1),
+FCE("ύ", 1, 2, 1),FCE("Ώ", 0, 2, 1),FCE("ώ", 1, 2, 1),FCE("ΐ", 0, 3, 1),
+FCE("ΐ", 1, 3, 1),FCE("ΐ", 2, 3, 3),FCE("Α", 0, 2, 1),FCE("α", 1, 2, 1),
+FCE("Β", 0, 3, 1),FCE("β", 1, 3, 1),FCE("ϐ", 2, 3, 1),FCE("Γ", 0, 2, 1),
+FCE("γ", 1, 2, 1),FCE("Δ", 0, 2, 1),FCE("δ", 1, 2, 1),FCE("Ε", 0, 3, 1),
+FCE("ε", 1, 3, 1),FCE("ϵ", 2, 3, 1),FCE("Ζ", 0, 2, 1),FCE("ζ", 1, 2, 1),
+FCE("Η", 0, 2, 1),FCE("η", 1, 2, 1),FCE("Θ", 0, 4, 1),FCE("θ", 1, 4, 1),
+FCE("ϑ", 2, 4, 1),FCE("ϴ", 3, 4, 1),FCE("ͅ", 0, 4, 1),FCE("Ι", 1, 4, 1),
+FCE("ι", 2, 4, 1),FCE("ι", 3, 4, 1),FCE("Κ", 0, 3, 1),FCE("κ", 1, 3, 1),
+FCE("ϰ", 2, 3, 1),FCE("Λ", 0, 2, 1),FCE("λ", 1, 2, 1),FCE("µ", 0, 3, 1),
+FCE("Μ", 1, 3, 1),FCE("μ", 2, 3, 1),FCE("Ν", 0, 2, 1),FCE("ν", 1, 2, 1),
+FCE("Ξ", 0, 2, 1),FCE("ξ", 1, 2, 1),FCE("Ο", 0, 2, 1),FCE("ο", 1, 2, 1),
+FCE("Π", 0, 3, 1),FCE("π", 1, 3, 1),FCE("ϖ", 2, 3, 1),FCE("Ρ", 0, 3, 1),
+FCE("ρ", 1, 3, 1),FCE("ϱ", 2, 3, 1),FCE("Σ", 0, 3, 1),FCE("ς", 1, 3, 1),
+FCE("σ", 2, 3, 1),FCE("Τ", 0, 2, 1),FCE("τ", 1, 2, 1),FCE("Υ", 0, 2, 1),
+FCE("υ", 1, 2, 1),FCE("Φ", 0, 3, 1),FCE("φ", 1, 3, 1),FCE("ϕ", 2, 3, 1),
+FCE("Χ", 0, 2, 1),FCE("χ", 1, 2, 1),FCE("Ψ", 0, 2, 1),FCE("ψ", 1, 2, 1),
+FCE("Ω", 0, 3, 1),FCE("ω", 1, 3, 1),FCE("Ω", 2, 3, 1),FCE("Ϊ", 0, 2, 1),
+FCE("ϊ", 1, 2, 1),FCE("Ϋ", 0, 2, 1),FCE("ϋ", 1, 2, 1),FCE("Ⓣ", 0, 2, 1),
+FCE("ⓣ", 1, 2, 1),FCE("Ⳡ", 0, 2, 1),FCE("ⳡ", 1, 2, 1),FCE("ΰ", 0, 3, 1),
+FCE("ΰ", 1, 3, 1),FCE("ΰ", 2, 3, 3),FCE("Ꝉ", 0, 2, 1),FCE("ꝉ", 1, 2, 1),
+FCE("Ⲝ", 0, 2, 1),FCE("ⲝ", 1, 2, 1),FCE("Ⲟ", 0, 2, 1),FCE("ⲟ", 1, 2, 1),
+FCE("Ꝋ", 0, 2, 1),FCE("ꝋ", 1, 2, 1),FCE("Ⲡ", 0, 2, 1),FCE("ⲡ", 1, 2, 1),
+FCE("Σ", 0, 3, 1),FCE("ς", 1, 3, 1),FCE("σ", 2, 3, 1),FCE("𐐟", 0, 2, 1),
+FCE("𐑇", 1, 2, 1),FCE("Ꝍ", 0, 2, 1),FCE("ꝍ", 1, 2, 1),FCE("Ꚋ", 0, 2, 1),
+FCE("ꚋ", 1, 2, 1),FCE("Ⲣ", 0, 2, 1),FCE("ⲣ", 1, 2, 1),FCE("Ϗ", 0, 2, 1),
+FCE("ϗ", 1, 2, 1),FCE("Β", 0, 3, 1),FCE("β", 1, 3, 1),FCE("ϐ", 2, 3, 1),
+FCE("Θ", 0, 4, 1),FCE("θ", 1, 4, 1),FCE("ϑ", 2, 4, 1),FCE("ϴ", 3, 4, 1),
+FCE("Φ", 0, 3, 1),FCE("φ", 1, 3, 1),FCE("ϕ", 2, 3, 1),FCE("Π", 0, 3, 1),
+FCE("π", 1, 3, 1),FCE("ϖ", 2, 3, 1),FCE("Ϙ", 0, 2, 1),FCE("ϙ", 1, 2, 1),
+FCE("Ⲥ", 0, 2, 1),FCE("ⲥ", 1, 2, 1),FCE("Ϛ", 0, 2, 1),FCE("ϛ", 1, 2, 1),
+FCE("Ϝ", 0, 2, 1),FCE("ϝ", 1, 2, 1),FCE("Ϟ", 0, 2, 1),FCE("ϟ", 1, 2, 1),
+FCE("Ϡ", 0, 2, 1),FCE("ϡ", 1, 2, 1),FCE("Ꝑ", 0, 2, 1),FCE("ꝑ", 1, 2, 1),
+FCE("Ϣ", 0, 2, 1),FCE("ϣ", 1, 2, 1),FCE("Ϥ", 0, 2, 1),FCE("ϥ", 1, 2, 1),
+FCE("Ⲧ", 0, 2, 1),FCE("ⲧ", 1, 2, 1),FCE("Ϧ", 0, 2, 1),FCE("ϧ", 1, 2, 1),
+FCE("𐐠", 0, 2, 1),FCE("𐑈", 1, 2, 1),FCE("Ϩ", 0, 2, 1),FCE("ϩ", 1, 2, 1),
+FCE("Ⳣ", 0, 2, 1),FCE("ⳣ", 1, 2, 1),FCE("Ϫ", 0, 2, 1),FCE("ϫ", 1, 2, 1),
+FCE("Ϭ", 0, 2, 1),FCE("ϭ", 1, 2, 1),FCE("Ꝓ", 0, 2, 1),FCE("ꝓ", 1, 2, 1),
+FCE("Ϯ", 0, 2, 1),FCE("ϯ", 1, 2, 1),FCE("Κ", 0, 3, 1),FCE("κ", 1, 3, 1),
+FCE("ϰ", 2, 3, 1),FCE("Ρ", 0, 3, 1),FCE("ρ", 1, 3, 1),FCE("ϱ", 2, 3, 1),
+FCE("Θ", 0, 4, 1),FCE("θ", 1, 4, 1),FCE("ϑ", 2, 4, 1),FCE("ϴ", 3, 4, 1),
+FCE("Ε", 0, 3, 1),FCE("ε", 1, 3, 1),FCE("ϵ", 2, 3, 1),FCE("Ϸ", 0, 2, 1),
+FCE("ϸ", 1, 2, 1),FCE("ϲ", 0, 2, 1),FCE("Ϲ", 1, 2, 1),FCE("Ϻ", 0, 2, 1),
+FCE("ϻ", 1, 2, 1),FCE("ͻ", 0, 2, 1),FCE("Ͻ", 1, 2, 1),FCE("ͼ", 0, 2, 1),
+FCE("Ͼ", 1, 2, 1),FCE("ͽ", 0, 2, 1),FCE("Ͽ", 1, 2, 1),FCE("Ѐ", 0, 2, 1),
+FCE("ѐ", 1, 2, 1),FCE("Ё", 0, 2, 1),FCE("ё", 1, 2, 1),FCE("Ђ", 0, 2, 1),
+FCE("ђ", 1, 2, 1),FCE("Ѓ", 0, 2, 1),FCE("ѓ", 1, 2, 1),FCE("Є", 0, 2, 1),
+FCE("є", 1, 2, 1),FCE("Ѕ", 0, 2, 1),FCE("ѕ", 1, 2, 1),FCE("І", 0, 2, 1),
+FCE("і", 1, 2, 1),FCE("Ї", 0, 2, 1),FCE("ї", 1, 2, 1),FCE("Ј", 0, 2, 1),
+FCE("ј", 1, 2, 1),FCE("Љ", 0, 2, 1),FCE("љ", 1, 2, 1),FCE("Њ", 0, 2, 1),
+FCE("њ", 1, 2, 1),FCE("Ћ", 0, 2, 1),FCE("ћ", 1, 2, 1),FCE("Ќ", 0, 2, 1),
+FCE("ќ", 1, 2, 1),FCE("Ѝ", 0, 2, 1),FCE("ѝ", 1, 2, 1),FCE("Ў", 0, 2, 1),
+FCE("ў", 1, 2, 1),FCE("Џ", 0, 2, 1),FCE("џ", 1, 2, 1),FCE("А", 0, 2, 1),
+FCE("а", 1, 2, 1),FCE("Б", 0, 2, 1),FCE("б", 1, 2, 1),FCE("В", 0, 2, 1),
+FCE("в", 1, 2, 1),FCE("Г", 0, 2, 1),FCE("г", 1, 2, 1),FCE("Д", 0, 2, 1),
+FCE("д", 1, 2, 1),FCE("Е", 0, 2, 1),FCE("е", 1, 2, 1),FCE("Ж", 0, 2, 1),
+FCE("ж", 1, 2, 1),FCE("З", 0, 2, 1),FCE("з", 1, 2, 1),FCE("И", 0, 2, 1),
+FCE("и", 1, 2, 1),FCE("Й", 0, 2, 1),FCE("й", 1, 2, 1),FCE("К", 0, 2, 1),
+FCE("к", 1, 2, 1),FCE("Л", 0, 2, 1),FCE("л", 1, 2, 1),FCE("М", 0, 2, 1),
+FCE("м", 1, 2, 1),FCE("Н", 0, 2, 1),FCE("н", 1, 2, 1),FCE("О", 0, 2, 1),
+FCE("о", 1, 2, 1),FCE("П", 0, 2, 1),FCE("п", 1, 2, 1),FCE("Р", 0, 2, 1),
+FCE("р", 1, 2, 1),FCE("С", 0, 2, 1),FCE("с", 1, 2, 1),FCE("Т", 0, 2, 1),
+FCE("т", 1, 2, 1),FCE("У", 0, 2, 1),FCE("у", 1, 2, 1),FCE("Ф", 0, 2, 1),
+FCE("ф", 1, 2, 1),FCE("Х", 0, 2, 1),FCE("х", 1, 2, 1),FCE("Ц", 0, 2, 1),
+FCE("ц", 1, 2, 1),FCE("Ч", 0, 2, 1),FCE("ч", 1, 2, 1),FCE("Ш", 0, 2, 1),
+FCE("ш", 1, 2, 1),FCE("Щ", 0, 2, 1),FCE("щ", 1, 2, 1),FCE("Ъ", 0, 2, 1),
+FCE("ъ", 1, 2, 1),FCE("Ы", 0, 2, 1),FCE("ы", 1, 2, 1),FCE("Ь", 0, 2, 1),
+FCE("ь", 1, 2, 1),FCE("Э", 0, 2, 1),FCE("э", 1, 2, 1),FCE("Ю", 0, 2, 1),
+FCE("ю", 1, 2, 1),FCE("Я", 0, 2, 1),FCE("я", 1, 2, 1),FCE("Z", 0, 2, 1),
+FCE("z", 1, 2, 1),FCE("Ⲵ", 0, 2, 1),FCE("ⲵ", 1, 2, 1),FCE("µ", 0, 3, 1),
+FCE("Μ", 1, 3, 1),FCE("μ", 2, 3, 1),FCE("𐐣", 0, 2, 1),FCE("𐑋", 1, 2, 1),
+FCE("Ⓐ", 0, 2, 1),FCE("ⓐ", 1, 2, 1),FCE("Ⓒ", 0, 2, 1),FCE("ⓒ", 1, 2, 1),
+FCE("L", 0, 2, 1),FCE("l", 1, 2, 1),FCE("𐐡", 0, 2, 1),FCE("𐑉", 1, 2, 1),
+FCE("Ⓔ", 0, 2, 1),FCE("ⓔ", 1, 2, 1),FCE("𐐙", 0, 2, 1),FCE("𐑁", 1, 2, 1),
+FCE("Ѡ", 0, 2, 1),FCE("ѡ", 1, 2, 1),FCE("Ѣ", 0, 2, 1),FCE("ѣ", 1, 2, 1),
+FCE("ᵽ", 0, 2, 1),FCE("Ᵽ", 1, 2, 1),FCE("Ѥ", 0, 2, 1),FCE("ѥ", 1, 2, 1),
+FCE("Ѧ", 0, 2, 1),FCE("ѧ", 1, 2, 1),FCE("Ⱨ", 0, 2, 1),FCE("ⱨ", 1, 2, 1),
+FCE("Ѩ", 0, 2, 1),FCE("ѩ", 1, 2, 1),FCE("Ⓖ", 0, 2, 1),FCE("ⓖ", 1, 2, 1),
+FCE("Ѫ", 0, 2, 1),FCE("ѫ", 1, 2, 1),FCE("Ⱬ", 0, 2, 1),FCE("ⱬ", 1, 2, 1),
+FCE("Ѭ", 0, 2, 1),FCE("ѭ", 1, 2, 1),FCE("ɑ", 0, 2, 1),FCE("Ɑ", 1, 2, 1),
+FCE("Ѯ", 0, 2, 1),FCE("ѯ", 1, 2, 1),FCE("ɐ", 0, 2, 1),FCE("Ɐ", 1, 2, 1),
+FCE("Ѱ", 0, 2, 1),FCE("ѱ", 1, 2, 1),FCE("Ꝩ", 0, 2, 1),FCE("ꝩ", 1, 2, 1),
+FCE("Ѳ", 0, 2, 1),FCE("ѳ", 1, 2, 1),FCE("Ѵ", 0, 2, 1),FCE("ѵ", 1, 2, 1),
+FCE("Ⓘ", 0, 2, 1),FCE("ⓘ", 1, 2, 1),FCE("Ѷ", 0, 2, 1),FCE("ѷ", 1, 2, 1),
+FCE("Ѹ", 0, 2, 1),FCE("ѹ", 1, 2, 1),FCE("Ѻ", 0, 2, 1),FCE("ѻ", 1, 2, 1),
+FCE("Ѽ", 0, 2, 1),FCE("ѽ", 1, 2, 1),FCE("Ꝫ", 0, 2, 1),FCE("ꝫ", 1, 2, 1),
+FCE("Ѿ", 0, 2, 1),FCE("ѿ", 1, 2, 1),FCE("ɀ", 0, 2, 1),FCE("Ɀ", 1, 2, 1),
+FCE("Ҁ", 0, 2, 1),FCE("ҁ", 1, 2, 1),FCE("Ⴠ", 0, 2, 1),FCE("ⴠ", 1, 2, 1),
+FCE("Ⲃ", 0, 2, 1),FCE("ⲃ", 1, 2, 1),FCE("Ⲅ", 0, 2, 1),FCE("ⲅ", 1, 2, 1),
+FCE("Ⲇ", 0, 2, 1),FCE("ⲇ", 1, 2, 1),FCE("Ⴡ", 0, 2, 1),FCE("ⴡ", 1, 2, 1),
+FCE("Ⲉ", 0, 2, 1),FCE("ⲉ", 1, 2, 1),FCE("Ꝭ", 0, 2, 1),FCE("ꝭ", 1, 2, 1),
+FCE("Ҋ", 0, 2, 1),FCE("ҋ", 1, 2, 1),FCE("Ҍ", 0, 2, 1),FCE("ҍ", 1, 2, 1),
+FCE("Â", 0, 2, 1),FCE("â", 1, 2, 1),FCE("Ҏ", 0, 2, 1),FCE("ҏ", 1, 2, 1),
+FCE("Ґ", 0, 2, 1),FCE("ґ", 1, 2, 1),FCE("Ғ", 0, 2, 1),FCE("ғ", 1, 2, 1),
+FCE("Ⴣ", 0, 2, 1),FCE("ⴣ", 1, 2, 1),FCE("Ҕ", 0, 2, 1),FCE("ҕ", 1, 2, 1),
+FCE("Ꝯ", 0, 2, 1),FCE("ꝯ", 1, 2, 1),FCE("Җ", 0, 2, 1),FCE("җ", 1, 2, 1),
+FCE("Ҙ", 0, 2, 1),FCE("ҙ", 1, 2, 1),FCE("Ä", 0, 2, 1),FCE("ä", 1, 2, 1),
+FCE("Қ", 0, 2, 1),FCE("қ", 1, 2, 1),FCE("𐐦", 0, 2, 1),FCE("𐑎", 1, 2, 1),
+FCE("Ҝ", 0, 2, 1),FCE("ҝ", 1, 2, 1),FCE("Ҟ", 0, 2, 1),FCE("ҟ", 1, 2, 1),
+FCE("Ⴥ", 0, 2, 1),FCE("ⴥ", 1, 2, 1),FCE("Ҡ", 0, 2, 1),FCE("ҡ", 1, 2, 1),
+FCE("Ң", 0, 2, 1),FCE("ң", 1, 2, 1),FCE("Ҥ", 0, 2, 1),FCE("ҥ", 1, 2, 1),
+FCE("Ⳇ", 0, 2, 1),FCE("ⳇ", 1, 2, 1),FCE("Ҧ", 0, 2, 1),FCE("ҧ", 1, 2, 1),
+FCE("Ҩ", 0, 2, 1),FCE("ҩ", 1, 2, 1),FCE("Ⱡ", 0, 2, 1),FCE("ⱡ", 1, 2, 1),
+FCE("Ҫ", 0, 2, 1),FCE("ҫ", 1, 2, 1),FCE("Ⴧ", 0, 2, 1),FCE("ⴧ", 1, 2, 1),
+FCE("Ҭ", 0, 2, 1),FCE("ҭ", 1, 2, 1),FCE("𐐓", 0, 2, 1),FCE("𐐻", 1, 2, 1),
+FCE("Ү", 0, 2, 1),FCE("ү", 1, 2, 1),FCE("Ұ", 0, 2, 1),FCE("ұ", 1, 2, 1),
+FCE("Ⳉ", 0, 2, 1),FCE("ⳉ", 1, 2, 1),FCE("Ҳ", 0, 2, 1),FCE("ҳ", 1, 2, 1),
+FCE("Ҵ", 0, 2, 1),FCE("ҵ", 1, 2, 1),FCE("Ҷ", 0, 2, 1),FCE("ҷ", 1, 2, 1),
+FCE("Ⓑ", 0, 2, 1),FCE("ⓑ", 1, 2, 1),FCE("Ҹ", 0, 2, 1),FCE("ҹ", 1, 2, 1),
+FCE("Ⓓ", 0, 2, 1),FCE("ⓓ", 1, 2, 1),FCE("Һ", 0, 2, 1),FCE("һ", 1, 2, 1),
+FCE("Ⓕ", 0, 2, 1),FCE("ⓕ", 1, 2, 1),FCE("Ҽ", 0, 2, 1),FCE("ҽ", 1, 2, 1),
+FCE("Ⓗ", 0, 2, 1),FCE("ⓗ", 1, 2, 1),FCE("Ҿ", 0, 2, 1),FCE("ҿ", 1, 2, 1),
+FCE("Ⓙ", 0, 2, 1),FCE("ⓙ", 1, 2, 1),FCE("Ӏ", 0, 2, 1),FCE("ӏ", 1, 2, 1),
+FCE("Ӂ", 0, 2, 1),FCE("ӂ", 1, 2, 1),FCE("Ⓜ", 0, 2, 1),FCE("ⓜ", 1, 2, 1),
+FCE("Ӄ", 0, 2, 1),FCE("ӄ", 1, 2, 1),FCE("Ⓞ", 0, 2, 1),FCE("ⓞ", 1, 2, 1),
+FCE("Ӆ", 0, 2, 1),FCE("ӆ", 1, 2, 1),FCE("Ⓠ", 0, 2, 1),FCE("ⓠ", 1, 2, 1),
+FCE("Ӈ", 0, 2, 1),FCE("ӈ", 1, 2, 1),FCE("Ⓢ", 0, 2, 1),FCE("ⓢ", 1, 2, 1),
+FCE("Ӊ", 0, 2, 1),FCE("ӊ", 1, 2, 1),FCE("Ⓤ", 0, 2, 1),FCE("ⓤ", 1, 2, 1),
+FCE("Ӌ", 0, 2, 1),FCE("ӌ", 1, 2, 1),FCE("Ⓦ", 0, 2, 1),FCE("ⓦ", 1, 2, 1),
+FCE("Ӎ", 0, 2, 1),FCE("ӎ", 1, 2, 1),FCE("Ⓨ", 0, 2, 1),FCE("ⓨ", 1, 2, 1),
+FCE("Ⴭ", 0, 2, 1),FCE("ⴭ", 1, 2, 1),FCE("Ӑ", 0, 2, 1),FCE("ӑ", 1, 2, 1),
+FCE("Ӓ", 0, 2, 1),FCE("ӓ", 1, 2, 1),FCE("Ӕ", 0, 2, 1),FCE("ӕ", 1, 2, 1),
+FCE("Ⳏ", 0, 2, 1),FCE("ⳏ", 1, 2, 1),FCE("Ӗ", 0, 2, 1),FCE("ӗ", 1, 2, 1),
+FCE("Ꝺ", 0, 2, 1),FCE("ꝺ", 1, 2, 1),FCE("Ә", 0, 2, 1),FCE("ә", 1, 2, 1),
+FCE("Ӛ", 0, 2, 1),FCE("ӛ", 1, 2, 1),FCE("Ⓩ", 0, 2, 1),FCE("ⓩ", 1, 2, 1),
+FCE("Ӝ", 0, 2, 1),FCE("ӝ", 1, 2, 1),FCE("Ӟ", 0, 2, 1),FCE("ӟ", 1, 2, 1),
+FCE("Ӡ", 0, 2, 1),FCE("ӡ", 1, 2, 1),FCE("Ⳑ", 0, 2, 1),FCE("ⳑ", 1, 2, 1),
+FCE("Ӣ", 0, 2, 1),FCE("ӣ", 1, 2, 1),FCE("Ӥ", 0, 2, 1),FCE("ӥ", 1, 2, 1),
+FCE("ɫ", 0, 2, 1),FCE("Ɫ", 1, 2, 1),FCE("Ӧ", 0, 2, 1),FCE("ӧ", 1, 2, 1),
+FCE("Ө", 0, 2, 1),FCE("ө", 1, 2, 1),FCE("Ӫ", 0, 2, 1),FCE("ӫ", 1, 2, 1),
+FCE("Ⅎ", 0, 2, 1),FCE("ⅎ", 1, 2, 1),FCE("Ӭ", 0, 2, 1),FCE("ӭ", 1, 2, 1),
+FCE("Ⳓ", 0, 2, 1),FCE("ⳓ", 1, 2, 1),FCE("Ӯ", 0, 2, 1),FCE("ӯ", 1, 2, 1),
+FCE("Ӱ", 0, 2, 1),FCE("ӱ", 1, 2, 1),FCE("𐐢", 0, 2, 1),FCE("𐑊", 1, 2, 1),
+FCE("Ӳ", 0, 2, 1),FCE("ӳ", 1, 2, 1),FCE("Ӵ", 0, 2, 1),FCE("ӵ", 1, 2, 1),
+FCE("Ӷ", 0, 2, 1),FCE("ӷ", 1, 2, 1),FCE("Ӹ", 0, 2, 1),FCE("ӹ", 1, 2, 1),
+FCE("Ⳕ", 0, 2, 1),FCE("ⳕ", 1, 2, 1),FCE("Ӻ", 0, 2, 1),FCE("ӻ", 1, 2, 1),
+FCE("Ӽ", 0, 2, 1),FCE("ӽ", 1, 2, 1),FCE("Ӿ", 0, 2, 1),FCE("ӿ", 1, 2, 1),
+FCE("Ԁ", 0, 2, 1),FCE("ԁ", 1, 2, 1),FCE("Ꞁ", 0, 2, 1),FCE("ꞁ", 1, 2, 1),
+FCE("Ԃ", 0, 2, 1),FCE("ԃ", 1, 2, 1),FCE("Ԅ", 0, 2, 1),FCE("ԅ", 1, 2, 1),
+FCE("Ⳗ", 0, 2, 1),FCE("ⳗ", 1, 2, 1),FCE("Ԇ", 0, 2, 1),FCE("ԇ", 1, 2, 1),
+FCE("Ԉ", 0, 2, 1),FCE("ԉ", 1, 2, 1),FCE("Ԋ", 0, 2, 1),FCE("ԋ", 1, 2, 1),
+FCE("Ԍ", 0, 2, 1),FCE("ԍ", 1, 2, 1),FCE("Ꞃ", 0, 2, 1),FCE("ꞃ", 1, 2, 1),
+FCE("Ԏ", 0, 2, 1),FCE("ԏ", 1, 2, 1),FCE("Ԑ", 0, 2, 1),FCE("ԑ", 1, 2, 1),
+FCE("Ⳙ", 0, 2, 1),FCE("ⳙ", 1, 2, 1),FCE("Ԓ", 0, 2, 1),FCE("ԓ", 1, 2, 1),
+FCE("Ԕ", 0, 2, 1),FCE("ԕ", 1, 2, 1),FCE("Ԗ", 0, 2, 1),FCE("ԗ", 1, 2, 1),
+FCE("Ԙ", 0, 2, 1),FCE("ԙ", 1, 2, 1),FCE("Ꞅ", 0, 2, 1),FCE("ꞅ", 1, 2, 1),
+FCE("Ԛ", 0, 2, 1),FCE("ԛ", 1, 2, 1),FCE("Ⲩ", 0, 2, 1),FCE("ⲩ", 1, 2, 1),
+FCE("Ԝ", 0, 2, 1),FCE("ԝ", 1, 2, 1),FCE("Ⳛ", 0, 2, 1),FCE("ⳛ", 1, 2, 1),
+FCE("Ԟ", 0, 2, 1),FCE("ԟ", 1, 2, 1),FCE("Ԡ", 0, 2, 1),FCE("ԡ", 1, 2, 1),
+FCE("Ԣ", 0, 2, 1),FCE("ԣ", 1, 2, 1),FCE("Ԥ", 0, 2, 1),FCE("ԥ", 1, 2, 1),
+FCE("Ꞇ", 0, 2, 1),FCE("ꞇ", 1, 2, 1),FCE("Ԧ", 0, 2, 1),FCE("ԧ", 1, 2, 1),
+FCE("Ⱐ", 0, 2, 1),FCE("ⱐ", 1, 2, 1),FCE("Ⳝ", 0, 2, 1),FCE("ⳝ", 1, 2, 1),
+FCE("Ա", 0, 2, 1),FCE("ա", 1, 2, 1),FCE("Բ", 0, 2, 1),FCE("բ", 1, 2, 1),
+FCE("Գ", 0, 2, 1),FCE("գ", 1, 2, 1),FCE("Դ", 0, 2, 1),FCE("դ", 1, 2, 1),
+FCE("Ե", 0, 2, 1),FCE("ե", 1, 2, 1),FCE("Զ", 0, 2, 1),FCE("զ", 1, 2, 1),
+FCE("Է", 0, 2, 1),FCE("է", 1, 2, 1),FCE("Ը", 0, 2, 1),FCE("ը", 1, 2, 1),
+FCE("Թ", 0, 2, 1),FCE("թ", 1, 2, 1),FCE("Ժ", 0, 2, 1),FCE("ժ", 1, 2, 1),
+FCE("Ի", 0, 2, 1),FCE("ի", 1, 2, 1),FCE("Լ", 0, 2, 1),FCE("լ", 1, 2, 1),
+FCE("Խ", 0, 2, 1),FCE("խ", 1, 2, 1),FCE("Ծ", 0, 2, 1),FCE("ծ", 1, 2, 1),
+FCE("Կ", 0, 2, 1),FCE("կ", 1, 2, 1),FCE("Հ", 0, 2, 1),FCE("հ", 1, 2, 1),
+FCE("Ձ", 0, 2, 1),FCE("ձ", 1, 2, 1),FCE("Ղ", 0, 2, 1),FCE("ղ", 1, 2, 1),
+FCE("Ճ", 0, 2, 1),FCE("ճ", 1, 2, 1),FCE("Մ", 0, 2, 1),FCE("մ", 1, 2, 1),
+FCE("Յ", 0, 2, 1),FCE("յ", 1, 2, 1),FCE("Ն", 0, 2, 1),FCE("ն", 1, 2, 1),
+FCE("Շ", 0, 2, 1),FCE("շ", 1, 2, 1),FCE("Ո", 0, 2, 1),FCE("ո", 1, 2, 1),
+FCE("Չ", 0, 2, 1),FCE("չ", 1, 2, 1),FCE("Պ", 0, 2, 1),FCE("պ", 1, 2, 1),
+FCE("Ջ", 0, 2, 1),FCE("ջ", 1, 2, 1),FCE("Ռ", 0, 2, 1),FCE("ռ", 1, 2, 1),
+FCE("Ս", 0, 2, 1),FCE("ս", 1, 2, 1),FCE("Վ", 0, 2, 1),FCE("վ", 1, 2, 1),
+FCE("Տ", 0, 2, 1),FCE("տ", 1, 2, 1),FCE("Ր", 0, 2, 1),FCE("ր", 1, 2, 1),
+FCE("Ց", 0, 2, 1),FCE("ց", 1, 2, 1),FCE("Ւ", 0, 2, 1),FCE("ւ", 1, 2, 1),
+FCE("Փ", 0, 2, 1),FCE("փ", 1, 2, 1),FCE("Ք", 0, 2, 1),FCE("ք", 1, 2, 1),
+FCE("Օ", 0, 2, 1),FCE("օ", 1, 2, 1),FCE("Ֆ", 0, 2, 1),FCE("ֆ", 1, 2, 1),
+FCE("Ⲫ", 0, 2, 1),FCE("ⲫ", 1, 2, 1),FCE("Ꞑ", 0, 2, 1),FCE("ꞑ", 1, 2, 1),
+FCE("Ⱒ", 0, 2, 1),FCE("ⱒ", 1, 2, 1),FCE("Ꞓ", 0, 2, 1),FCE("ꞓ", 1, 2, 1),
+FCE("Ⱓ", 0, 2, 1),FCE("ⱓ", 1, 2, 1),FCE("Ⳬ", 0, 2, 1),FCE("ⳬ", 1, 2, 1),
+FCE("Ⳋ", 0, 2, 1),FCE("ⳋ", 1, 2, 1),FCE("և", 0, 2, 1),FCE("եւ", 1, 2, 2),
+FCE("Ꙃ", 0, 2, 1),FCE("ꙃ", 1, 2, 1),FCE("Ⳮ", 0, 2, 1),FCE("ⳮ", 1, 2, 1),
+FCE("Ⲭ", 0, 2, 1),FCE("ⲭ", 1, 2, 1),FCE("Ꙅ", 0, 2, 1),FCE("ꙅ", 1, 2, 1),
+FCE("Ⱔ", 0, 2, 1),FCE("ⱔ", 1, 2, 1),FCE("Ꝕ", 0, 2, 1),FCE("ꝕ", 1, 2, 1),
+FCE("Ꙇ", 0, 2, 1),FCE("ꙇ", 1, 2, 1),FCE("Ⳳ", 0, 2, 1),FCE("ⳳ", 1, 2, 1),
+FCE("𐐈", 0, 2, 1),FCE("𐐰", 1, 2, 1),FCE("Ꙉ", 0, 2, 1),FCE("ꙉ", 1, 2, 1),
+FCE("Ⱕ", 0, 2, 1),FCE("ⱕ", 1, 2, 1),FCE("Ꞡ", 0, 2, 1),FCE("ꞡ", 1, 2, 1),
+FCE("Ꙍ", 0, 2, 1),FCE("ꙍ", 1, 2, 1),FCE("Ꞣ", 0, 2, 1),FCE("ꞣ", 1, 2, 1),
+FCE("Ⲯ", 0, 2, 1),FCE("ⲯ", 1, 2, 1),FCE("Ꙏ", 0, 2, 1),FCE("ꙏ", 1, 2, 1),
+FCE("Ꞥ", 0, 2, 1),FCE("ꞥ", 1, 2, 1),FCE("Ꙑ", 0, 2, 1),FCE("ꙑ", 1, 2, 1),
+FCE("Ꞧ", 0, 2, 1),FCE("ꞧ", 1, 2, 1),FCE("Ꞌ", 0, 2, 1),FCE("ꞌ", 1, 2, 1),
+FCE("Ꙓ", 0, 2, 1),FCE("ꙓ", 1, 2, 1),FCE("Ꞩ", 0, 2, 1),FCE("ꞩ", 1, 2, 1),
+FCE("Ꙕ", 0, 2, 1),FCE("ꙕ", 1, 2, 1),FCE("ɦ", 0, 2, 1),FCE("Ɦ", 1, 2, 1),
+FCE("Ḁ", 0, 2, 1),FCE("ḁ", 1, 2, 1),FCE("Ḃ", 0, 2, 1),FCE("ḃ", 1, 2, 1),
+FCE("Ḅ", 0, 2, 1),FCE("ḅ", 1, 2, 1),FCE("Ⱏ", 0, 2, 1),FCE("ⱏ", 1, 2, 1),
+FCE("Ḇ", 0, 2, 1),FCE("ḇ", 1, 2, 1),FCE("𐐎", 0, 2, 1),FCE("𐐶", 1, 2, 1),
+FCE("Ḉ", 0, 2, 1),FCE("ḉ", 1, 2, 1),FCE("Ḋ", 0, 2, 1),FCE("ḋ", 1, 2, 1),
+FCE("Ⲱ", 0, 2, 1),FCE("ⲱ", 1, 2, 1),FCE("Ḍ", 0, 2, 1),FCE("ḍ", 1, 2, 1),
+FCE("Ḏ", 0, 2, 1),FCE("ḏ", 1, 2, 1),FCE("Ḑ", 0, 2, 1),FCE("ḑ", 1, 2, 1),
+FCE("Ꙙ", 0, 2, 1),FCE("ꙙ", 1, 2, 1),FCE("Ḓ", 0, 2, 1),FCE("ḓ", 1, 2, 1),
+FCE("Ḕ", 0, 2, 1),FCE("ḕ", 1, 2, 1),FCE("Ⓧ", 0, 2, 1),FCE("ⓧ", 1, 2, 1),
+FCE("Ḗ", 0, 2, 1),FCE("ḗ", 1, 2, 1),FCE("Ḙ", 0, 2, 1),FCE("ḙ", 1, 2, 1),
+FCE("Ḛ", 0, 2, 1),FCE("ḛ", 1, 2, 1),FCE("Ḝ", 0, 2, 1),FCE("ḝ", 1, 2, 1),
+FCE("Ꙛ", 0, 2, 1),FCE("ꙛ", 1, 2, 1),FCE("Ḟ", 0, 2, 1),FCE("ḟ", 1, 2, 1),
+FCE("Ḡ", 0, 2, 1),FCE("ḡ", 1, 2, 1),FCE("Ḣ", 0, 2, 1),FCE("ḣ", 1, 2, 1),
+FCE("Ḥ", 0, 2, 1),FCE("ḥ", 1, 2, 1),FCE("Ḧ", 0, 2, 1),FCE("ḧ", 1, 2, 1),
+FCE("Ḩ", 0, 2, 1),FCE("ḩ", 1, 2, 1),FCE("Ꙝ", 0, 2, 1),FCE("ꙝ", 1, 2, 1),
+FCE("Ḫ", 0, 2, 1),FCE("ḫ", 1, 2, 1),FCE("Ḭ", 0, 2, 1),FCE("ḭ", 1, 2, 1),
+FCE("Ḯ", 0, 2, 1),FCE("ḯ", 1, 2, 1),FCE("Ḱ", 0, 2, 1),FCE("ḱ", 1, 2, 1),
+FCE("Ḳ", 0, 2, 1),FCE("ḳ", 1, 2, 1),FCE("Ḵ", 0, 2, 1),FCE("ḵ", 1, 2, 1),
+FCE("Ꙟ", 0, 2, 1),FCE("ꙟ", 1, 2, 1),FCE("Ḷ", 0, 2, 1),FCE("ḷ", 1, 2, 1),
+FCE("Ḹ", 0, 2, 1),FCE("ḹ", 1, 2, 1),FCE("Ḻ", 0, 2, 1),FCE("ḻ", 1, 2, 1),
+FCE("Ḽ", 0, 2, 1),FCE("ḽ", 1, 2, 1),FCE("Ḿ", 0, 2, 1),FCE("ḿ", 1, 2, 1),
+FCE("Ṁ", 0, 2, 1),FCE("ṁ", 1, 2, 1),FCE("Ꙡ", 0, 2, 1),FCE("ꙡ", 1, 2, 1),
+FCE("Ṃ", 0, 2, 1),FCE("ṃ", 1, 2, 1),FCE("Ṅ", 0, 2, 1),FCE("ṅ", 1, 2, 1),
+FCE("Ṇ", 0, 2, 1),FCE("ṇ", 1, 2, 1),FCE("Ⲳ", 0, 2, 1),FCE("ⲳ", 1, 2, 1),
+FCE("Ṉ", 0, 2, 1),FCE("ṉ", 1, 2, 1),FCE("Ⳁ", 0, 2, 1),FCE("ⳁ", 1, 2, 1),
+FCE("Ṋ", 0, 2, 1),FCE("ṋ", 1, 2, 1),FCE("Ṍ", 0, 2, 1),FCE("ṍ", 1, 2, 1),
+FCE("Ꙣ", 0, 2, 1),FCE("ꙣ", 1, 2, 1),FCE("Ṏ", 0, 2, 1),FCE("ṏ", 1, 2, 1),
+FCE("Ṑ", 0, 2, 1),FCE("ṑ", 1, 2, 1),FCE("Ṓ", 0, 2, 1),FCE("ṓ", 1, 2, 1),
+FCE("Ṕ", 0, 2, 1),FCE("ṕ", 1, 2, 1),FCE("Ṗ", 0, 2, 1),FCE("ṗ", 1, 2, 1),
+FCE("Ṙ", 0, 2, 1),FCE("ṙ", 1, 2, 1),FCE("Ꙥ", 0, 2, 1),FCE("ꙥ", 1, 2, 1),
+FCE("Ṛ", 0, 2, 1),FCE("ṛ", 1, 2, 1),FCE("Ṝ", 0, 2, 1),FCE("ṝ", 1, 2, 1),
+FCE("Ṟ", 0, 2, 1),FCE("ṟ", 1, 2, 1),FCE("Ṡ", 0, 3, 1),FCE("ṡ", 1, 3, 1),
+FCE("ẛ", 2, 3, 1),FCE("Ṣ", 0, 2, 1),FCE("ṣ", 1, 2, 1),FCE("𐐤", 0, 2, 1),
+FCE("𐑌", 1, 2, 1),FCE("Ṥ", 0, 2, 1),FCE("ṥ", 1, 2, 1),FCE("Ꙧ", 0, 2, 1),
+FCE("ꙧ", 1, 2, 1),FCE("Ṧ", 0, 2, 1),FCE("ṧ", 1, 2, 1),FCE("Ṩ", 0, 2, 1),
+FCE("ṩ", 1, 2, 1),FCE("Ṫ", 0, 2, 1),FCE("ṫ", 1, 2, 1),FCE("Ṭ", 0, 2, 1),
+FCE("ṭ", 1, 2, 1),FCE("Ṯ", 0, 2, 1),FCE("ṯ", 1, 2, 1),FCE("Ṱ", 0, 2, 1),
+FCE("ṱ", 1, 2, 1),FCE("Ꙩ", 0, 2, 1),FCE("ꙩ", 1, 2, 1),FCE("Ṳ", 0, 2, 1),
+FCE("ṳ", 1, 2, 1),FCE("Ṵ", 0, 2, 1),FCE("ṵ", 1, 2, 1),FCE("Ṷ", 0, 2, 1),
+FCE("ṷ", 1, 2, 1),FCE("Ⲿ", 0, 2, 1),FCE("ⲿ", 1, 2, 1),FCE("Ṹ", 0, 2, 1),
+FCE("ṹ", 1, 2, 1),FCE("Ṻ", 0, 2, 1),FCE("ṻ", 1, 2, 1),FCE("Ṽ", 0, 2, 1),
+FCE("ṽ", 1, 2, 1),FCE("Ꙫ", 0, 2, 1),FCE("ꙫ", 1, 2, 1),FCE("Ṿ", 0, 2, 1),
+FCE("ṿ", 1, 2, 1),FCE("Ẁ", 0, 2, 1),FCE("ẁ", 1, 2, 1),FCE("Ẃ", 0, 2, 1),
+FCE("ẃ", 1, 2, 1),FCE("Ẅ", 0, 2, 1),FCE("ẅ", 1, 2, 1),FCE("Ẇ", 0, 2, 1),
+FCE("ẇ", 1, 2, 1),FCE("Ẉ", 0, 2, 1),FCE("ẉ", 1, 2, 1),FCE("Ꙭ", 0, 2, 1),
+FCE("ꙭ", 1, 2, 1),FCE("Ẋ", 0, 2, 1),FCE("ẋ", 1, 2, 1),FCE("Ẍ", 0, 2, 1),
+FCE("ẍ", 1, 2, 1),FCE("Ẏ", 0, 2, 1),FCE("ẏ", 1, 2, 1),FCE("Ẑ", 0, 2, 1),
+FCE("ẑ", 1, 2, 1),FCE("Ẓ", 0, 2, 1),FCE("ẓ", 1, 2, 1),FCE("Ẕ", 0, 2, 1),
+FCE("ẕ", 1, 2, 1),FCE("ẖ", 0, 2, 1),FCE("ẖ", 1, 2, 2),FCE("ẗ", 0, 2, 1),
+FCE("ẗ", 1, 2, 2),FCE("ẘ", 0, 2, 1),FCE("ẘ", 1, 2, 2),FCE("ẙ", 0, 2, 1),
+FCE("ẙ", 1, 2, 2),FCE("ẚ", 0, 2, 1),FCE("aʾ", 1, 2, 2),FCE("Ṡ", 0, 3, 1),
+FCE("ṡ", 1, 3, 1),FCE("ẛ", 2, 3, 1),FCE("ß", 0, 3, 1),FCE("ẞ", 1, 3, 1),
+FCE("ss", 2, 3, 2),FCE("Ạ", 0, 2, 1),FCE("ạ", 1, 2, 1),FCE("Ả", 0, 2, 1),
+FCE("ả", 1, 2, 1),FCE("Ấ", 0, 2, 1),FCE("ấ", 1, 2, 1),FCE("Ⓟ", 0, 2, 1),
+FCE("ⓟ", 1, 2, 1),FCE("Ầ", 0, 2, 1),FCE("ầ", 1, 2, 1),FCE("Ẩ", 0, 2, 1),
+FCE("ẩ", 1, 2, 1),FCE("Ẫ", 0, 2, 1),FCE("ẫ", 1, 2, 1),FCE("Ậ", 0, 2, 1),
+FCE("ậ", 1, 2, 1),FCE("Ắ", 0, 2, 1),FCE("ắ", 1, 2, 1),FCE("H", 0, 2, 1),
+FCE("h", 1, 2, 1),FCE("Ằ", 0, 2, 1),FCE("ằ", 1, 2, 1),FCE("Ẳ", 0, 2, 1),
+FCE("ẳ", 1, 2, 1),FCE("𐐥", 0, 2, 1),FCE("𐑍", 1, 2, 1),FCE("Ẵ", 0, 2, 1),
+FCE("ẵ", 1, 2, 1),FCE("Ặ", 0, 2, 1),FCE("ặ", 1, 2, 1),FCE("Ẹ", 0, 2, 1),
+FCE("ẹ", 1, 2, 1),FCE("Ẻ", 0, 2, 1),FCE("ẻ", 1, 2, 1),FCE("Ẽ", 0, 2, 1),
+FCE("ẽ", 1, 2, 1),FCE("Ế", 0, 2, 1),FCE("ế", 1, 2, 1),FCE("Ⲷ", 0, 2, 1),
+FCE("ⲷ", 1, 2, 1),FCE("Ề", 0, 2, 1),FCE("ề", 1, 2, 1),FCE("Ể", 0, 2, 1),
+FCE("ể", 1, 2, 1),FCE("Ⲛ", 0, 2, 1),FCE("ⲛ", 1, 2, 1),FCE("Ễ", 0, 2, 1),
+FCE("ễ", 1, 2, 1),FCE("Ệ", 0, 2, 1),FCE("ệ", 1, 2, 1),FCE("Ỉ", 0, 2, 1),
+FCE("ỉ", 1, 2, 1),FCE("Ị", 0, 2, 1),FCE("ị", 1, 2, 1),FCE("Ọ", 0, 2, 1),
+FCE("ọ", 1, 2, 1),FCE("Ỏ", 0, 2, 1),FCE("ỏ", 1, 2, 1),FCE("Ố", 0, 2, 1),
+FCE("ố", 1, 2, 1),FCE("Ồ", 0, 2, 1),FCE("ồ", 1, 2, 1),FCE("Ổ", 0, 2, 1),
+FCE("ổ", 1, 2, 1),FCE("Ỗ", 0, 2, 1),FCE("ỗ", 1, 2, 1),FCE("Ộ", 0, 2, 1),
+FCE("ộ", 1, 2, 1),FCE("Ớ", 0, 2, 1),FCE("ớ", 1, 2, 1),FCE("Ờ", 0, 2, 1),
+FCE("ờ", 1, 2, 1),FCE("Ở", 0, 2, 1),FCE("ở", 1, 2, 1),FCE("Ỡ", 0, 2, 1),
+FCE("ỡ", 1, 2, 1),FCE("Ợ", 0, 2, 1),FCE("ợ", 1, 2, 1),FCE("Ụ", 0, 2, 1),
+FCE("ụ", 1, 2, 1),FCE("Ω", 0, 3, 1),FCE("ω", 1, 3, 1),FCE("Ω", 2, 3, 1),
+FCE("Ủ", 0, 2, 1),FCE("ủ", 1, 2, 1),FCE("Ứ", 0, 2, 1),FCE("ứ", 1, 2, 1),
+FCE("Ừ", 0, 2, 1),FCE("ừ", 1, 2, 1),FCE("J", 0, 2, 1),FCE("j", 1, 2, 1),
+FCE("Ử", 0, 2, 1),FCE("ử", 1, 2, 1),FCE("Ữ", 0, 2, 1),FCE("ữ", 1, 2, 1),
+FCE("Ự", 0, 2, 1),FCE("ự", 1, 2, 1),FCE("Ỳ", 0, 2, 1),FCE("ỳ", 1, 2, 1),
+FCE("Ỵ", 0, 2, 1),FCE("ỵ", 1, 2, 1),FCE("Ỷ", 0, 2, 1),FCE("ỷ", 1, 2, 1),
+FCE("Ỹ", 0, 2, 1),FCE("ỹ", 1, 2, 1),FCE("Ỻ", 0, 2, 1),FCE("ỻ", 1, 2, 1),
+FCE("Ⲹ", 0, 2, 1),FCE("ⲹ", 1, 2, 1),FCE("Ỽ", 0, 2, 1),FCE("ỽ", 1, 2, 1),
+FCE("K", 0, 3, 1),FCE("k", 1, 3, 1),FCE("K", 2, 3, 1),FCE("Ỿ", 0, 2, 1),
+FCE("ỿ", 1, 2, 1),FCE("Ꚁ", 0, 2, 1),FCE("ꚁ", 1, 2, 1),FCE("ἀ", 0, 2, 1),
+FCE("Ἀ", 1, 2, 1),FCE("ἁ", 0, 2, 1),FCE("Ἁ", 1, 2, 1),FCE("ἂ", 0, 2, 1),
+FCE("Ἂ", 1, 2, 1),FCE("ἃ", 0, 2, 1),FCE("Ἃ", 1, 2, 1),FCE("ἄ", 0, 2, 1),
+FCE("Ἄ", 1, 2, 1),FCE("ἅ", 0, 2, 1),FCE("Ἅ", 1, 2, 1),FCE("ἆ", 0, 2, 1),
+FCE("Ἆ", 1, 2, 1),FCE("ἇ", 0, 2, 1),FCE("Ἇ", 1, 2, 1),FCE("𐐘", 0, 2, 1),
+FCE("𐑀", 1, 2, 1),FCE("ɥ", 0, 2, 1),FCE("Ɥ", 1, 2, 1),FCE("ἐ", 0, 2, 1),
+FCE("Ἐ", 1, 2, 1),FCE("ἑ", 0, 2, 1),FCE("Ἑ", 1, 2, 1),FCE("ἒ", 0, 2, 1),
+FCE("Ἒ", 1, 2, 1),FCE("ἓ", 0, 2, 1),FCE("Ἓ", 1, 2, 1),FCE("ἔ", 0, 2, 1),
+FCE("Ἔ", 1, 2, 1),FCE("ἕ", 0, 2, 1),FCE("Ἕ", 1, 2, 1),FCE("A", 0, 2, 1),
+FCE("a", 1, 2, 1),FCE("Ꜣ", 0, 2, 1),FCE("ꜣ", 1, 2, 1),FCE("C", 0, 2, 1),
+FCE("c", 1, 2, 1),FCE("Ꜥ", 0, 2, 1),FCE("ꜥ", 1, 2, 1),FCE("Ꚇ", 0, 2, 1),
+FCE("ꚇ", 1, 2, 1),FCE("Ꜧ", 0, 2, 1),FCE("ꜧ", 1, 2, 1),FCE("G", 0, 2, 1),
+FCE("g", 1, 2, 1),FCE("ἠ", 0, 2, 1),FCE("Ἠ", 1, 2, 1),FCE("ἡ", 0, 2, 1),
+FCE("Ἡ", 1, 2, 1),FCE("ἢ", 0, 2, 1),FCE("Ἢ", 1, 2, 1),FCE("ἣ", 0, 2, 1),
+FCE("Ἣ", 1, 2, 1),FCE("ἤ", 0, 2, 1),FCE("Ἤ", 1, 2, 1),FCE("ἥ", 0, 2, 1),
+FCE("Ἥ", 1, 2, 1),FCE("ἦ", 0, 2, 1),FCE("Ἦ", 1, 2, 1),FCE("ἧ", 0, 2, 1),
+FCE("Ἧ", 1, 2, 1),FCE("P", 0, 2, 1),FCE("p", 1, 2, 1),FCE("Ꚉ", 0, 2, 1),
+FCE("ꚉ", 1, 2, 1),FCE("Ꜳ", 0, 2, 1),FCE("ꜳ", 1, 2, 1),FCE("S", 0, 2, 1),
+FCE("s", 1, 2, 1),FCE("Ꜵ", 0, 2, 1),FCE("ꜵ", 1, 2, 1),FCE("U", 0, 2, 1),
+FCE("u", 1, 2, 1),FCE("Ꜷ", 0, 2, 1),FCE("ꜷ", 1, 2, 1),FCE("Ⲻ", 0, 2, 1),
+FCE("ⲻ", 1, 2, 1),FCE("ἰ", 0, 2, 1),FCE("Ἰ", 1, 2, 1),FCE("ἱ", 0, 2, 1),
+FCE("Ἱ", 1, 2, 1),FCE("ἲ", 0, 2, 1),FCE("Ἲ", 1, 2, 1),FCE("ἳ", 0, 2, 1),
+FCE("Ἳ", 1, 2, 1),FCE("ἴ", 0, 2, 1),FCE("Ἴ", 1, 2, 1),FCE("ἵ", 0, 2, 1),
+FCE("Ἵ", 1, 2, 1),FCE("ἶ", 0, 2, 1),FCE("Ἶ", 1, 2, 1),FCE("ἷ", 0, 2, 1),
+FCE("Ἷ", 1, 2, 1),FCE("Ꝁ", 0, 2, 1),FCE("ꝁ", 1, 2, 1),FCE("Ꝃ", 0, 2, 1),
+FCE("ꝃ", 1, 2, 1),FCE("Ꝅ", 0, 2, 1),FCE("ꝅ", 1, 2, 1),FCE("Ꝇ", 0, 2, 1),
+FCE("ꝇ", 1, 2, 1),FCE("ὀ", 0, 2, 1),FCE("Ὀ", 1, 2, 1),FCE("ὁ", 0, 2, 1),
+FCE("Ὁ", 1, 2, 1),FCE("ὂ", 0, 2, 1),FCE("Ὂ", 1, 2, 1),FCE("ὃ", 0, 2, 1),
+FCE("Ὃ", 1, 2, 1),FCE("ὄ", 0, 2, 1),FCE("Ὄ", 1, 2, 1),FCE("ὅ", 0, 2, 1),
+FCE("Ὅ", 1, 2, 1),FCE("Ꝏ", 0, 2, 1),FCE("ꝏ", 1, 2, 1),FCE("ὐ", 0, 2, 1),
+FCE("ὐ", 1, 2, 2),FCE("ὒ", 0, 2, 1),FCE("ὒ", 1, 2, 3),FCE("ὔ", 0, 2, 1),
+FCE("ὔ", 1, 2, 3),FCE("Ꚏ", 0, 2, 1),FCE("ꚏ", 1, 2, 1),FCE("ὖ", 0, 2, 1),
+FCE("ὖ", 1, 2, 3),FCE("Ꝙ", 0, 2, 1),FCE("ꝙ", 1, 2, 1),FCE("ὑ", 0, 2, 1),
+FCE("Ὑ", 1, 2, 1),FCE("Ꝛ", 0, 2, 1),FCE("ꝛ", 1, 2, 1),FCE("ὓ", 0, 2, 1),
+FCE("Ὓ", 1, 2, 1),FCE("Ꝝ", 0, 2, 1),FCE("ꝝ", 1, 2, 1),FCE("ὕ", 0, 2, 1),
+FCE("Ὕ", 1, 2, 1),FCE("Ꝟ", 0, 2, 1),FCE("ꝟ", 1, 2, 1),FCE("ὗ", 0, 2, 1),
+FCE("Ὗ", 1, 2, 1),FCE("Ꝡ", 0, 2, 1),FCE("ꝡ", 1, 2, 1),FCE("Ꚑ", 0, 2, 1),
+FCE("ꚑ", 1, 2, 1),FCE("Ꝣ", 0, 2, 1),FCE("ꝣ", 1, 2, 1),FCE("N", 0, 2, 1),
+FCE("n", 1, 2, 1),FCE("Ꝥ", 0, 2, 1),FCE("ꝥ", 1, 2, 1),FCE("Ꝧ", 0, 2, 1),
+FCE("ꝧ", 1, 2, 1),FCE("ὠ", 0, 2, 1),FCE("Ὠ", 1, 2, 1),FCE("ὡ", 0, 2, 1),
+FCE("Ὡ", 1, 2, 1),FCE("ὢ", 0, 2, 1),FCE("Ὢ", 1, 2, 1),FCE("ὣ", 0, 2, 1),
+FCE("Ὣ", 1, 2, 1),FCE("ὤ", 0, 2, 1),FCE("Ὤ", 1, 2, 1),FCE("ὥ", 0, 2, 1),
+FCE("Ὥ", 1, 2, 1),FCE("ὦ", 0, 2, 1),FCE("Ὦ", 1, 2, 1),FCE("ὧ", 0, 2, 1),
+FCE("Ὧ", 1, 2, 1),FCE("Ⱌ", 0, 2, 1),FCE("ⱌ", 1, 2, 1),FCE("Ⲽ", 0, 2, 1),
+FCE("ⲽ", 1, 2, 1),FCE("Ꚕ", 0, 2, 1),FCE("ꚕ", 1, 2, 1),FCE("Ꝼ", 0, 2, 1),
+FCE("ꝼ", 1, 2, 1),FCE("ᵹ", 0, 2, 1),FCE("Ᵹ", 1, 2, 1),FCE("Ꝿ", 0, 2, 1),
+FCE("ꝿ", 1, 2, 1),FCE("ᾀ", 0, 3, 1),FCE("ᾈ", 1, 3, 1),FCE("ἀι", 2, 3, 2),
+FCE("ᾁ", 0, 3, 1),FCE("ᾉ", 1, 3, 1),FCE("ἁι", 2, 3, 2),FCE("ᾂ", 0, 3, 1),
+FCE("ᾊ", 1, 3, 1),FCE("ἂι", 2, 3, 2),FCE("ᾃ", 0, 3, 1),FCE("ᾋ", 1, 3, 1),
+FCE("ἃι", 2, 3, 2),FCE("ᾄ", 0, 3, 1),FCE("ᾌ", 1, 3, 1),FCE("ἄι", 2, 3, 2),
+FCE("ᾅ", 0, 3, 1),FCE("ᾍ", 1, 3, 1),FCE("ἅι", 2, 3, 2),FCE("ᾆ", 0, 3, 1),
+FCE("ᾎ", 1, 3, 1),FCE("ἆι", 2, 3, 2),FCE("ᾇ", 0, 3, 1),FCE("ᾏ", 1, 3, 1),
+FCE("ἇι", 2, 3, 2),FCE("ᾀ", 0, 3, 1),FCE("ᾈ", 1, 3, 1),FCE("ἀι", 2, 3, 2),
+FCE("ᾁ", 0, 3, 1),FCE("ᾉ", 1, 3, 1),FCE("ἁι", 2, 3, 2),FCE("ᾂ", 0, 3, 1),
+FCE("ᾊ", 1, 3, 1),FCE("ἂι", 2, 3, 2),FCE("ᾃ", 0, 3, 1),FCE("ᾋ", 1, 3, 1),
+FCE("ἃι", 2, 3, 2),FCE("ᾄ", 0, 3, 1),FCE("ᾌ", 1, 3, 1),FCE("ἄι", 2, 3, 2),
+FCE("ᾅ", 0, 3, 1),FCE("ᾍ", 1, 3, 1),FCE("ἅι", 2, 3, 2),FCE("ᾆ", 0, 3, 1),
+FCE("ᾎ", 1, 3, 1),FCE("ἆι", 2, 3, 2),FCE("ᾇ", 0, 3, 1),FCE("ᾏ", 1, 3, 1),
+FCE("ἇι", 2, 3, 2),FCE("ᾐ", 0, 3, 1),FCE("ᾘ", 1, 3, 1),FCE("ἠι", 2, 3, 2),
+FCE("ᾑ", 0, 3, 1),FCE("ᾙ", 1, 3, 1),FCE("ἡι", 2, 3, 2),FCE("ᾒ", 0, 3, 1),
+FCE("ᾚ", 1, 3, 1),FCE("ἢι", 2, 3, 2),FCE("ᾓ", 0, 3, 1),FCE("ᾛ", 1, 3, 1),
+FCE("ἣι", 2, 3, 2),FCE("ᾔ", 0, 3, 1),FCE("ᾜ", 1, 3, 1),FCE("ἤι", 2, 3, 2),
+FCE("ᾕ", 0, 3, 1),FCE("ᾝ", 1, 3, 1),FCE("ἥι", 2, 3, 2),FCE("ᾖ", 0, 3, 1),
+FCE("ᾞ", 1, 3, 1),FCE("ἦι", 2, 3, 2),FCE("ᾗ", 0, 3, 1),FCE("ᾟ", 1, 3, 1),
+FCE("ἧι", 2, 3, 2),FCE("ᾐ", 0, 3, 1),FCE("ᾘ", 1, 3, 1),FCE("ἠι", 2, 3, 2),
+FCE("ᾑ", 0, 3, 1),FCE("ᾙ", 1, 3, 1),FCE("ἡι", 2, 3, 2),FCE("ᾒ", 0, 3, 1),
+FCE("ᾚ", 1, 3, 1),FCE("ἢι", 2, 3, 2),FCE("ᾓ", 0, 3, 1),FCE("ᾛ", 1, 3, 1),
+FCE("ἣι", 2, 3, 2),FCE("ᾔ", 0, 3, 1),FCE("ᾜ", 1, 3, 1),FCE("ἤι", 2, 3, 2),
+FCE("ᾕ", 0, 3, 1),FCE("ᾝ", 1, 3, 1),FCE("ἥι", 2, 3, 2),FCE("ᾖ", 0, 3, 1),
+FCE("ᾞ", 1, 3, 1),FCE("ἦι", 2, 3, 2),FCE("ᾗ", 0, 3, 1),FCE("ᾟ", 1, 3, 1),
+FCE("ἧι", 2, 3, 2),FCE("ᾠ", 0, 3, 1),FCE("ᾨ", 1, 3, 1),FCE("ὠι", 2, 3, 2),
+FCE("ᾡ", 0, 3, 1),FCE("ᾩ", 1, 3, 1),FCE("ὡι", 2, 3, 2),FCE("ᾢ", 0, 3, 1),
+FCE("ᾪ", 1, 3, 1),FCE("ὢι", 2, 3, 2),FCE("ᾣ", 0, 3, 1),FCE("ᾫ", 1, 3, 1),
+FCE("ὣι", 2, 3, 2),FCE("ᾤ", 0, 3, 1),FCE("ᾬ", 1, 3, 1),FCE("ὤι", 2, 3, 2),
+FCE("ᾥ", 0, 3, 1),FCE("ᾭ", 1, 3, 1),FCE("ὥι", 2, 3, 2),FCE("ᾦ", 0, 3, 1),
+FCE("ᾮ", 1, 3, 1),FCE("ὦι", 2, 3, 2),FCE("ᾧ", 0, 3, 1),FCE("ᾯ", 1, 3, 1),
+FCE("ὧι", 2, 3, 2),FCE("ᾠ", 0, 3, 1),FCE("ᾨ", 1, 3, 1),FCE("ὠι", 2, 3, 2),
+FCE("ᾡ", 0, 3, 1),FCE("ᾩ", 1, 3, 1),FCE("ὡι", 2, 3, 2),FCE("ᾢ", 0, 3, 1),
+FCE("ᾪ", 1, 3, 1),FCE("ὢι", 2, 3, 2),FCE("ᾣ", 0, 3, 1),FCE("ᾫ", 1, 3, 1),
+FCE("ὣι", 2, 3, 2),FCE("ᾤ", 0, 3, 1),FCE("ᾬ", 1, 3, 1),FCE("ὤι", 2, 3, 2),
+FCE("ᾥ", 0, 3, 1),FCE("ᾭ", 1, 3, 1),FCE("ὥι", 2, 3, 2),FCE("ᾦ", 0, 3, 1),
+FCE("ᾮ", 1, 3, 1),FCE("ὦι", 2, 3, 2),FCE("ᾧ", 0, 3, 1),FCE("ᾯ", 1, 3, 1),
+FCE("ὧι", 2, 3, 2),FCE("ᾲ", 0, 2, 1),FCE("ὰι", 1, 2, 2),FCE("ᾳ", 0, 3, 1),
+FCE("ᾼ", 1, 3, 1),FCE("αι", 2, 3, 2),FCE("ᾴ", 0, 2, 1),FCE("άι", 1, 2, 2),
+FCE("ᾶ", 0, 2, 1),FCE("ᾶ", 1, 2, 2),FCE("ᾷ", 0, 2, 1),FCE("ᾶι", 1, 2, 3),
+FCE("ᾰ", 0, 2, 1),FCE("Ᾰ", 1, 2, 1),FCE("ᾱ", 0, 2, 1),FCE("Ᾱ", 1, 2, 1),
+FCE("ὰ", 0, 2, 1),FCE("Ὰ", 1, 2, 1),FCE("ά", 0, 2, 1),FCE("Ά", 1, 2, 1),
+FCE("ᾳ", 0, 3, 1),FCE("ᾼ", 1, 3, 1),FCE("αι", 2, 3, 2),FCE("ͅ", 0, 4, 1),
+FCE("Ι", 1, 4, 1),FCE("ι", 2, 4, 1),FCE("ι", 3, 4, 1),FCE("ῂ", 0, 2, 1),
+FCE("ὴι", 1, 2, 2),FCE("ῃ", 0, 3, 1),FCE("ῌ", 1, 3, 1),FCE("ηι", 2, 3, 2),
+FCE("ῄ", 0, 2, 1),FCE("ήι", 1, 2, 2),FCE("𐐌", 0, 2, 1),FCE("𐐴", 1, 2, 1),
+FCE("ῆ", 0, 2, 1),FCE("ῆ", 1, 2, 2),FCE("ῇ", 0, 2, 1),FCE("ῆι", 1, 2, 3),
+FCE("ὲ", 0, 2, 1),FCE("Ὲ", 1, 2, 1),FCE("έ", 0, 2, 1),FCE("Έ", 1, 2, 1),
+FCE("ὴ", 0, 2, 1),FCE("Ὴ", 1, 2, 1),FCE("ή", 0, 2, 1),FCE("Ή", 1, 2, 1),
+FCE("ῃ", 0, 3, 1),FCE("ῌ", 1, 3, 1),FCE("ηι", 2, 3, 2),FCE("ῒ", 0, 2, 1),
+FCE("ῒ", 1, 2, 3),FCE("ΐ", 0, 3, 1),FCE("ΐ", 1, 3, 1),FCE("ΐ", 2, 3, 3),
+FCE("ῖ", 0, 2, 1),FCE("ῖ", 1, 2, 2),FCE("ῗ", 0, 2, 1),FCE("ῗ", 1, 2, 3),
+FCE("ῐ", 0, 2, 1),FCE("Ῐ", 1, 2, 1),FCE("ῑ", 0, 2, 1),FCE("Ῑ", 1, 2, 1),
+FCE("ὶ", 0, 2, 1),FCE("Ὶ", 1, 2, 1),FCE("ί", 0, 2, 1),FCE("Ί", 1, 2, 1),
+FCE("𐐧", 0, 2, 1),FCE("𐑏", 1, 2, 1),FCE("ῢ", 0, 2, 1),FCE("ῢ", 1, 2, 3),
+FCE("ΰ", 0, 3, 1),FCE("ΰ", 1, 3, 1),FCE("ΰ", 2, 3, 3),FCE("ῤ", 0, 2, 1),
+FCE("ῤ", 1, 2, 2),FCE("ῦ", 0, 2, 1),FCE("ῦ", 1, 2, 2),FCE("ῧ", 0, 2, 1),
+FCE("ῧ", 1, 2, 3),FCE("ῠ", 0, 2, 1),FCE("Ῠ", 1, 2, 1),FCE("ῡ", 0, 2, 1),
+FCE("Ῡ", 1, 2, 1),FCE("ὺ", 0, 2, 1),FCE("Ὺ", 1, 2, 1),FCE("ύ", 0, 2, 1),
+FCE("Ύ", 1, 2, 1),FCE("ῥ", 0, 2, 1),FCE("Ῥ", 1, 2, 1),FCE("𐐁", 0, 2, 1),
+FCE("𐐩", 1, 2, 1),FCE("ῲ", 0, 2, 1),FCE("ὼι", 1, 2, 2),FCE("ῳ", 0, 3, 1),
+FCE("ῼ", 1, 3, 1),FCE("ωι", 2, 3, 2),FCE("ῴ", 0, 2, 1),FCE("ώι", 1, 2, 2),
+FCE("ῶ", 0, 2, 1),FCE("ῶ", 1, 2, 2),FCE("ῷ", 0, 2, 1),FCE("ῶι", 1, 2, 3),
+FCE("ὸ", 0, 2, 1),FCE("Ὸ", 1, 2, 1),FCE("ό", 0, 2, 1),FCE("Ό", 1, 2, 1),
+FCE("ὼ", 0, 2, 1),FCE("Ὼ", 1, 2, 1),FCE("ώ", 0, 2, 1),FCE("Ώ", 1, 2, 1),
+FCE("ῳ", 0, 3, 1),FCE("ῼ", 1, 3, 1),FCE("ωι", 2, 3, 2),FCE("Ⓚ", 0, 2, 1),
+FCE("ⓚ", 1, 2, 1),];
+return t;
+}
+
+struct uniProps
+{
+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 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 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 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 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 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 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 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 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 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 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 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 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];
+_U[] _tab = [
+_U("Alphabetic", Alphabetic),
+_U("ASCII_Hex_Digit", ASCII_Hex_Digit),
+_U("Bidi_Control", Bidi_Control),
+_U("Cased", Cased),
+_U("Case_Ignorable", Case_Ignorable),
+_U("Cc", Cc),
+_U("Cf", Cf),
+_U("Close_Punctuation", Pe),
+_U("Cn", Cn),
+_U("Co", Co),
+_U("Connector_Punctuation", Pc),
+_U("Control", Cc),
+_U("Cs", Cs),
+_U("Currency_Symbol", Sc),
+_U("Dash", Dash),
+_U("Dash_Punctuation", Pd),
+_U("Decimal_Number", Nd),
+_U("Default_Ignorable_Code_Point", Default_Ignorable_Code_Point),
+_U("Deprecated", Deprecated),
+_U("Diacritic", Diacritic),
+_U("Enclosing_Mark", Me),
+_U("Extender", Extender),
+_U("Final_Punctuation", Pf),
+_U("Format", Cf),
+_U("Grapheme_Base", Grapheme_Base),
+_U("Grapheme_Extend", Grapheme_Extend),
+_U("Grapheme_Link", Grapheme_Link),
+_U("Hex_Digit", Hex_Digit),
+_U("Hyphen", Hyphen),
+_U("ID_Continue", ID_Continue),
+_U("Ideographic", Ideographic),
+_U("IDS_Binary_Operator", IDS_Binary_Operator),
+_U("ID_Start", ID_Start),
+_U("IDS_Trinary_Operator", IDS_Trinary_Operator),
+_U("Initial_Punctuation", Pi),
+_U("Join_Control", Join_Control),
+_U("Letter_Number", Nl),
+_U("Line_Separator", Zl),
+_U("Ll", Ll),
+_U("Lm", Lm),
+_U("Lo", Lo),
+_U("Logical_Order_Exception", Logical_Order_Exception),
+_U("Lowercase", Lowercase),
+_U("Lowercase_Letter", Ll),
+_U("Lt", Lt),
+_U("Lu", Lu),
+_U("Math", Math),
+_U("Math_Symbol", Sm),
+_U("Mc", Mc),
+_U("Me", Me),
+_U("Mn", Mn),
+_U("Modifier_Letter", Lm),
+_U("Modifier_Symbol", Sk),
+_U("Nd", Nd),
+_U("Nl", Nl),
+_U("No", No),
+_U("Noncharacter_Code_Point", Noncharacter_Code_Point),
+_U("Nonspacing_Mark", Mn),
+_U("Open_Punctuation", Ps),
+_U("Other_Alphabetic", Other_Alphabetic),
+_U("Other_Default_Ignorable_Code_Point", Other_Default_Ignorable_Code_Point),
+_U("Other_Grapheme_Extend", Other_Grapheme_Extend),
+_U("Other_ID_Continue", Other_ID_Continue),
+_U("Other_ID_Start", Other_ID_Start),
+_U("Other_Letter", Lo),
+_U("Other_Lowercase", Other_Lowercase),
+_U("Other_Math", Other_Math),
+_U("Other_Number", No),
+_U("Other_Punctuation", Po),
+_U("Other_Symbol", So),
+_U("Other_Uppercase", Other_Uppercase),
+_U("Paragraph_Separator", Zp),
+_U("Pattern_Syntax", Pattern_Syntax),
+_U("Pattern_White_Space", Pattern_White_Space),
+_U("Pc", Pc),
+_U("Pd", Pd),
+_U("Pe", Pe),
+_U("Pf", Pf),
+_U("Pi", Pi),
+_U("Po", Po),
+_U("Private_Use", Co),
+_U("Ps", Ps),
+_U("Quotation_Mark", Quotation_Mark),
+_U("Radical", Radical),
+_U("Sc", Sc),
+_U("Sk", Sk),
+_U("Sm", Sm),
+_U("So", So),
+_U("Soft_Dotted", Soft_Dotted),
+_U("Space_Separator", Zs),
+_U("Spacing_Mark", Mc),
+_U("STerm", STerm),
+_U("Surrogate", Cs),
+_U("Terminal_Punctuation", Terminal_Punctuation),
+_U("Titlecase_Letter", Lt),
+_U("Unassigned", Cn),
+_U("Unified_Ideograph", Unified_Ideograph),
+_U("Uppercase", Uppercase),
+_U("Uppercase_Letter", Lu),
+_U("Variation_Selector", Variation_Selector),
+_U("White_Space", White_Space),
+_U("XID_Continue", XID_Continue),
+_U("XID_Start", XID_Start),
+_U("Zl", Zl),
+_U("Zp", Zp),
+_U("Zs", Zs),
+];
+}
+
+struct blocks
+{
+private alias _U = immutable(UnicodeProperty);
+@property static _U[] tab() pure { return _tab; }
+static immutable:
+private alias _T = ubyte[];
+_T Number_Forms = [0xa0, 0x21, 0x50, 0x40];
+_T Sinhala = [0x8d, 0x80, 0x80, 0x80];
+_T Domino_Tiles = [0xa1, 0xf0, 0x30, 0x70];
+_T Oriya = [0x8b, 0x0, 0x80, 0x80];
+_T Thaana = [0x87, 0x80, 0x40];
+_T New_Tai_Lue = [0x99, 0x80, 0x60];
+_T Byzantine_Musical_Symbols = [0xa1, 0xd0, 0x0, 0x81, 0x0];
+_T Cham = [0xa0, 0xaa, 0x0, 0x60];
+_T IPA_Extensions = [0x82, 0x50, 0x60];
+_T Bopomofo = [0xa0, 0x31, 0x0, 0x30];
+_T Katakana_Phonetic_Extensions = [0xa0, 0x31, 0xf0, 0x10];
+_T Khmer_Symbols = [0x99, 0xe0, 0x20];
+_T Hebrew = [0x85, 0x90, 0x70];
+_T Saurashtra = [0xa0, 0xa8, 0x80, 0x60];
+_T Inscriptional_Parthian = [0xa1, 0xb, 0x40, 0x20];
+_T Lisu = [0xa0, 0xa4, 0xd0, 0x30];
+_T Latin_1_Supplement = [0x80, 0x80, 0x80, 0x80];
+_T Arabic_Extended_A = [0x88, 0xa0, 0x60];
+_T Tai_Tham = [0x9a, 0x20, 0x80, 0x90];
+_T Latin_Extended_A = [0x81, 0x0, 0x80, 0x80];
+_T Latin_Extended_B = [0x81, 0x80, 0x80, 0xd0];
+_T Latin_Extended_C = [0xa0, 0x2c, 0x60, 0x20];
+_T Latin_Extended_D = [0xa0, 0xa7, 0x20, 0x80, 0xe0];
+_T CJK_Radicals_Supplement = [0xa0, 0x2e, 0x80, 0x80, 0x80];
+_T Meroitic_Hieroglyphs = [0xa1, 0x9, 0x80, 0x20];
+_T Linear_B_Syllabary = [0xa1, 0x0, 0x0, 0x80, 0x80];
+_T Phonetic_Extensions_Supplement = [0x9d, 0x80, 0x40];
+_T Meroitic_Cursive = [0xa1, 0x9, 0xa0, 0x60];
+_T Enclosed_Ideographic_Supplement = [0xa1, 0xf2, 0x0, 0x81, 0x0];
+_T Halfwidth_and_Fullwidth_Forms = [0xa0, 0xff, 0x0, 0x80, 0xf0];
+_T Takri = [0xa1, 0x16, 0x80, 0x50];
+_T Supplemental_Punctuation = [0xa0, 0x2e, 0x0, 0x80, 0x80];
+_T Malayalam = [0x8d, 0x0, 0x80, 0x80];
+_T Lepcha = [0x9c, 0x0, 0x50];
+_T Miscellaneous_Symbols_And_Pictographs = [0xa1, 0xf3, 0x0, 0x83, 0x0];
+_T Arabic_Presentation_Forms_A = [0xa0, 0xfb, 0x50, 0x82, 0xb0];
+_T Sora_Sompeng = [0xa1, 0x10, 0xd0, 0x30];
+_T Lydian = [0xa1, 0x9, 0x20, 0x20];
+_T Hangul_Jamo_Extended_B = [0xa0, 0xd7, 0xb0, 0x50];
+_T Private_Use_Area = [0xa0, 0xe0, 0x0, 0x99, 0x0];
+_T Coptic = [0xa0, 0x2c, 0x80, 0x80, 0x80];
+_T Phaistos_Disc = [0xa1, 0x1, 0xd0, 0x30];
+_T Batak = [0x9b, 0xc0, 0x40];
+_T Khmer = [0x97, 0x80, 0x80, 0x80];
+_T Counting_Rod_Numerals = [0xa1, 0xd3, 0x60, 0x20];
+_T Old_South_Arabian = [0xa1, 0xa, 0x60, 0x20];
+_T Kannada = [0x8c, 0x80, 0x80, 0x80];
+_T Arrows = [0xa0, 0x21, 0x90, 0x70];
+_T CJK_Compatibility_Ideographs_Supplement = [0xa2, 0xf8, 0x0, 0x82, 0x20];
+_T Combining_Half_Marks = [0xa0, 0xfe, 0x20, 0x10];
+_T Miscellaneous_Technical = [0xa0, 0x23, 0x0, 0x81, 0x0];
+_T Thai = [0x8e, 0x0, 0x80, 0x80];
+_T Alphabetic_Presentation_Forms = [0xa0, 0xfb, 0x0, 0x50];
+_T CJK_Unified_Ideographs = [0xa0, 0x4e, 0x0, 0xa0, 0x52, 0x0];
+_T Phonetic_Extensions = [0x9d, 0x0, 0x80, 0x80];
+_T Kayah_Li = [0xa0, 0xa9, 0x0, 0x30];
+_T Supplementary_Private_Use_Area_B = [0xb0, 0x0, 0x0];
+_T Gujarati = [0x8a, 0x80, 0x80, 0x80];
+_T Unified_Canadian_Aboriginal_Syllabics_Extended = [0x98, 0xb0, 0x50];
+_T Hangul_Syllables = [0xa0, 0xac, 0x0, 0xa0, 0x2b, 0xb0];
+_T Vertical_Forms = [0xa0, 0xfe, 0x10, 0x10];
+_T Inscriptional_Pahlavi = [0xa1, 0xb, 0x60, 0x20];
+_T Control_Pictures = [0xa0, 0x24, 0x0, 0x40];
+_T Carian = [0xa1, 0x2, 0xa0, 0x40];
+_T Mahjong_Tiles = [0xa1, 0xf0, 0x0, 0x30];
+_T Geometric_Shapes = [0xa0, 0x25, 0xa0, 0x60];
+_T Cherokee = [0x93, 0xa0, 0x60];
+_T Imperial_Aramaic = [0xa1, 0x8, 0x40, 0x20];
+_T Rumi_Numeral_Symbols = [0xa1, 0xe, 0x60, 0x20];
+_T Combining_Diacritical_Marks = [0x83, 0x0, 0x70];
+_T Specials = [0xa0, 0xff, 0xf0, 0x10];
+_T Greek_Extended = [0x9f, 0x0, 0x81, 0x0];
+_T Ethiopic_Supplement = [0x93, 0x80, 0x20];
+_T Limbu = [0x99, 0x0, 0x50];
+_T Basic_Latin = [0x0, 0x80, 0x80];
+_T Enclosed_Alphanumeric_Supplement = [0xa1, 0xf1, 0x0, 0x81, 0x0];
+_T Cyrillic_Supplement = [0x85, 0x0, 0x30];
+_T Hangul_Compatibility_Jamo = [0xa0, 0x31, 0x30, 0x60];
+_T Supplemental_Arrows_A = [0xa0, 0x27, 0xf0, 0x10];
+_T Supplemental_Arrows_B = [0xa0, 0x29, 0x0, 0x80, 0x80];
+_T Katakana = [0xa0, 0x30, 0xa0, 0x60];
+_T Ancient_Greek_Musical_Notation = [0xa1, 0xd2, 0x0, 0x50];
+_T CJK_Compatibility = [0xa0, 0x33, 0x0, 0x81, 0x0];
+_T Old_Persian = [0xa1, 0x3, 0xa0, 0x40];
+_T Small_Form_Variants = [0xa0, 0xfe, 0x50, 0x20];
+_T General_Punctuation = [0xa0, 0x20, 0x0, 0x70];
+_T Miscellaneous_Mathematical_Symbols_A = [0xa0, 0x27, 0xc0, 0x30];
+_T Latin_Extended_Additional = [0x9e, 0x0, 0x81, 0x0];
+_T Playing_Cards = [0xa1, 0xf0, 0xa0, 0x60];
+_T Syriac = [0x87, 0x0, 0x50];
+_T Alchemical_Symbols = [0xa1, 0xf7, 0x0, 0x80, 0x80];
+_T Tibetan = [0x8f, 0x0, 0x81, 0x0];
+_T CJK_Strokes = [0xa0, 0x31, 0xc0, 0x30];
+_T Tamil = [0x8b, 0x80, 0x80, 0x80];
+_T Balinese = [0x9b, 0x0, 0x80, 0x80];
+_T Shavian = [0xa1, 0x4, 0x50, 0x30];
+_T Greek_and_Coptic = [0x83, 0x70, 0x80, 0x90];
+_T Telugu = [0x8c, 0x0, 0x80, 0x80];
+_T Runic = [0x96, 0xa0, 0x60];
+_T Javanese = [0xa0, 0xa9, 0x80, 0x60];
+_T Bopomofo_Extended = [0xa0, 0x31, 0xa0, 0x20];
+_T Ideographic_Description_Characters = [0xa0, 0x2f, 0xf0, 0x10];
+_T Old_Turkic = [0xa1, 0xc, 0x0, 0x50];
+_T Unified_Canadian_Aboriginal_Syllabics = [0x94, 0x0, 0x82, 0x80];
+_T Ugaritic = [0xa1, 0x3, 0x80, 0x20];
+_T Egyptian_Hieroglyphs = [0xa1, 0x30, 0x0, 0x84, 0x30];
+_T Buginese = [0x9a, 0x0, 0x20];
+_T Kangxi_Radicals = [0xa0, 0x2f, 0x0, 0x80, 0xe0];
+_T Cuneiform = [0xa1, 0x20, 0x0, 0x84, 0x0];
+_T NKo = [0x87, 0xc0, 0x40];
+_T Sundanese_Supplement = [0x9c, 0xc0, 0x10];
+_T Buhid = [0x97, 0x40, 0x20];
+_T Modifier_Tone_Letters = [0xa0, 0xa7, 0x0, 0x20];
+_T Kanbun = [0xa0, 0x31, 0x90, 0x10];
+_T Superscripts_and_Subscripts = [0xa0, 0x20, 0x70, 0x30];
+_T Lao = [0x8e, 0x80, 0x80, 0x80];
+_T Ol_Chiki = [0x9c, 0x50, 0x30];
+_T Common_Indic_Number_Forms = [0xa0, 0xa8, 0x30, 0x10];
+_T Hangul_Jamo_Extended_A = [0xa0, 0xa9, 0x60, 0x20];
+_T Arabic_Presentation_Forms_B = [0xa0, 0xfe, 0x70, 0x80, 0x90];
+_T Sharada = [0xa1, 0x11, 0x80, 0x60];
+_T Miscellaneous_Symbols = [0xa0, 0x26, 0x0, 0x81, 0x0];
+_T Variation_Selectors_Supplement = [0xae, 0x1, 0x0, 0x80, 0xf0];
+_T Rejang = [0xa0, 0xa9, 0x30, 0x30];
+_T Georgian_Supplement = [0xa0, 0x2d, 0x0, 0x30];
+_T Braille_Patterns = [0xa0, 0x28, 0x0, 0x81, 0x0];
+_T Lycian = [0xa1, 0x2, 0x80, 0x20];
+_T Tai_Le = [0x99, 0x50, 0x30];
+_T Miscellaneous_Mathematical_Symbols_B = [0xa0, 0x29, 0x80, 0x80, 0x80];
+_T Musical_Symbols = [0xa1, 0xd1, 0x0, 0x81, 0x0];
+_T Avestan = [0xa1, 0xb, 0x0, 0x40];
+_T Ethiopic = [0x92, 0x0, 0x81, 0x80];
+_T Arabic_Supplement = [0x87, 0x50, 0x30];
+_T Samaritan = [0x88, 0x0, 0x40];
+_T Cuneiform_Numbers_and_Punctuation = [0xa1, 0x24, 0x0, 0x80, 0x80];
+_T Mongolian = [0x98, 0x0, 0x80, 0xb0];
+_T Arabic = [0x86, 0x0, 0x81, 0x0];
+_T Vai = [0xa0, 0xa5, 0x0, 0x81, 0x40];
+_T Tifinagh = [0xa0, 0x2d, 0x30, 0x50];
+_T Bamum_Supplement = [0xa1, 0x68, 0x0, 0x82, 0x40];
+_T Tai_Viet = [0xa0, 0xaa, 0x80, 0x60];
+_T Mandaic = [0x88, 0x40, 0x20];
+_T Sundanese = [0x9b, 0x80, 0x40];
+_T Block_Elements = [0xa0, 0x25, 0x80, 0x20];
+_T Phoenician = [0xa1, 0x9, 0x0, 0x20];
+_T Hanunoo = [0x97, 0x20, 0x20];
+_T Supplemental_Mathematical_Operators = [0xa0, 0x2a, 0x0, 0x81, 0x0];
+_T Deseret = [0xa1, 0x4, 0x0, 0x50];
+_T Brahmi = [0xa1, 0x10, 0x0, 0x80, 0x80];
+_T Devanagari_Extended = [0xa0, 0xa8, 0xe0, 0x20];
+_T Supplementary_Private_Use_Area_A = [0xaf, 0x0, 0x0, 0xa1, 0x0, 0x0];
+_T Box_Drawing = [0xa0, 0x25, 0x0, 0x80, 0x80];
+_T Mathematical_Operators = [0xa0, 0x22, 0x0, 0x81, 0x0];
+_T Ogham = [0x96, 0x80, 0x20];
+_T Meetei_Mayek_Extensions = [0xa0, 0xaa, 0xe0, 0x20];
+_T Hangul_Jamo = [0x91, 0x0, 0x81, 0x0];
+_T Miao = [0xa1, 0x6f, 0x0, 0x80, 0xa0];
+_T Emoticons = [0xa1, 0xf6, 0x0, 0x50];
+_T Tags = [0xae, 0x0, 0x0, 0x80, 0x80];
+_T Yi_Syllables = [0xa0, 0xa0, 0x0, 0x84, 0x90];
+_T Gurmukhi = [0x8a, 0x0, 0x80, 0x80];
+_T Syloti_Nagri = [0xa0, 0xa8, 0x0, 0x30];
+_T Spacing_Modifier_Letters = [0x82, 0xb0, 0x50];
+_T Yi_Radicals = [0xa0, 0xa4, 0x90, 0x40];
+_T Ancient_Greek_Numbers = [0xa1, 0x1, 0x40, 0x50];
+_T Glagolitic = [0xa0, 0x2c, 0x0, 0x60];
+_T Georgian = [0x90, 0xa0, 0x60];
+_T Osmanya = [0xa1, 0x4, 0x80, 0x30];
+_T Variation_Selectors = [0xa0, 0xfe, 0x0, 0x10];
+_T Mathematical_Alphanumeric_Symbols = [0xa1, 0xd4, 0x0, 0x84, 0x0];
+_T Yijing_Hexagram_Symbols = [0xa0, 0x4d, 0xc0, 0x40];
+_T Ethiopic_Extended = [0xa0, 0x2d, 0x80, 0x60];
+_T Transport_And_Map_Symbols = [0xa1, 0xf6, 0x80, 0x80, 0x80];
+_T High_Private_Use_Surrogates = [0xa0, 0xdb, 0x80, 0x80, 0x80];
+_T Meetei_Mayek = [0xa0, 0xab, 0xc0, 0x40];
+_T CJK_Compatibility_Forms = [0xa0, 0xfe, 0x30, 0x20];
+_T Enclosed_Alphanumerics = [0xa0, 0x24, 0x60, 0x80, 0xa0];
+_T Ancient_Symbols = [0xa1, 0x1, 0x90, 0x40];
+_T Ethiopic_Extended_A = [0xa0, 0xab, 0x0, 0x30];
+_T Bengali = [0x89, 0x80, 0x80, 0x80];
+_T Currency_Symbols = [0xa0, 0x20, 0xa0, 0x30];
+_T Myanmar = [0x90, 0x0, 0x80, 0xa0];
+_T Cyrillic_Extended_A = [0xa0, 0x2d, 0xe0, 0x20];
+_T Cyrillic_Extended_B = [0xa0, 0xa6, 0x40, 0x60];
+_T Myanmar_Extended_A = [0xa0, 0xaa, 0x60, 0x20];
+_T Hiragana = [0xa0, 0x30, 0x40, 0x60];
+_T Dingbats = [0xa0, 0x27, 0x0, 0x80, 0xc0];
+_T Armenian = [0x85, 0x30, 0x60];
+_T Tai_Xuan_Jing_Symbols = [0xa1, 0xd3, 0x0, 0x60];
+_T Linear_B_Ideograms = [0xa1, 0x0, 0x80, 0x80, 0x80];
+_T Kharoshthi = [0xa1, 0xa, 0x0, 0x60];
+_T Optical_Character_Recognition = [0xa0, 0x24, 0x40, 0x20];
+_T Enclosed_CJK_Letters_and_Months = [0xa0, 0x32, 0x0, 0x81, 0x0];
+_T Cypriot_Syllabary = [0xa1, 0x8, 0x0, 0x40];
+_T Vedic_Extensions = [0x9c, 0xd0, 0x30];
+_T Kaithi = [0xa1, 0x10, 0x80, 0x50];
+_T Low_Surrogates = [0xa0, 0xdc, 0x0, 0x84, 0x0];
+_T Letterlike_Symbols = [0xa0, 0x21, 0x0, 0x50];
+_T Combining_Diacritical_Marks_for_Symbols = [0xa0, 0x20, 0xd0, 0x30];
+_T Aegean_Numbers = [0xa1, 0x1, 0x0, 0x40];
+_T High_Surrogates = [0xa0, 0xd8, 0x0, 0x83, 0x80];
+_T CJK_Compatibility_Ideographs = [0xa0, 0xf9, 0x0, 0x82, 0x0];
+_T CJK_Symbols_and_Punctuation = [0xa0, 0x30, 0x0, 0x40];
+_T Gothic = [0xa1, 0x3, 0x30, 0x20];
+_T Combining_Diacritical_Marks_Supplement = [0x9d, 0xc0, 0x40];
+_T Phags_pa = [0xa0, 0xa8, 0x40, 0x40];
+_T Miscellaneous_Symbols_and_Arrows = [0xa0, 0x2b, 0x0, 0x81, 0x0];
+_T Bamum = [0xa0, 0xa6, 0xa0, 0x60];
+_T Chakma = [0xa1, 0x11, 0x0, 0x50];
+_T Kana_Supplement = [0xa1, 0xb0, 0x0, 0x81, 0x0];
+_T Tagalog = [0x97, 0x0, 0x20];
+_T Tagbanwa = [0x97, 0x60, 0x20];
+_T Devanagari = [0x89, 0x0, 0x80, 0x80];
+_T Old_Italic = [0xa1, 0x3, 0x0, 0x30];
+_T Arabic_Mathematical_Alphabetic_Symbols = [0xa1, 0xee, 0x0, 0x81, 0x0];
+_T CJK_Unified_Ideographs_Extension_D = [0xa2, 0xb7, 0x40, 0x80, 0xe0];
+_T CJK_Unified_Ideographs_Extension_A = [0xa0, 0x34, 0x0, 0x99, 0xc0];
+_T CJK_Unified_Ideographs_Extension_B = [0xa2, 0x0, 0x0, 0xa0, 0xa6, 0xe0];
+_T CJK_Unified_Ideographs_Extension_C = [0xa2, 0xa7, 0x0, 0x90, 0x40];
+_T Cyrillic = [0x84, 0x0, 0x81, 0x0];
+_U[] _tab = [
+_U("Aegean Numbers", Aegean_Numbers),
+_U("Alchemical Symbols", Alchemical_Symbols),
+_U("Alphabetic Presentation Forms", Alphabetic_Presentation_Forms),
+_U("Ancient Greek Musical Notation", Ancient_Greek_Musical_Notation),
+_U("Ancient Greek Numbers", Ancient_Greek_Numbers),
+_U("Ancient Symbols", Ancient_Symbols),
+_U("Arabic", Arabic),
+_U("Arabic Extended-A", Arabic_Extended_A),
+_U("Arabic Mathematical Alphabetic Symbols", Arabic_Mathematical_Alphabetic_Symbols),
+_U("Arabic Presentation Forms-A", Arabic_Presentation_Forms_A),
+_U("Arabic Presentation Forms-B", Arabic_Presentation_Forms_B),
+_U("Arabic Supplement", Arabic_Supplement),
+_U("Armenian", Armenian),
+_U("Arrows", Arrows),
+_U("Avestan", Avestan),
+_U("Balinese", Balinese),
+_U("Bamum", Bamum),
+_U("Bamum Supplement", Bamum_Supplement),
+_U("Basic Latin", Basic_Latin),
+_U("Batak", Batak),
+_U("Bengali", Bengali),
+_U("Block Elements", Block_Elements),
+_U("Bopomofo", Bopomofo),
+_U("Bopomofo Extended", Bopomofo_Extended),
+_U("Box Drawing", Box_Drawing),
+_U("Brahmi", Brahmi),
+_U("Braille Patterns", Braille_Patterns),
+_U("Buginese", Buginese),
+_U("Buhid", Buhid),
+_U("Byzantine Musical Symbols", Byzantine_Musical_Symbols),
+_U("Carian", Carian),
+_U("Chakma", Chakma),
+_U("Cham", Cham),
+_U("Cherokee", Cherokee),
+_U("CJK Compatibility", CJK_Compatibility),
+_U("CJK Compatibility Forms", CJK_Compatibility_Forms),
+_U("CJK Compatibility Ideographs", CJK_Compatibility_Ideographs),
+_U("CJK Compatibility Ideographs Supplement", CJK_Compatibility_Ideographs_Supplement),
+_U("CJK Radicals Supplement", CJK_Radicals_Supplement),
+_U("CJK Strokes", CJK_Strokes),
+_U("CJK Symbols and Punctuation", CJK_Symbols_and_Punctuation),
+_U("CJK Unified Ideographs", CJK_Unified_Ideographs),
+_U("CJK Unified Ideographs Extension A", CJK_Unified_Ideographs_Extension_A),
+_U("CJK Unified Ideographs Extension B", CJK_Unified_Ideographs_Extension_B),
+_U("CJK Unified Ideographs Extension C", CJK_Unified_Ideographs_Extension_C),
+_U("CJK Unified Ideographs Extension D", CJK_Unified_Ideographs_Extension_D),
+_U("Combining Diacritical Marks", Combining_Diacritical_Marks),
+_U("Combining Diacritical Marks for Symbols", Combining_Diacritical_Marks_for_Symbols),
+_U("Combining Diacritical Marks Supplement", Combining_Diacritical_Marks_Supplement),
+_U("Combining Half Marks", Combining_Half_Marks),
+_U("Common Indic Number Forms", Common_Indic_Number_Forms),
+_U("Control Pictures", Control_Pictures),
+_U("Coptic", Coptic),
+_U("Counting Rod Numerals", Counting_Rod_Numerals),
+_U("Cuneiform", Cuneiform),
+_U("Cuneiform Numbers and Punctuation", Cuneiform_Numbers_and_Punctuation),
+_U("Currency Symbols", Currency_Symbols),
+_U("Cypriot Syllabary", Cypriot_Syllabary),
+_U("Cyrillic", Cyrillic),
+_U("Cyrillic Extended-A", Cyrillic_Extended_A),
+_U("Cyrillic Extended-B", Cyrillic_Extended_B),
+_U("Cyrillic Supplement", Cyrillic_Supplement),
+_U("Deseret", Deseret),
+_U("Devanagari", Devanagari),
+_U("Devanagari Extended", Devanagari_Extended),
+_U("Dingbats", Dingbats),
+_U("Domino Tiles", Domino_Tiles),
+_U("Egyptian Hieroglyphs", Egyptian_Hieroglyphs),
+_U("Emoticons", Emoticons),
+_U("Enclosed Alphanumerics", Enclosed_Alphanumerics),
+_U("Enclosed Alphanumeric Supplement", Enclosed_Alphanumeric_Supplement),
+_U("Enclosed CJK Letters and Months", Enclosed_CJK_Letters_and_Months),
+_U("Enclosed Ideographic Supplement", Enclosed_Ideographic_Supplement),
+_U("Ethiopic", Ethiopic),
+_U("Ethiopic Extended", Ethiopic_Extended),
+_U("Ethiopic Extended-A", Ethiopic_Extended_A),
+_U("Ethiopic Supplement", Ethiopic_Supplement),
+_U("General Punctuation", General_Punctuation),
+_U("Geometric Shapes", Geometric_Shapes),
+_U("Georgian", Georgian),
+_U("Georgian Supplement", Georgian_Supplement),
+_U("Glagolitic", Glagolitic),
+_U("Gothic", Gothic),
+_U("Greek and Coptic", Greek_and_Coptic),
+_U("Greek Extended", Greek_Extended),
+_U("Gujarati", Gujarati),
+_U("Gurmukhi", Gurmukhi),
+_U("Halfwidth and Fullwidth Forms", Halfwidth_and_Fullwidth_Forms),
+_U("Hangul Compatibility Jamo", Hangul_Compatibility_Jamo),
+_U("Hangul Jamo", Hangul_Jamo),
+_U("Hangul Jamo Extended-A", Hangul_Jamo_Extended_A),
+_U("Hangul Jamo Extended-B", Hangul_Jamo_Extended_B),
+_U("Hangul Syllables", Hangul_Syllables),
+_U("Hanunoo", Hanunoo),
+_U("Hebrew", Hebrew),
+_U("High Private Use Surrogates", High_Private_Use_Surrogates),
+_U("High Surrogates", High_Surrogates),
+_U("Hiragana", Hiragana),
+_U("Ideographic Description Characters", Ideographic_Description_Characters),
+_U("Imperial Aramaic", Imperial_Aramaic),
+_U("Inscriptional Pahlavi", Inscriptional_Pahlavi),
+_U("Inscriptional Parthian", Inscriptional_Parthian),
+_U("IPA Extensions", IPA_Extensions),
+_U("Javanese", Javanese),
+_U("Kaithi", Kaithi),
+_U("Kana Supplement", Kana_Supplement),
+_U("Kanbun", Kanbun),
+_U("Kangxi Radicals", Kangxi_Radicals),
+_U("Kannada", Kannada),
+_U("Katakana", Katakana),
+_U("Katakana Phonetic Extensions", Katakana_Phonetic_Extensions),
+_U("Kayah Li", Kayah_Li),
+_U("Kharoshthi", Kharoshthi),
+_U("Khmer", Khmer),
+_U("Khmer Symbols", Khmer_Symbols),
+_U("Lao", Lao),
+_U("Latin-1 Supplement", Latin_1_Supplement),
+_U("Latin Extended-A", Latin_Extended_A),
+_U("Latin Extended Additional", Latin_Extended_Additional),
+_U("Latin Extended-B", Latin_Extended_B),
+_U("Latin Extended-C", Latin_Extended_C),
+_U("Latin Extended-D", Latin_Extended_D),
+_U("Lepcha", Lepcha),
+_U("Letterlike Symbols", Letterlike_Symbols),
+_U("Limbu", Limbu),
+_U("Linear B Ideograms", Linear_B_Ideograms),
+_U("Linear B Syllabary", Linear_B_Syllabary),
+_U("Lisu", Lisu),
+_U("Low Surrogates", Low_Surrogates),
+_U("Lycian", Lycian),
+_U("Lydian", Lydian),
+_U("Mahjong Tiles", Mahjong_Tiles),
+_U("Malayalam", Malayalam),
+_U("Mandaic", Mandaic),
+_U("Mathematical Alphanumeric Symbols", Mathematical_Alphanumeric_Symbols),
+_U("Mathematical Operators", Mathematical_Operators),
+_U("Meetei Mayek", Meetei_Mayek),
+_U("Meetei Mayek Extensions", Meetei_Mayek_Extensions),
+_U("Meroitic Cursive", Meroitic_Cursive),
+_U("Meroitic Hieroglyphs", Meroitic_Hieroglyphs),
+_U("Miao", Miao),
+_U("Miscellaneous Mathematical Symbols-A", Miscellaneous_Mathematical_Symbols_A),
+_U("Miscellaneous Mathematical Symbols-B", Miscellaneous_Mathematical_Symbols_B),
+_U("Miscellaneous Symbols", Miscellaneous_Symbols),
+_U("Miscellaneous Symbols and Arrows", Miscellaneous_Symbols_and_Arrows),
+_U("Miscellaneous Symbols And Pictographs", Miscellaneous_Symbols_And_Pictographs),
+_U("Miscellaneous Technical", Miscellaneous_Technical),
+_U("Modifier Tone Letters", Modifier_Tone_Letters),
+_U("Mongolian", Mongolian),
+_U("Musical Symbols", Musical_Symbols),
+_U("Myanmar", Myanmar),
+_U("Myanmar Extended-A", Myanmar_Extended_A),
+_U("New Tai Lue", New_Tai_Lue),
+_U("NKo", NKo),
+_U("Number Forms", Number_Forms),
+_U("Ogham", Ogham),
+_U("Ol Chiki", Ol_Chiki),
+_U("Old Italic", Old_Italic),
+_U("Old Persian", Old_Persian),
+_U("Old South Arabian", Old_South_Arabian),
+_U("Old Turkic", Old_Turkic),
+_U("Optical Character Recognition", Optical_Character_Recognition),
+_U("Oriya", Oriya),
+_U("Osmanya", Osmanya),
+_U("Phags-pa", Phags_pa),
+_U("Phaistos Disc", Phaistos_Disc),
+_U("Phoenician", Phoenician),
+_U("Phonetic Extensions", Phonetic_Extensions),
+_U("Phonetic Extensions Supplement", Phonetic_Extensions_Supplement),
+_U("Playing Cards", Playing_Cards),
+_U("Private Use Area", Private_Use_Area),
+_U("Rejang", Rejang),
+_U("Rumi Numeral Symbols", Rumi_Numeral_Symbols),
+_U("Runic", Runic),
+_U("Samaritan", Samaritan),
+_U("Saurashtra", Saurashtra),
+_U("Sharada", Sharada),
+_U("Shavian", Shavian),
+_U("Sinhala", Sinhala),
+_U("Small Form Variants", Small_Form_Variants),
+_U("Sora Sompeng", Sora_Sompeng),
+_U("Spacing Modifier Letters", Spacing_Modifier_Letters),
+_U("Specials", Specials),
+_U("Sundanese", Sundanese),
+_U("Sundanese Supplement", Sundanese_Supplement),
+_U("Superscripts and Subscripts", Superscripts_and_Subscripts),
+_U("Supplemental Arrows-A", Supplemental_Arrows_A),
+_U("Supplemental Arrows-B", Supplemental_Arrows_B),
+_U("Supplemental Mathematical Operators", Supplemental_Mathematical_Operators),
+_U("Supplemental Punctuation", Supplemental_Punctuation),
+_U("Supplementary Private Use Area-A", Supplementary_Private_Use_Area_A),
+_U("Supplementary Private Use Area-B", Supplementary_Private_Use_Area_B),
+_U("Syloti Nagri", Syloti_Nagri),
+_U("Syriac", Syriac),
+_U("Tagalog", Tagalog),
+_U("Tagbanwa", Tagbanwa),
+_U("Tags", Tags),
+_U("Tai Le", Tai_Le),
+_U("Tai Tham", Tai_Tham),
+_U("Tai Viet", Tai_Viet),
+_U("Tai Xuan Jing Symbols", Tai_Xuan_Jing_Symbols),
+_U("Takri", Takri),
+_U("Tamil", Tamil),
+_U("Telugu", Telugu),
+_U("Thaana", Thaana),
+_U("Thai", Thai),
+_U("Tibetan", Tibetan),
+_U("Tifinagh", Tifinagh),
+_U("Transport And Map Symbols", Transport_And_Map_Symbols),
+_U("Ugaritic", Ugaritic),
+_U("Unified Canadian Aboriginal Syllabics", Unified_Canadian_Aboriginal_Syllabics),
+_U("Unified Canadian Aboriginal Syllabics Extended", Unified_Canadian_Aboriginal_Syllabics_Extended),
+_U("Vai", Vai),
+_U("Variation Selectors", Variation_Selectors),
+_U("Variation Selectors Supplement", Variation_Selectors_Supplement),
+_U("Vedic Extensions", Vedic_Extensions),
+_U("Vertical Forms", Vertical_Forms),
+_U("Yijing Hexagram Symbols", Yijing_Hexagram_Symbols),
+_U("Yi Radicals", Yi_Radicals),
+_U("Yi Syllables", Yi_Syllables),
+];
+}
+
+struct scripts
+{
+private alias _U = immutable(UnicodeProperty);
+@property static _U[] tab() pure nothrow @nogc { return _tab; }
+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 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 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 Sharada = [0xa1, 0x11, 0x80, 0x49, 0x7, 0xa];
+_T Rejang = [0xa0, 0xa9, 0x30, 0x24, 0xb, 0x1];
+_T Imperial_Aramaic = [0xa1, 0x8, 0x40, 0x16, 0x1, 0x9];
+_T Cham = [0xa0, 0xaa, 0x0, 0x37, 0x9, 0xe, 0x2, 0xa, 0x2, 0x4];
+_T Kaithi = [0xa1, 0x10, 0x80, 0x42];
+_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 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 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 Takri = [0xa1, 0x16, 0x80, 0x38, 0x8, 0xa];
+_T Phoenician = [0xa1, 0x9, 0x0, 0x1c, 0x3, 0x1];
+_T Vai = [0xa0, 0xa5, 0x0, 0x81, 0x2c];
+_T Batak = [0x9b, 0xc0, 0x34, 0x8, 0x4];
+_T Yi = [0xa0, 0xa0, 0x0, 0x84, 0x8d, 0x3, 0x37];
+_T Tifinagh = [0xa0, 0x2d, 0x30, 0x38, 0x7, 0x2, 0xe, 0x1];
+_T Glagolitic = [0xa0, 0x2c, 0x0, 0x2f, 0x1, 0x2f];
+_T Tai_Tham = [0x9a, 0x20, 0x3f, 0x1, 0x1d, 0x2, 0xb, 0x6, 0xa, 0x6, 0xe];
+_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 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 Hanunoo = [0x97, 0x20, 0x15];
+_T Lydian = [0xa1, 0x9, 0x20, 0x1a, 0x5, 0x1];
+_T Tai_Viet = [0xa0, 0xaa, 0x80, 0x43, 0x18, 0x5];
+_T Coptic = [0x83, 0xe2, 0xe, 0xa0, 0x28, 0x90, 0x74, 0x5, 0x7];
+_T Brahmi = [0xa1, 0x10, 0x0, 0x4e, 0x4, 0x1e];
+_T Runic = [0x96, 0xa0, 0x4b, 0x3, 0x3];
+_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 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 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 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 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 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 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 Inscriptional_Parthian = [0xa1, 0xb, 0x40, 0x16, 0x2, 0x8];
+_T Nko = [0x87, 0xc0, 0x3b];
+_T Javanese = [0xa0, 0xa9, 0x80, 0x4e, 0x2, 0xa, 0x4, 0x2];
+_T Tai_Le = [0x99, 0x50, 0x1e, 0x2, 0x5];
+_T Old_South_Arabian = [0xa1, 0xa, 0x60, 0x20];
+_T Bamum = [0xa0, 0xa6, 0xa0, 0x58, 0xa0, 0xc1, 0x8, 0x82, 0x39];
+_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 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
+];
+_U[] _tab = [
+_U("Arabic", Arabic),
+_U("Armenian", Armenian),
+_U("Avestan", Avestan),
+_U("Balinese", Balinese),
+_U("Bamum", Bamum),
+_U("Batak", Batak),
+_U("Bengali", Bengali),
+_U("Bopomofo", Bopomofo),
+_U("Brahmi", Brahmi),
+_U("Braille", Braille),
+_U("Buginese", Buginese),
+_U("Buhid", Buhid),
+_U("Canadian_Aboriginal", Canadian_Aboriginal),
+_U("Carian", Carian),
+_U("Chakma", Chakma),
+_U("Cham", Cham),
+_U("Cherokee", Cherokee),
+_U("Common", Common),
+_U("Coptic", Coptic),
+_U("Cuneiform", Cuneiform),
+_U("Cypriot", Cypriot),
+_U("Cyrillic", Cyrillic),
+_U("Deseret", Deseret),
+_U("Devanagari", Devanagari),
+_U("Egyptian_Hieroglyphs", Egyptian_Hieroglyphs),
+_U("Ethiopic", Ethiopic),
+_U("Georgian", Georgian),
+_U("Glagolitic", Glagolitic),
+_U("Gothic", Gothic),
+_U("Greek", Greek),
+_U("Gujarati", Gujarati),
+_U("Gurmukhi", Gurmukhi),
+_U("Han", Han),
+_U("Hangul", Hangul),
+_U("Hanunoo", Hanunoo),
+_U("Hebrew", Hebrew),
+_U("Hiragana", Hiragana),
+_U("Imperial_Aramaic", Imperial_Aramaic),
+_U("Inherited", Inherited),
+_U("Inscriptional_Pahlavi", Inscriptional_Pahlavi),
+_U("Inscriptional_Parthian", Inscriptional_Parthian),
+_U("Javanese", Javanese),
+_U("Kaithi", Kaithi),
+_U("Kannada", Kannada),
+_U("Katakana", Katakana),
+_U("Kayah_Li", Kayah_Li),
+_U("Kharoshthi", Kharoshthi),
+_U("Khmer", Khmer),
+_U("Lao", Lao),
+_U("Latin", Latin),
+_U("Lepcha", Lepcha),
+_U("Limbu", Limbu),
+_U("Linear_B", Linear_B),
+_U("Lisu", Lisu),
+_U("Lycian", Lycian),
+_U("Lydian", Lydian),
+_U("Malayalam", Malayalam),
+_U("Mandaic", Mandaic),
+_U("Meetei_Mayek", Meetei_Mayek),
+_U("Meroitic_Cursive", Meroitic_Cursive),
+_U("Meroitic_Hieroglyphs", Meroitic_Hieroglyphs),
+_U("Miao", Miao),
+_U("Mongolian", Mongolian),
+_U("Myanmar", Myanmar),
+_U("New_Tai_Lue", New_Tai_Lue),
+_U("Nko", Nko),
+_U("Ogham", Ogham),
+_U("Ol_Chiki", Ol_Chiki),
+_U("Old_Italic", Old_Italic),
+_U("Old_Persian", Old_Persian),
+_U("Old_South_Arabian", Old_South_Arabian),
+_U("Old_Turkic", Old_Turkic),
+_U("Oriya", Oriya),
+_U("Osmanya", Osmanya),
+_U("Phags_Pa", Phags_Pa),
+_U("Phoenician", Phoenician),
+_U("Rejang", Rejang),
+_U("Runic", Runic),
+_U("Samaritan", Samaritan),
+_U("Saurashtra", Saurashtra),
+_U("Sharada", Sharada),
+_U("Shavian", Shavian),
+_U("Sinhala", Sinhala),
+_U("Sora_Sompeng", Sora_Sompeng),
+_U("Sundanese", Sundanese),
+_U("Syloti_Nagri", Syloti_Nagri),
+_U("Syriac", Syriac),
+_U("Tagalog", Tagalog),
+_U("Tagbanwa", Tagbanwa),
+_U("Tai_Le", Tai_Le),
+_U("Tai_Tham", Tai_Tham),
+_U("Tai_Viet", Tai_Viet),
+_U("Takri", Takri),
+_U("Tamil", Tamil),
+_U("Telugu", Telugu),
+_U("Thaana", Thaana),
+_U("Thai", Thai),
+_U("Tibetan", Tibetan),
+_U("Tifinagh", Tifinagh),
+_U("Ugaritic", Ugaritic),
+_U("Vai", Vai),
+_U("Yi", Yi),
+];
+}
+
+struct hangul
+{
+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 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 L = [0x91, 0x0, 0x60, 0xa0, 0x98, 0x0, 0x1d];
+_U[] _tab = [
+_U("L", L),
+_U("Leading_Jamo", L),
+_U("LV", LV),
+_U("LV_Syllable", LV),
+_U("LVT", LVT),
+_U("LVT_Syllable", LVT),
+_U("T", T),
+_U("Trailing_Jamo", T),
+_U("V", V),
+_U("Vowel_Jamo", V),
+];
+}
+bool isFormatGen(dchar ch) @safe pure nothrow
+{
+ if (ch < 8288)
+ {
+ if (ch < 1807)
+ {
+ if (ch < 1564)
+ {
+ 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;
+ return false;
+ }
+ }
+ else if (ch < 1808) return true;
+ else
+ {
+ if (ch < 8203)
+ {
+ if (ch == 6158) return true;
+ return false;
+ }
+ else if (ch < 8208) return true;
+ else
+ {
+ if (ch < 8234) return false;
+ if (ch < 8239) return true;
+ return false;
+ }
+ }
+ }
+ else if (ch < 8293) return true;
+ else
+ {
+ if (ch < 69_821)
+ {
+ if (ch < 65_279)
+ {
+ if (ch < 8294) return false;
+ if (ch < 8304) return true;
+ return false;
+ }
+ else if (ch < 65_280) return true;
+ else
+ {
+ if (ch < 65_529) return false;
+ if (ch < 65_532) return true;
+ return false;
+ }
+ }
+ else if (ch < 69_822) return true;
+ else
+ {
+ if (ch < 917_505)
+ {
+ if (ch < 119_155) return false;
+ if (ch < 119_163) return true;
+ return false;
+ }
+ else if (ch < 917_506) return true;
+ else
+ {
+ if (ch < 917_536) return false;
+ if (ch < 917_632) return true;
+ return false;
+ }
+ }
+ }
+}
+
+bool isControlGen(dchar ch) @safe pure nothrow
+{
+ 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 == 32) return true;
+ return false;
+ }
+ else if (ch < 161) return true;
+ else
+ {
+ if (ch < 8239)
+ {
+ 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 == 12_288) return true;
+ return false;
+ }
+ }
+}
+
+bool isWhiteGen(dchar ch) @safe pure nothrow @nogc
+{
+ if (ch < 133)
+ {
+ 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 < 5760)
+ {
+ if (ch == 160) return true;
+ return false;
+ }
+ else if (ch < 5761) return true;
+ else
+ {
+ if (ch < 8192) return false;
+ if (ch < 8203) return true;
+ return false;
+ }
+ }
+ else if (ch < 8234) return true;
+ else
+ {
+ if (ch < 8287)
+ {
+ if (ch == 8239) return true;
+ return false;
+ }
+ else if (ch < 8288) return true;
+ else
+ {
+ if (ch == 12_288) return true;
+ return false;
+ }
+ }
+ }
+}
+
+bool isHangL(dchar ch) @safe pure nothrow
+{
+ 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 < 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 < 55_243) return false;
+ if (ch < 55_292) return true;
+ return false;
+}
+
+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]);
+//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]);
+//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]);
+//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]);
+//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]);
+//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]);
+//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]);
+//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]);
+//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]);
+//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]);
+//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 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]);
+//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]);
+//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]);
+//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]);
+//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]);
+//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]);
+@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;
+ }
+}
+
+}
+
+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]);
+//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]);
+//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]);
+//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]);
+//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]);
+//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]);
+//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]);
+//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]);
+//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]);
+//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]);
+//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 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]);
+//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]);
+//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]);
+//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]);
+//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]);
+//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]);
+@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;
+}
+}
+} \ No newline at end of file
diff --git a/libphobos/src/std/internal/windows/advapi32.d b/libphobos/src/std/internal/windows/advapi32.d
new file mode 100644
index 0000000..b25956b
--- /dev/null
+++ b/libphobos/src/std/internal/windows/advapi32.d
@@ -0,0 +1,69 @@
+// Written in the D programming language.
+
+/**
+ * The only purpose of this module is to do the static construction for
+ * std.windows.registry, to eliminate cyclic construction errors.
+ *
+ * License: $(HTTP www.boost.org/LICENSE_1_0.txt, Boost License 1.0).
+ * Authors: Kenji Hara
+ * Source: $(PHOBOSSRC std/internal/windows/_advapi32.d)
+ */
+module std.internal.windows.advapi32;
+
+version (Windows):
+
+import core.sys.windows.windows;
+
+version (GNU) {}
+else pragma(lib, "advapi32.lib");
+
+immutable bool isWow64;
+
+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 fptr_t = extern(Windows) BOOL function(HANDLE, PBOOL);
+ auto hKernel = GetModuleHandleA("kernel32");
+ auto IsWow64Process = cast(fptr_t) GetProcAddress(hKernel, "IsWow64Process");
+ BOOL bIsWow64;
+ isWow64 = IsWow64Process && IsWow64Process(GetCurrentProcess(), &bIsWow64) && bIsWow64;
+}
+
+HMODULE hAdvapi32 = null;
+extern (Windows)
+{
+ LONG function(in HKEY hkey, in LPCWSTR lpSubKey, in REGSAM samDesired, in DWORD reserved) pRegDeleteKeyExW;
+}
+
+void loadAdvapi32()
+{
+ if (!hAdvapi32)
+ {
+ hAdvapi32 = LoadLibraryA("Advapi32.dll");
+ if (!hAdvapi32)
+ throw new Exception(`LoadLibraryA("Advapi32.dll")`);
+
+ pRegDeleteKeyExW = cast(typeof(pRegDeleteKeyExW)) GetProcAddress(hAdvapi32 , "RegDeleteKeyExW");
+ if (!pRegDeleteKeyExW)
+ throw new Exception(`GetProcAddress(hAdvapi32 , "RegDeleteKeyExW")`);
+ }
+}
+
+// It will free Advapi32.dll, which may be loaded for RegDeleteKeyEx function
+private void freeAdvapi32()
+{
+ if (hAdvapi32)
+ {
+ if (!FreeLibrary(hAdvapi32))
+ throw new Exception(`FreeLibrary("Advapi32.dll")`);
+ hAdvapi32 = null;
+
+ pRegDeleteKeyExW = null;
+ }
+}
+
+static ~this()
+{
+ freeAdvapi32();
+}
diff --git a/libphobos/src/std/json.d b/libphobos/src/std/json.d
new file mode 100644
index 0000000..fd6cf41
--- /dev/null
+++ b/libphobos/src/std/json.d
@@ -0,0 +1,1859 @@
+// Written in the D programming language.
+
+/**
+JavaScript Object Notation
+
+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.array;
+import std.conv;
+import std.range.primitives;
+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
+ long x;
+ if (const(JSONValue)* code = "code" in j)
+ {
+ 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.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");
+
+ string jjStr = `{"language":"D","list":["a","b","c","D"],"rating":3.5}`;
+ assert(jj.toString == jjStr);
+}
+
+/**
+String literals used to represent special float values within JSON strings.
+*/
+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
+}
+
+/**
+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
+ doNotEscapeSlashes = 0x4, /// do not escape slashes ('/')
+}
+
+/**
+JSON type enumeration
+*/
+enum JSON_TYPE : byte
+{
+ /// Indicates the type of a $(D JSONValue).
+ NULL,
+ STRING, /// ditto
+ INTEGER, /// ditto
+ UINTEGER,/// ditto
+ FLOAT, /// ditto
+ OBJECT, /// ditto
+ ARRAY, /// ditto
+ TRUE, /// ditto
+ FALSE /// ditto
+}
+
+/**
+JSON value node
+*/
+struct JSONValue
+{
+ import std.exception : enforceEx, enforce;
+
+ union Store
+ {
+ string str;
+ long integer;
+ ulong uinteger;
+ double floating;
+ JSONValue[string] object;
+ JSONValue[] array;
+ }
+ private Store store;
+ private JSON_TYPE type_tag;
+
+ /**
+ Returns the JSON_TYPE of the value stored in this structure.
+ */
+ @property JSON_TYPE type() const pure nothrow @safe @nogc
+ {
+ return type_tag;
+ }
+ ///
+ @safe unittest
+ {
+ string s = "{ \"language\": \"D\" }";
+ JSONValue j = parseJSON(s);
+ assert(j.type == JSON_TYPE.OBJECT);
+ assert(j["language"].type == JSON_TYPE.STRING);
+ }
+
+ /***
+ * 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) pure nothrow @nogc @safe
+ {
+ assign(v);
+ return v;
+ }
+ ///
+ @safe unittest
+ {
+ JSONValue j = [ "language": "D" ];
+
+ // get value
+ assert(j["language"].str == "D");
+
+ // change existing key to new string
+ j["language"].str = "Perl";
+ 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 pure @safe
+ {
+ enforce!JSONException(type == JSON_TYPE.INTEGER,
+ "JSONValue is not an integer");
+ return store.integer;
+ }
+ /// ditto
+ @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 pure @safe
+ {
+ enforce!JSONException(type == JSON_TYPE.UINTEGER,
+ "JSONValue is not an unsigned integer");
+ return store.uinteger;
+ }
+ /// ditto
+ @property ulong uinteger(ulong v) pure nothrow @safe @nogc
+ {
+ assign(v);
+ return store.uinteger;
+ }
+
+ /***
+ * 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) 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).
+ * 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) 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).
+ * 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) 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 pure nothrow @safe @nogc
+ {
+ return type == JSON_TYPE.NULL;
+ }
+
+ private void assign(T)(T arg) @safe
+ {
+ static if (is(T : typeof(null)))
+ {
+ type_tag = JSON_TYPE.NULL;
+ }
+ 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;
+ // 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))
+ {
+ type_tag = arg ? JSON_TYPE.TRUE : JSON_TYPE.FALSE;
+ }
+ else static if (is(T : ulong) && isUnsigned!T)
+ {
+ type_tag = JSON_TYPE.UINTEGER;
+ store.uinteger = arg;
+ }
+ else static if (is(T : long))
+ {
+ type_tag = JSON_TYPE.INTEGER;
+ store.integer = arg;
+ }
+ else static if (isFloatingPoint!T)
+ {
+ type_tag = JSON_TYPE.FLOAT;
+ store.floating = arg;
+ }
+ 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))
+ {
+ JSONValue[string] t = arg;
+ () @trusted { store.object = t; }();
+ }
+ else
+ {
+ JSONValue[string] aa;
+ foreach (key, value; arg)
+ aa[key] = JSONValue(value);
+ () @trusted { store.object = aa; }();
+ }
+ }
+ else static if (isArray!T)
+ {
+ type_tag = JSON_TYPE.ARRAY;
+ static if (is(ElementEncodingType!T : JSONValue))
+ {
+ JSONValue[] t = arg;
+ () @trusted { store.array = t; }();
+ }
+ else
+ {
+ JSONValue[] new_arg = new JSONValue[arg.length];
+ foreach (i, e; arg)
+ new_arg[i] = JSONValue(e);
+ () @trusted { store.array = new_arg; }();
+ }
+ }
+ else static if (is(T : JSONValue))
+ {
+ type_tag = arg.type;
+ store = arg.store;
+ }
+ else
+ {
+ static assert(false, text(`unable to convert type "`, T.stringof, `" to json`));
+ }
+ }
+
+ private void assignRef(T)(ref T arg) if (isStaticArray!T)
+ {
+ type_tag = JSON_TYPE.ARRAY;
+ static if (is(ElementEncodingType!T : JSONValue))
+ {
+ store.array = arg;
+ }
+ else
+ {
+ JSONValue[] new_arg = new JSONValue[arg.length];
+ foreach (i, e; arg)
+ new_arg[i] = JSONValue(e);
+ store.array = new_arg;
+ }
+ }
+
+ /**
+ * Constructor for $(D JSONValue). If $(D arg) is a $(D JSONValue)
+ * its value and type will be copied to the new $(D JSONValue).
+ * Note that this is a shallow copy: if type is $(D JSON_TYPE.OBJECT)
+ * or $(D JSON_TYPE.ARRAY) then only the reference to the data will
+ * be copied.
+ * Otherwise, $(D arg) must be implicitly convertible to one of the
+ * following types: $(D typeof(null)), $(D string), $(D ulong),
+ * $(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)
+ {
+ assign(arg);
+ }
+ /// Ditto
+ this(T)(ref T arg) if (isStaticArray!T)
+ {
+ assignRef(arg);
+ }
+ /// Ditto
+ this(T : JSONValue)(inout T arg) inout
+ {
+ store = arg.store;
+ type_tag = arg.type;
+ }
+ ///
+ @safe unittest
+ {
+ JSONValue j = JSONValue( "a string" );
+ j = JSONValue(42);
+
+ j = JSONValue( [1, 2, 3] );
+ assert(j.type == JSON_TYPE.ARRAY);
+
+ j = JSONValue( ["language": "D"] );
+ assert(j.type == JSON_TYPE.OBJECT);
+ }
+
+ void opAssign(T)(T arg) if (!isStaticArray!T && !is(T : JSONValue))
+ {
+ assign(arg);
+ }
+
+ 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 pure @safe
+ {
+ auto a = this.arrayNoRef;
+ enforceEx!JSONException(i < a.length,
+ "JSONValue array index is out of range");
+ return a[i];
+ }
+ ///
+ @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 pure @safe
+ {
+ auto o = this.objectNoRef;
+ return *enforce!JSONException(k in o,
+ "Key not found: " ~ k);
+ }
+ ///
+ @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) 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;
+ }
+
+ aa[key] = value;
+ this.object = aa;
+ }
+ ///
+ @safe unittest
+ {
+ JSONValue j = JSONValue( ["language": "D"] );
+ j["language"].str = "Perl";
+ assert( j["language"].str == "Perl" );
+ }
+
+ void opIndexAssign(T)(T arg, size_t i) pure
+ {
+ auto a = this.arrayNoRef;
+ enforceEx!JSONException(i < a.length,
+ "JSONValue array index is out of range");
+ a[i] = arg;
+ this.array = a;
+ }
+ ///
+ @safe unittest
+ {
+ JSONValue j = JSONValue( ["Perl", "C"] );
+ j[1].str = "D";
+ assert( j[1].str == "D" );
+ }
+
+ JSONValue opBinary(string op : "~", T)(T arg) @safe
+ {
+ auto a = this.arrayNoRef;
+ static if (isArray!T)
+ {
+ return JSONValue(a ~ JSONValue(arg).arrayNoRef);
+ }
+ else static if (is(T : JSONValue))
+ {
+ return JSONValue(a ~ arg.arrayNoRef);
+ }
+ else
+ {
+ static assert(false, "argument is not an array or a JSONValue array");
+ }
+ }
+
+ void opOpAssign(string op : "~", T)(T arg) @safe
+ {
+ auto a = this.arrayNoRef;
+ static if (isArray!T)
+ {
+ a ~= JSONValue(arg).arrayNoRef;
+ }
+ else static if (is(T : JSONValue))
+ {
+ a ~= arg.arrayNoRef;
+ }
+ else
+ {
+ static assert(false, "argument is not an array or a JSONValue array");
+ }
+ this.array = a;
+ }
+
+ /**
+ * 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
+ {
+ return k in this.objectNoRef;
+ }
+ ///
+ @safe unittest
+ {
+ JSONValue j = [ "language": "D", "author": "walter" ];
+ string a = ("author" in j).str;
+ }
+
+ bool opEquals(const JSONValue rhs) const @nogc nothrow pure @safe
+ {
+ return opEquals(rhs);
+ }
+
+ 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)
+ {
+ case JSON_TYPE.STRING:
+ return store.str == rhs.store.str;
+ case JSON_TYPE.INTEGER:
+ return store.integer == rhs.store.integer;
+ case JSON_TYPE.UINTEGER:
+ return store.uinteger == rhs.store.uinteger;
+ case JSON_TYPE.FLOAT:
+ return store.floating == rhs.store.floating;
+ case JSON_TYPE.OBJECT:
+ return store.object == rhs.store.object;
+ case JSON_TYPE.ARRAY:
+ return store.array == rhs.store.array;
+ case JSON_TYPE.TRUE:
+ case JSON_TYPE.FALSE:
+ case JSON_TYPE.NULL:
+ return true;
+ }
+ }
+
+ /// Implements the foreach $(D opApply) interface for json arrays.
+ int opApply(scope int delegate(size_t index, ref JSONValue) dg) @system
+ {
+ int result;
+
+ foreach (size_t index, ref value; array)
+ {
+ result = dg(index, value);
+ if (result)
+ break;
+ }
+
+ return result;
+ }
+
+ /// Implements the foreach $(D opApply) interface for json objects.
+ 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; object)
+ {
+ result = dg(key, value);
+ if (result)
+ break;
+ }
+
+ return result;
+ }
+
+ /***
+ * 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, false, options);
+ }
+
+ /***
+ * 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, 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, JSONOptions options = JSONOptions.none)
+if (isInputRange!T && !isInfinite!T && isSomeChar!(ElementEncodingType!T))
+{
+ import std.ascii : isWhite, isDigit, isHexDigit, toUpper, toLower;
+ import std.typecons : Yes;
+ JSONValue root;
+ root.type_tag = JSON_TYPE.NULL;
+
+ // Avoid UTF decoding when possible, as it is unnecessary when
+ // processing JSON.
+ static if (is(T : const(char)[]))
+ alias Char = char;
+ else
+ alias Char = Unqual!(ElementType!T);
+
+ if (json.empty) return root;
+
+ int depth = -1;
+ Char next = 0;
+ int line = 1, pos = 0;
+
+ void error(string msg)
+ {
+ throw new JSONException(msg, line, pos);
+ }
+
+ Char popChar()
+ {
+ if (json.empty) error("Unexpected end of data.");
+ static if (is(T : const(char)[]))
+ {
+ Char c = json[0];
+ json = json[1..$];
+ }
+ else
+ {
+ Char c = json.front;
+ json.popFront();
+ }
+
+ if (c == '\n')
+ {
+ line++;
+ pos = 0;
+ }
+ else
+ {
+ pos++;
+ }
+
+ return c;
+ }
+
+ Char peekChar()
+ {
+ if (!next)
+ {
+ if (json.empty) return '\0';
+ next = popChar();
+ }
+ return next;
+ }
+
+ void skipWhitespace()
+ {
+ while (isWhite(peekChar())) next = 0;
+ }
+
+ Char getChar(bool SkipWhitespace = false)()
+ {
+ static if (SkipWhitespace) skipWhitespace();
+
+ Char c;
+ if (next)
+ {
+ c = next;
+ next = 0;
+ }
+ else
+ c = popChar();
+
+ return c;
+ }
+
+ void checkChar(bool SkipWhitespace = true, bool CaseSensitive = true)(char c)
+ {
+ static if (SkipWhitespace) skipWhitespace();
+ auto c2 = getChar();
+ static if (!CaseSensitive) c2 = toLower(c2);
+
+ if (c2 != c) error(text("Found '", c2, "' when expecting '", c, "'."));
+ }
+
+ bool testChar(bool SkipWhitespace = true, bool CaseSensitive = true)(char c)
+ {
+ static if (SkipWhitespace) skipWhitespace();
+ auto c2 = peekChar();
+ static if (!CaseSensitive) c2 = toLower(c2);
+
+ if (c2 != c) return false;
+
+ getChar();
+ return true;
+ }
+
+ wchar parseWChar()
+ {
+ wchar val = 0;
+ foreach_reverse (i; 0 .. 4)
+ {
+ auto hex = toUpper(getChar());
+ if (!isHexDigit(hex)) error("Expecting hex character");
+ val += (isDigit(hex) ? hex - '0' : hex - ('A' - 10)) << (4 * i);
+ }
+ return val;
+ }
+
+ string parseString()
+ {
+ import std.ascii : isControl;
+ import std.uni : isSurrogateHi, isSurrogateLo;
+ import std.utf : encode, decode;
+
+ auto str = appender!string();
+
+ Next:
+ switch (peekChar())
+ {
+ case '"':
+ getChar();
+ break;
+
+ case '\\':
+ getChar();
+ auto c = getChar();
+ switch (c)
+ {
+ case '"': str.put('"'); break;
+ case '\\': str.put('\\'); break;
+ case '/': str.put('/'); break;
+ case 'b': str.put('\b'); break;
+ case 'f': str.put('\f'); break;
+ case 'n': str.put('\n'); break;
+ case 'r': str.put('\r'); break;
+ case 't': str.put('\t'); break;
+ case 'u':
+ wchar wc = parseWChar();
+ dchar val;
+ // Non-BMP characters are escaped as a pair of
+ // UTF-16 surrogate characters (see RFC 4627).
+ if (isSurrogateHi(wc))
+ {
+ wchar[2] pair;
+ pair[0] = wc;
+ if (getChar() != '\\') error("Expected escaped low surrogate after escaped high surrogate");
+ if (getChar() != 'u') error("Expected escaped low surrogate after escaped high surrogate");
+ pair[1] = parseWChar();
+ size_t index = 0;
+ val = decode(pair[], index);
+ if (index != 2) error("Invalid escaped surrogate pair");
+ }
+ else
+ if (isSurrogateLo(wc))
+ error(text("Unexpected low surrogate"));
+ else
+ val = wc;
+
+ char[4] buf;
+ immutable len = encode!(Yes.useReplacementDchar)(buf, val);
+ str.put(buf[0 .. len]);
+ break;
+
+ default:
+ error(text("Invalid escape sequence '\\", c, "'."));
+ }
+ goto Next;
+
+ default:
+ // RFC 7159 states that control characters U+0000 through
+ // U+001F must not appear unescaped in a JSON string.
+ auto c = getChar();
+ if (isControl(c))
+ error("Illegal control character.");
+ str.put(c);
+ goto Next;
+ }
+
+ return str.data.length ? str.data : "";
+ }
+
+ 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.");
+
+ auto c = getChar!true();
+
+ switch (c)
+ {
+ case '{':
+ if (testChar('}'))
+ {
+ value.object = null;
+ break;
+ }
+
+ JSONValue[string] obj;
+ do
+ {
+ checkChar('"');
+ string name = parseString();
+ checkChar(':');
+ JSONValue member;
+ parseValue(member);
+ obj[name] = member;
+ }
+ while (testChar(','));
+ value.object = obj;
+
+ checkChar('}');
+ break;
+
+ case '[':
+ if (testChar(']'))
+ {
+ value.type_tag = JSON_TYPE.ARRAY;
+ break;
+ }
+
+ JSONValue[] arr;
+ do
+ {
+ JSONValue element;
+ parseValue(element);
+ arr ~= element;
+ }
+ 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 = str;
+ break;
+
+ case '0': .. case '9':
+ case '-':
+ auto number = appender!string();
+ bool isFloat, isNegative;
+
+ void readInteger()
+ {
+ if (!isDigit(c)) error("Digit expected");
+
+ Next: number.put(c);
+
+ if (isDigit(peekChar()))
+ {
+ c = getChar();
+ goto Next;
+ }
+ }
+
+ if (c == '-')
+ {
+ number.put('-');
+ c = getChar();
+ isNegative = true;
+ }
+
+ readInteger();
+
+ if (testChar('.'))
+ {
+ isFloat = true;
+ number.put('.');
+ c = getChar();
+ readInteger();
+ }
+ if (testChar!(false, false)('e'))
+ {
+ isFloat = true;
+ number.put('e');
+ if (testChar('+')) number.put('+');
+ else if (testChar('-')) number.put('-');
+ c = getChar();
+ readInteger();
+ }
+
+ string data = number.data;
+ if (isFloat)
+ {
+ value.type_tag = JSON_TYPE.FLOAT;
+ value.store.floating = parse!double(data);
+ }
+ else
+ {
+ if (isNegative)
+ value.store.integer = parse!long(data);
+ else
+ value.store.uinteger = parse!ulong(data);
+
+ value.type_tag = !isNegative && value.store.uinteger & (1UL << 63) ?
+ JSON_TYPE.UINTEGER : JSON_TYPE.INTEGER;
+ }
+ break;
+
+ case 't':
+ case 'T':
+ value.type_tag = JSON_TYPE.TRUE;
+ checkChar!(false, false)('r');
+ checkChar!(false, false)('u');
+ checkChar!(false, false)('e');
+ break;
+
+ case 'f':
+ case 'F':
+ value.type_tag = JSON_TYPE.FALSE;
+ checkChar!(false, false)('a');
+ checkChar!(false, false)('l');
+ checkChar!(false, false)('s');
+ checkChar!(false, false)('e');
+ break;
+
+ case 'n':
+ case 'N':
+ value.type_tag = JSON_TYPE.NULL;
+ checkChar!(false, false)('u');
+ checkChar!(false, false)('l');
+ checkChar!(false, false)('l');
+ break;
+
+ default:
+ error(text("Unexpected character '", c, "'."));
+ }
+
+ depth--;
+ }
+
+ 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.
+
+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 $(LREF JSONOptions.specialFloatLiterals) flag is set in $(D options) to encode NaN/Infinity as strings.
+*/
+string toJSON(const ref JSONValue root, in bool pretty = false, in JSONOptions options = JSONOptions.none) @safe
+{
+ auto json = appender!string();
+
+ void toStringImpl(Char)(string str) @safe
+ {
+ json.put('"');
+
+ foreach (Char c; str)
+ {
+ switch (c)
+ {
+ case '"': json.put("\\\""); break;
+ case '\\': json.put("\\\\"); break;
+
+ case '/':
+ if (!(options & JSONOptions.doNotEscapeSlashes))
+ json.put('\\');
+ json.put('/');
+ break;
+
+ case '\b': json.put("\\b"); break;
+ case '\f': json.put("\\f"); break;
+ case '\n': json.put("\\n"); break;
+ case '\r': json.put("\\r"); break;
+ case '\t': json.put("\\t"); break;
+ default:
+ {
+ import std.ascii : isControl;
+ import std.utf : encode;
+
+ // Make sure we do UTF decoding iff we want to
+ // escape Unicode characters.
+ assert(((options & JSONOptions.escapeNonAsciiChars) != 0)
+ == is(Char == dchar));
+
+ with (JSONOptions) if (isControl(c) ||
+ ((options & escapeNonAsciiChars) >= escapeNonAsciiChars && c >= 0x80))
+ {
+ // Ensure non-BMP characters are encoded as a pair
+ // of UTF-16 surrogate characters, as per RFC 4627.
+ wchar[2] wchars; // 1 or 2 UTF-16 code units
+ size_t wNum = encode(wchars, c); // number of UTF-16 code units
+ foreach (wc; wchars[0 .. wNum])
+ {
+ json.put("\\u");
+ foreach_reverse (i; 0 .. 4)
+ {
+ char ch = (wc >>> (4 * i)) & 0x0f;
+ ch += ch < 10 ? '0' : 'A' - 10;
+ json.put(ch);
+ }
+ }
+ }
+ else
+ {
+ json.put(c);
+ }
+ }
+ }
+ }
+
+ json.put('"');
+ }
+
+ void toString(string str) @safe
+ {
+ // Avoid UTF decoding when possible, as it is unnecessary when
+ // processing JSON.
+ if (options & JSONOptions.escapeNonAsciiChars)
+ toStringImpl!dchar(str);
+ else
+ toStringImpl!char(str);
+ }
+
+ void toValue(ref in JSONValue value, ulong indentLevel) @safe
+ {
+ void putTabs(ulong additionalIndent = 0)
+ {
+ if (pretty)
+ foreach (i; 0 .. indentLevel + additionalIndent)
+ json.put(" ");
+ }
+ void putEOL()
+ {
+ if (pretty)
+ json.put('\n');
+ }
+ void putCharAndEOL(char ch)
+ {
+ json.put(ch);
+ putEOL();
+ }
+
+ final switch (value.type)
+ {
+ case JSON_TYPE.OBJECT:
+ auto obj = value.objectNoRef;
+ if (!obj.length)
+ {
+ json.put("{}");
+ }
+ else
+ {
+ putCharAndEOL('{');
+ bool first = true;
+
+ void emit(R)(R names)
+ {
+ foreach (name; names)
+ {
+ auto member = obj[name];
+ if (!first)
+ putCharAndEOL(',');
+ first = false;
+ putTabs(1);
+ toString(name);
+ json.put(':');
+ if (pretty)
+ json.put(' ');
+ toValue(member, indentLevel + 1);
+ }
+ }
+
+ 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);
+
+ putEOL();
+ putTabs();
+ json.put('}');
+ }
+ break;
+
+ case JSON_TYPE.ARRAY:
+ auto arr = value.arrayNoRef;
+ if (arr.empty)
+ {
+ json.put("[]");
+ }
+ else
+ {
+ putCharAndEOL('[');
+ foreach (i, el; arr)
+ {
+ if (i)
+ putCharAndEOL(',');
+ putTabs(1);
+ toValue(el, indentLevel + 1);
+ }
+ putEOL();
+ putTabs();
+ json.put(']');
+ }
+ break;
+
+ case JSON_TYPE.STRING:
+ toString(value.str);
+ break;
+
+ case JSON_TYPE.INTEGER:
+ json.put(to!string(value.store.integer));
+ break;
+
+ case JSON_TYPE.UINTEGER:
+ json.put(to!string(value.store.uinteger));
+ break;
+
+ case JSON_TYPE.FLOAT:
+ 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:
+ json.put("true");
+ break;
+
+ case JSON_TYPE.FALSE:
+ json.put("false");
+ break;
+
+ case JSON_TYPE.NULL:
+ json.put("null");
+ break;
+ }
+ }
+
+ toValue(root, 0);
+ return json.data;
+}
+
+@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é"`);
+}
+
+/**
+Exception thrown on JSON errors
+*/
+class JSONException : Exception
+{
+ this(string msg, int line = 0, int pos = 0) pure nothrow @safe
+ {
+ if (line)
+ super(text(msg, " (Line ", line, ":", pos, ")"));
+ else
+ super(msg);
+ }
+
+ this(string msg, string file, size_t line) pure nothrow @safe
+ {
+ super(msg, file, line);
+ }
+}
+
+
+@system unittest
+{
+ import std.exception;
+ JSONValue jv = "123";
+ assert(jv.type == JSON_TYPE.STRING);
+ assertNotThrown(jv.str);
+ assertThrown!JSONException(jv.integer);
+ assertThrown!JSONException(jv.uinteger);
+ assertThrown!JSONException(jv.floating);
+ assertThrown!JSONException(jv.object);
+ assertThrown!JSONException(jv.array);
+ assertThrown!JSONException(jv["aa"]);
+ assertThrown!JSONException(jv[2]);
+
+ jv = -3;
+ assert(jv.type == JSON_TYPE.INTEGER);
+ assertNotThrown(jv.integer);
+
+ jv = cast(uint) 3;
+ assert(jv.type == JSON_TYPE.UINTEGER);
+ assertNotThrown(jv.uinteger);
+
+ jv = 3.0;
+ assert(jv.type == JSON_TYPE.FLOAT);
+ assertNotThrown(jv.floating);
+
+ jv = ["key" : "value"];
+ assert(jv.type == JSON_TYPE.OBJECT);
+ assertNotThrown(jv.object);
+ assertNotThrown(jv["key"]);
+ assert("key" in jv);
+ assert("notAnElement" !in jv);
+ assertThrown!JSONException(jv["notAnElement"]);
+ const cjv = jv;
+ assert("key" in cjv);
+ assertThrown!JSONException(cjv["notAnElement"]);
+
+ foreach (string key, value; jv)
+ {
+ static assert(is(typeof(value) == JSONValue));
+ assert(key == "key");
+ assert(value.type == JSON_TYPE.STRING);
+ assertNotThrown(value.str);
+ assert(value.str == "value");
+ }
+
+ jv = [3, 4, 5];
+ assert(jv.type == JSON_TYPE.ARRAY);
+ assertNotThrown(jv.array);
+ assertNotThrown(jv[2]);
+ foreach (size_t index, value; jv)
+ {
+ static assert(is(typeof(value) == JSONValue));
+ assert(value.type == JSON_TYPE.INTEGER);
+ assertNotThrown(value.integer);
+ assert(index == (value.integer-3));
+ }
+
+ jv = null;
+ assert(jv.type == JSON_TYPE.NULL);
+ assert(jv.isNull);
+ jv = "foo";
+ assert(!jv.isNull);
+
+ jv = JSONValue("value");
+ assert(jv.type == JSON_TYPE.STRING);
+ assert(jv.str == "value");
+
+ 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");
+}
+
+@system unittest
+{
+ // Bugzilla 11504
+
+ JSONValue jv = 1;
+ assert(jv.type == JSON_TYPE.INTEGER);
+
+ jv.str = "123";
+ assert(jv.type == JSON_TYPE.STRING);
+ assert(jv.str == "123");
+
+ jv.integer = 1;
+ assert(jv.type == JSON_TYPE.INTEGER);
+ assert(jv.integer == 1);
+
+ jv.uinteger = 2u;
+ assert(jv.type == JSON_TYPE.UINTEGER);
+ assert(jv.uinteger == 2u);
+
+ jv.floating = 1.5;
+ assert(jv.type == JSON_TYPE.FLOAT);
+ assert(jv.floating == 1.5);
+
+ jv.object = ["key" : JSONValue("value")];
+ assert(jv.type == JSON_TYPE.OBJECT);
+ assert(jv.object == ["key" : JSONValue("value")]);
+
+ jv.array = [JSONValue(1), JSONValue(2), JSONValue(3)];
+ assert(jv.type == JSON_TYPE.ARRAY);
+ assert(jv.array == [JSONValue(1), JSONValue(2), JSONValue(3)]);
+
+ jv = true;
+ assert(jv.type == JSON_TYPE.TRUE);
+
+ jv = false;
+ assert(jv.type == JSON_TYPE.FALSE);
+
+ enum E{True = true}
+ jv = E.True;
+ assert(jv.type == JSON_TYPE.TRUE);
+}
+
+@system pure unittest
+{
+ // Adding new json element via array() / object() directly
+
+ JSONValue jarr = JSONValue([10]);
+ foreach (i; 0 .. 9)
+ jarr.array ~= JSONValue(i);
+ assert(jarr.array.length == 10);
+
+ JSONValue jobj = JSONValue(["key" : JSONValue("value")]);
+ foreach (i; 0 .. 9)
+ jobj.object[text("key", i)] = JSONValue(text("value", i));
+ assert(jobj.object.length == 10);
+}
+
+@system pure unittest
+{
+ // Adding new json element without array() / object() access
+
+ JSONValue jarr = JSONValue([10]);
+ foreach (i; 0 .. 9)
+ jarr ~= [JSONValue(i)];
+ assert(jarr.array.length == 10);
+
+ JSONValue jobj = JSONValue(["key" : JSONValue("value")]);
+ foreach (i; 0 .. 9)
+ jobj[text("key", i)] = JSONValue(text("value", i));
+ assert(jobj.object.length == 10);
+
+ // No array alias
+ auto jarr2 = jarr ~ [1,2,3];
+ jarr2[0] = 999;
+ assert(jarr[0] == JSONValue(10));
+}
+
+@system unittest
+{
+ // @system because JSONValue.array is @system
+ import std.exception;
+
+ // An overly simple test suite, if it can parse a serializated string and
+ // then use the resulting values tree to generate an identical
+ // serialization, both the decoder and encoder works.
+
+ auto jsons = [
+ `null`,
+ `true`,
+ `false`,
+ `0`,
+ `123`,
+ `-4321`,
+ `0.25`,
+ `-0.25`,
+ `""`,
+ `"hello\nworld"`,
+ `"\"\\\/\b\f\n\r\t"`,
+ `[]`,
+ `[12,"foo",true,false]`,
+ `{}`,
+ `{"a":1,"b":null}`,
+ `{"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 ~= dbl1_844 ~ `e+019`;
+ else
+ jsons ~= dbl1_844 ~ `e+19`;
+
+ JSONValue val;
+ string result;
+ foreach (json; jsons)
+ {
+ try
+ {
+ val = parseJSON(json);
+ enum pretty = false;
+ result = toJSON(val, pretty);
+ assert(result == json, text(result, " should be ", json));
+ }
+ catch (JSONException e)
+ {
+ import std.stdio : writefln;
+ writefln(text(json, "\n", e.toString()));
+ }
+ }
+
+ // Should be able to correctly interpret unicode entities
+ val = parseJSON(`"\u003C\u003E"`);
+ assert(toJSON(val) == "\"\&lt;\&gt;\"");
+ assert(val.to!string() == "\"\&lt;\&gt;\"");
+ val = parseJSON(`"\u0391\u0392\u0393"`);
+ assert(toJSON(val) == "\"\&Alpha;\&Beta;\&Gamma;\"");
+ assert(val.to!string() == "\"\&Alpha;\&Beta;\&Gamma;\"");
+ val = parseJSON(`"\u2660\u2666"`);
+ assert(toJSON(val) == "\"\&spades;\&diams;\"");
+ assert(val.to!string() == "\"\&spades;\&diams;\"");
+
+ //0x7F is a control character (see Unicode spec)
+ val = parseJSON(`"\u007F"`);
+ assert(toJSON(val) == "\"\\u007F\"");
+ assert(val.to!string() == "\"\\u007F\"");
+
+ with(parseJSON(`""`))
+ assert(str == "" && str !is null);
+ with(parseJSON(`[]`))
+ assert(!array.length);
+
+ // Formatting
+ val = parseJSON(`{"a":[null,{"x":1},{},[]]}`);
+ assert(toJSON(val, true) == `{
+ "a": [
+ null,
+ {
+ "x": 1
+ },
+ {},
+ []
+ ]
+}`);
+}
+
+@safe unittest
+{
+ auto json = `"hello\nworld"`;
+ const jv = parseJSON(json);
+ assert(jv.toString == json);
+ assert(jv.toPrettyString == json);
+}
+
+@system pure unittest
+{
+ // Bugzilla 12969
+
+ JSONValue jv;
+ jv["int"] = 123;
+
+ assert(jv.type == JSON_TYPE.OBJECT);
+ assert("int" in jv);
+ assert(jv["int"].integer == 123);
+
+ jv["array"] = [1, 2, 3, 4, 5];
+
+ assert(jv["array"].type == JSON_TYPE.ARRAY);
+ assert(jv["array"][2].integer == 3);
+
+ jv["str"] = "D language";
+ assert(jv["str"].type == JSON_TYPE.STRING);
+ assert(jv["str"].str == "D language");
+
+ jv["bool"] = false;
+ assert(jv["bool"].type == JSON_TYPE.FALSE);
+
+ assert(jv.object.length == 4);
+
+ jv = [5, 4, 3, 2, 1];
+ assert( jv.type == JSON_TYPE.ARRAY );
+ assert( jv[3].integer == 2 );
+}
+
+@safe unittest
+{
+ auto s = q"EOF
+[
+ 1,
+ 2,
+ 3,
+ potato
+]
+EOF";
+
+ import std.exception;
+
+ 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.exception : assertThrown;
+ import std.math : isNaN, isInfinity;
+
+ // 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
+{
+ enum bool realInDoublePrecision = real.mant_dig == double.mant_dig;
+
+ static bool test(const double num0)
+ {
+ import std.math : feqrel;
+ const json0 = JSONValue(num0);
+ const num1 = to!double(toJSON(json0));
+ static if (realInDoublePrecision)
+ 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.min_normal));
+ static if (realInDoublePrecision)
+ assert(test(-double.max / 2));
+ else
+ assert(test(-double.max));
+
+ const minSub = double.min_normal * double.epsilon;
+ assert(test(minSub));
+ assert(test(3*minSub));
+}
+
+@safe unittest // issue 17555
+{
+ import std.exception : assertThrown;
+
+ assertThrown!JSONException(parseJSON("\"a\nb\""));
+}
+
+@safe unittest // issue 17556
+{
+ auto v = JSONValue("\U0001D11E");
+ auto j = toJSON(v, false, JSONOptions.escapeNonAsciiChars);
+ assert(j == `"\uD834\uDD1E"`);
+}
+
+@safe unittest // issue 5904
+{
+ string s = `"\uD834\uDD1E"`;
+ auto j = parseJSON(s);
+ assert(j.str == "\U0001D11E");
+}
+
+@safe unittest // issue 17557
+{
+ assert(parseJSON("\"\xFF\"").str == "\xFF");
+ assert(parseJSON("\"\U0001D11E\"").str == "\U0001D11E");
+}
+
+@safe unittest // issue 17553
+{
+ auto v = JSONValue("\xFF");
+ assert(toJSON(v) == "\"\xFF\"");
+}
+
+@safe unittest
+{
+ import std.utf;
+ assert(parseJSON("\"\xFF\"".byChar).str == "\xFF");
+ assert(parseJSON("\"\U0001D11E\"".byChar).str == "\U0001D11E");
+}
+
+@safe unittest // JSONOptions.doNotEscapeSlashes (issue 17587)
+{
+ assert(parseJSON(`"/"`).toString == `"\/"`);
+ assert(parseJSON(`"\/"`).toString == `"\/"`);
+ assert(parseJSON(`"/"`).toString(JSONOptions.doNotEscapeSlashes) == `"/"`);
+ assert(parseJSON(`"\/"`).toString(JSONOptions.doNotEscapeSlashes) == `"/"`);
+}
diff --git a/libphobos/src/std/math.d b/libphobos/src/std/math.d
new file mode 100644
index 0000000..84cf4a7
--- /dev/null
+++ b/libphobos/src/std/math.d
@@ -0,0 +1,8413 @@
+// Written in the D programming language.
+
+/**
+ * Contains the elementary mathematical functions (powers, roots,
+ * and trigonometric functions), and low-level floating-point operations.
+ * Mathematical special functions are available in $(D std.mathspecial).
+ *
+$(SCRIPT inhibitQuickIndex = 1;)
+
+$(DIVC quickindex,
+$(BOOKTABLE ,
+$(TR $(TH Category) $(TH Members) )
+$(TR $(TDNW Constants) $(TD
+ $(MYREF E) $(MYREF PI) $(MYREF PI_2) $(MYREF PI_4) $(MYREF M_1_PI)
+ $(MYREF M_2_PI) $(MYREF M_2_SQRTPI) $(MYREF LN10) $(MYREF LN2)
+ $(MYREF LOG2) $(MYREF LOG2E) $(MYREF LOG2T) $(MYREF LOG10E)
+ $(MYREF SQRT2) $(MYREF SQRT1_2)
+))
+$(TR $(TDNW Classics) $(TD
+ $(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)
+ $(MYREF atan) $(MYREF atan2) $(MYREF sinh) $(MYREF cosh) $(MYREF tanh)
+ $(MYREF asinh) $(MYREF acosh) $(MYREF atanh) $(MYREF expi)
+))
+$(TR $(TDNW Rounding) $(TD
+ $(MYREF ceil) $(MYREF floor) $(MYREF round) $(MYREF lround)
+ $(MYREF trunc) $(MYREF rint) $(MYREF lrint) $(MYREF nearbyint)
+ $(MYREF rndtol) $(MYREF quantize)
+))
+$(TR $(TDNW Exponentiation & Logarithms) $(TD
+ $(MYREF pow) $(MYREF exp) $(MYREF exp2) $(MYREF expm1) $(MYREF ldexp)
+ $(MYREF frexp) $(MYREF log) $(MYREF log2) $(MYREF log10) $(MYREF logb)
+ $(MYREF ilogb) $(MYREF log1p) $(MYREF scalbn)
+))
+$(TR $(TDNW Modulus) $(TD
+ $(MYREF fmod) $(MYREF modf) $(MYREF remainder)
+))
+$(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 isPowerOf2)
+))
+$(TR $(TDNW Complex Numbers) $(TD
+ $(MYREF abs) $(MYREF conj) $(MYREF sin) $(MYREF cos) $(MYREF expi)
+))
+$(TR $(TDNW Hardware Control) $(TD
+ $(MYREF IeeeFlags) $(MYREF FloatingPointControl)
+))
+)
+)
+
+ * The functionality closely follows the IEEE754-2008 standard for
+ * floating-point arithmetic, including the use of camelCase names rather
+ * than C99-style lower case names. All of these functions behave correctly
+ * when presented with an infinity or NaN.
+ *
+ * The following IEEE 'real' formats are currently supported:
+ * $(UL
+ * $(LI 64 bit Big-endian 'double' (eg PowerPC))
+ * $(LI 128 bit Big-endian 'quadruple' (eg SPARC))
+ * $(LI 64 bit Little-endian 'double' (eg x86-SSE2))
+ * $(LI 80 bit Little-endian, with implied bit 'real80' (eg x87, Itanium))
+ * $(LI 128 bit Little-endian 'quadruple' (not implemented on any known processor!))
+ * $(LI Non-IEEE 128 bit Big-endian 'doubledouble' (eg PowerPC) has partial support)
+ * )
+ * Unlike C, there is no global 'errno' variable. Consequently, almost all of
+ * these functions are pure nothrow.
+ *
+ * Status:
+ * The semantics and names of feqrel and approxEqual will be revised.
+ *
+ * Macros:
+ * TABLE_SV = <table border="1" cellpadding="4" cellspacing="0">
+ * <caption>Special Values</caption>
+ * $0</table>
+ * SVH = $(TR $(TH $1) $(TH $2))
+ * SV = $(TR $(TD $1) $(TD $2))
+ * TH3 = $(TR $(TH $1) $(TH $2) $(TH $3))
+ * TD3 = $(TR $(TD $1) $(TD $2) $(TD $3))
+ * TABLE_DOMRG = <table border="1" cellpadding="4" cellspacing="0">
+ * $(SVH Domain X, Range Y)
+ $(SV $1, $2)
+ * </table>
+ * DOMAIN=$1
+ * RANGE=$1
+
+ * NAN = $(RED NAN)
+ * SUP = <span style="vertical-align:super;font-size:smaller">$0</span>
+ * GAMMA = &#915;
+ * THETA = &theta;
+ * INTEGRAL = &#8747;
+ * INTEGRATE = $(BIG &#8747;<sub>$(SMALL $1)</sub><sup>$2</sup>)
+ * POWER = $1<sup>$2</sup>
+ * SUB = $1<sub>$2</sub>
+ * BIGSUM = $(BIG &Sigma; <sup>$2</sup><sub>$(SMALL $1)</sub>)
+ * CHOOSE = $(BIG &#40;) <sup>$(SMALL $1)</sup><sub>$(SMALL $2)</sub> $(BIG &#41;)
+ * PLUSMN = &plusmn;
+ * INFIN = &infin;
+ * PLUSMNINF = &plusmn;&infin;
+ * PI = &pi;
+ * LT = &lt;
+ * GT = &gt;
+ * SQRT = &radic;
+ * HALF = &frac12;
+ *
+ * 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 $(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: $(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 and David Nadlinger
+ * Source: $(PHOBOSSRC std/_math.d)
+ */
+
+/* NOTE: This file has been patched from the original DMD distribution to
+ * work with the GDC compiler.
+ */
+module std.math;
+
+version (Win64)
+{
+ version (D_InlineAsm_X86_64)
+ version = Win64_DMD_InlineAsm;
+}
+
+static import core.math;
+static import core.stdc.math;
+static import core.stdc.fenv;
+import std.traits; // CommonType, isFloatingPoint, isIntegral, isSigned, isUnsigned, Largest, Unqual
+
+version (LDC)
+{
+ import ldc.intrinsics;
+}
+
+version (DigitalMars)
+{
+ version = INLINE_YL2X; // x87 has opcodes for these
+}
+
+version (X86) version = X86_Any;
+version (X86_64) version = X86_Any;
+version (PPC) version = PPC_Any;
+version (PPC64) version = PPC_Any;
+version (MIPS32) version = MIPS_Any;
+version (MIPS64) version = MIPS_Any;
+version (AArch64) version = ARM_Any;
+version (ARM) version = ARM_Any;
+
+version (D_InlineAsm_X86)
+{
+ version = InlineAsm_X86_Any;
+}
+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; // : sprintf;
+
+ static if (real.sizeof > double.sizeof)
+ enum uint useDigits = 16;
+ else
+ enum uint useDigits = 15;
+
+ /******************************************
+ * Compare floating point numbers to n decimal digits of precision.
+ * Returns:
+ * 1 match
+ * 0 nomatch
+ */
+
+ private bool equalsDigit(real x, real y, uint ndigits)
+ {
+ if (signbit(x) != signbit(y))
+ return 0;
+
+ if (isInfinity(x) && isInfinity(y))
+ return 1;
+ if (isInfinity(x) || isInfinity(y))
+ return 0;
+
+ if (isNaN(x) && isNaN(y))
+ return 1;
+ if (isNaN(x) || isNaN(y))
+ return 0;
+
+ char[30] bufx;
+ char[30] bufy;
+ assert(ndigits < bufx.length);
+
+ int ix;
+ int iy;
+ version (CRuntime_Microsoft)
+ alias real_t = double;
+ else
+ 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);
+ assert(ix < bufy.length && ix > 0);
+
+ return bufx[0 .. ix] == bufy[0 .. iy];
+ }
+}
+
+
+
+package:
+// The following IEEE 'real' formats are currently supported.
+version (LittleEndian)
+{
+ static assert(real.mant_dig == 53 || real.mant_dig == 64
+ || real.mant_dig == 113,
+ "Only 64-bit, 80-bit, and 128-bit reals"~
+ " are supported for LittleEndian CPUs");
+}
+else
+{
+ static assert(real.mant_dig == 53 || real.mant_dig == 106
+ || real.mant_dig == 113,
+ "Only 64-bit and 128-bit reals are supported for BigEndian CPUs."~
+ " double-double reals have partial support");
+}
+
+// Underlying format exposed through floatTraits
+enum RealFormat
+{
+ ieeeHalf,
+ ieeeSingle,
+ ieeeDouble,
+ ieeeExtended, // x87 80-bit real
+ ieeeExtended53, // x87 real rounded to precision of double.
+ ibmExtended, // IBM 128-bit extended
+ ieeeQuadruple,
+}
+
+// Constants used for extracting the components of the representation.
+// They supplement the built-in floating point properties.
+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
+ // EXPBIAS is the exponent bias - 1 (exp == EXPBIAS yields ×2^-1).
+ // 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
+ enum T RECIP_EPSILON = (1/T.epsilon);
+ static if (T.mant_dig == 24)
+ {
+ // 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;
+ enum realFormat = RealFormat.ieeeSingle;
+ version (LittleEndian)
+ {
+ enum EXPPOS_SHORT = 1;
+ enum SIGNPOS_BYTE = 3;
+ }
+ else
+ {
+ enum EXPPOS_SHORT = 0;
+ enum SIGNPOS_BYTE = 0;
+ }
+ }
+ else static if (T.mant_dig == 53)
+ {
+ static if (T.sizeof == 8)
+ {
+ // 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
+ enum realFormat = RealFormat.ieeeDouble;
+ version (LittleEndian)
+ {
+ enum EXPPOS_SHORT = 3;
+ enum SIGNPOS_BYTE = 7;
+ }
+ else
+ {
+ enum EXPPOS_SHORT = 0;
+ enum SIGNPOS_BYTE = 0;
+ }
+ }
+ else static if (T.sizeof == 12)
+ {
+ // Intel extended real80 rounded to double
+ enum ushort EXPMASK = 0x7FFF;
+ enum ushort EXPSHIFT = 0;
+ enum ushort EXPBIAS = 0x3FFE;
+ enum realFormat = RealFormat.ieeeExtended53;
+ version (LittleEndian)
+ {
+ enum EXPPOS_SHORT = 4;
+ enum SIGNPOS_BYTE = 9;
+ }
+ else
+ {
+ enum EXPPOS_SHORT = 0;
+ enum SIGNPOS_BYTE = 0;
+ }
+ }
+ else
+ static assert(false, "No traits support for " ~ T.stringof);
+ }
+ else static if (T.mant_dig == 64)
+ {
+ // Intel extended real80
+ enum ushort EXPMASK = 0x7FFF;
+ enum ushort EXPSHIFT = 0;
+ enum ushort EXPBIAS = 0x3FFE;
+ enum realFormat = RealFormat.ieeeExtended;
+ version (LittleEndian)
+ {
+ enum EXPPOS_SHORT = 4;
+ enum SIGNPOS_BYTE = 9;
+ }
+ else
+ {
+ enum EXPPOS_SHORT = 0;
+ enum SIGNPOS_BYTE = 0;
+ }
+ }
+ else static if (T.mant_dig == 113)
+ {
+ // Quadruple precision float
+ enum ushort EXPMASK = 0x7FFF;
+ enum ushort EXPSHIFT = 0;
+ enum ushort EXPBIAS = 0x3FFE;
+ enum realFormat = RealFormat.ieeeQuadruple;
+ version (LittleEndian)
+ {
+ enum EXPPOS_SHORT = 7;
+ enum SIGNPOS_BYTE = 15;
+ }
+ else
+ {
+ enum EXPPOS_SHORT = 0;
+ enum SIGNPOS_BYTE = 0;
+ }
+ }
+ else static if (T.mant_dig == 106)
+ {
+ // IBM Extended doubledouble
+ enum ushort EXPMASK = 0x7FF0;
+ enum ushort EXPSHIFT = 4;
+ enum realFormat = RealFormat.ibmExtended;
+ // the exponent byte is not unique
+ version (LittleEndian)
+ {
+ enum EXPPOS_SHORT = 7; // [3] is also an exp short
+ enum SIGNPOS_BYTE = 15;
+ }
+ else
+ {
+ enum EXPPOS_SHORT = 0; // [4] is also an exp short
+ enum SIGNPOS_BYTE = 0;
+ }
+ }
+ else
+ static assert(false, "No traits support for " ~ T.stringof);
+}
+
+// These apply to all floating-point types
+version (LittleEndian)
+{
+ enum MANTISSA_LSB = 0;
+ enum MANTISSA_MSB = 1;
+}
+else
+{
+ enum MANTISSA_LSB = 1;
+ enum MANTISSA_MSB = 0;
+}
+
+// Common code for math implementations.
+
+// Helper for floor/ceil
+T floorImpl(T)(const T x) @trusted pure nothrow @nogc
+{
+ alias F = floatTraits!(T);
+ // Take care not to trigger library calls from the compiler,
+ // while ensuring that we don't get defeated by some optimizers.
+ union floatBits
+ {
+ T rv;
+ ushort[T.sizeof/2] vu;
+ }
+ floatBits y = void;
+ y.rv = x;
+
+ // Find the exponent (power of 2)
+ static if (F.realFormat == RealFormat.ieeeSingle)
+ {
+ int exp = ((y.vu[F.EXPPOS_SHORT] >> 7) & 0xff) - 0x7f;
+
+ version (LittleEndian)
+ int pos = 0;
+ else
+ int pos = 3;
+ }
+ else static if (F.realFormat == RealFormat.ieeeDouble)
+ {
+ int exp = ((y.vu[F.EXPPOS_SHORT] >> 4) & 0x7ff) - 0x3ff;
+
+ version (LittleEndian)
+ int pos = 0;
+ else
+ int pos = 3;
+ }
+ else static if (F.realFormat == RealFormat.ieeeExtended)
+ {
+ int exp = (y.vu[F.EXPPOS_SHORT] & 0x7fff) - 0x3fff;
+
+ version (LittleEndian)
+ int pos = 0;
+ else
+ int pos = 4;
+ }
+ else static if (F.realFormat == RealFormat.ieeeQuadruple)
+ {
+ int exp = (y.vu[F.EXPPOS_SHORT] & 0x7fff) - 0x3fff;
+
+ version (LittleEndian)
+ int pos = 0;
+ else
+ int pos = 7;
+ }
+ else
+ static assert(false, "Not implemented for this architecture");
+
+ if (exp < 0)
+ {
+ if (x < 0.0)
+ return -1.0;
+ else
+ return 0.0;
+ }
+
+ exp = (T.mant_dig - 1) - exp;
+
+ // Zero 16 bits at a time.
+ while (exp >= 16)
+ {
+ version (LittleEndian)
+ y.vu[pos++] = 0;
+ else
+ y.vu[pos--] = 0;
+ exp -= 16;
+ }
+
+ // Clear the remaining bits.
+ if (exp > 0)
+ y.vu[pos] &= 0xffff ^ ((1 << exp) - 1);
+
+ if ((x < 0.0) && (x != y.rv))
+ y.rv -= 1.0;
+
+ return y.rv;
+}
+
+public:
+
+// Values obtained from Wolfram Alpha. 116 bits ought to be enough for anybody.
+// Wolfram Alpha LLC. 2011. Wolfram|Alpha. http://www.wolframalpha.com/input/?i=e+in+base+16 (access July 6, 2011).
+enum real E = 0x1.5bf0a8b1457695355fb8ac404e7a8p+1L; /** e = 2.718281... */
+enum real LOG2T = 0x1.a934f0979a3715fc9257edfe9b5fbp+1L; /** $(SUB log, 2)10 = 3.321928... */
+enum real LOG2E = 0x1.71547652b82fe1777d0ffda0d23a8p+0L; /** $(SUB log, 2)e = 1.442695... */
+enum real LOG2 = 0x1.34413509f79fef311f12b35816f92p-2L; /** $(SUB log, 10)2 = 0.301029... */
+enum real LOG10E = 0x1.bcb7b1526e50e32a6ab7555f5a67cp-2L; /** $(SUB log, 10)e = 0.434294... */
+enum real LN2 = 0x1.62e42fefa39ef35793c7673007e5fp-1L; /** ln 2 = 0.693147... */
+enum real LN10 = 0x1.26bb1bbb5551582dd4adac5705a61p+1L; /** ln 10 = 2.302585... */
+enum real PI = 0x1.921fb54442d18469898cc51701b84p+1L; /** $(_PI) = 3.141592... */
+enum real PI_2 = PI/2; /** $(PI) / 2 = 1.570796... */
+enum real PI_4 = PI/4; /** $(PI) / 4 = 0.785398... */
+enum real M_1_PI = 0x1.45f306dc9c882a53f84eafa3ea69cp-2L; /** 1 / $(PI) = 0.318309... */
+enum real M_2_PI = 2*M_1_PI; /** 2 / $(PI) = 0.636619... */
+enum real M_2_SQRTPI = 0x1.20dd750429b6d11ae3a914fed7fd8p+0L; /** 2 / $(SQRT)$(PI) = 1.128379... */
+enum real SQRT2 = 0x1.6a09e667f3bcc908b2fb1366ea958p+0L; /** $(SQRT)2 = 1.414213... */
+enum real SQRT1_2 = SQRT2/2; /** $(SQRT)$(HALF) = 0.707106... */
+// Note: Make sure the magic numbers in compiler backend for x87 match these.
+
+
+/***********************************
+ * Calculates the absolute value of a number
+ *
+ * Params:
+ * Num = (template parameter) type of number
+ * x = real number value
+ * z = complex number value
+ * y = imaginary number value
+ *
+ * Returns:
+ * The absolute value of the number. If floating-point or integral,
+ * the return type will be the same as the input; if complex or
+ * imaginary, the returned value will be the corresponding floating
+ * point type.
+ *
+ * For complex numbers, abs(z) = sqrt( $(POWER z.re, 2) + $(POWER z.im, 2) )
+ * = 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*))))
+{
+ static if (isFloatingPoint!(Num))
+ return fabs(x);
+ else
+ 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*)))
+{
+ 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*)))
+{
+ return fabs(y.im);
+}
+
+/// ditto
+@safe pure nothrow @nogc unittest
+{
+ assert(isIdentical(abs(-0.0L), 0.0L));
+ assert(isNaN(abs(real.nan)));
+ assert(abs(-real.infinity) == real.infinity);
+ assert(abs(-3.2Li) == 3.2L);
+ assert(abs(71.6Li) == 71.6L);
+ assert(abs(-56) == 56);
+ assert(abs(2321312L) == 2321312L);
+ assert(abs(-1L+1i) == sqrt(2.0L));
+}
+
+@safe pure nothrow @nogc unittest
+{
+ import std.meta : AliasSeq;
+ foreach (T; AliasSeq!(float, double, real))
+ {
+ T f = 3;
+ assert(abs(f) == f);
+ assert(abs(-f) == f);
+ }
+ foreach (T; AliasSeq!(cfloat, cdouble, creal))
+ {
+ T f = -12+3i;
+ assert(abs(f) == hypot(f.re, f.im));
+ assert(abs(-f) == hypot(f.re, f.im));
+ }
+}
+
+/***********************************
+ * Complex conjugate
+ *
+ * conj(x + iy) = x - iy
+ *
+ * Note that z * conj(z) = $(POWER z.re, 2) - $(POWER z.im, 2)
+ * 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*)))
+{
+ //FIXME
+ //Issue 14206
+ 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*)))
+{
+ return -y;
+}
+
+///
+@safe pure nothrow @nogc unittest
+{
+ creal c = 7 + 3Li;
+ assert(conj(c) == 7-3Li);
+ ireal z = -3.2Li;
+ assert(conj(z) == -z);
+}
+//Issue 14206
+@safe pure nothrow @nogc unittest
+{
+ cdouble c = 7 + 3i;
+ assert(conj(c) == 7-3i);
+ idouble z = -3.2i;
+ assert(conj(z) == -z);
+}
+//Issue 14206
+@safe pure nothrow @nogc unittest
+{
+ cfloat c = 7f + 3fi;
+ assert(conj(c) == 7f-3fi);
+ ifloat z = -3.2fi;
+ assert(conj(z) == -z);
+}
+
+/***********************************
+ * Returns cosine of x. x is in radians.
+ *
+ * $(TABLE_SV
+ * $(TR $(TH x) $(TH cos(x)) $(TH invalid?))
+ * $(TR $(TD $(NAN)) $(TD $(NAN)) $(TD yes) )
+ * $(TR $(TD $(PLUSMN)$(INFIN)) $(TD $(NAN)) $(TD yes) )
+ * )
+ * Bugs:
+ * Results are undefined if |x| >= $(POWER 2,64).
+ */
+
+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 $(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?)
+ * $(TD3 $(NAN) , $(NAN) , yes )
+ * $(TD3 $(PLUSMN)0.0, $(PLUSMN)0.0, no )
+ * $(TD3 $(PLUSMNINF), $(NAN) , yes )
+ * )
+ *
+ * Params:
+ * x = angle in radians (not degrees)
+ * Returns:
+ * sine of x
+ * See_Also:
+ * $(MYREF cos), $(MYREF tan), $(MYREF asin)
+ * Bugs:
+ * Results are undefined if |x| >= $(POWER 2,64).
+ */
+
+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); }
+
+///
+@safe unittest
+{
+ import std.math : sin, PI;
+ import std.stdio : writefln;
+
+ void someFunc()
+ {
+ real x = 30.0;
+ auto result = sin(x * (PI / 180)); // convert degrees to radians
+ writefln("The sine of %s degrees is %s", x, result);
+ }
+}
+
+@safe unittest
+{
+ real function(real) psin = &sin;
+ assert(psin != null);
+}
+
+/***********************************
+ * Returns sine for complex and imaginary arguments.
+ *
+ * sin(z) = sin(z.re)*cosh(z.im) + cos(z.re)*sinh(z.im)i
+ *
+ * If both sin($(THETA)) and cos($(THETA)) are required,
+ * it is most efficient to use expi($(THETA)).
+ */
+creal sin(creal z) @safe pure nothrow @nogc
+{
+ const creal cs = expi(z.re);
+ const creal csh = coshisinh(z.im);
+ return cs.im * csh.re + cs.re * csh.im * 1i;
+}
+
+/** ditto */
+ireal sin(ireal y) @safe pure nothrow @nogc
+{
+ return cosh(y.im)*1i;
+}
+
+///
+@safe pure nothrow @nogc unittest
+{
+ assert(sin(0.0+0.0i) == 0.0);
+ assert(sin(2.0+0.0i) == sin(2.0L) );
+}
+
+/***********************************
+ * cosine, complex and imaginary
+ *
+ * cos(z) = cos(z.re)*cosh(z.im) - sin(z.re)*sinh(z.im)i
+ */
+creal cos(creal z) @safe pure nothrow @nogc
+{
+ const creal cs = expi(z.re);
+ const creal csh = coshisinh(z.im);
+ return cs.re * csh.re - cs.im * csh.im * 1i;
+}
+
+/** ditto */
+real cos(ireal y) @safe pure nothrow @nogc
+{
+ return cosh(y.im);
+}
+
+///
+@safe pure nothrow @nogc unittest
+{
+ assert(cos(0.0+0.0i)==1.0);
+ assert(cos(1.3L+0.0i)==cos(1.3L));
+ assert(cos(5.2Li)== cosh(5.2L));
+}
+
+/****************************************************************************
+ * Returns tangent of x. x is in radians.
+ *
+ * $(TABLE_SV
+ * $(TR $(TH x) $(TH tan(x)) $(TH invalid?))
+ * $(TR $(TD $(NAN)) $(TD $(NAN)) $(TD yes))
+ * $(TR $(TD $(PLUSMN)0.0) $(TD $(PLUSMN)0.0) $(TD no))
+ * $(TR $(TD $(PLUSMNINF)) $(TD $(NAN)) $(TD yes))
+ * )
+ */
+
+real tan(real x) @trusted pure nothrow @nogc
+{
+ version (D_InlineAsm_X86)
+ {
+ asm pure nothrow @nogc
+ {
+ fld x[EBP] ; // load theta
+ fxam ; // test for oddball values
+ fstsw AX ;
+ sahf ;
+ jc trigerr ; // x is NAN, infinity, or empty
+ // 387's can handle subnormals
+SC18: fptan ;
+ fstsw AX ;
+ sahf ;
+ jnp Clear1 ; // C2 = 1 (x is out of range)
+
+ // Do argument reduction to bring x into range
+ fldpi ;
+ fxch ;
+SC17: fprem1 ;
+ fstsw AX ;
+ sahf ;
+ jp SC17 ;
+ fstp ST(1) ; // remove pi from stack
+ jmp SC18 ;
+
+trigerr:
+ jnp Lret ; // if theta is NAN, return theta
+ fstp ST(0) ; // dump theta
+ }
+ return real.nan;
+
+Clear1: asm pure nothrow @nogc{
+ fstp ST(0) ; // dump X, which is always 1
+ }
+
+Lret: {}
+ }
+ else version (D_InlineAsm_X86_64)
+ {
+ version (Win64)
+ {
+ asm pure nothrow @nogc
+ {
+ fld real ptr [RCX] ; // load theta
+ }
+ }
+ else
+ {
+ asm pure nothrow @nogc
+ {
+ fld x[RBP] ; // load theta
+ }
+ }
+ asm pure nothrow @nogc
+ {
+ fxam ; // test for oddball values
+ fstsw AX ;
+ test AH,1 ;
+ jnz trigerr ; // x is NAN, infinity, or empty
+ // 387's can handle subnormals
+SC18: fptan ;
+ fstsw AX ;
+ test AH,4 ;
+ jz Clear1 ; // C2 = 1 (x is out of range)
+
+ // Do argument reduction to bring x into range
+ fldpi ;
+ fxch ;
+SC17: fprem1 ;
+ fstsw AX ;
+ test AH,4 ;
+ jnz SC17 ;
+ fstp ST(1) ; // remove pi from stack
+ jmp SC18 ;
+
+trigerr:
+ test AH,4 ;
+ jz Lret ; // if theta is NAN, return theta
+ fstp ST(0) ; // dump theta
+ }
+ return real.nan;
+
+Clear1: asm pure nothrow @nogc{
+ fstp ST(0) ; // dump X, which is always 1
+ }
+
+Lret: {}
+ }
+ else
+ {
+ // Coefficients for tan(x) and PI/4 split into three parts.
+ static if (floatTraits!real.realFormat == RealFormat.ieeeQuadruple)
+ {
+ static immutable real[6] P = [
+ 2.883414728874239697964612246732416606301E10L,
+ -2.307030822693734879744223131873392503321E9L,
+ 5.160188250214037865511600561074819366815E7L,
+ -4.249691853501233575668486667664718192660E5L,
+ 1.272297782199996882828849455156962260810E3L,
+ -9.889929415807650724957118893791829849557E-1L
+ ];
+ static immutable real[7] Q = [
+ 8.650244186622719093893836740197250197602E10L
+ -4.152206921457208101480801635640958361612E10L,
+ 2.758476078803232151774723646710890525496E9L,
+ -5.733709132766856723608447733926138506824E7L,
+ 4.529422062441341616231663543669583527923E5L,
+ -1.317243702830553658702531997959756728291E3L,
+ 1.0
+ ];
+
+ enum real P1 =
+ 7.853981633974483067550664827649598009884357452392578125E-1L;
+ enum real P2 =
+ 2.8605943630549158983813312792950660807511260829685741796657E-18L;
+ enum real P3 =
+ 2.1679525325309452561992610065108379921905808E-35L;
+ }
+ else
+ {
+ static immutable real[3] P = [
+ -1.7956525197648487798769E7L,
+ 1.1535166483858741613983E6L,
+ -1.3093693918138377764608E4L,
+ ];
+ static immutable real[5] Q = [
+ -5.3869575592945462988123E7L,
+ 2.5008380182335791583922E7L,
+ -1.3208923444021096744731E6L,
+ 1.3681296347069295467845E4L,
+ 1.0000000000000000000000E0L,
+ ];
+
+ enum real P1 = 7.853981554508209228515625E-1L;
+ enum real P2 = 7.946627356147928367136046290398E-9L;
+ enum real P3 = 3.061616997868382943065164830688E-17L;
+ }
+
+ // Special cases.
+ if (x == 0.0 || isNaN(x))
+ return x;
+ if (isInfinity(x))
+ return real.nan;
+
+ // Make argument positive but save the sign.
+ bool sign = false;
+ if (signbit(x))
+ {
+ sign = true;
+ x = -x;
+ }
+
+ // Compute x mod PI/4.
+ real y = floor(x / PI_4);
+ // Strip high bits of integer part.
+ real z = ldexp(y, -4);
+ // Compute y - 16 * (y / 16).
+ z = y - ldexp(floor(z), 4);
+
+ // Integer and fraction part modulo one octant.
+ int j = cast(int)(z);
+
+ // Map zeros and singularities to origin.
+ if (j & 1)
+ {
+ j += 1;
+ y += 1.0;
+ }
+
+ z = ((x - y * P1) - y * P2) - y * P3;
+ const real zz = z * z;
+
+ if (zz > 1.0e-20L)
+ y = z + z * (zz * poly(zz, P) / poly(zz, Q));
+ else
+ y = z;
+
+ if (j & 2)
+ y = -1.0 / y;
+
+ return (sign) ? -y : y;
+ }
+}
+
+@safe nothrow @nogc unittest
+{
+ static real[2][] vals = // angle,tan
+ [
+ [ 0, 0],
+ [ .5, .5463024898],
+ [ 1, 1.557407725],
+ [ 1.5, 14.10141995],
+ [ 2, -2.185039863],
+ [ 2.5,-.7470222972],
+ [ 3, -.1425465431],
+ [ 3.5, .3745856402],
+ [ 4, 1.157821282],
+ [ 4.5, 4.637332055],
+ [ 5, -3.380515006],
+ [ 5.5,-.9955840522],
+ [ 6, -.2910061914],
+ [ 6.5, .2202772003],
+ [ 10, .6483608275],
+
+ // special angles
+ [ PI_4, 1],
+ //[ PI_2, real.infinity], // PI_2 is not _exactly_ pi/2.
+ [ 3*PI_4, -1],
+ [ PI, 0],
+ [ 5*PI_4, 1],
+ //[ 3*PI_2, -real.infinity],
+ [ 7*PI_4, -1],
+ [ 2*PI, 0],
+ ];
+ int i;
+
+ for (i = 0; i < vals.length; i++)
+ {
+ real x = vals[i][0];
+ real r = vals[i][1];
+ real t = tan(x);
+
+ //printf("tan(%Lg) = %Lg, should be %Lg\n", x, t, r);
+ if (!isIdentical(r, t)) assert(fabs(r-t) <= .0000001);
+
+ x = -x;
+ 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);
+ }
+ // overflow
+ assert(isNaN(tan(real.infinity)));
+ assert(isNaN(tan(-real.infinity)));
+ // NaN propagation
+ assert(isIdentical( tan(NaN(0x0123L)), NaN(0x0123L) ));
+}
+
+@system unittest
+{
+ assert(equalsDigit(tan(PI / 3), std.math.sqrt(3.0), useDigits));
+}
+
+/***************
+ * Calculates the arc cosine of x,
+ * returning a value ranging from 0 to $(PI).
+ *
+ * $(TABLE_SV
+ * $(TR $(TH x) $(TH acos(x)) $(TH invalid?))
+ * $(TR $(TD $(GT)1.0) $(TD $(NAN)) $(TD yes))
+ * $(TR $(TD $(LT)-1.0) $(TD $(NAN)) $(TD yes))
+ * $(TR $(TD $(NAN)) $(TD $(NAN)) $(TD yes))
+ * )
+ */
+real acos(real x) @safe pure nothrow @nogc
+{
+ return atan2(sqrt(1-x*x), x);
+}
+
+/// ditto
+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); }
+
+@system unittest
+{
+ assert(equalsDigit(acos(0.5), std.math.PI / 3, useDigits));
+}
+
+/***************
+ * Calculates the arc sine of x,
+ * returning a value ranging from -$(PI)/2 to $(PI)/2.
+ *
+ * $(TABLE_SV
+ * $(TR $(TH x) $(TH asin(x)) $(TH invalid?))
+ * $(TR $(TD $(PLUSMN)0.0) $(TD $(PLUSMN)0.0) $(TD no))
+ * $(TR $(TD $(GT)1.0) $(TD $(NAN)) $(TD yes))
+ * $(TR $(TD $(LT)-1.0) $(TD $(NAN)) $(TD yes))
+ * )
+ */
+real asin(real x) @safe pure nothrow @nogc
+{
+ return atan2(x, sqrt(1-x*x));
+}
+
+/// ditto
+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); }
+
+@system unittest
+{
+ assert(equalsDigit(asin(0.5), PI / 6, useDigits));
+}
+
+/***************
+ * Calculates the arc tangent of x,
+ * returning a value ranging from -$(PI)/2 to $(PI)/2.
+ *
+ * $(TABLE_SV
+ * $(TR $(TH x) $(TH atan(x)) $(TH invalid?))
+ * $(TR $(TD $(PLUSMN)0.0) $(TD $(PLUSMN)0.0) $(TD no))
+ * $(TR $(TD $(PLUSMN)$(INFIN)) $(TD $(NAN)) $(TD yes))
+ * )
+ */
+real atan(real x) @safe pure nothrow @nogc
+{
+ version (InlineAsm_X86_Any)
+ {
+ return atan2(x, 1.0L);
+ }
+ else
+ {
+ // Coefficients for atan(x)
+ static if (floatTraits!real.realFormat == RealFormat.ieeeQuadruple)
+ {
+ static immutable real[9] P = [
+ -6.880597774405940432145577545328795037141E2L,
+ -2.514829758941713674909996882101723647996E3L,
+ -3.696264445691821235400930243493001671932E3L,
+ -2.792272753241044941703278827346430350236E3L,
+ -1.148164399808514330375280133523543970854E3L,
+ -2.497759878476618348858065206895055957104E2L,
+ -2.548067867495502632615671450650071218995E1L,
+ -8.768423468036849091777415076702113400070E-1L,
+ -6.635810778635296712545011270011752799963E-4L
+ ];
+ static immutable real[9] Q = [
+ 2.064179332321782129643673263598686441900E3L,
+ 8.782996876218210302516194604424986107121E3L,
+ 1.547394317752562611786521896296215170819E4L,
+ 1.458510242529987155225086911411015961174E4L,
+ 7.928572347062145288093560392463784743935E3L,
+ 2.494680540950601626662048893678584497900E3L,
+ 4.308348370818927353321556740027020068897E2L,
+ 3.566239794444800849656497338030115886153E1L,
+ 1.0
+ ];
+ }
+ else
+ {
+ static immutable real[5] P = [
+ -5.0894116899623603312185E1L,
+ -9.9988763777265819915721E1L,
+ -6.3976888655834347413154E1L,
+ -1.4683508633175792446076E1L,
+ -8.6863818178092187535440E-1L,
+ ];
+ static immutable real[6] Q = [
+ 1.5268235069887081006606E2L,
+ 3.9157570175111990631099E2L,
+ 3.6144079386152023162701E2L,
+ 1.4399096122250781605352E2L,
+ 2.2981886733594175366172E1L,
+ 1.0000000000000000000000E0L,
+ ];
+ }
+
+ // tan(PI/8)
+ enum real TAN_PI_8 = 0.414213562373095048801688724209698078569672L;
+ // tan(3 * PI/8)
+ enum real TAN3_PI_8 = 2.414213562373095048801688724209698078569672L;
+
+ // Special cases.
+ if (x == 0.0)
+ return x;
+ if (isInfinity(x))
+ return copysign(PI_2, x);
+
+ // Make argument positive but save the sign.
+ bool sign = false;
+ if (signbit(x))
+ {
+ sign = true;
+ x = -x;
+ }
+
+ // Range reduction.
+ real y;
+ if (x > TAN3_PI_8)
+ {
+ y = PI_2;
+ x = -(1.0 / x);
+ }
+ else if (x > TAN_PI_8)
+ {
+ y = PI_4;
+ x = (x - 1.0)/(x + 1.0);
+ }
+ else
+ y = 0.0;
+
+ // Rational form in x^^2.
+ const real z = x * x;
+ y = y + (poly(z, P) / poly(z, Q)) * z * x + x;
+
+ return (sign) ? -y : y;
+ }
+}
+
+/// ditto
+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); }
+
+@system unittest
+{
+ assert(equalsDigit(atan(std.math.sqrt(3.0)), PI / 3, useDigits));
+}
+
+/***************
+ * Calculates the arc tangent of y / x,
+ * returning a value ranging from -$(PI) to $(PI).
+ *
+ * $(TABLE_SV
+ * $(TR $(TH y) $(TH x) $(TH atan(y, x)))
+ * $(TR $(TD $(NAN)) $(TD anything) $(TD $(NAN)) )
+ * $(TR $(TD anything) $(TD $(NAN)) $(TD $(NAN)) )
+ * $(TR $(TD $(PLUSMN)0.0) $(TD $(GT)0.0) $(TD $(PLUSMN)0.0) )
+ * $(TR $(TD $(PLUSMN)0.0) $(TD +0.0) $(TD $(PLUSMN)0.0) )
+ * $(TR $(TD $(PLUSMN)0.0) $(TD $(LT)0.0) $(TD $(PLUSMN)$(PI)))
+ * $(TR $(TD $(PLUSMN)0.0) $(TD -0.0) $(TD $(PLUSMN)$(PI)))
+ * $(TR $(TD $(GT)0.0) $(TD $(PLUSMN)0.0) $(TD $(PI)/2) )
+ * $(TR $(TD $(LT)0.0) $(TD $(PLUSMN)0.0) $(TD -$(PI)/2) )
+ * $(TR $(TD $(GT)0.0) $(TD $(INFIN)) $(TD $(PLUSMN)0.0) )
+ * $(TR $(TD $(PLUSMN)$(INFIN)) $(TD anything) $(TD $(PLUSMN)$(PI)/2))
+ * $(TR $(TD $(GT)0.0) $(TD -$(INFIN)) $(TD $(PLUSMN)$(PI)) )
+ * $(TR $(TD $(PLUSMN)$(INFIN)) $(TD $(INFIN)) $(TD $(PLUSMN)$(PI)/4))
+ * $(TR $(TD $(PLUSMN)$(INFIN)) $(TD -$(INFIN)) $(TD $(PLUSMN)3$(PI)/4))
+ * )
+ */
+real atan2(real y, real x) @trusted pure nothrow @nogc
+{
+ version (InlineAsm_X86_Any)
+ {
+ version (Win64)
+ {
+ asm pure nothrow @nogc {
+ naked;
+ fld real ptr [RDX]; // y
+ fld real ptr [RCX]; // x
+ fpatan;
+ ret;
+ }
+ }
+ else
+ {
+ asm pure nothrow @nogc {
+ fld y;
+ fld x;
+ fpatan;
+ }
+ }
+ }
+ else
+ {
+ // Special cases.
+ if (isNaN(x) || isNaN(y))
+ return real.nan;
+ if (y == 0.0)
+ {
+ if (x >= 0 && !signbit(x))
+ return copysign(0, y);
+ else
+ return copysign(PI, y);
+ }
+ if (x == 0.0)
+ return copysign(PI_2, y);
+ if (isInfinity(x))
+ {
+ if (signbit(x))
+ {
+ if (isInfinity(y))
+ return copysign(3*PI_4, y);
+ else
+ return copysign(PI, y);
+ }
+ else
+ {
+ if (isInfinity(y))
+ return copysign(PI_4, y);
+ else
+ return copysign(0.0, y);
+ }
+ }
+ if (isInfinity(y))
+ return copysign(PI_2, y);
+
+ // Call atan and determine the quadrant.
+ real z = atan(y / x);
+
+ if (signbit(x))
+ {
+ if (signbit(y))
+ z = z - PI;
+ else
+ z = z + PI;
+ }
+
+ if (z == 0.0)
+ return copysign(z, y);
+
+ return z;
+ }
+}
+
+/// ditto
+double atan2(double y, double x) @safe pure nothrow @nogc
+{
+ 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);
+}
+
+@system unittest
+{
+ assert(equalsDigit(atan2(1.0L, std.math.sqrt(3.0L)), PI / 6, useDigits));
+}
+
+/***********************************
+ * Calculates the hyperbolic cosine of x.
+ *
+ * $(TABLE_SV
+ * $(TR $(TH x) $(TH cosh(x)) $(TH invalid?))
+ * $(TR $(TD $(PLUSMN)$(INFIN)) $(TD $(PLUSMN)0.0) $(TD no) )
+ * )
+ */
+real cosh(real x) @safe pure nothrow @nogc
+{
+ // cosh = (exp(x)+exp(-x))/2.
+ // The naive implementation works correctly.
+ 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); }
+
+/// ditto
+float cosh(float x) @safe pure nothrow @nogc { return cosh(cast(real) x); }
+
+@system unittest
+{
+ assert(equalsDigit(cosh(1.0), (E + 1.0 / E) / 2, useDigits));
+}
+
+/***********************************
+ * Calculates the hyperbolic sine of x.
+ *
+ * $(TABLE_SV
+ * $(TR $(TH x) $(TH sinh(x)) $(TH invalid?))
+ * $(TR $(TD $(PLUSMN)0.0) $(TD $(PLUSMN)0.0) $(TD no))
+ * $(TR $(TD $(PLUSMN)$(INFIN)) $(TD $(PLUSMN)$(INFIN)) $(TD no))
+ * )
+ */
+real sinh(real x) @safe pure nothrow @nogc
+{
+ // sinh(x) = (exp(x)-exp(-x))/2;
+ // Very large arguments could cause an overflow, but
+ // the maximum value of x for which exp(x) + exp(-x)) != exp(x)
+ // is x = 0.5 * (real.mant_dig) * LN2. // = 22.1807 for real80.
+ if (fabs(x) > real.mant_dig * LN2)
+ {
+ return copysign(0.5 * exp(fabs(x)), 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); }
+
+/// ditto
+float sinh(float x) @safe pure nothrow @nogc { return sinh(cast(real) x); }
+
+@system unittest
+{
+ assert(equalsDigit(sinh(1.0), (E - 1.0 / E) / 2, useDigits));
+}
+
+/***********************************
+ * Calculates the hyperbolic tangent of x.
+ *
+ * $(TABLE_SV
+ * $(TR $(TH x) $(TH tanh(x)) $(TH invalid?))
+ * $(TR $(TD $(PLUSMN)0.0) $(TD $(PLUSMN)0.0) $(TD no) )
+ * $(TR $(TD $(PLUSMN)$(INFIN)) $(TD $(PLUSMN)1.0) $(TD no))
+ * )
+ */
+real tanh(real x) @safe pure nothrow @nogc
+{
+ // tanh(x) = (exp(x) - exp(-x))/(exp(x)+exp(-x))
+ if (fabs(x) > real.mant_dig * LN2)
+ {
+ return copysign(1, 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); }
+
+/// ditto
+float tanh(float x) @safe pure nothrow @nogc { return tanh(cast(real) x); }
+
+@system unittest
+{
+ assert(equalsDigit(tanh(1.0), sinh(1.0) / cosh(1.0), 15));
+}
+
+package:
+
+/* Returns cosh(x) + I * sinh(x)
+ * Only one call to exp() is performed.
+ */
+creal coshisinh(real x) @safe pure nothrow @nogc
+{
+ // See comments for cosh, sinh.
+ if (fabs(x) > real.mant_dig * LN2)
+ {
+ const real y = exp(fabs(x));
+ return y * 0.5 + 0.5i * copysign(y, x);
+ }
+ else
+ {
+ const real y = expm1(x);
+ return (y + 1.0 + 1.0/(y + 1.0)) * 0.5 + 0.5i * y / (y+1) * (y+2);
+ }
+}
+
+@safe pure nothrow @nogc unittest
+{
+ creal c = coshisinh(3.0L);
+ assert(c.re == cosh(3.0L));
+ assert(c.im == sinh(3.0L));
+}
+
+public:
+
+/***********************************
+ * Calculates the inverse hyperbolic cosine of x.
+ *
+ * Mathematically, acosh(x) = log(x + sqrt( x*x - 1))
+ *
+ * $(TABLE_DOMRG
+ * $(DOMAIN 1..$(INFIN)),
+ * $(RANGE 0..$(INFIN))
+ * )
+ *
+ * $(TABLE_SV
+ * $(SVH x, acosh(x) )
+ * $(SV $(NAN), $(NAN) )
+ * $(SV $(LT)1, $(NAN) )
+ * $(SV 1, 0 )
+ * $(SV +$(INFIN),+$(INFIN))
+ * )
+ */
+real acosh(real x) @safe pure nothrow @nogc
+{
+ if (x > 1/real.epsilon)
+ return LN2 + log(x);
+ else
+ return log(x + sqrt(x*x - 1));
+}
+
+/// ditto
+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); }
+
+
+@system unittest
+{
+ assert(isNaN(acosh(0.9)));
+ assert(isNaN(acosh(real.nan)));
+ assert(acosh(1.0)==0.0);
+ assert(acosh(real.infinity) == real.infinity);
+ assert(isNaN(acosh(0.5)));
+ assert(equalsDigit(acosh(cosh(3.0)), 3, useDigits));
+}
+
+/***********************************
+ * Calculates the inverse hyperbolic sine of x.
+ *
+ * Mathematically,
+ * ---------------
+ * asinh(x) = log( x + sqrt( x*x + 1 )) // if x >= +0
+ * asinh(x) = -log(-x + sqrt( x*x + 1 )) // if x <= -0
+ * -------------
+ *
+ * $(TABLE_SV
+ * $(SVH x, asinh(x) )
+ * $(SV $(NAN), $(NAN) )
+ * $(SV $(PLUSMN)0, $(PLUSMN)0 )
+ * $(SV $(PLUSMN)$(INFIN),$(PLUSMN)$(INFIN))
+ * )
+ */
+real asinh(real x) @safe pure nothrow @nogc
+{
+ return (fabs(x) > 1 / real.epsilon)
+ // beyond this point, x*x + 1 == x*x
+ ? copysign(LN2 + log(fabs(x)), x)
+ // sqrt(x*x + 1) == 1 + x * x / ( 1 + sqrt(x*x + 1) )
+ : copysign(log1p(fabs(x) + x*x / (1 + sqrt(x*x + 1)) ), x);
+}
+
+/// ditto
+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); }
+
+@system unittest
+{
+ assert(isIdentical(asinh(0.0), 0.0));
+ assert(isIdentical(asinh(-0.0), -0.0));
+ assert(asinh(real.infinity) == real.infinity);
+ assert(asinh(-real.infinity) == -real.infinity);
+ assert(isNaN(asinh(real.nan)));
+ assert(equalsDigit(asinh(sinh(3.0)), 3, useDigits));
+}
+
+/***********************************
+ * Calculates the inverse hyperbolic tangent of x,
+ * returning a value from ranging from -1 to 1.
+ *
+ * Mathematically, atanh(x) = log( (1+x)/(1-x) ) / 2
+ *
+ * $(TABLE_DOMRG
+ * $(DOMAIN -$(INFIN)..$(INFIN)),
+ * $(RANGE -1 .. 1)
+ * )
+ * $(BR)
+ * $(TABLE_SV
+ * $(SVH x, acosh(x) )
+ * $(SV $(NAN), $(NAN) )
+ * $(SV $(PLUSMN)0, $(PLUSMN)0)
+ * $(SV -$(INFIN), -0)
+ * )
+ */
+real atanh(real x) @safe pure nothrow @nogc
+{
+ // log( (1+x)/(1-x) ) == log ( 1 + (2*x)/(1-x) )
+ return 0.5 * log1p( 2 * x / (1 - x) );
+}
+
+/// ditto
+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); }
+
+
+@system unittest
+{
+ assert(isIdentical(atanh(0.0), 0.0));
+ assert(isIdentical(atanh(-0.0),-0.0));
+ assert(isNaN(atanh(real.nan)));
+ assert(isNaN(atanh(-real.infinity)));
+ assert(atanh(0.0) == 0);
+ assert(equalsDigit(atanh(tanh(0.5L)), 0.5, useDigits));
+}
+
+/*****************************************
+ * Returns x rounded to a long value using the current rounding mode.
+ * If the integer value of x is
+ * greater than long.max, the result is
+ * indeterminate.
+ */
+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.
+ * If the integer value of x is
+ * greater than long.max, the result is
+ * indeterminate.
+ */
+extern (C) real rndtonl(real x);
+
+/***************************************
+ * Compute square root of x.
+ *
+ * $(TABLE_SV
+ * $(TR $(TH x) $(TH sqrt(x)) $(TH invalid?))
+ * $(TR $(TD -0.0) $(TD -0.0) $(TD no))
+ * $(TR $(TD $(LT)0.0) $(TD $(NAN)) $(TD yes))
+ * $(TR $(TD +$(INFIN)) $(TD +$(INFIN)) $(TD no))
+ * )
+ */
+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 { pragma(inline, true); return core.math.sqrt(x); }
+
+/// ditto
+real sqrt(real x) @nogc @safe pure nothrow { pragma(inline, true); return core.math.sqrt(x); }
+
+@safe pure nothrow @nogc unittest
+{
+ //ctfe
+ 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
+{
+ creal c;
+ real x,y,w,r;
+
+ if (z == 0)
+ {
+ c = 0 + 0i;
+ }
+ else
+ {
+ const real z_re = z.re;
+ const real z_im = z.im;
+
+ x = fabs(z_re);
+ y = fabs(z_im);
+ if (x >= y)
+ {
+ r = y / x;
+ w = sqrt(x) * sqrt(0.5 * (1 + sqrt(1 + r * r)));
+ }
+ else
+ {
+ r = x / y;
+ w = sqrt(y) * sqrt(0.5 * (r + sqrt(1 + r * r)));
+ }
+
+ if (z_re >= 0)
+ {
+ c = w + (z_im / (w + w)) * 1.0i;
+ }
+ else
+ {
+ if (z_im < 0)
+ w = -w;
+ c = z_im / (w + w) + w * 1.0i;
+ }
+ }
+ return c;
+}
+
+/**
+ * Calculates e$(SUPERSCRIPT x).
+ *
+ * $(TABLE_SV
+ * $(TR $(TH x) $(TH e$(SUPERSCRIPT x)) )
+ * $(TR $(TD +$(INFIN)) $(TD +$(INFIN)) )
+ * $(TR $(TD -$(INFIN)) $(TD +0.0) )
+ * $(TR $(TD $(NAN)) $(TD $(NAN)) )
+ * )
+ */
+real exp(real x) @trusted pure nothrow @nogc
+{
+ version (D_InlineAsm_X86)
+ {
+ // e^^x = 2^^(LOG2E*x)
+ // (This is valid because the overflow & underflow limits for exp
+ // and exp2 are so similar).
+ return exp2(LOG2E*x);
+ }
+ else version (D_InlineAsm_X86_64)
+ {
+ // e^^x = 2^^(LOG2E*x)
+ // (This is valid because the overflow & underflow limits for exp
+ // and exp2 are so similar).
+ return exp2(LOG2E*x);
+ }
+ else
+ {
+ 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.93145751953125E-1;
+ enum real C2 = 1.42860682030941723212E-6;
+
+ // 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,
+ ];
+
+ // 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 if (F.realFormat == RealFormat.ieeeQuadruple)
+ {
+ // Coefficients for exp(x) - 1
+ static immutable real[5] P = [
+ 9.999999999999999999999999999999999998502E-1L,
+ 3.508710990737834361215404761139478627390E-2L,
+ 2.708775201978218837374512615596512792224E-4L,
+ 6.141506007208645008909088812338454698548E-7L,
+ 3.279723985560247033712687707263393506266E-10L
+ ];
+ static immutable real[6] Q = [
+ 2.000000000000000000000000000000000000150E0,
+ 2.368408864814233538909747618894558968880E-1L,
+ 3.611828913847589925056132680618007270344E-3L,
+ 1.504792651814944826817779302637284053660E-5L,
+ 1.771372078166251484503904874657985291164E-8L,
+ 2.980756652081995192255342779918052538681E-12L
+ ];
+
+ // C1 + C2 = LN2.
+ enum real C1 = 6.93145751953125E-1L;
+ enum real C2 = 1.428606820309417232121458176568075500134E-6L;
+
+ // Overflow and Underflow limits.
+ enum real OF = 1.135583025911358400418251384584930671458833e4L;
+ enum real UF = -1.143276959615573793352782661133116431383730e4L;
+ }
+ 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)
+ {
+ if (__ctfe)
+ return real.infinity;
+ else
+ return real.max * copysign(real.max, real.infinity);
+ }
+ if (x < UF)
+ {
+ 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);
+ 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))
+ const real xx = x * x;
+ const real px = x * poly(xx, P);
+ x = px / (poly(xx, Q) - px);
+ x = 1.0 + ldexp(x, 1);
+
+ // Scale by power of 2.
+ x = ldexp(x, n);
+
+ return x;
+ }
+}
+
+/// ditto
+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); }
+
+@system unittest
+{
+ assert(equalsDigit(exp(3.0L), E * E * E, useDigits));
+}
+
+/**
+ * Calculates the value of the natural logarithm base (e)
+ * raised to the power of x, minus 1.
+ *
+ * For very small x, expm1(x) is more accurate
+ * than exp(x)-1.
+ *
+ * $(TABLE_SV
+ * $(TR $(TH x) $(TH e$(SUPERSCRIPT x)-1) )
+ * $(TR $(TD $(PLUSMN)0.0) $(TD $(PLUSMN)0.0) )
+ * $(TR $(TD +$(INFIN)) $(TD +$(INFIN)) )
+ * $(TR $(TD -$(INFIN)) $(TD -1.0) )
+ * $(TR $(TD $(NAN)) $(TD $(NAN)) )
+ * )
+ */
+real expm1(real x) @trusted pure nothrow @nogc
+{
+ version (D_InlineAsm_X86)
+ {
+ enum PARAMSIZE = (real.sizeof+3)&(0xFFFF_FFFC); // always a multiple of 4
+ asm pure nothrow @nogc
+ {
+ /* expm1() for x87 80-bit reals, IEEE754-2008 conformant.
+ * Author: Don Clugston.
+ *
+ * expm1(x) = 2^^(rndint(y))* 2^^(y-rndint(y)) - 1 where y = LN2*x.
+ * = 2rndy * 2ym1 + 2rndy - 1, where 2rndy = 2^^(rndint(y))
+ * and 2ym1 = (2^^(y-rndint(y))-1).
+ * If 2rndy < 0.5*real.epsilon, result is -1.
+ * Implementation is otherwise the same as for exp2()
+ */
+ naked;
+ fld real ptr [ESP+4] ; // x
+ mov AX, [ESP+4+8]; // AX = exponent and sign
+ sub ESP, 12+8; // Create scratch space on the stack
+ // [ESP,ESP+2] = scratchint
+ // [ESP+4..+6, +8..+10, +10] = scratchreal
+ // set scratchreal mantissa = 1.0
+ mov dword ptr [ESP+8], 0;
+ mov dword ptr [ESP+8+4], 0x80000000;
+ and AX, 0x7FFF; // drop sign bit
+ cmp AX, 0x401D; // avoid InvalidException in fist
+ jae L_extreme;
+ fldl2e;
+ fmulp ST(1), ST; // y = x*log2(e)
+ fist dword ptr [ESP]; // scratchint = rndint(y)
+ fisub dword ptr [ESP]; // y - rndint(y)
+ // and now set scratchreal exponent
+ mov EAX, [ESP];
+ add EAX, 0x3fff;
+ jle short L_largenegative;
+ cmp EAX,0x8000;
+ jge short L_largepositive;
+ mov [ESP+8+8],AX;
+ f2xm1; // 2ym1 = 2^^(y-rndint(y)) -1
+ fld real ptr [ESP+8] ; // 2rndy = 2^^rndint(y)
+ fmul ST(1), ST; // ST=2rndy, ST(1)=2rndy*2ym1
+ fld1;
+ fsubp ST(1), ST; // ST = 2rndy-1, ST(1) = 2rndy * 2ym1 - 1
+ faddp ST(1), ST; // ST = 2rndy * 2ym1 + 2rndy - 1
+ add ESP,12+8;
+ ret PARAMSIZE;
+
+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
+ jz L_was_nan; // if x is NaN, returns x
+ test AX, 0x0200;
+ jnz L_largenegative;
+L_largepositive:
+ // Set scratchreal = real.max.
+ // squaring it will create infinity, and set overflow flag.
+ mov word ptr [ESP+8+8], 0x7FFE;
+ fstp ST(0);
+ fld real ptr [ESP+8]; // load scratchreal
+ fmul ST(0), ST; // square it, to create havoc!
+L_was_nan:
+ add ESP,12+8;
+ ret PARAMSIZE;
+L_largenegative:
+ fstp ST(0);
+ fld1;
+ fchs; // return -1. Underflow flag is not set.
+ add ESP,12+8;
+ ret PARAMSIZE;
+ }
+ }
+ else version (D_InlineAsm_X86_64)
+ {
+ asm pure nothrow @nogc
+ {
+ naked;
+ }
+ version (Win64)
+ {
+ asm pure nothrow @nogc
+ {
+ fld real ptr [RCX]; // x
+ mov AX,[RCX+8]; // AX = exponent and sign
+ }
+ }
+ else
+ {
+ asm pure nothrow @nogc
+ {
+ fld real ptr [RSP+8]; // x
+ mov AX,[RSP+8+8]; // AX = exponent and sign
+ }
+ }
+ asm pure nothrow @nogc
+ {
+ /* expm1() for x87 80-bit reals, IEEE754-2008 conformant.
+ * Author: Don Clugston.
+ *
+ * expm1(x) = 2^(rndint(y))* 2^(y-rndint(y)) - 1 where y = LN2*x.
+ * = 2rndy * 2ym1 + 2rndy - 1, where 2rndy = 2^(rndint(y))
+ * and 2ym1 = (2^(y-rndint(y))-1).
+ * If 2rndy < 0.5*real.epsilon, result is -1.
+ * Implementation is otherwise the same as for exp2()
+ */
+ sub RSP, 24; // Create scratch space on the stack
+ // [RSP,RSP+2] = scratchint
+ // [RSP+4..+6, +8..+10, +10] = scratchreal
+ // set scratchreal mantissa = 1.0
+ mov dword ptr [RSP+8], 0;
+ mov dword ptr [RSP+8+4], 0x80000000;
+ and AX, 0x7FFF; // drop sign bit
+ cmp AX, 0x401D; // avoid InvalidException in fist
+ jae L_extreme;
+ fldl2e;
+ fmul ; // y = x*log2(e)
+ fist dword ptr [RSP]; // scratchint = rndint(y)
+ fisub dword ptr [RSP]; // y - rndint(y)
+ // and now set scratchreal exponent
+ mov EAX, [RSP];
+ add EAX, 0x3fff;
+ jle short L_largenegative;
+ cmp EAX,0x8000;
+ jge short L_largepositive;
+ mov [RSP+8+8],AX;
+ f2xm1; // 2^(y-rndint(y)) -1
+ fld real ptr [RSP+8] ; // 2^rndint(y)
+ fmul ST(1), ST;
+ fld1;
+ fsubp ST(1), ST;
+ fadd;
+ add RSP,24;
+ ret;
+
+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
+ jz L_was_nan; // if x is NaN, returns x
+ test AX, 0x0200;
+ jnz L_largenegative;
+L_largepositive:
+ // Set scratchreal = real.max.
+ // squaring it will create infinity, and set overflow flag.
+ mov word ptr [RSP+8+8], 0x7FFE;
+ fstp ST(0);
+ fld real ptr [RSP+8]; // load scratchreal
+ fmul ST(0), ST; // square it, to create havoc!
+L_was_nan:
+ add RSP,24;
+ ret;
+
+L_largenegative:
+ fstp ST(0);
+ fld1;
+ fchs; // return -1. Underflow flag is not set.
+ add RSP,24;
+ ret;
+ }
+ }
+ else
+ {
+ // Coefficients for exp(x) - 1 and overflow/underflow limits.
+ static if (floatTraits!real.realFormat == RealFormat.ieeeQuadruple)
+ {
+ static immutable real[8] P = [
+ 2.943520915569954073888921213330863757240E8L,
+ -5.722847283900608941516165725053359168840E7L,
+ 8.944630806357575461578107295909719817253E6L,
+ -7.212432713558031519943281748462837065308E5L,
+ 4.578962475841642634225390068461943438441E4L,
+ -1.716772506388927649032068540558788106762E3L,
+ 4.401308817383362136048032038528753151144E1L,
+ -4.888737542888633647784737721812546636240E-1L
+ ];
+
+ static immutable real[9] Q = [
+ 1.766112549341972444333352727998584753865E9L,
+ -7.848989743695296475743081255027098295771E8L,
+ 1.615869009634292424463780387327037251069E8L,
+ -2.019684072836541751428967854947019415698E7L,
+ 1.682912729190313538934190635536631941751E6L,
+ -9.615511549171441430850103489315371768998E4L,
+ 3.697714952261803935521187272204485251835E3L,
+ -8.802340681794263968892934703309274564037E1L,
+ 1.0
+ ];
+
+ enum real OF = 1.1356523406294143949491931077970764891253E4L;
+ enum real UF = -1.143276959615573793352782661133116431383730e4L;
+ }
+ else
+ {
+ static immutable real[5] P = [
+ -1.586135578666346600772998894928250240826E4L,
+ 2.642771505685952966904660652518429479531E3L,
+ -3.423199068835684263987132888286791620673E2L,
+ 1.800826371455042224581246202420972737840E1L,
+ -5.238523121205561042771939008061958820811E-1L,
+ ];
+ static immutable real[6] Q = [
+ -9.516813471998079611319047060563358064497E4L,
+ 3.964866271411091674556850458227710004570E4L,
+ -7.207678383830091850230366618190187434796E3L,
+ 7.206038318724600171970199625081491823079E2L,
+ -4.002027679107076077238836622982900945173E1L,
+ 1.0
+ ];
+
+ enum real OF = 1.1356523406294143949492E4L;
+ enum real UF = -4.5054566736396445112120088E1L;
+ }
+
+
+ // C1 + C2 = LN2.
+ enum real C1 = 6.9314575195312500000000E-1L;
+ enum real C2 = 1.428606820309417232121458176568075500134E-6L;
+
+ // Special cases. Raises an overflow flag, except in the case
+ // for CTFE, where there are no hardware controls.
+ if (x > OF)
+ {
+ 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);
+ x -= n * C1;
+ x -= n * C2;
+
+ // Rational approximation:
+ // 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);
+ const real xx = x * x;
+ qx = x + (0.5 * xx + xx * px / qx);
+
+ // We have qx = exp(remainder LN2) - 1, so:
+ // exp(x) - 1 = 2^^n (qx + 1) - 1 = 2^^n qx + 2^^n - 1.
+ px = ldexp(1.0, n);
+ x = px * qx + (px - 1.0);
+
+ return x;
+ }
+}
+
+
+
+/**
+ * Calculates 2$(SUPERSCRIPT x).
+ *
+ * $(TABLE_SV
+ * $(TR $(TH x) $(TH exp2(x)) )
+ * $(TR $(TD +$(INFIN)) $(TD +$(INFIN)) )
+ * $(TR $(TD -$(INFIN)) $(TD +0.0) )
+ * $(TR $(TD $(NAN)) $(TD $(NAN)) )
+ * )
+ */
+pragma(inline, true)
+real exp2(real x) @nogc @trusted pure nothrow
+{
+ version (InlineAsm_X86_Any)
+ {
+ if (!__ctfe)
+ return exp2Asm(x);
+ else
+ return exp2Impl(x);
+ }
+ else
+ {
+ return exp2Impl(x);
+ }
+}
+
+version (InlineAsm_X86_Any)
+private real exp2Asm(real x) @nogc @trusted pure nothrow
+{
+ version (D_InlineAsm_X86)
+ {
+ enum PARAMSIZE = (real.sizeof+3)&(0xFFFF_FFFC); // always a multiple of 4
+
+ asm pure nothrow @nogc
+ {
+ /* exp2() for x87 80-bit reals, IEEE754-2008 conformant.
+ * Author: Don Clugston.
+ *
+ * exp2(x) = 2^^(rndint(x))* 2^^(y-rndint(x))
+ * The trick for high performance is to avoid the fscale(28cycles on core2),
+ * frndint(19 cycles), leaving f2xm1(19 cycles) as the only slow instruction.
+ *
+ * We can do frndint by using fist. BUT we can't use it for huge numbers,
+ * because it will set the Invalid Operation flag if overflow or NaN occurs.
+ * Fortunately, whenever this happens the result would be zero or infinity.
+ *
+ * We can perform fscale by directly poking into the exponent. BUT this doesn't
+ * work for the (very rare) cases where the result is subnormal. So we fall back
+ * to the slow method in that case.
+ */
+ naked;
+ fld real ptr [ESP+4] ; // x
+ mov AX, [ESP+4+8]; // AX = exponent and sign
+ sub ESP, 12+8; // Create scratch space on the stack
+ // [ESP,ESP+2] = scratchint
+ // [ESP+4..+6, +8..+10, +10] = scratchreal
+ // set scratchreal mantissa = 1.0
+ mov dword ptr [ESP+8], 0;
+ mov dword ptr [ESP+8+4], 0x80000000;
+ and AX, 0x7FFF; // drop sign bit
+ cmp AX, 0x401D; // avoid InvalidException in fist
+ jae L_extreme;
+ fist dword ptr [ESP]; // scratchint = rndint(x)
+ fisub dword ptr [ESP]; // x - rndint(x)
+ // and now set scratchreal exponent
+ mov EAX, [ESP];
+ add EAX, 0x3fff;
+ jle short L_subnormal;
+ cmp EAX,0x8000;
+ jge short L_overflow;
+ mov [ESP+8+8],AX;
+L_normal:
+ f2xm1;
+ fld1;
+ faddp ST(1), ST; // 2^^(x-rndint(x))
+ fld real ptr [ESP+8] ; // 2^^rndint(x)
+ add ESP,12+8;
+ fmulp ST(1), ST;
+ ret PARAMSIZE;
+
+L_subnormal:
+ // Result will be subnormal.
+ // In this rare case, the simple poking method doesn't work.
+ // The speed doesn't matter, so use the slow fscale method.
+ fild dword ptr [ESP]; // scratchint
+ fld1;
+ fscale;
+ fstp real ptr [ESP+8]; // scratchreal = 2^^scratchint
+ fstp ST(0); // drop scratchint
+ jmp L_normal;
+
+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
+ jz L_was_nan; // if x is NaN, returns x
+ // set scratchreal = real.min_normal
+ // squaring it will return 0, setting underflow flag
+ mov word ptr [ESP+8+8], 1;
+ test AX, 0x0200;
+ jnz L_waslargenegative;
+L_overflow:
+ // Set scratchreal = real.max.
+ // squaring it will create infinity, and set overflow flag.
+ mov word ptr [ESP+8+8], 0x7FFE;
+L_waslargenegative:
+ fstp ST(0);
+ fld real ptr [ESP+8]; // load scratchreal
+ fmul ST(0), ST; // square it, to create havoc!
+L_was_nan:
+ add ESP,12+8;
+ ret PARAMSIZE;
+ }
+ }
+ else version (D_InlineAsm_X86_64)
+ {
+ asm pure nothrow @nogc
+ {
+ naked;
+ }
+ version (Win64)
+ {
+ asm pure nothrow @nogc
+ {
+ fld real ptr [RCX]; // x
+ mov AX,[RCX+8]; // AX = exponent and sign
+ }
+ }
+ else
+ {
+ asm pure nothrow @nogc
+ {
+ fld real ptr [RSP+8]; // x
+ mov AX,[RSP+8+8]; // AX = exponent and sign
+ }
+ }
+ asm pure nothrow @nogc
+ {
+ /* exp2() for x87 80-bit reals, IEEE754-2008 conformant.
+ * Author: Don Clugston.
+ *
+ * exp2(x) = 2^(rndint(x))* 2^(y-rndint(x))
+ * The trick for high performance is to avoid the fscale(28cycles on core2),
+ * frndint(19 cycles), leaving f2xm1(19 cycles) as the only slow instruction.
+ *
+ * We can do frndint by using fist. BUT we can't use it for huge numbers,
+ * because it will set the Invalid Operation flag is overflow or NaN occurs.
+ * Fortunately, whenever this happens the result would be zero or infinity.
+ *
+ * We can perform fscale by directly poking into the exponent. BUT this doesn't
+ * work for the (very rare) cases where the result is subnormal. So we fall back
+ * to the slow method in that case.
+ */
+ sub RSP, 24; // Create scratch space on the stack
+ // [RSP,RSP+2] = scratchint
+ // [RSP+4..+6, +8..+10, +10] = scratchreal
+ // set scratchreal mantissa = 1.0
+ mov dword ptr [RSP+8], 0;
+ mov dword ptr [RSP+8+4], 0x80000000;
+ and AX, 0x7FFF; // drop sign bit
+ cmp AX, 0x401D; // avoid InvalidException in fist
+ jae L_extreme;
+ fist dword ptr [RSP]; // scratchint = rndint(x)
+ fisub dword ptr [RSP]; // x - rndint(x)
+ // and now set scratchreal exponent
+ mov EAX, [RSP];
+ add EAX, 0x3fff;
+ jle short L_subnormal;
+ cmp EAX,0x8000;
+ jge short L_overflow;
+ mov [RSP+8+8],AX;
+L_normal:
+ f2xm1;
+ fld1;
+ fadd; // 2^(x-rndint(x))
+ fld real ptr [RSP+8] ; // 2^rndint(x)
+ add RSP,24;
+ fmulp ST(1), ST;
+ ret;
+
+L_subnormal:
+ // Result will be subnormal.
+ // In this rare case, the simple poking method doesn't work.
+ // The speed doesn't matter, so use the slow fscale method.
+ fild dword ptr [RSP]; // scratchint
+ fld1;
+ fscale;
+ fstp real ptr [RSP+8]; // scratchreal = 2^scratchint
+ fstp ST(0); // drop scratchint
+ jmp L_normal;
+
+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
+ jz L_was_nan; // if x is NaN, returns x
+ // set scratchreal = real.min
+ // squaring it will return 0, setting underflow flag
+ mov word ptr [RSP+8+8], 1;
+ test AX, 0x0200;
+ jnz L_waslargenegative;
+L_overflow:
+ // Set scratchreal = real.max.
+ // squaring it will create infinity, and set overflow flag.
+ mov word ptr [RSP+8+8], 0x7FFE;
+L_waslargenegative:
+ fstp ST(0);
+ fld real ptr [RSP+8]; // load scratchreal
+ fmul ST(0), ST; // square it, to create havoc!
+L_was_nan:
+ add RSP,24;
+ ret;
+ }
+ }
+ else
+ static assert(0);
+}
+
+private real exp2Impl(real x) @nogc @trusted pure nothrow
+{
+ // Coefficients for exp2(x)
+ static if (floatTraits!real.realFormat == RealFormat.ieeeQuadruple)
+ {
+ static immutable real[5] P = [
+ 9.079594442980146270952372234833529694788E12L,
+ 1.530625323728429161131811299626419117557E11L,
+ 5.677513871931844661829755443994214173883E8L,
+ 6.185032670011643762127954396427045467506E5L,
+ 1.587171580015525194694938306936721666031E2L
+ ];
+
+ static immutable real[6] Q = [
+ 2.619817175234089411411070339065679229869E13L,
+ 1.490560994263653042761789432690793026977E12L,
+ 1.092141473886177435056423606755843616331E10L,
+ 2.186249607051644894762167991800811827835E7L,
+ 1.236602014442099053716561665053645270207E4L,
+ 1.0
+ ];
+ }
+ else
+ {
+ static immutable real[3] P = [
+ 2.0803843631901852422887E6L,
+ 3.0286971917562792508623E4L,
+ 6.0614853552242266094567E1L,
+ ];
+ static immutable real[4] Q = [
+ 6.0027204078348487957118E6L,
+ 3.2772515434906797273099E5L,
+ 1.7492876999891839021063E3L,
+ 1.0000000000000000000000E0L,
+ ];
+ }
+
+ // Overflow and Underflow limits.
+ enum real OF = 16_384.0L;
+ enum real UF = -16_382.0L;
+
+ // 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)
+ {
+ if (__ctfe)
+ return real.infinity;
+ else
+ return real.max * copysign(real.max, real.infinity);
+ }
+ if (x < UF)
+ {
+ 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);
+ x -= n;
+
+ // Rational approximation:
+ // exp2(x) = 1.0 + 2x P(x^^2) / (Q(x^^2) - P(x^^2))
+ const real xx = x * x;
+ const real px = x * poly(xx, P);
+ x = px / (poly(xx, Q) - px);
+ x = 1.0 + ldexp(x, 1);
+
+ // Scale by power of 2.
+ x = ldexp(x, n);
+
+ return x;
+}
+
+///
+@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);
+}
+
+@safe unittest
+{
+ version (CRuntime_Microsoft) {} else // aexp2/exp2f/exp2l not implemented
+ {
+ assert( core.stdc.math.exp2f(0.0f) == 1 );
+ assert( core.stdc.math.exp2 (0.0) == 1 );
+ assert( core.stdc.math.exp2l(0.0L) == 1 );
+ }
+}
+
+@system unittest
+{
+ FloatingPointControl ctrl;
+ if (FloatingPointControl.hasExceptionTraps)
+ ctrl.disableExceptions(FloatingPointControl.allExceptions);
+ ctrl.rounding = FloatingPointControl.roundToNearest;
+
+ static if (real.mant_dig == 113)
+ {
+ static immutable real[2][] exptestpoints =
+ [ // x exp(x)
+ [ 1.0L, E ],
+ [ 0.5L, 0x1.a61298e1e069bc972dfefab6df34p+0L ],
+ [ 3.0L, E*E*E ],
+ [ 0x1.6p+13L, 0x1.6e509d45728655cdb4840542acb5p+16250L ], // near overflow
+ [ 0x1.7p+13L, real.infinity ], // close overflow
+ [ 0x1p+80L, real.infinity ], // far overflow
+ [ real.infinity, real.infinity ],
+ [-0x1.18p+13L, 0x1.5e4bf54b4807034ea97fef0059a6p-12927L ], // near underflow
+ [-0x1.625p+13L, 0x1.a6bd68a39d11fec3a250cd97f524p-16358L ], // ditto
+ [-0x1.62dafp+13L, 0x0.cb629e9813b80ed4d639e875be6cp-16382L ], // near underflow - subnormal
+ [-0x1.6549p+13L, 0x0.0000000000000000000000000001p-16382L ], // ditto
+ [-0x1.655p+13L, 0 ], // close underflow
+ [-0x1p+30L, 0 ], // far underflow
+ ];
+ }
+ else 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.06f84920bb2d3p-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 minEqualDecimalDigits = real.dig - 3;
+ real x;
+ IeeeFlags f;
+ foreach (ref pair; exptestpoints)
+ {
+ resetIeeeFlags();
+ x = exp(pair[0]);
+ f = ieeeFlags;
+ assert(equalsDigit(x, pair[1], minEqualDecimalDigits));
+
+ 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!
+ // So it's not realistic to avoid setting it.
+ assert(exp(0.0L) == 1.0);
+
+ // NaN propagation. Doesn't set flags, bcos was already NaN.
+ resetIeeeFlags();
+ x = exp(real.nan);
+ f = ieeeFlags;
+ assert(isIdentical(abs(x), real.nan));
+ assert(f.flags == 0);
+
+ resetIeeeFlags();
+ x = exp(-real.nan);
+ f = ieeeFlags;
+ assert(isIdentical(abs(x), real.nan));
+ assert(f.flags == 0);
+
+ x = exp(NaN(0x123));
+ assert(isIdentical(x, NaN(0x123)));
+
+ // High resolution test (verified against GNU MPFR/Mathematica).
+ assert(exp(0.5L) == 0x1.A612_98E1_E069_BC97_2DFE_FAB6_DF34p+0L);
+}
+
+
+/**
+ * Calculate cos(y) + i sin(y).
+ *
+ * On many CPUs (such as x86), this is a very efficient operation;
+ * almost twice as fast as calculating sin(y) and cos(y) separately,
+ * and is the preferred method when both are required.
+ */
+creal expi(real y) @trusted pure nothrow @nogc
+{
+ version (InlineAsm_X86_Any)
+ {
+ version (Win64)
+ {
+ asm pure nothrow @nogc
+ {
+ naked;
+ fld real ptr [ECX];
+ fsincos;
+ fxch ST(1), ST(0);
+ ret;
+ }
+ }
+ else
+ {
+ asm pure nothrow @nogc
+ {
+ fld y;
+ fsincos;
+ fxch ST(1), ST(0);
+ }
+ }
+ }
+ else
+ {
+ return cos(y) + sin(y)*1i;
+ }
+}
+
+///
+@safe pure nothrow @nogc unittest
+{
+ assert(expi(1.3e5L) == cos(1.3e5L) + sin(1.3e5L) * 1i);
+ assert(expi(0.0L) == 1L + 0.0Li);
+}
+
+/*********************************************************************
+ * Separate floating point value into significand and exponent.
+ *
+ * Returns:
+ * Calculate and return $(I x) and $(I exp) such that
+ * value =$(I x)*2$(SUPERSCRIPT exp) and
+ * .5 $(LT)= |$(I x)| $(LT) 1.0
+ *
+ * $(I x) has same sign as value.
+ *
+ * $(TABLE_SV
+ * $(TR $(TH value) $(TH returns) $(TH exp))
+ * $(TR $(TD $(PLUSMN)0.0) $(TD $(PLUSMN)0.0) $(TD 0))
+ * $(TR $(TD +$(INFIN)) $(TD +$(INFIN)) $(TD int.max))
+ * $(TR $(TD -$(INFIN)) $(TD -$(INFIN)) $(TD int.min))
+ * $(TR $(TD $(PLUSMN)$(NAN)) $(TD $(PLUSMN)$(NAN)) $(TD int.min))
+ * )
+ */
+T frexp(T)(const T value, out int exp) @trusted pure nothrow @nogc
+if (isFloatingPoint!T)
+{
+ Unqual!T vf = value;
+ ushort* vu = cast(ushort*)&vf;
+ static if (is(Unqual!T == float))
+ int* vi = cast(int*)&vf;
+ else
+ long* vl = cast(long*)&vf;
+ int ex;
+ alias F = floatTraits!T;
+
+ ex = 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 (*vl & 0x7FFF_FFFF_FFFF_FFFF) // NaN
+ {
+ *vl |= 0xC000_0000_0000_0000; // convert NaNS to NaNQ
+ exp = int.min;
+ }
+ else if (vu[F.EXPPOS_SHORT] & 0x8000) // negative infinity
+ exp = int.min;
+ else // positive infinity
+ exp = int.max;
+
+ }
+ else
+ {
+ exp = ex - F.EXPBIAS;
+ vu[F.EXPPOS_SHORT] = (0x8000 & vu[F.EXPPOS_SHORT]) | 0x3FFE;
+ }
+ }
+ else if (!*vl)
+ {
+ // vf is +-0.0
+ exp = 0;
+ }
+ else
+ {
+ // subnormal
+
+ vf *= F.RECIP_EPSILON;
+ ex = vu[F.EXPPOS_SHORT] & F.EXPMASK;
+ exp = ex - F.EXPBIAS - T.mant_dig + 1;
+ vu[F.EXPPOS_SHORT] = ((-1 - F.EXPMASK) & vu[F.EXPPOS_SHORT]) | 0x3FFE;
+ }
+ return vf;
+ }
+ else static if (F.realFormat == RealFormat.ieeeQuadruple)
+ {
+ if (ex) // If exponent is non-zero
+ {
+ if (ex == F.EXPMASK)
+ {
+ // infinity or NaN
+ if (vl[MANTISSA_LSB] |
+ (vl[MANTISSA_MSB] & 0x0000_FFFF_FFFF_FFFF)) // NaN
+ {
+ // convert NaNS to NaNQ
+ vl[MANTISSA_MSB] |= 0x0000_8000_0000_0000;
+ exp = int.min;
+ }
+ else if (vu[F.EXPPOS_SHORT] & 0x8000) // negative infinity
+ exp = int.min;
+ else // positive infinity
+ exp = int.max;
+ }
+ else
+ {
+ exp = ex - F.EXPBIAS;
+ vu[F.EXPPOS_SHORT] = F.EXPBIAS | (0x8000 & vu[F.EXPPOS_SHORT]);
+ }
+ }
+ else if ((vl[MANTISSA_LSB] |
+ (vl[MANTISSA_MSB] & 0x0000_FFFF_FFFF_FFFF)) == 0)
+ {
+ // vf is +-0.0
+ exp = 0;
+ }
+ else
+ {
+ // subnormal
+ vf *= F.RECIP_EPSILON;
+ ex = vu[F.EXPPOS_SHORT] & F.EXPMASK;
+ exp = ex - F.EXPBIAS - T.mant_dig + 1;
+ vu[F.EXPPOS_SHORT] = F.EXPBIAS | (0x8000 & vu[F.EXPPOS_SHORT]);
+ }
+ return vf;
+ }
+ else static if (F.realFormat == RealFormat.ieeeDouble)
+ {
+ if (ex) // If exponent is non-zero
+ {
+ if (ex == F.EXPMASK) // infinity or NaN
+ {
+ if (*vl == 0x7FF0_0000_0000_0000) // positive infinity
+ {
+ exp = int.max;
+ }
+ else if (*vl == 0xFFF0_0000_0000_0000) // negative infinity
+ exp = int.min;
+ else
+ { // NaN
+ *vl |= 0x0008_0000_0000_0000; // convert NaNS to NaNQ
+ exp = int.min;
+ }
+ }
+ else
+ {
+ exp = (ex - F.EXPBIAS) >> 4;
+ vu[F.EXPPOS_SHORT] = cast(ushort)((0x800F & vu[F.EXPPOS_SHORT]) | 0x3FE0);
+ }
+ }
+ else if (!(*vl & 0x7FFF_FFFF_FFFF_FFFF))
+ {
+ // vf is +-0.0
+ exp = 0;
+ }
+ else
+ {
+ // subnormal
+ vf *= F.RECIP_EPSILON;
+ ex = vu[F.EXPPOS_SHORT] & F.EXPMASK;
+ exp = ((ex - F.EXPBIAS) >> 4) - T.mant_dig + 1;
+ vu[F.EXPPOS_SHORT] =
+ cast(ushort)(((-1 - F.EXPMASK) & vu[F.EXPPOS_SHORT]) | 0x3FE0);
+ }
+ return vf;
+ }
+ else static if (F.realFormat == RealFormat.ieeeSingle)
+ {
+ if (ex) // If exponent is non-zero
+ {
+ if (ex == F.EXPMASK) // infinity or NaN
+ {
+ if (*vi == 0x7F80_0000) // positive infinity
+ {
+ exp = int.max;
+ }
+ else if (*vi == 0xFF80_0000) // negative infinity
+ exp = int.min;
+ else
+ { // NaN
+ *vi |= 0x0040_0000; // convert NaNS to NaNQ
+ exp = int.min;
+ }
+ }
+ else
+ {
+ exp = (ex - F.EXPBIAS) >> 7;
+ vu[F.EXPPOS_SHORT] = cast(ushort)((0x807F & vu[F.EXPPOS_SHORT]) | 0x3F00);
+ }
+ }
+ else if (!(*vi & 0x7FFF_FFFF))
+ {
+ // vf is +-0.0
+ exp = 0;
+ }
+ else
+ {
+ // subnormal
+ vf *= F.RECIP_EPSILON;
+ ex = vu[F.EXPPOS_SHORT] & F.EXPMASK;
+ exp = ((ex - F.EXPBIAS) >> 7) - T.mant_dig + 1;
+ vu[F.EXPPOS_SHORT] =
+ cast(ushort)(((-1 - F.EXPMASK) & vu[F.EXPPOS_SHORT]) | 0x3F00);
+ }
+ return vf;
+ }
+ else // static if (F.realFormat == RealFormat.ibmExtended)
+ {
+ assert(0, "frexp not implemented");
+ }
+}
+
+///
+@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(frexp(-real.nan, exp) && exp == int.min);
+ assert(frexp(real.nan, exp) && exp == int.min);
+ assert(frexp(-real.infinity, exp) == -real.infinity && exp == int.min);
+ assert(frexp(real.infinity, exp) == real.infinity && exp == int.max);
+ assert(frexp(-0.0, exp) == -0.0 && exp == 0);
+ assert(frexp(0.0, exp) == 0.0 && exp == 0);
+}
+
+@safe unittest
+{
+ import std.meta : AliasSeq;
+ import std.typecons : tuple, Tuple;
+
+ foreach (T; AliasSeq!(real, double, float))
+ {
+ Tuple!(T, T, int)[] vals = // x,frexp,exp
+ [
+ tuple(T(0.0), T( 0.0 ), 0),
+ tuple(T(-0.0), T( -0.0), 0),
+ tuple(T(1.0), T( .5 ), 1),
+ tuple(T(-1.0), T( -.5 ), 1),
+ tuple(T(2.0), T( .5 ), 2),
+ tuple(T(float.min_normal/2.0f), T(.5), -126),
+ tuple(T.infinity, T.infinity, int.max),
+ 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)
+ {
+ T x = elem[0];
+ T e = elem[1];
+ int exp = elem[2];
+ int eptr;
+ T v = frexp(x, eptr);
+ assert(isIdentical(e, v));
+ assert(exp == eptr);
+
+ }
+
+ 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
+ ];
+ foreach (elem; extendedvals)
+ {
+ T x = elem[0];
+ T e = elem[1];
+ int exp = cast(int) elem[2];
+ int eptr;
+ T v = frexp(x, eptr);
+ assert(isIdentical(e, v));
+ assert(exp == eptr);
+
+ }
+ }
+ }
+}
+
+@safe unittest
+{
+ import std.meta : AliasSeq;
+ void foo() {
+ foreach (T; AliasSeq!(real, double, float))
+ {
+ int exp;
+ const T a = 1;
+ immutable T b = 2;
+ auto c = frexp(a, exp);
+ auto d = frexp(b, exp);
+ }
+ }
+}
+
+/******************************************
+ * 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)).
+ *
+ * $(TABLE_SV
+ * $(TR $(TH x) $(TH ilogb(x)) $(TH Range error?))
+ * $(TR $(TD 0) $(TD FP_ILOGB0) $(TD yes))
+ * $(TR $(TD $(PLUSMN)$(INFIN)) $(TD int.max) $(TD no))
+ * $(TR $(TD $(NAN)) $(TD FP_ILOGBNAN) $(TD no))
+ * )
+ */
+int ilogb(T)(const T x) @trusted pure nothrow @nogc
+if (isFloatingPoint!T)
+{
+ import core.bitop : bsr;
+ alias F = floatTraits!T;
+
+ 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;
+
+ 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 static if (F.realFormat == RealFormat.ieeeQuadruple)
+ {
+ if (ex) // If exponent is non-zero
+ {
+ 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
+ {
+ 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 { 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
+{
+ import std.meta : AliasSeq;
+ foreach (T; AliasSeq!(float, double, real))
+ {
+ T r;
+
+ r = ldexp(3.0L, 3);
+ assert(r == 24);
+
+ r = ldexp(cast(T) 3.0, cast(int) 3);
+ 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.0L, -16384) == 0x1p-16384L);
+ assert(ldexp(1.0L, -16382) == 0x1p-16382L);
+ int x;
+ real n = frexp(0x1p-16384L, x);
+ assert(n == 0.5L);
+ assert(x==-16383);
+ assert(ldexp(n, x)==0x1p-16384L);
+ }
+ else static if (floatTraits!(real).realFormat == RealFormat.ieeeDouble)
+ {
+ 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(x==-1023);
+ assert(ldexp(n, x)==0x1p-1024L);
+ }
+ else static assert(false, "Floating point type real not supported");
+}
+
+/* workaround Issue 14718, float parsing depends on platform strtold
+@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
+ [
+ [ 0, 0, 0],
+ [ 1, 0, 1],
+ [ -1, 0, -1],
+ [ 1, 1, 2],
+ [ 123, 10, 125952],
+ [ real.max, int.max, real.infinity],
+ [ real.max, -int.max, 0],
+ [ real.min_normal, -int.max, 0],
+ ];
+ int i;
+
+ for (i = 0; i < vals.length; i++)
+ {
+ real x = vals[i][0];
+ 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);
+}
+
+private
+{
+ version (INLINE_YL2X) {} else
+ {
+ static if (floatTraits!real.realFormat == RealFormat.ieeeQuadruple)
+ {
+ // Coefficients for log(1 + x) = x - x**2/2 + x**3 P(x)/Q(x)
+ static immutable real[13] logCoeffsP = [
+ 1.313572404063446165910279910527789794488E4L,
+ 7.771154681358524243729929227226708890930E4L,
+ 2.014652742082537582487669938141683759923E5L,
+ 3.007007295140399532324943111654767187848E5L,
+ 2.854829159639697837788887080758954924001E5L,
+ 1.797628303815655343403735250238293741397E5L,
+ 7.594356839258970405033155585486712125861E4L,
+ 2.128857716871515081352991964243375186031E4L,
+ 3.824952356185897735160588078446136783779E3L,
+ 4.114517881637811823002128927449878962058E2L,
+ 2.321125933898420063925789532045674660756E1L,
+ 4.998469661968096229986658302195402690910E-1L,
+ 1.538612243596254322971797716843006400388E-6L
+ ];
+ static immutable real[13] logCoeffsQ = [
+ 3.940717212190338497730839731583397586124E4L,
+ 2.626900195321832660448791748036714883242E5L,
+ 7.777690340007566932935753241556479363645E5L,
+ 1.347518538384329112529391120390701166528E6L,
+ 1.514882452993549494932585972882995548426E6L,
+ 1.158019977462989115839826904108208787040E6L,
+ 6.132189329546557743179177159925690841200E5L,
+ 2.248234257620569139969141618556349415120E5L,
+ 5.605842085972455027590989944010492125825E4L,
+ 9.147150349299596453976674231612674085381E3L,
+ 9.104928120962988414618126155557301584078E2L,
+ 4.839208193348159620282142911143429644326E1L,
+ 1.0
+ ];
+
+ // Coefficients for log(x) = z + z^3 P(z^2)/Q(z^2)
+ // where z = 2(x-1)/(x+1)
+ static immutable real[6] logCoeffsR = [
+ -8.828896441624934385266096344596648080902E-1L,
+ 8.057002716646055371965756206836056074715E1L,
+ -2.024301798136027039250415126250455056397E3L,
+ 2.048819892795278657810231591630928516206E4L,
+ -8.977257995689735303686582344659576526998E4L,
+ 1.418134209872192732479751274970992665513E5L
+ ];
+ static immutable real[6] logCoeffsS = [
+ 1.701761051846631278975701529965589676574E6L
+ -1.332535117259762928288745111081235577029E6L,
+ 4.001557694070773974936904547424676279307E5L,
+ -5.748542087379434595104154610899551484314E4L,
+ 3.998526750980007367835804959888064681098E3L,
+ -1.186359407982897997337150403816839480438E2L,
+ 1.0
+ ];
+ }
+ else
+ {
+ // Coefficients for log(1 + x) = x - x**2/2 + x**3 P(x)/Q(x)
+ static immutable real[7] logCoeffsP = [
+ 2.0039553499201281259648E1L,
+ 5.7112963590585538103336E1L,
+ 6.0949667980987787057556E1L,
+ 2.9911919328553073277375E1L,
+ 6.5787325942061044846969E0L,
+ 4.9854102823193375972212E-1L,
+ 4.5270000862445199635215E-5L,
+ ];
+ static immutable real[7] logCoeffsQ = [
+ 6.0118660497603843919306E1L,
+ 2.1642788614495947685003E2L,
+ 3.0909872225312059774938E2L,
+ 2.2176239823732856465394E2L,
+ 8.3047565967967209469434E1L,
+ 1.5062909083469192043167E1L,
+ 1.0000000000000000000000E0L,
+ ];
+
+ // Coefficients for log(x) = z + z^3 P(z^2)/Q(z^2)
+ // where z = 2(x-1)/(x+1)
+ static immutable real[4] logCoeffsR = [
+ -3.5717684488096787370998E1L,
+ 1.0777257190312272158094E1L,
+ -7.1990767473014147232598E-1L,
+ 1.9757429581415468984296E-3L,
+ ];
+ static immutable real[4] logCoeffsS = [
+ -4.2861221385716144629696E2L,
+ 1.9361891836232102174846E2L,
+ -2.6201045551331104417768E1L,
+ 1.0000000000000000000000E0L,
+ ];
+ }
+ }
+}
+
+/**************************************
+ * Calculate the natural logarithm of x.
+ *
+ * $(TABLE_SV
+ * $(TR $(TH x) $(TH log(x)) $(TH divide by 0?) $(TH invalid?))
+ * $(TR $(TD $(PLUSMN)0.0) $(TD -$(INFIN)) $(TD yes) $(TD no))
+ * $(TR $(TD $(LT)0.0) $(TD $(NAN)) $(TD no) $(TD yes))
+ * $(TR $(TD +$(INFIN)) $(TD +$(INFIN)) $(TD no) $(TD no))
+ * )
+ */
+real log(real x) @safe pure nothrow @nogc
+{
+ version (INLINE_YL2X)
+ return core.math.yl2x(x, LN2);
+ else
+ {
+ // C1 + C2 = LN2.
+ enum real C1 = 6.93145751953125E-1L;
+ enum real C2 = 1.428606820309417232121458176568075500134E-6L;
+
+ // Special cases.
+ if (isNaN(x))
+ return x;
+ if (isInfinity(x) && !signbit(x))
+ return x;
+ if (x == 0.0)
+ return -real.infinity;
+ if (x < 0.0)
+ return real.nan;
+
+ // Separate mantissa from exponent.
+ // Note, frexp is used so that denormal numbers will be handled properly.
+ real y, z;
+ int exp;
+
+ x = frexp(x, exp);
+
+ // Logarithm using log(x) = z + z^^3 R(z) / S(z),
+ // where z = 2(x - 1)/(x + 1)
+ if ((exp > 2) || (exp < -2))
+ {
+ if (x < SQRT1_2)
+ { // 2(2x - 1)/(2x + 1)
+ exp -= 1;
+ z = x - 0.5;
+ y = 0.5 * z + 0.5;
+ }
+ else
+ { // 2(x - 1)/(x + 1)
+ z = x - 0.5;
+ z -= 0.5;
+ y = 0.5 * x + 0.5;
+ }
+ x = z / y;
+ z = x * x;
+ z = x * (z * poly(z, logCoeffsR) / poly(z, logCoeffsS));
+ z += exp * C2;
+ z += x;
+ z += exp * C1;
+
+ return z;
+ }
+
+ // Logarithm using log(1 + x) = x - .5x^^2 + x^^3 P(x) / Q(x)
+ if (x < SQRT1_2)
+ { // 2x - 1
+ exp -= 1;
+ x = ldexp(x, 1) - 1.0;
+ }
+ else
+ {
+ x = x - 1.0;
+ }
+ z = x * x;
+ y = x * (z * poly(x, logCoeffsP) / poly(x, logCoeffsQ));
+ y += exp * C2;
+ z = y - ldexp(z, -1);
+
+ // Note, the sum of above terms does not exceed x/4,
+ // so it contributes at most about 1/4 lsb to the error.
+ z += x;
+ z += exp * C1;
+
+ return z;
+ }
+}
+
+///
+@safe pure nothrow @nogc unittest
+{
+ assert(log(E) == 1);
+}
+
+/**************************************
+ * Calculate the base-10 logarithm of x.
+ *
+ * $(TABLE_SV
+ * $(TR $(TH x) $(TH log10(x)) $(TH divide by 0?) $(TH invalid?))
+ * $(TR $(TD $(PLUSMN)0.0) $(TD -$(INFIN)) $(TD yes) $(TD no))
+ * $(TR $(TD $(LT)0.0) $(TD $(NAN)) $(TD no) $(TD yes))
+ * $(TR $(TD +$(INFIN)) $(TD +$(INFIN)) $(TD no) $(TD no))
+ * )
+ */
+real log10(real x) @safe pure nothrow @nogc
+{
+ version (INLINE_YL2X)
+ return core.math.yl2x(x, LOG2);
+ else
+ {
+ // log10(2) split into two parts.
+ enum real L102A = 0.3125L;
+ enum real L102B = -1.14700043360188047862611052755069732318101185E-2L;
+
+ // log10(e) split into two parts.
+ enum real L10EA = 0.5L;
+ enum real L10EB = -6.570551809674817234887108108339491770560299E-2L;
+
+ // Special cases are the same as for log.
+ if (isNaN(x))
+ return x;
+ if (isInfinity(x) && !signbit(x))
+ return x;
+ if (x == 0.0)
+ return -real.infinity;
+ if (x < 0.0)
+ return real.nan;
+
+ // Separate mantissa from exponent.
+ // Note, frexp is used so that denormal numbers will be handled properly.
+ real y, z;
+ int exp;
+
+ x = frexp(x, exp);
+
+ // Logarithm using log(x) = z + z^^3 R(z) / S(z),
+ // where z = 2(x - 1)/(x + 1)
+ if ((exp > 2) || (exp < -2))
+ {
+ if (x < SQRT1_2)
+ { // 2(2x - 1)/(2x + 1)
+ exp -= 1;
+ z = x - 0.5;
+ y = 0.5 * z + 0.5;
+ }
+ else
+ { // 2(x - 1)/(x + 1)
+ z = x - 0.5;
+ z -= 0.5;
+ y = 0.5 * x + 0.5;
+ }
+ x = z / y;
+ z = x * x;
+ y = x * (z * poly(z, logCoeffsR) / poly(z, logCoeffsS));
+ goto Ldone;
+ }
+
+ // Logarithm using log(1 + x) = x - .5x^^2 + x^^3 P(x) / Q(x)
+ if (x < SQRT1_2)
+ { // 2x - 1
+ exp -= 1;
+ x = ldexp(x, 1) - 1.0;
+ }
+ else
+ x = x - 1.0;
+
+ z = x * x;
+ y = x * (z * poly(x, logCoeffsP) / poly(x, logCoeffsQ));
+ y = y - ldexp(z, -1);
+
+ // Multiply log of fraction by log10(e) and base 2 exponent by log10(2).
+ // This sequence of operations is critical and it may be horribly
+ // defeated by some compiler optimizers.
+ Ldone:
+ z = y * L10EB;
+ z += x * L10EB;
+ z += exp * L102B;
+ z += y * L10EA;
+ z += x * L10EA;
+ z += exp * L102A;
+
+ return z;
+ }
+}
+
+///
+@safe pure nothrow @nogc unittest
+{
+ assert(fabs(log10(1000) - 3) < .000001);
+}
+
+/******************************************
+ * Calculates the natural logarithm of 1 + x.
+ *
+ * For very small x, log1p(x) will be more accurate than
+ * log(1 + x).
+ *
+ * $(TABLE_SV
+ * $(TR $(TH x) $(TH log1p(x)) $(TH divide by 0?) $(TH invalid?))
+ * $(TR $(TD $(PLUSMN)0.0) $(TD $(PLUSMN)0.0) $(TD no) $(TD no))
+ * $(TR $(TD -1.0) $(TD -$(INFIN)) $(TD yes) $(TD no))
+ * $(TR $(TD $(LT)-1.0) $(TD $(NAN)) $(TD no) $(TD yes))
+ * $(TR $(TD +$(INFIN)) $(TD -$(INFIN)) $(TD no) $(TD no))
+ * )
+ */
+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) ? core.math.yl2xp1(x, LN2) : core.math.yl2x(x+1, LN2);
+ }
+ else
+ {
+ // Special cases.
+ if (isNaN(x) || x == 0.0)
+ return x;
+ if (isInfinity(x) && !signbit(x))
+ return x;
+ if (x == -1.0)
+ return -real.infinity;
+ if (x < -1.0)
+ return real.nan;
+
+ return log(x + 1.0);
+ }
+}
+
+/***************************************
+ * Calculates the base-2 logarithm of x:
+ * $(SUB log, 2)x
+ *
+ * $(TABLE_SV
+ * $(TR $(TH x) $(TH log2(x)) $(TH divide by 0?) $(TH invalid?))
+ * $(TR $(TD $(PLUSMN)0.0) $(TD -$(INFIN)) $(TD yes) $(TD no) )
+ * $(TR $(TD $(LT)0.0) $(TD $(NAN)) $(TD no) $(TD yes) )
+ * $(TR $(TD +$(INFIN)) $(TD +$(INFIN)) $(TD no) $(TD no) )
+ * )
+ */
+real log2(real x) @safe pure nothrow @nogc
+{
+ version (INLINE_YL2X)
+ return core.math.yl2x(x, 1);
+ else
+ {
+ // Special cases are the same as for log.
+ if (isNaN(x))
+ return x;
+ if (isInfinity(x) && !signbit(x))
+ return x;
+ if (x == 0.0)
+ return -real.infinity;
+ if (x < 0.0)
+ return real.nan;
+
+ // Separate mantissa from exponent.
+ // Note, frexp is used so that denormal numbers will be handled properly.
+ real y, z;
+ int exp;
+
+ x = frexp(x, exp);
+
+ // Logarithm using log(x) = z + z^^3 R(z) / S(z),
+ // where z = 2(x - 1)/(x + 1)
+ if ((exp > 2) || (exp < -2))
+ {
+ if (x < SQRT1_2)
+ { // 2(2x - 1)/(2x + 1)
+ exp -= 1;
+ z = x - 0.5;
+ y = 0.5 * z + 0.5;
+ }
+ else
+ { // 2(x - 1)/(x + 1)
+ z = x - 0.5;
+ z -= 0.5;
+ y = 0.5 * x + 0.5;
+ }
+ x = z / y;
+ z = x * x;
+ y = x * (z * poly(z, logCoeffsR) / poly(z, logCoeffsS));
+ goto Ldone;
+ }
+
+ // Logarithm using log(1 + x) = x - .5x^^2 + x^^3 P(x) / Q(x)
+ if (x < SQRT1_2)
+ { // 2x - 1
+ exp -= 1;
+ x = ldexp(x, 1) - 1.0;
+ }
+ else
+ x = x - 1.0;
+
+ z = x * x;
+ y = x * (z * poly(x, logCoeffsP) / poly(x, logCoeffsQ));
+ y = y - ldexp(z, -1);
+
+ // Multiply log of fraction by log10(e) and base 2 exponent by log10(2).
+ // This sequence of operations is critical and it may be horribly
+ // defeated by some compiler optimizers.
+ Ldone:
+ z = y * (LOG2E - 1.0);
+ z += x * (LOG2E - 1.0);
+ z += y;
+ z += x;
+ z += exp;
+
+ return z;
+ }
+}
+
+///
+@system unittest
+{
+ // check if values are equal to 19 decimal digits of precision
+ assert(equalsDigit(log2(1024.0L), 10, 19));
+}
+
+/*****************************************
+ * Extracts the exponent of x as a signed integral value.
+ *
+ * If x is subnormal, it is treated as if it were normalized.
+ * For a positive, finite x:
+ *
+ * 1 $(LT)= $(I x) * FLT_RADIX$(SUPERSCRIPT -logb(x)) $(LT) FLT_RADIX
+ *
+ * $(TABLE_SV
+ * $(TR $(TH x) $(TH logb(x)) $(TH divide by 0?) )
+ * $(TR $(TD $(PLUSMN)$(INFIN)) $(TD +$(INFIN)) $(TD no))
+ * $(TR $(TD $(PLUSMN)0.0) $(TD -$(INFIN)) $(TD yes) )
+ * )
+ */
+real logb(real x) @trusted nothrow @nogc
+{
+ version (Win64_DMD_InlineAsm)
+ {
+ asm pure nothrow @nogc
+ {
+ naked ;
+ fld real ptr [RCX] ;
+ fxtract ;
+ fstp ST(0) ;
+ ret ;
+ }
+ }
+ else version (CRuntime_Microsoft)
+ {
+ asm pure nothrow @nogc
+ {
+ fld x ;
+ fxtract ;
+ fstp ST(0) ;
+ }
+ }
+ else
+ return core.stdc.math.logbl(x);
+}
+
+/************************************
+ * Calculates the remainder from the calculation x/y.
+ * Returns:
+ * The value of x - i * y, where i is the number of times that y can
+ * be completely subtracted from x. The result has the same sign as x.
+ *
+ * $(TABLE_SV
+ * $(TR $(TH x) $(TH y) $(TH fmod(x, y)) $(TH invalid?))
+ * $(TR $(TD $(PLUSMN)0.0) $(TD not 0.0) $(TD $(PLUSMN)0.0) $(TD no))
+ * $(TR $(TD $(PLUSMNINF)) $(TD anything) $(TD $(NAN)) $(TD yes))
+ * $(TR $(TD anything) $(TD $(PLUSMN)0.0) $(TD $(NAN)) $(TD yes))
+ * $(TR $(TD !=$(PLUSMNINF)) $(TD $(PLUSMNINF)) $(TD x) $(TD no))
+ * )
+ */
+real fmod(real x, real y) @trusted nothrow @nogc
+{
+ version (CRuntime_Microsoft)
+ {
+ return x % y;
+ }
+ else
+ return core.stdc.math.fmodl(x, y);
+}
+
+/************************************
+ * Breaks x into an integral part and a fractional part, each of which has
+ * the same sign as x. The integral part is stored in i.
+ * Returns:
+ * The fractional part of x.
+ *
+ * $(TABLE_SV
+ * $(TR $(TH x) $(TH i (on input)) $(TH modf(x, i)) $(TH i (on return)))
+ * $(TR $(TD $(PLUSMNINF)) $(TD anything) $(TD $(PLUSMN)0.0) $(TD $(PLUSMNINF)))
+ * )
+ */
+real modf(real x, ref real i) @trusted nothrow @nogc
+{
+ version (CRuntime_Microsoft)
+ {
+ i = trunc(x);
+ return copysign(isInfinity(x) ? 0.0 : x - i, x);
+ }
+ else
+ return core.stdc.math.modfl(x,&i);
+}
+
+/*************************************
+ * Efficiently calculates x * 2$(SUPERSCRIPT n).
+ *
+ * scalbn handles underflow and overflow in
+ * the same fashion as the basic arithmetic operators.
+ *
+ * $(TABLE_SV
+ * $(TR $(TH x) $(TH scalb(x)))
+ * $(TR $(TD $(PLUSMNINF)) $(TD $(PLUSMNINF)) )
+ * $(TR $(TD $(PLUSMN)0.0) $(TD $(PLUSMN)0.0) )
+ * )
+ */
+real scalbn(real x, int n) @trusted nothrow @nogc
+{
+ version (InlineAsm_X86_Any)
+ {
+ // scalbnl is not supported on DMD-Windows, so use asm pure nothrow @nogc.
+ version (Win64)
+ {
+ asm pure nothrow @nogc {
+ naked ;
+ mov 16[RSP],RCX ;
+ fild word ptr 16[RSP] ;
+ fld real ptr [RDX] ;
+ fscale ;
+ fstp ST(1) ;
+ ret ;
+ }
+ }
+ else
+ {
+ asm pure nothrow @nogc {
+ fild n;
+ fld x;
+ fscale;
+ fstp ST(1);
+ }
+ }
+ }
+ else
+ {
+ return core.stdc.math.scalbnl(x, n);
+ }
+}
+
+///
+@safe nothrow @nogc unittest
+{
+ assert(scalbn(-real.infinity, 5) == -real.infinity);
+}
+
+/***************
+ * Calculates the cube root of x.
+ *
+ * $(TABLE_SV
+ * $(TR $(TH $(I x)) $(TH cbrt(x)) $(TH invalid?))
+ * $(TR $(TD $(PLUSMN)0.0) $(TD $(PLUSMN)0.0) $(TD no) )
+ * $(TR $(TD $(NAN)) $(TD $(NAN)) $(TD yes) )
+ * $(TR $(TD $(PLUSMN)$(INFIN)) $(TD $(PLUSMN)$(INFIN)) $(TD no) )
+ * )
+ */
+real cbrt(real x) @trusted nothrow @nogc
+{
+ version (CRuntime_Microsoft)
+ {
+ version (INLINE_YL2X)
+ return copysign(exp2(core.math.yl2x(fabs(x), 1.0L/3.0L)), x);
+ else
+ return core.stdc.math.cbrtl(x);
+ }
+ else
+ return core.stdc.math.cbrtl(x);
+}
+
+
+/*******************************
+ * Returns |x|
+ *
+ * $(TABLE_SV
+ * $(TR $(TH x) $(TH fabs(x)))
+ * $(TR $(TD $(PLUSMN)0.0) $(TD +0.0) )
+ * $(TR $(TD $(PLUSMN)$(INFIN)) $(TD +$(INFIN)) )
+ * )
+ */
+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); }
+//FIXME
+///ditto
+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
+ * hypotenuse of a right-angled triangle with sides of length x and y.
+ * The hypotenuse is the value of the square root of
+ * the sums of the squares of x and y:
+ *
+ * sqrt($(POWER x, 2) + $(POWER y, 2))
+ *
+ * Note that hypot(x, y), hypot(y, x) and
+ * hypot(x, -y) are equivalent.
+ *
+ * $(TABLE_SV
+ * $(TR $(TH x) $(TH y) $(TH hypot(x, y)) $(TH invalid?))
+ * $(TR $(TD x) $(TD $(PLUSMN)0.0) $(TD |x|) $(TD no))
+ * $(TR $(TD $(PLUSMNINF)) $(TD y) $(TD +$(INFIN)) $(TD no))
+ * $(TR $(TD $(PLUSMNINF)) $(TD $(NAN)) $(TD +$(INFIN)) $(TD no))
+ * )
+ */
+
+real hypot(real x, real y) @safe pure nothrow @nogc
+{
+ // Scale x and y to avoid underflow and overflow.
+ // If one is huge and the other tiny, return the larger.
+ // If both are huge, avoid overflow by scaling by 1/sqrt(real.max/2).
+ // If both are tiny, avoid underflow by scaling by sqrt(real.min_normal*real.epsilon).
+
+ enum real SQRTMIN = 0.5 * sqrt(real.min_normal); // This is a power of 2.
+ enum real SQRTMAX = 1.0L / SQRTMIN; // 2^^((max_exp)/2) = nextUp(sqrt(real.max))
+
+ static assert(2*(SQRTMAX/2)*(SQRTMAX/2) <= real.max);
+
+ // Proves that sqrt(real.max) ~~ 0.5/sqrt(real.min_normal)
+ static assert(real.min_normal*real.max > 2 && real.min_normal*real.max <= 4);
+
+ real u = fabs(x);
+ real v = fabs(y);
+ if (!(u >= v)) // check for NaN as well.
+ {
+ v = u;
+ u = fabs(y);
+ if (u == real.infinity) return u; // hypot(inf, nan) == inf
+ if (v == real.infinity) return v; // hypot(nan, inf) == inf
+ }
+
+ // Now u >= v, or else one is NaN.
+ if (v >= SQRTMAX*0.5)
+ {
+ // hypot(huge, huge) -- avoid overflow
+ u *= SQRTMIN*0.5;
+ v *= SQRTMIN*0.5;
+ return sqrt(u*u + v*v) * SQRTMAX * 2.0;
+ }
+
+ if (u <= SQRTMIN)
+ {
+ // hypot (tiny, tiny) -- avoid underflow
+ // This is only necessary to avoid setting the underflow
+ // flag.
+ u *= SQRTMAX / real.epsilon;
+ v *= SQRTMAX / real.epsilon;
+ return sqrt(u*u + v*v) * SQRTMIN * real.epsilon;
+ }
+
+ if (u * real.epsilon > v)
+ {
+ // hypot (huge, tiny) = huge
+ return u;
+ }
+
+ // both are in the normal range
+ return sqrt(u*u + v*v);
+}
+
+@safe unittest
+{
+ static real[3][] vals = // x,y,hypot
+ [
+ [ 0.0, 0.0, 0.0],
+ [ 0.0, -0.0, 0.0],
+ [ -0.0, -0.0, 0.0],
+ [ 3.0, 4.0, 5.0],
+ [ -300, -400, 500],
+ [0.0, 7.0, 7.0],
+ [9.0, 9*real.epsilon, 9.0],
+ [88/(64*sqrt(real.min_normal)), 105/(64*sqrt(real.min_normal)), 137/(64*sqrt(real.min_normal))],
+ [88/(128*sqrt(real.min_normal)), 105/(128*sqrt(real.min_normal)), 137/(128*sqrt(real.min_normal))],
+ [3*real.min_normal*real.epsilon, 4*real.min_normal*real.epsilon, 5*real.min_normal*real.epsilon],
+ [ real.min_normal, real.min_normal, sqrt(2.0L)*real.min_normal],
+ [ real.max/sqrt(2.0L), real.max/sqrt(2.0L), real.max],
+ [ real.infinity, real.nan, real.infinity],
+ [ real.nan, real.infinity, real.infinity],
+ [ real.nan, real.nan, real.nan],
+ [ real.nan, real.max, real.nan],
+ [ real.max, real.nan, real.nan],
+ ];
+ for (int i = 0; i < vals.length; i++)
+ {
+ real x = vals[i][0];
+ real y = vals[i][1];
+ real z = vals[i][2];
+ real h = hypot(x, y);
+ assert(isIdentical(z,h) || feqrel(z, h) >= real.mant_dig - 1);
+ }
+}
+
+/**************************************
+ * Returns the value of x rounded upward to the next integer
+ * (toward positive infinity).
+ */
+real ceil(real x) @trusted pure nothrow @nogc
+{
+ version (Win64_DMD_InlineAsm)
+ {
+ asm pure nothrow @nogc
+ {
+ naked ;
+ fld real ptr [RCX] ;
+ fstcw 8[RSP] ;
+ mov AL,9[RSP] ;
+ mov DL,AL ;
+ and AL,0xC3 ;
+ or AL,0x08 ; // round to +infinity
+ mov 9[RSP],AL ;
+ fldcw 8[RSP] ;
+ frndint ;
+ mov 9[RSP],DL ;
+ fldcw 8[RSP] ;
+ ret ;
+ }
+ }
+ else version (CRuntime_Microsoft)
+ {
+ short cw;
+ asm pure nothrow @nogc
+ {
+ fld x ;
+ fstcw cw ;
+ mov AL,byte ptr cw+1 ;
+ mov DL,AL ;
+ and AL,0xC3 ;
+ or AL,0x08 ; // round to +infinity
+ mov byte ptr cw+1,AL ;
+ fldcw cw ;
+ frndint ;
+ mov byte ptr cw+1,DL ;
+ fldcw cw ;
+ }
+ }
+ else
+ {
+ // Special cases.
+ if (isNaN(x) || isInfinity(x))
+ return x;
+
+ real y = floorImpl(x);
+ if (y < x)
+ y += 1.0;
+
+ return y;
+ }
+}
+
+///
+@safe pure nothrow @nogc unittest
+{
+ assert(ceil(+123.456L) == +124);
+ assert(ceil(-123.456L) == -123);
+ assert(ceil(-1.234L) == -1);
+ assert(ceil(-0.123L) == 0);
+ assert(ceil(0.0L) == 0);
+ assert(ceil(+0.123L) == 1);
+ assert(ceil(+1.234L) == 2);
+ assert(ceil(real.infinity) == real.infinity);
+ assert(isNaN(ceil(real.nan)));
+ assert(isNaN(ceil(real.init)));
+}
+
+// ditto
+double ceil(double x) @trusted pure nothrow @nogc
+{
+ // Special cases.
+ if (isNaN(x) || isInfinity(x))
+ return x;
+
+ double y = floorImpl(x);
+ if (y < x)
+ y += 1.0;
+
+ return y;
+}
+
+@safe pure nothrow @nogc unittest
+{
+ assert(ceil(+123.456) == +124);
+ assert(ceil(-123.456) == -123);
+ assert(ceil(-1.234) == -1);
+ assert(ceil(-0.123) == 0);
+ assert(ceil(0.0) == 0);
+ assert(ceil(+0.123) == 1);
+ assert(ceil(+1.234) == 2);
+ assert(ceil(double.infinity) == double.infinity);
+ assert(isNaN(ceil(double.nan)));
+ assert(isNaN(ceil(double.init)));
+}
+
+// ditto
+float ceil(float x) @trusted pure nothrow @nogc
+{
+ // Special cases.
+ if (isNaN(x) || isInfinity(x))
+ return x;
+
+ float y = floorImpl(x);
+ if (y < x)
+ y += 1.0;
+
+ return y;
+}
+
+@safe pure nothrow @nogc unittest
+{
+ assert(ceil(+123.456f) == +124);
+ assert(ceil(-123.456f) == -123);
+ assert(ceil(-1.234f) == -1);
+ assert(ceil(-0.123f) == 0);
+ assert(ceil(0.0f) == 0);
+ assert(ceil(+0.123f) == 1);
+ assert(ceil(+1.234f) == 2);
+ assert(ceil(float.infinity) == float.infinity);
+ assert(isNaN(ceil(float.nan)));
+ assert(isNaN(ceil(float.init)));
+}
+
+/**************************************
+ * Returns the value of x rounded downward to the next integer
+ * (toward negative infinity).
+ */
+real floor(real x) @trusted pure nothrow @nogc
+{
+ version (Win64_DMD_InlineAsm)
+ {
+ asm pure nothrow @nogc
+ {
+ naked ;
+ fld real ptr [RCX] ;
+ fstcw 8[RSP] ;
+ mov AL,9[RSP] ;
+ mov DL,AL ;
+ and AL,0xC3 ;
+ or AL,0x04 ; // round to -infinity
+ mov 9[RSP],AL ;
+ fldcw 8[RSP] ;
+ frndint ;
+ mov 9[RSP],DL ;
+ fldcw 8[RSP] ;
+ ret ;
+ }
+ }
+ else version (CRuntime_Microsoft)
+ {
+ short cw;
+ asm pure nothrow @nogc
+ {
+ fld x ;
+ fstcw cw ;
+ mov AL,byte ptr cw+1 ;
+ mov DL,AL ;
+ and AL,0xC3 ;
+ or AL,0x04 ; // round to -infinity
+ mov byte ptr cw+1,AL ;
+ fldcw cw ;
+ frndint ;
+ mov byte ptr cw+1,DL ;
+ fldcw cw ;
+ }
+ }
+ else
+ {
+ // Special cases.
+ if (isNaN(x) || isInfinity(x) || x == 0.0)
+ return x;
+
+ return floorImpl(x);
+ }
+}
+
+///
+@safe pure nothrow @nogc unittest
+{
+ assert(floor(+123.456L) == +123);
+ assert(floor(-123.456L) == -124);
+ assert(floor(-1.234L) == -2);
+ assert(floor(-0.123L) == -1);
+ assert(floor(0.0L) == 0);
+ assert(floor(+0.123L) == 0);
+ assert(floor(+1.234L) == 1);
+ assert(floor(real.infinity) == real.infinity);
+ assert(isNaN(floor(real.nan)));
+ assert(isNaN(floor(real.init)));
+}
+
+// ditto
+double floor(double x) @trusted pure nothrow @nogc
+{
+ // Special cases.
+ if (isNaN(x) || isInfinity(x) || x == 0.0)
+ return x;
+
+ return floorImpl(x);
+}
+
+@safe pure nothrow @nogc unittest
+{
+ assert(floor(+123.456) == +123);
+ assert(floor(-123.456) == -124);
+ assert(floor(-1.234) == -2);
+ assert(floor(-0.123) == -1);
+ assert(floor(0.0) == 0);
+ assert(floor(+0.123) == 0);
+ assert(floor(+1.234) == 1);
+ assert(floor(double.infinity) == double.infinity);
+ assert(isNaN(floor(double.nan)));
+ assert(isNaN(floor(double.init)));
+}
+
+// ditto
+float floor(float x) @trusted pure nothrow @nogc
+{
+ // Special cases.
+ if (isNaN(x) || isInfinity(x) || x == 0.0)
+ return x;
+
+ return floorImpl(x);
+}
+
+@safe pure nothrow @nogc unittest
+{
+ assert(floor(+123.456f) == +123);
+ assert(floor(-123.456f) == -124);
+ assert(floor(-1.234f) == -2);
+ assert(floor(-0.123f) == -1);
+ assert(floor(0.0f) == 0);
+ assert(floor(+0.123f) == 0);
+ assert(floor(+1.234f) == 1);
+ assert(floor(float.infinity) == float.infinity);
+ assert(isNaN(floor(float.nan)));
+ assert(isNaN(floor(float.init)));
+}
+
+/**
+ * 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.
+ */
+Unqual!F quantize(alias rfunc = rint, F)(const F val, const F unit)
+if (is(typeof(rfunc(F.init)) : F) && isFloatingPoint!F)
+{
+ typeof(return) ret = val;
+ if (unit != 0)
+ {
+ const scaled = val / unit;
+ if (!scaled.isInfinity)
+ ret = rfunc(scaled) * unit;
+ }
+ return ret;
+}
+
+///
+@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 { 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
+ * mode.
+ *
+ * This is generally the fastest method to convert a floating-point number
+ * to an integer. Note that the results from this function
+ * depend on the rounding mode, if the fractional part of x is exactly 0.5.
+ * If using the default rounding mode (ties round to even integers)
+ * lrint(4.5) == 4, lrint(5.5)==6.
+ */
+long lrint(real x) @trusted pure nothrow @nogc
+{
+ version (InlineAsm_X86_Any)
+ {
+ version (Win64)
+ {
+ asm pure nothrow @nogc
+ {
+ naked;
+ fld real ptr [RCX];
+ fistp qword ptr 8[RSP];
+ mov RAX,8[RSP];
+ ret;
+ }
+ }
+ else
+ {
+ long n;
+ asm pure nothrow @nogc
+ {
+ fld x;
+ fistp n;
+ }
+ return n;
+ }
+ }
+ else
+ {
+ alias F = floatTraits!(real);
+ static if (F.realFormat == RealFormat.ieeeDouble)
+ {
+ long result;
+
+ // Rounding limit when casting from real(double) to ulong.
+ enum real OF = 4.50359962737049600000E15L;
+
+ uint* vi = cast(uint*)(&x);
+
+ // Find the exponent and sign
+ uint msb = vi[MANTISSA_MSB];
+ uint lsb = vi[MANTISSA_LSB];
+ int exp = ((msb >> 20) & 0x7ff) - 0x3ff;
+ const int sign = msb >> 31;
+ msb &= 0xfffff;
+ msb |= 0x100000;
+
+ if (exp < 63)
+ {
+ if (exp >= 52)
+ result = (cast(long) msb << (exp - 20)) | (lsb << (exp - 52));
+ else
+ {
+ // Adjust x and check result.
+ const real j = sign ? -OF : OF;
+ x = (j + x) - j;
+ msb = vi[MANTISSA_MSB];
+ lsb = vi[MANTISSA_LSB];
+ exp = ((msb >> 20) & 0x7ff) - 0x3ff;
+ msb &= 0xfffff;
+ msb |= 0x100000;
+
+ if (exp < 0)
+ result = 0;
+ else if (exp < 20)
+ result = cast(long) msb >> (20 - exp);
+ else if (exp == 20)
+ result = cast(long) msb;
+ else
+ result = (cast(long) msb << (exp - 20)) | (lsb >> (52 - exp));
+ }
+ }
+ else
+ {
+ // It is left implementation defined when the number is too large.
+ return cast(long) x;
+ }
+
+ return sign ? -result : result;
+ }
+ else static if (F.realFormat == RealFormat.ieeeExtended)
+ {
+ long result;
+
+ // Rounding limit when casting from real(80-bit) to ulong.
+ enum real OF = 9.22337203685477580800E18L;
+
+ ushort* vu = cast(ushort*)(&x);
+ uint* vi = cast(uint*)(&x);
+
+ // Find the exponent and sign
+ int exp = (vu[F.EXPPOS_SHORT] & 0x7fff) - 0x3fff;
+ const int sign = (vu[F.EXPPOS_SHORT] >> 15) & 1;
+
+ if (exp < 63)
+ {
+ // Adjust x and check result.
+ const real j = sign ? -OF : OF;
+ x = (j + x) - j;
+ exp = (vu[F.EXPPOS_SHORT] & 0x7fff) - 0x3fff;
+
+ version (LittleEndian)
+ {
+ if (exp < 0)
+ result = 0;
+ else if (exp <= 31)
+ result = vi[1] >> (31 - exp);
+ else
+ result = (cast(long) vi[1] << (exp - 31)) | (vi[0] >> (63 - exp));
+ }
+ else
+ {
+ if (exp < 0)
+ result = 0;
+ else if (exp <= 31)
+ result = vi[1] >> (31 - exp);
+ else
+ result = (cast(long) vi[1] << (exp - 31)) | (vi[2] >> (63 - exp));
+ }
+ }
+ else
+ {
+ // It is left implementation defined when the number is too large
+ // to fit in a 64bit long.
+ return cast(long) x;
+ }
+
+ return sign ? -result : result;
+ }
+ else static if (F.realFormat == RealFormat.ieeeQuadruple)
+ {
+ const vu = cast(ushort*)(&x);
+
+ // Find the exponent and sign
+ const sign = (vu[F.EXPPOS_SHORT] >> 15) & 1;
+ if ((vu[F.EXPPOS_SHORT] & F.EXPMASK) - (F.EXPBIAS + 1) > 63)
+ {
+ // The result is left implementation defined when the number is
+ // too large to fit in a 64 bit long.
+ return cast(long) x;
+ }
+
+ // Force rounding of lower bits according to current rounding
+ // mode by adding ±2^-112 and subtracting it again.
+ enum OF = 5.19229685853482762853049632922009600E33L;
+ const j = sign ? -OF : OF;
+ x = (j + x) - j;
+
+ const implicitOne = 1UL << 48;
+ auto vl = cast(ulong*)(&x);
+ vl[MANTISSA_MSB] &= implicitOne - 1;
+ vl[MANTISSA_MSB] |= implicitOne;
+
+ long result;
+
+ const exp = (vu[F.EXPPOS_SHORT] & F.EXPMASK) - (F.EXPBIAS + 1);
+ if (exp < 0)
+ result = 0;
+ else if (exp <= 48)
+ result = vl[MANTISSA_MSB] >> (48 - exp);
+ else
+ result = (vl[MANTISSA_MSB] << (exp - 48)) | (vl[MANTISSA_LSB] >> (112 - exp));
+
+ return sign ? -result : result;
+ }
+ else
+ {
+ static assert(false, "real type not supported by lrint()");
+ }
+ }
+}
+
+///
+@safe pure nothrow @nogc unittest
+{
+ assert(lrint(4.5) == 4);
+ assert(lrint(5.5) == 6);
+ assert(lrint(-4.5) == -4);
+ assert(lrint(-5.5) == -6);
+
+ assert(lrint(int.max - 0.5) == 2147483646L);
+ assert(lrint(int.max + 0.5) == 2147483648L);
+ assert(lrint(int.min - 0.5) == -2147483648L);
+ assert(lrint(int.min + 0.5) == -2147483648L);
+}
+
+static if (real.mant_dig >= long.sizeof * 8)
+{
+ @safe pure nothrow @nogc unittest
+ {
+ assert(lrint(long.max - 1.5L) == long.max - 1);
+ assert(lrint(long.max - 0.5L) == long.max - 1);
+ assert(lrint(long.min + 0.5L) == long.min);
+ assert(lrint(long.min + 1.5L) == long.min + 2);
+ }
+}
+
+/*******************************************
+ * 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 away from zero.
+ */
+real round(real x) @trusted nothrow @nogc
+{
+ version (CRuntime_Microsoft)
+ {
+ auto old = FloatingPointControl.getControlState();
+ FloatingPointControl.setControlState(
+ (old & ~FloatingPointControl.roundingMask) | FloatingPointControl.roundToZero
+ );
+ x = rint((x >= 0) ? x + 0.5 : x - 0.5);
+ FloatingPointControl.setControlState(old);
+ return x;
+ }
+ else
+ return core.stdc.math.roundl(x);
+}
+
+/**********************************************
+ * 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
+ * away from zero.
+ *
+ * $(BLUE This function is Posix-Only.)
+ */
+long lround(real x) @trusted nothrow @nogc
+{
+ version (Posix)
+ return core.stdc.math.llroundl(x);
+ else
+ assert(0, "lround not implemented");
+}
+
+version (Posix)
+{
+ @safe nothrow @nogc unittest
+ {
+ assert(lround(0.49) == 0);
+ assert(lround(0.5) == 1);
+ assert(lround(1.5) == 2);
+ }
+}
+
+/****************************************************
+ * Returns the integer portion of x, dropping the fractional portion.
+ *
+ * This is also known as "chop" rounding.
+ */
+real trunc(real x) @trusted nothrow @nogc
+{
+ version (Win64_DMD_InlineAsm)
+ {
+ asm pure nothrow @nogc
+ {
+ naked ;
+ fld real ptr [RCX] ;
+ fstcw 8[RSP] ;
+ mov AL,9[RSP] ;
+ mov DL,AL ;
+ and AL,0xC3 ;
+ or AL,0x0C ; // round to 0
+ mov 9[RSP],AL ;
+ fldcw 8[RSP] ;
+ frndint ;
+ mov 9[RSP],DL ;
+ fldcw 8[RSP] ;
+ ret ;
+ }
+ }
+ else version (CRuntime_Microsoft)
+ {
+ short cw;
+ asm pure nothrow @nogc
+ {
+ fld x ;
+ fstcw cw ;
+ mov AL,byte ptr cw+1 ;
+ mov DL,AL ;
+ and AL,0xC3 ;
+ or AL,0x0C ; // round to 0
+ mov byte ptr cw+1,AL ;
+ fldcw cw ;
+ frndint ;
+ mov byte ptr cw+1,DL ;
+ fldcw cw ;
+ }
+ }
+ else
+ return core.stdc.math.truncl(x);
+}
+
+/****************************************************
+ * Calculate the remainder x REM y, following IEC 60559.
+ *
+ * REM is the value of x - y * n, where n is the integer nearest the exact
+ * value of x / y.
+ * If |n - x / y| == 0.5, n is even.
+ * If the result is zero, it has the same sign as x.
+ * Otherwise, the sign of the result is the sign of x / y.
+ * Precision mode has no effect on the remainder functions.
+ *
+ * remquo returns n in the parameter n.
+ *
+ * $(TABLE_SV
+ * $(TR $(TH x) $(TH y) $(TH remainder(x, y)) $(TH n) $(TH invalid?))
+ * $(TR $(TD $(PLUSMN)0.0) $(TD not 0.0) $(TD $(PLUSMN)0.0) $(TD 0.0) $(TD no))
+ * $(TR $(TD $(PLUSMNINF)) $(TD anything) $(TD $(NAN)) $(TD ?) $(TD yes))
+ * $(TR $(TD anything) $(TD $(PLUSMN)0.0) $(TD $(NAN)) $(TD ?) $(TD yes))
+ * $(TR $(TD != $(PLUSMNINF)) $(TD $(PLUSMNINF)) $(TD x) $(TD ?) $(TD no))
+ * )
+ *
+ * $(BLUE `remquo` and `remainder` not supported on Windows.)
+ */
+real remainder(real x, real y) @trusted nothrow @nogc
+{
+ version (CRuntime_Microsoft)
+ {
+ int n;
+ return remquo(x, y, n);
+ }
+ else
+ return core.stdc.math.remainderl(x, y);
+}
+
+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");
+}
+
+/** IEEE exception status flags ('sticky bits')
+
+ These flags indicate that an exceptional floating-point condition has occurred.
+ They indicate that a NaN or an infinity has been generated, that a result
+ is inexact, or that a signalling NaN has been encountered. If floating-point
+ exceptions are enabled (unmasked), a hardware exception will be generated
+ instead of setting these flags.
+ */
+struct IeeeFlags
+{
+private:
+ // The x87 FPU status register is 16 bits.
+ // The Pentium SSE2 status register is 32 bits.
+ // The ARM and PowerPC FPSCR is a 32-bit register.
+ // The SPARC FSR is a 32bit register (64 bits for SPARC 7 & 8, but high bits are uninteresting).
+ uint flags;
+
+ version (CRuntime_Microsoft)
+ {
+ // Microsoft uses hardware-incompatible custom constants in fenv.h (core.stdc.fenv).
+ // Applies to both x87 status word (16 bits) and SSE2 status word(32 bits).
+ enum : int
+ {
+ INEXACT_MASK = 0x20,
+ UNDERFLOW_MASK = 0x10,
+ OVERFLOW_MASK = 0x08,
+ DIVBYZERO_MASK = 0x04,
+ INVALID_MASK = 0x01,
+
+ EXCEPTIONS_MASK = 0b11_1111
+ }
+ // Don't bother about subnormals, they are not supported on most CPUs.
+ // SUBNORMAL_MASK = 0x02;
+ }
+ else
+ {
+ enum : int
+ {
+ INEXACT_MASK = core.stdc.fenv.FE_INEXACT,
+ UNDERFLOW_MASK = core.stdc.fenv.FE_UNDERFLOW,
+ OVERFLOW_MASK = core.stdc.fenv.FE_OVERFLOW,
+ DIVBYZERO_MASK = core.stdc.fenv.FE_DIVBYZERO,
+ INVALID_MASK = core.stdc.fenv.FE_INVALID,
+ EXCEPTIONS_MASK = core.stdc.fenv.FE_ALL_EXCEPT,
+ }
+ }
+
+private:
+ static uint getIeeeFlags()
+ {
+ version (GNU)
+ {
+ version (X86_Any)
+ {
+ ushort sw;
+ asm pure nothrow @nogc
+ {
+ "fstsw %0" : "=a" (sw);
+ }
+ // OR the result with the SSE2 status register (MXCSR).
+ if (haveSSE)
+ {
+ uint mxcsr;
+ asm pure nothrow @nogc
+ {
+ "stmxcsr %0" : "=m" (mxcsr);
+ }
+ return (sw | mxcsr) & EXCEPTIONS_MASK;
+ }
+ else
+ return sw & EXCEPTIONS_MASK;
+ }
+ else version (ARM)
+ {
+ version (ARM_SoftFloat)
+ return 0;
+ else
+ {
+ uint result = void;
+ asm pure nothrow @nogc
+ {
+ "vmrs %0, FPSCR; and %0, %0, #0x1F;" : "=r" result;
+ }
+ return result;
+ }
+ }
+ else
+ assert(0, "Not yet supported");
+ }
+ else
+ version (InlineAsm_X86_Any)
+ {
+ ushort sw;
+ asm pure nothrow @nogc { fstsw sw; }
+
+ // OR the result with the SSE2 status register (MXCSR).
+ if (haveSSE)
+ {
+ uint mxcsr;
+ asm pure nothrow @nogc { stmxcsr mxcsr; }
+ return (sw | mxcsr) & EXCEPTIONS_MASK;
+ }
+ else return sw & EXCEPTIONS_MASK;
+ }
+ else version (SPARC)
+ {
+ /*
+ int retval;
+ asm pure nothrow @nogc { st %fsr, retval; }
+ return retval;
+ */
+ assert(0, "Not yet supported");
+ }
+ else version (ARM)
+ {
+ assert(false, "Not yet supported.");
+ }
+ else
+ assert(0, "Not yet supported");
+ }
+ static void resetIeeeFlags() @nogc
+ {
+ version (GNU)
+ {
+ version (X86_Any)
+ {
+ asm pure nothrow @nogc
+ {
+ "fnclex";
+ }
+
+ // Also clear exception flags in MXCSR, SSE's control register.
+ if (haveSSE)
+ {
+ uint mxcsr;
+ asm pure nothrow @nogc
+ {
+ "stmxcsr %0" : "=m" (mxcsr);
+ }
+ mxcsr &= ~EXCEPTIONS_MASK;
+ asm pure nothrow @nogc
+ {
+ "ldmxcsr %0" : : "m" (mxcsr);
+ }
+ }
+ }
+ else version (ARM)
+ {
+ version (ARM_SoftFloat)
+ return;
+ else
+ {
+ uint old = FloatingPointControl.getControlState();
+ old &= ~0b11111; // http://infocenter.arm.com/help/topic/com.arm.doc.ddi0408i/Chdfifdc.html
+ asm pure nothrow @nogc
+ {
+ "vmsr FPSCR, %0" : : "r" (old);
+ }
+ }
+ }
+ else
+ assert(0, "Not yet supported");
+ }
+ else
+ version (InlineAsm_X86_Any)
+ {
+ asm pure nothrow @nogc
+ {
+ 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
+ {
+ /* SPARC:
+ int tmpval;
+ asm pure nothrow @nogc { st %fsr, tmpval; }
+ tmpval &=0xFFFF_FC00;
+ asm pure nothrow @nogc { ld tmpval, %fsr; }
+ */
+ assert(0, "Not yet supported");
+ }
+ }
+public:
+ 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; }
+
+ }
+}
+
+///
+version (GNU)
+{
+ unittest
+ {
+ pragma(msg, "ieeeFlags test disabled, see LDC Issue #888");
+ }
+}
+else
+@system unittest
+{
+ static void func() {
+ int a = 10 * 10;
+ }
+
+ real a=3.5;
+ // Set all the flags to zero
+ resetIeeeFlags();
+ assert(!ieeeFlags.divByZero);
+ // Perform a division by zero.
+ a/=0.0L;
+ assert(a == real.infinity);
+ assert(ieeeFlags.divByZero);
+ // Create a NaN
+ a*=0.0L;
+ assert(ieeeFlags.invalid);
+ assert(isNaN(a));
+
+ // Check that calling func() has no effect on the
+ // status flags.
+ IeeeFlags f = ieeeFlags;
+ func();
+ assert(ieeeFlags == f);
+}
+
+version (GNU)
+{
+ unittest
+ {
+ pragma(msg, "ieeeFlags test disabled, see LDC Issue #888");
+ }
+}
+else
+@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;
+}
+version (X86_Any)
+{
+ version = IeeeFlagsSupport;
+}
+else version (PPC_Any)
+{
+ version = IeeeFlagsSupport;
+}
+else version (MIPS_Any)
+{
+ version = IeeeFlagsSupport;
+}
+else version (ARM_Any)
+{
+ version = IeeeFlagsSupport;
+}
+
+/// Set all of the floating-point status flags to false.
+void resetIeeeFlags() @nogc { IeeeFlags.resetIeeeFlags(); }
+
+/// Returns: snapshot of the current state of the floating-point status flags
+@property IeeeFlags ieeeFlags()
+{
+ return IeeeFlags(IeeeFlags.getIeeeFlags());
+}
+
+/** Control the Floating point hardware
+
+ Change the IEEE754 floating-point rounding mode and the floating-point
+ hardware exceptions.
+
+ By default, the rounding mode is roundToNearest and all hardware exceptions
+ are disabled. For most applications, debugging is easier if the $(I division
+ by zero), $(I overflow), and $(I invalid operation) exceptions are enabled.
+ These three are combined into a $(I severeExceptions) value for convenience.
+ Note in particular that if $(I invalidException) is enabled, a hardware trap
+ will be generated whenever an uninitialized floating-point variable is used.
+
+ All changes are temporary. The previous state is restored at the
+ end of the scope.
+
+
+Example:
+----
+{
+ FloatingPointControl fpctrl;
+
+ // Enable hardware exceptions for division by zero, overflow to infinity,
+ // invalid operations, and uninitialized floating-point variables.
+ fpctrl.enableExceptions(FloatingPointControl.severeExceptions);
+
+ // This will generate a hardware exception, if x is a
+ // default-initialized floating point variable:
+ real x; // Add `= 0` or even `= real.nan` to not throw the exception.
+ real y = x * 3.0;
+
+ // The exception is only thrown for default-uninitialized NaN-s.
+ // NaN-s with other payload are valid:
+ real z = y * real.nan; // ok
+
+ // Changing the rounding mode:
+ fpctrl.rounding = FloatingPointControl.roundUp;
+ assert(rint(1.1) == 2);
+
+ // The set hardware exceptions will be disabled when leaving this scope.
+ // The original rounding mode will also be restored.
+}
+
+// Ensure previous values are returned:
+assert(!FloatingPointControl.enabledExceptions);
+assert(FloatingPointControl.rounding == FloatingPointControl.roundToNearest);
+assert(rint(1.1) == 1);
+----
+
+ */
+struct FloatingPointControl
+{
+ alias RoundingMode = uint; ///
+
+ version (StdDdoc)
+ {
+ enum : RoundingMode
+ {
+ /** IEEE rounding modes.
+ * The default mode is roundToNearest.
+ *
+ * roundingMask = A mask of all rounding modes.
+ */
+ roundToNearest,
+ roundDown, /// ditto
+ roundUp, /// ditto
+ roundToZero, /// ditto
+ roundingMask, /// ditto
+ }
+ }
+ else version (CRuntime_Microsoft)
+ {
+ // Microsoft uses hardware-incompatible custom constants in fenv.h (core.stdc.fenv).
+ enum : RoundingMode
+ {
+ roundToNearest = 0x0000,
+ roundDown = 0x0400,
+ roundUp = 0x0800,
+ roundToZero = 0x0C00,
+ roundingMask = roundToNearest | roundDown
+ | roundUp | roundToZero,
+ }
+ }
+ else
+ {
+ enum : RoundingMode
+ {
+ roundToNearest = core.stdc.fenv.FE_TONEAREST,
+ roundDown = core.stdc.fenv.FE_DOWNWARD,
+ roundUp = core.stdc.fenv.FE_UPWARD,
+ roundToZero = core.stdc.fenv.FE_TOWARDZERO,
+ roundingMask = roundToNearest | roundDown
+ | roundUp | roundToZero,
+ }
+ }
+
+ //// Change the floating-point hardware rounding mode
+ @property void rounding(RoundingMode newMode) @nogc
+ {
+ initialize();
+ setControlState(cast(ushort)((getControlState() & (-1 - roundingMask)) | (newMode & roundingMask)));
+ }
+
+ /// Returns: the currently active rounding mode
+ @property static RoundingMode rounding() @nogc
+ {
+ return cast(RoundingMode)(getControlState() & roundingMask);
+ }
+
+ alias ExceptionMask = uint; ///
+
+ version (StdDdoc)
+ {
+ enum : ExceptionMask
+ {
+ /** IEEE hardware exceptions.
+ * By default, all exceptions are masked (disabled).
+ *
+ * severeExceptions = The overflow, division by zero, and invalid
+ * exceptions.
+ */
+ subnormalException,
+ inexactException, /// ditto
+ underflowException, /// ditto
+ overflowException, /// ditto
+ divByZeroException, /// ditto
+ invalidException, /// ditto
+ severeExceptions, /// ditto
+ allExceptions, /// ditto
+ }
+ }
+ else version (ARM_Any)
+ {
+ enum : ExceptionMask
+ {
+ subnormalException = 0x8000,
+ inexactException = 0x1000,
+ underflowException = 0x0800,
+ overflowException = 0x0400,
+ divByZeroException = 0x0200,
+ invalidException = 0x0100,
+ severeExceptions = overflowException | divByZeroException
+ | invalidException,
+ allExceptions = severeExceptions | underflowException
+ | inexactException | subnormalException,
+ }
+ }
+ else version (MIPS_Any)
+ {
+ enum : ExceptionMask
+ {
+ inexactException = 0x0080,
+ underflowException = 0x0100,
+ overflowException = 0x0200,
+ divByZeroException = 0x0400,
+ invalidException = 0x0800,
+ severeExceptions = overflowException | divByZeroException
+ | invalidException,
+ allExceptions = severeExceptions | underflowException
+ | inexactException,
+ }
+ }
+ else version (PPC_Any)
+ {
+ enum : ExceptionMask
+ {
+ inexactException = 0x08,
+ divByZeroException = 0x10,
+ underflowException = 0x20,
+ overflowException = 0x40,
+ invalidException = 0x80,
+ severeExceptions = overflowException | divByZeroException
+ | invalidException,
+ allExceptions = severeExceptions | underflowException
+ | inexactException,
+ }
+ }
+ else version (SPARC64)
+ {
+ enum : ExceptionMask
+ {
+ inexactException = 0x0800000,
+ divByZeroException = 0x1000000,
+ overflowException = 0x4000000,
+ underflowException = 0x2000000,
+ invalidException = 0x8000000,
+ severeExceptions = overflowException | divByZeroException
+ | invalidException,
+ allExceptions = severeExceptions | underflowException
+ | inexactException,
+ }
+ }
+ else version (SystemZ)
+ {
+ enum : ExceptionMask
+ {
+ inexactException = 0x08000000,
+ divByZeroException = 0x40000000,
+ overflowException = 0x20000000,
+ underflowException = 0x10000000,
+ invalidException = 0x80000000,
+ severeExceptions = overflowException | divByZeroException
+ | invalidException,
+ allExceptions = severeExceptions | underflowException
+ | inexactException,
+ }
+ }
+ else version (X86_Any)
+ {
+ enum : ExceptionMask
+ {
+ inexactException = 0x20,
+ underflowException = 0x10,
+ overflowException = 0x08,
+ divByZeroException = 0x04,
+ subnormalException = 0x02,
+ invalidException = 0x01,
+ severeExceptions = overflowException | divByZeroException
+ | invalidException,
+ allExceptions = severeExceptions | underflowException
+ | inexactException | subnormalException,
+ }
+ }
+ else
+ static assert(false, "Not implemented for this architecture");
+
+public:
+ /// Returns: true if the current FPU supports exception trapping
+ @property static bool hasExceptionTraps() @safe nothrow @nogc
+ {
+ version (X86_Any)
+ return true;
+ else version (PPC_Any)
+ return true;
+ else version (MIPS_Any)
+ return true;
+ else version (ARM_Any)
+ {
+ auto oldState = getControlState();
+ // 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);
+ immutable result = (getControlState() & allExceptions) != 0;
+ setControlState(oldState);
+ return result;
+ }
+ else
+ assert(0, "Not yet supported");
+ }
+
+ /// Enable (unmask) specific hardware exceptions. Multiple exceptions may be ORed together.
+ void enableExceptions(ExceptionMask exceptions) @nogc
+ {
+ assert(hasExceptionTraps);
+ initialize();
+ version (X86_Any)
+ setControlState(getControlState() & ~(exceptions & allExceptions));
+ else
+ setControlState(getControlState() | (exceptions & allExceptions));
+ }
+
+ /// Disable (mask) specific hardware exceptions. Multiple exceptions may be ORed together.
+ void disableExceptions(ExceptionMask exceptions) @nogc
+ {
+ assert(hasExceptionTraps);
+ initialize();
+ version (X86_Any)
+ setControlState(getControlState() | (exceptions & allExceptions));
+ else
+ setControlState(getControlState() & ~(exceptions & allExceptions));
+ }
+
+ /// Returns: the exceptions which are currently enabled (unmasked)
+ @property static ExceptionMask enabledExceptions() @nogc
+ {
+ assert(hasExceptionTraps);
+ version (X86_Any)
+ return (getControlState() & allExceptions) ^ allExceptions;
+ else
+ return (getControlState() & allExceptions);
+ }
+
+ /// Clear all pending exceptions, then restore the original exception state and rounding mode.
+ ~this() @nogc
+ {
+ clearExceptions();
+ if (initialized)
+ setControlState(savedState);
+ }
+
+private:
+ ControlState savedState;
+
+ bool initialized = false;
+
+ version (ARM_Any)
+ {
+ alias ControlState = uint;
+ }
+ else version (PPC_Any)
+ {
+ alias ControlState = uint;
+ }
+ else version (MIPS_Any)
+ {
+ alias ControlState = uint;
+ }
+ else version (SPARC64)
+ {
+ alias ControlState = ulong;
+ }
+ else version (SystemZ)
+ {
+ alias ControlState = uint;
+ }
+ else version (X86_Any)
+ {
+ alias ControlState = ushort;
+ }
+ else
+ static assert(false, "Not implemented for this architecture");
+
+ void initialize() @nogc
+ {
+ // BUG: This works around the absence of this() constructors.
+ if (initialized) return;
+ clearExceptions();
+ savedState = getControlState();
+ initialized = true;
+ }
+
+ // Clear all pending exceptions
+ static void clearExceptions() @nogc
+ {
+ resetIeeeFlags();
+ }
+
+ // Read from the control register
+ static ControlState getControlState() @trusted nothrow @nogc
+ {
+ version (GNU)
+ {
+ version (X86_Any)
+ {
+ ControlState cont;
+ asm pure nothrow @nogc
+ {
+ "fstcw %0" : "=m" cont;
+ }
+ return cont;
+ }
+ else version (AArch64)
+ {
+ asm pure nothrow @nogc
+ {
+ "mrs %0, FPCR;" : "=r" cont;
+ }
+ return cont;
+ }
+ else version (ARM)
+ {
+ ControlState cont;
+ version (ARM_SoftFloat)
+ cont = 0;
+ else
+ {
+ asm pure nothrow @nogc
+ {
+ "vmrs %0, FPSCR" : "=r" cont;
+ }
+ }
+ return cont;
+ }
+ else
+ assert(0, "Not yet supported");
+ }
+ else
+ version (D_InlineAsm_X86)
+ {
+ short cont;
+ asm nothrow @nogc
+ {
+ xor EAX, EAX;
+ fstcw cont;
+ }
+ return cont;
+ }
+ else
+ version (D_InlineAsm_X86_64)
+ {
+ short cont;
+ asm nothrow @nogc
+ {
+ xor RAX, RAX;
+ fstcw cont;
+ }
+ return cont;
+ }
+ else
+ assert(0, "Not yet supported");
+ }
+
+ // Set the control register
+ static void setControlState(ControlState newState) @trusted nothrow @nogc
+ {
+ version (GNU)
+ {
+ version (X86_Any)
+ {
+ asm pure nothrow @nogc
+ {
+ "fclex; fldcw %0" : : "m" newState;
+ }
+
+ // Also update MXCSR, SSE's control register.
+ if (haveSSE)
+ {
+ uint mxcsr;
+ asm pure nothrow @nogc
+ {
+ "stmxcsr %0" : "=m" mxcsr;
+ }
+
+ /* In the FPU control register, rounding mode is in bits 10 and
+ 11. In MXCSR it's in bits 13 and 14. */
+ mxcsr &= ~(roundingMask << 3); // delete old rounding mode
+ mxcsr |= (newState & roundingMask) << 3; // write new rounding mode
+
+ /* In the FPU control register, masks are bits 0 through 5.
+ In MXCSR they're 7 through 12. */
+ mxcsr &= ~(allExceptions << 7); // delete old masks
+ mxcsr |= (newState & allExceptions) << 7; // write new exception masks
+
+ asm pure nothrow @nogc
+ {
+ "ldmxcsr %0" : : "m" mxcsr;
+ }
+ }
+ }
+ else version (AArch64)
+ {
+ asm pure nothrow @nogc
+ {
+ "msr FPCR, %0;" : : "r" (newState);
+ }
+ }
+ else version (ARM)
+ {
+ version (ARM_SoftFloat)
+ return;
+ else
+ {
+ asm pure nothrow @nogc
+ {
+ "vmsr FPSCR, %0" : : "r" (newState);
+ }
+ }
+ }
+ else
+ assert(0, "Not yet supported");
+ }
+ else
+ version (InlineAsm_X86_Any)
+ {
+ asm nothrow @nogc
+ {
+ fclex;
+ fldcw newState;
+ }
+
+ // Also update MXCSR, SSE's control register.
+ if (haveSSE)
+ {
+ 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. */
+ mxcsr &= ~(roundingMask << 3); // delete old rounding mode
+ mxcsr |= (newState & roundingMask) << 3; // write new rounding mode
+
+ /* In the FPU control register, masks are bits 0 through 5.
+ In MXCSR they're 7 through 12. */
+ mxcsr &= ~(allExceptions << 7); // delete old masks
+ mxcsr |= (newState & allExceptions) << 7; // write new exception masks
+
+ asm nothrow @nogc { ldmxcsr mxcsr; }
+ }
+ }
+ else
+ assert(0, "Not yet supported");
+ }
+}
+
+@system unittest
+{
+ // GCC floating point emulation doesn't allow changing
+ // rounding modes, getting error bits etc
+ version (GNU) version (D_SoftFloat)
+ return;
+
+ void ensureDefaults()
+ {
+ assert(FloatingPointControl.rounding
+ == FloatingPointControl.roundToNearest);
+ if (FloatingPointControl.hasExceptionTraps)
+ assert(FloatingPointControl.enabledExceptions == 0);
+ }
+
+ {
+ FloatingPointControl ctrl;
+ }
+ ensureDefaults();
+
+ version (D_HardFloat)
+ {
+ {
+ FloatingPointControl ctrl;
+ ctrl.rounding = FloatingPointControl.roundDown;
+ assert(FloatingPointControl.rounding == FloatingPointControl.roundDown);
+ }
+ ensureDefaults();
+ }
+
+ if (FloatingPointControl.hasExceptionTraps)
+ {
+ FloatingPointControl ctrl;
+ ctrl.enableExceptions(FloatingPointControl.divByZeroException
+ | FloatingPointControl.overflowException);
+ assert(ctrl.enabledExceptions ==
+ (FloatingPointControl.divByZeroException
+ | FloatingPointControl.overflowException));
+
+ ctrl.rounding = FloatingPointControl.roundUp;
+ assert(FloatingPointControl.rounding == FloatingPointControl.roundUp);
+ }
+ 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);
+ }
+}
+
+
+/*********************************
+ * 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))
+{
+ alias F = floatTraits!(X);
+ static if (F.realFormat == RealFormat.ieeeSingle)
+ {
+ const uint p = *cast(uint *)&x;
+ return ((p & 0x7F80_0000) == 0x7F80_0000)
+ && p & 0x007F_FFFF; // not infinity
+ }
+ else static if (F.realFormat == RealFormat.ieeeDouble)
+ {
+ 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)
+ {
+ 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
+ }
+ else static if (F.realFormat == RealFormat.ieeeQuadruple)
+ {
+ 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 &&
+ (psLsb | (psMsb& 0x0000_FFFF_FFFF_FFFF)) != 0;
+ }
+ else
+ {
+ return x != x;
+ }
+}
+
+///
+@safe pure nothrow @nogc unittest
+{
+ assert( isNaN(float.init));
+ assert( isNaN(-double.init));
+ assert( isNaN(real.nan));
+ assert( isNaN(-real.nan));
+ assert(!isNaN(cast(float) 53.6));
+ assert(!isNaN(cast(real)-53.6));
+}
+
+@safe pure nothrow @nogc unittest
+{
+ import std.meta : AliasSeq;
+
+ foreach (T; AliasSeq!(float, double, real))
+ {
+ // CTFE-able tests
+ assert(isNaN(T.init));
+ assert(isNaN(-T.init));
+ assert(isNaN(T.nan));
+ assert(isNaN(-T.nan));
+ assert(!isNaN(T.infinity));
+ assert(!isNaN(-T.infinity));
+ assert(!isNaN(cast(T) 53.6));
+ assert(!isNaN(cast(T)-53.6));
+
+ // Runtime tests
+ shared T f;
+ f = T.init;
+ assert(isNaN(f));
+ assert(isNaN(-f));
+ f = T.nan;
+ assert(isNaN(f));
+ assert(isNaN(-f));
+ f = T.infinity;
+ assert(!isNaN(f));
+ assert(!isNaN(-f));
+ f = cast(T) 53.6;
+ assert(!isNaN(f));
+ assert(!isNaN(-f));
+ }
+}
+
+/*********************************
+ * Determines if $(D_PARAM x) is finite.
+ * Params:
+ * x = a floating point number.
+ * Returns:
+ * $(D true) if $(D_PARAM x) is finite.
+ */
+bool isFinite(X)(X x) @trusted pure nothrow @nogc
+{
+ alias F = floatTraits!(X);
+ ushort* pe = cast(ushort *)&x;
+ return (pe[F.EXPPOS_SHORT] & F.EXPMASK) != F.EXPMASK;
+}
+
+///
+@safe pure nothrow @nogc unittest
+{
+ assert( isFinite(1.23f));
+ assert( isFinite(float.max));
+ assert( isFinite(float.min_normal));
+ assert(!isFinite(float.nan));
+ assert(!isFinite(float.infinity));
+}
+
+@safe pure nothrow @nogc unittest
+{
+ assert(isFinite(1.23));
+ assert(isFinite(double.max));
+ assert(isFinite(double.min_normal));
+ assert(!isFinite(double.nan));
+ assert(!isFinite(double.infinity));
+
+ assert(isFinite(1.23L));
+ assert(isFinite(real.max));
+ assert(isFinite(real.min_normal));
+ assert(!isFinite(real.nan));
+ assert(!isFinite(real.infinity));
+}
+
+
+/*********************************
+ * 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.
+ */
+bool isNormal(X)(X x) @trusted pure nothrow @nogc
+{
+ alias F = floatTraits!(X);
+ static if (F.realFormat == RealFormat.ibmExtended)
+ {
+ // doubledouble is normal if the least significant part is normal.
+ return isNormal((cast(double*)&x)[MANTISSA_LSB]);
+ }
+ else
+ {
+ ushort e = F.EXPMASK & (cast(ushort *)&x)[F.EXPPOS_SHORT];
+ return (e != F.EXPMASK && e != 0);
+ }
+}
+
+///
+@safe pure nothrow @nogc unittest
+{
+ float f = 3;
+ double d = 500;
+ real e = 10e+48;
+
+ assert(isNormal(f));
+ assert(isNormal(d));
+ assert(isNormal(e));
+ f = d = e = 0;
+ assert(!isNormal(f));
+ assert(!isNormal(d));
+ assert(!isNormal(e));
+ assert(!isNormal(real.infinity));
+ assert(isNormal(-real.max));
+ assert(!isNormal(real.min_normal/4));
+
+}
+
+/*********************************
+ * Determines if $(D_PARAM x) is subnormal.
+ *
+ * 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.
+ */
+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)
+ {
+ uint *p = cast(uint *)&x;
+ return (*p & F.EXPMASK_INT) == 0 && *p & F.MANTISSAMASK_INT;
+ }
+ else static if (F.realFormat == RealFormat.ieeeDouble)
+ {
+ uint *p = cast(uint *)&x;
+ return (p[MANTISSA_MSB] & F.EXPMASK_INT) == 0
+ && (p[MANTISSA_LSB] || p[MANTISSA_MSB] & F.MANTISSAMASK_INT);
+ }
+ else static if (F.realFormat == RealFormat.ieeeQuadruple)
+ {
+ 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));
+ }
+ else static if (F.realFormat == RealFormat.ieeeExtended)
+ {
+ ushort* pe = cast(ushort *)&x;
+ long* ps = cast(long *)&x;
+
+ return (pe[F.EXPPOS_SHORT] & F.EXPMASK) == 0 && *ps > 0;
+ }
+ else static if (F.realFormat == RealFormat.ibmExtended)
+ {
+ return isSubnormal((cast(double*)&x)[MANTISSA_MSB]);
+ }
+ else
+ {
+ static assert(false, "Not implemented for this architecture");
+ }
+}
+
+///
+@safe pure nothrow @nogc unittest
+{
+ import std.meta : AliasSeq;
+
+ foreach (T; AliasSeq!(float, double, real))
+ {
+ T f;
+ for (f = 1.0; !isSubnormal(f); f /= 2)
+ assert(f != 0);
+ }
+}
+
+/*********************************
+ * 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))
+{
+ alias F = floatTraits!(X);
+ static if (F.realFormat == RealFormat.ieeeSingle)
+ {
+ return ((*cast(uint *)&x) & 0x7FFF_FFFF) == 0x7F80_0000;
+ }
+ else static if (F.realFormat == RealFormat.ieeeDouble)
+ {
+ return ((*cast(ulong *)&x) & 0x7FFF_FFFF_FFFF_FFFF)
+ == 0x7FF0_0000_0000_0000;
+ }
+ else static if (F.realFormat == RealFormat.ieeeExtended)
+ {
+ 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;
+ }
+ else static if (F.realFormat == RealFormat.ibmExtended)
+ {
+ return (((cast(ulong *)&x)[MANTISSA_MSB]) & 0x7FFF_FFFF_FFFF_FFFF)
+ == 0x7FF8_0000_0000_0000;
+ }
+ else static if (F.realFormat == RealFormat.ieeeQuadruple)
+ {
+ 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 < -X.max) || (X.max < x);
+ }
+}
+
+///
+@nogc @safe pure nothrow unittest
+{
+ assert(!isInfinity(float.init));
+ assert(!isInfinity(-float.init));
+ assert(!isInfinity(float.nan));
+ assert(!isInfinity(-float.nan));
+ assert(isInfinity(float.infinity));
+ assert(isInfinity(-float.infinity));
+ assert(isInfinity(-1.0f / 0.0f));
+}
+
+@safe pure nothrow @nogc unittest
+{
+ // CTFE-able tests
+ assert(!isInfinity(double.init));
+ assert(!isInfinity(-double.init));
+ assert(!isInfinity(double.nan));
+ assert(!isInfinity(-double.nan));
+ assert(isInfinity(double.infinity));
+ assert(isInfinity(-double.infinity));
+ assert(isInfinity(-1.0 / 0.0));
+
+ assert(!isInfinity(real.init));
+ assert(!isInfinity(-real.init));
+ assert(!isInfinity(real.nan));
+ assert(!isInfinity(-real.nan));
+ assert(isInfinity(real.infinity));
+ assert(isInfinity(-real.infinity));
+ assert(isInfinity(-1.0L / 0.0L));
+
+ // Runtime tests
+ shared float f;
+ f = float.init;
+ assert(!isInfinity(f));
+ assert(!isInfinity(-f));
+ f = float.nan;
+ assert(!isInfinity(f));
+ assert(!isInfinity(-f));
+ f = float.infinity;
+ assert(isInfinity(f));
+ assert(isInfinity(-f));
+ f = (-1.0f / 0.0f);
+ assert(isInfinity(f));
+
+ shared double d;
+ d = double.init;
+ assert(!isInfinity(d));
+ assert(!isInfinity(-d));
+ d = double.nan;
+ assert(!isInfinity(d));
+ assert(!isInfinity(-d));
+ d = double.infinity;
+ assert(isInfinity(d));
+ assert(isInfinity(-d));
+ d = (-1.0 / 0.0);
+ assert(isInfinity(d));
+
+ shared real e;
+ e = real.init;
+ assert(!isInfinity(e));
+ assert(!isInfinity(-e));
+ e = real.nan;
+ assert(!isInfinity(e));
+ assert(!isInfinity(-e));
+ e = real.infinity;
+ assert(isInfinity(e));
+ assert(isInfinity(-e));
+ e = (-1.0L / 0.0L);
+ assert(isInfinity(e));
+}
+
+/*********************************
+ * Is the binary representation of x identical to y?
+ *
+ * Same as ==, except that positive and negative zero are not identical,
+ * and two $(NAN)s are identical if they have the same 'payload'.
+ */
+bool isIdentical(real x, real y) @trusted pure nothrow @nogc
+{
+ // We're doing a bitwise comparison so the endianness is irrelevant.
+ long* pxs = cast(long *)&x;
+ long* pys = cast(long *)&y;
+ alias F = floatTraits!(real);
+ static if (F.realFormat == RealFormat.ieeeDouble)
+ {
+ return pxs[0] == pys[0];
+ }
+ else static if (F.realFormat == RealFormat.ieeeQuadruple
+ || F.realFormat == RealFormat.ibmExtended)
+ {
+ return pxs[0] == pys[0] && pxs[1] == pys[1];
+ }
+ else
+ {
+ ushort* pxe = cast(ushort *)&x;
+ ushort* pye = cast(ushort *)&y;
+ return pxe[4] == pye[4] && pxs[0] == pys[0];
+ }
+}
+
+/*********************************
+ * Return 1 if sign bit of e is set, 0 if not.
+ */
+int signbit(X)(X x) @nogc @trusted pure nothrow
+{
+ alias F = floatTraits!(X);
+ return ((cast(ubyte *)&x)[F.SIGNPOS_BYTE] & 0x80) != 0;
+}
+
+///
+@nogc @safe pure nothrow unittest
+{
+ assert(!signbit(float.nan));
+ assert(signbit(-float.nan));
+ assert(!signbit(168.1234f));
+ assert(signbit(-168.1234f));
+ assert(!signbit(0.0f));
+ assert(signbit(-0.0f));
+ assert(signbit(-float.max));
+ assert(!signbit(float.max));
+
+ assert(!signbit(double.nan));
+ assert(signbit(-double.nan));
+ assert(!signbit(168.1234));
+ assert(signbit(-168.1234));
+ assert(!signbit(0.0));
+ assert(signbit(-0.0));
+ assert(signbit(-double.max));
+ assert(!signbit(double.max));
+
+ assert(!signbit(real.nan));
+ assert(signbit(-real.nan));
+ assert(!signbit(168.1234L));
+ assert(signbit(-168.1234L));
+ assert(!signbit(0.0L));
+ assert(signbit(-0.0L));
+ assert(signbit(-real.max));
+ assert(!signbit(real.max));
+}
+
+
+/*********************************
+ * 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))
+{
+ ubyte* pto = cast(ubyte *)&to;
+ const ubyte* pfrom = cast(ubyte *)&from;
+
+ alias T = floatTraits!(R);
+ alias F = floatTraits!(X);
+ pto[T.SIGNPOS_BYTE] &= 0x7F;
+ pto[T.SIGNPOS_BYTE] |= pfrom[F.SIGNPOS_BYTE] & 0x80;
+ return to;
+}
+
+// ditto
+R copysign(R, X)(X to, R from) @trusted pure nothrow @nogc
+if (isIntegral!(X) && isFloatingPoint!(R))
+{
+ return copysign(cast(R) to, from);
+}
+
+@safe pure nothrow @nogc unittest
+{
+ import std.meta : AliasSeq;
+
+ foreach (X; AliasSeq!(float, double, real, int, long))
+ {
+ foreach (Y; AliasSeq!(float, double, real))
+ (){ // avoid slow optimizations for large functions @@@BUG@@@ 2396
+ X x = 21;
+ Y y = 23.8;
+ Y e = void;
+
+ e = copysign(x, y);
+ assert(e == 21.0);
+
+ e = copysign(-x, y);
+ assert(e == 21.0);
+
+ e = copysign(x, -y);
+ assert(e == -21.0);
+
+ e = copysign(-x, -y);
+ assert(e == -21.0);
+
+ static if (isFloatingPoint!X)
+ {
+ e = copysign(X.nan, y);
+ assert(isNaN(e) && !signbit(e));
+
+ e = copysign(X.nan, -y);
+ assert(isNaN(e) && signbit(e));
+ }
+ }();
+ }
+}
+
+/*********************************
+Returns $(D -1) if $(D x < 0), $(D x) if $(D x == 0), $(D 1) if
+$(D x > 0), and $(NAN) if x==$(NAN).
+ */
+F sgn(F)(F x) @safe pure nothrow @nogc
+{
+ // @@@TODO@@@: make this faster
+ return x > 0 ? 1 : x < 0 ? -1 : x;
+}
+
+///
+@safe pure nothrow @nogc unittest
+{
+ assert(sgn(168.1234) == 1);
+ assert(sgn(-168.1234) == -1);
+ assert(sgn(0.0) == 0);
+ assert(sgn(-0.0) == 0);
+}
+
+// Functions for NaN payloads
+/*
+ * A 'payload' can be stored in the significand of a $(NAN). One bit is required
+ * to distinguish between a quiet and a signalling $(NAN). This leaves 22 bits
+ * of payload for a float; 51 bits for a double; 62 bits for an 80-bit real;
+ * and 111 bits for a 128-bit quad.
+*/
+/**
+ * Create a quiet $(NAN), storing an integer inside the payload.
+ *
+ * For floats, the largest possible payload is 0x3F_FFFF.
+ * For doubles, it is 0x3_FFFF_FFFF_FFFF.
+ * For 80-bit or 128-bit reals, it is 0x3FFF_FFFF_FFFF_FFFF.
+ */
+real NaN(ulong payload) @trusted pure nothrow @nogc
+{
+ alias F = floatTraits!(real);
+ static if (F.realFormat == RealFormat.ieeeExtended)
+ {
+ // real80 (in x86 real format, the implied bit is actually
+ // not implied but a real bit which is stored in the real)
+ ulong v = 3; // implied bit = 1, quiet bit = 1
+ }
+ else
+ {
+ ulong v = 1; // no implied bit. quiet bit = 1
+ }
+
+ ulong a = payload;
+
+ // 22 Float bits
+ ulong w = a & 0x3F_FFFF;
+ a -= w;
+
+ v <<=22;
+ v |= w;
+ a >>=22;
+
+ // 29 Double bits
+ v <<=29;
+ w = a & 0xFFF_FFFF;
+ v |= w;
+ a -= w;
+ a >>=29;
+
+ static if (F.realFormat == RealFormat.ieeeDouble)
+ {
+ v |= 0x7FF0_0000_0000_0000;
+ real x;
+ * cast(ulong *)(&x) = v;
+ return x;
+ }
+ else
+ {
+ v <<=11;
+ a &= 0x7FF;
+ v |= a;
+ real x = real.nan;
+
+ // Extended real bits
+ static if (F.realFormat == RealFormat.ieeeQuadruple)
+ {
+ v <<= 1; // there's no implicit bit
+
+ version (LittleEndian)
+ {
+ *cast(ulong*)(6+cast(ubyte*)(&x)) = v;
+ }
+ else
+ {
+ *cast(ulong*)(2+cast(ubyte*)(&x)) = v;
+ }
+ }
+ else
+ {
+ *cast(ulong *)(&x) = v;
+ }
+ return x;
+ }
+}
+
+@system pure nothrow @nogc unittest // not @safe because taking address of local.
+{
+ static if (floatTraits!(real).realFormat == RealFormat.ieeeDouble)
+ {
+ auto x = NaN(1);
+ auto xl = *cast(ulong*)&x;
+ assert(xl & 0x8_0000_0000_0000UL); //non-signaling bit, bit 52
+ assert((xl & 0x7FF0_0000_0000_0000UL) == 0x7FF0_0000_0000_0000UL); //all exp bits set
+ }
+}
+
+/**
+ * Extract an integral payload from a $(NAN).
+ *
+ * Returns:
+ * the integer payload as a ulong.
+ *
+ * For floats, the largest possible payload is 0x3F_FFFF.
+ * For doubles, it is 0x3_FFFF_FFFF_FFFF.
+ * For 80-bit or 128-bit reals, it is 0x3FFF_FFFF_FFFF_FFFF.
+ */
+ulong getNaNPayload(real x) @trusted pure nothrow @nogc
+{
+ // assert(isNaN(x));
+ alias F = floatTraits!(real);
+ static if (F.realFormat == RealFormat.ieeeDouble)
+ {
+ ulong m = *cast(ulong *)(&x);
+ // Make it look like an 80-bit significand.
+ // Skip exponent, and quiet bit
+ m &= 0x0007_FFFF_FFFF_FFFF;
+ m <<= 11;
+ }
+ else static if (F.realFormat == RealFormat.ieeeQuadruple)
+ {
+ version (LittleEndian)
+ {
+ ulong m = *cast(ulong*)(6+cast(ubyte*)(&x));
+ }
+ else
+ {
+ ulong m = *cast(ulong*)(2+cast(ubyte*)(&x));
+ }
+
+ m >>= 1; // there's no implicit bit
+ }
+ else
+ {
+ ulong m = *cast(ulong *)(&x);
+ }
+
+ // ignore implicit bit and quiet bit
+
+ const ulong f = m & 0x3FFF_FF00_0000_0000L;
+
+ ulong w = f >>> 40;
+ w |= (m & 0x00FF_FFFF_F800L) << (22 - 11);
+ w |= (m & 0x7FF) << 51;
+ return w;
+}
+
+debug(UnitTest)
+{
+ @safe pure nothrow @nogc unittest
+ {
+ real nan4 = NaN(0x789_ABCD_EF12_3456);
+ static if (floatTraits!(real).realFormat == RealFormat.ieeeExtended
+ || floatTraits!(real).realFormat == RealFormat.ieeeQuadruple)
+ {
+ assert(getNaNPayload(nan4) == 0x789_ABCD_EF12_3456);
+ }
+ else
+ {
+ assert(getNaNPayload(nan4) == 0x1_ABCD_EF12_3456);
+ }
+ double nan5 = nan4;
+ assert(getNaNPayload(nan5) == 0x1_ABCD_EF12_3456);
+ float nan6 = nan4;
+ assert(getNaNPayload(nan6) == 0x12_3456);
+ nan4 = NaN(0xFABCD);
+ assert(getNaNPayload(nan4) == 0xFABCD);
+ nan6 = nan4;
+ assert(getNaNPayload(nan6) == 0xFABCD);
+ nan5 = NaN(0x100_0000_0000_3456);
+ assert(getNaNPayload(nan5) == 0x0000_0000_3456);
+ }
+}
+
+/**
+ * Calculate the next largest floating point value after x.
+ *
+ * Return the least number greater than x that is representable as a real;
+ * thus, it gives the next point on the IEEE number line.
+ *
+ * $(TABLE_SV
+ * $(SVH x, nextUp(x) )
+ * $(SV -$(INFIN), -real.max )
+ * $(SV $(PLUSMN)0.0, real.min_normal*real.epsilon )
+ * $(SV real.max, $(INFIN) )
+ * $(SV $(INFIN), $(INFIN) )
+ * $(SV $(NAN), $(NAN) )
+ * )
+ */
+real nextUp(real x) @trusted pure nothrow @nogc
+{
+ alias F = floatTraits!(real);
+ static if (F.realFormat == RealFormat.ieeeDouble)
+ {
+ return nextUp(cast(double) x);
+ }
+ else static if (F.realFormat == RealFormat.ieeeQuadruple)
+ {
+ ushort e = F.EXPMASK & (cast(ushort *)&x)[F.EXPPOS_SHORT];
+ if (e == F.EXPMASK)
+ {
+ // NaN or Infinity
+ if (x == -real.infinity) return -real.max;
+ return x; // +Inf and NaN are unchanged.
+ }
+
+ auto ps = cast(ulong *)&x;
+ if (ps[MANTISSA_MSB] & 0x8000_0000_0000_0000)
+ {
+ // Negative number
+ if (ps[MANTISSA_LSB] == 0 && ps[MANTISSA_MSB] == 0x8000_0000_0000_0000)
+ {
+ // it was negative zero, change to smallest subnormal
+ ps[MANTISSA_LSB] = 1;
+ ps[MANTISSA_MSB] = 0;
+ return x;
+ }
+ if (ps[MANTISSA_LSB] == 0) --ps[MANTISSA_MSB];
+ --ps[MANTISSA_LSB];
+ }
+ else
+ {
+ // Positive number
+ ++ps[MANTISSA_LSB];
+ if (ps[MANTISSA_LSB] == 0) ++ps[MANTISSA_MSB];
+ }
+ return x;
+ }
+ else static if (F.realFormat == RealFormat.ieeeExtended)
+ {
+ // For 80-bit reals, the "implied bit" is a nuisance...
+ ushort *pe = cast(ushort *)&x;
+ ulong *ps = cast(ulong *)&x;
+
+ if ((pe[F.EXPPOS_SHORT] & F.EXPMASK) == F.EXPMASK)
+ {
+ // First, deal with NANs and infinity
+ if (x == -real.infinity) return -real.max;
+ return x; // +Inf and NaN are unchanged.
+ }
+ if (pe[F.EXPPOS_SHORT] & 0x8000)
+ {
+ // Negative number -- need to decrease the significand
+ --*ps;
+ // Need to mask with 0x7FFF... so subnormals are treated correctly.
+ if ((*ps & 0x7FFF_FFFF_FFFF_FFFF) == 0x7FFF_FFFF_FFFF_FFFF)
+ {
+ if (pe[F.EXPPOS_SHORT] == 0x8000) // it was negative zero
+ {
+ *ps = 1;
+ pe[F.EXPPOS_SHORT] = 0; // smallest subnormal.
+ return x;
+ }
+
+ --pe[F.EXPPOS_SHORT];
+
+ if (pe[F.EXPPOS_SHORT] == 0x8000)
+ return x; // it's become a subnormal, implied bit stays low.
+
+ *ps = 0xFFFF_FFFF_FFFF_FFFF; // set the implied bit
+ return x;
+ }
+ return x;
+ }
+ else
+ {
+ // Positive number -- need to increase the significand.
+ // Works automatically for positive zero.
+ ++*ps;
+ if ((*ps & 0x7FFF_FFFF_FFFF_FFFF) == 0)
+ {
+ // change in exponent
+ ++pe[F.EXPPOS_SHORT];
+ *ps = 0x8000_0000_0000_0000; // set the high bit
+ }
+ }
+ return x;
+ }
+ else // static if (F.realFormat == RealFormat.ibmExtended)
+ {
+ assert(0, "nextUp not implemented");
+ }
+}
+
+/** ditto */
+double nextUp(double x) @trusted pure nothrow @nogc
+{
+ ulong *ps = cast(ulong *)&x;
+
+ if ((*ps & 0x7FF0_0000_0000_0000) == 0x7FF0_0000_0000_0000)
+ {
+ // First, deal with NANs and infinity
+ if (x == -x.infinity) return -x.max;
+ return x; // +INF and NAN are unchanged.
+ }
+ if (*ps & 0x8000_0000_0000_0000) // Negative number
+ {
+ if (*ps == 0x8000_0000_0000_0000) // it was negative zero
+ {
+ *ps = 0x0000_0000_0000_0001; // change to smallest subnormal
+ return x;
+ }
+ --*ps;
+ }
+ else
+ { // Positive number
+ ++*ps;
+ }
+ return x;
+}
+
+/** ditto */
+float nextUp(float x) @trusted pure nothrow @nogc
+{
+ uint *ps = cast(uint *)&x;
+
+ if ((*ps & 0x7F80_0000) == 0x7F80_0000)
+ {
+ // First, deal with NANs and infinity
+ if (x == -x.infinity) return -x.max;
+
+ return x; // +INF and NAN are unchanged.
+ }
+ if (*ps & 0x8000_0000) // Negative number
+ {
+ if (*ps == 0x8000_0000) // it was negative zero
+ {
+ *ps = 0x0000_0001; // change to smallest subnormal
+ return x;
+ }
+
+ --*ps;
+ }
+ else
+ {
+ // Positive number
+ ++*ps;
+ }
+ return x;
+}
+
+/**
+ * Calculate the next smallest floating point value before x.
+ *
+ * Return the greatest number less than x that is representable as a real;
+ * thus, it gives the previous point on the IEEE number line.
+ *
+ * $(TABLE_SV
+ * $(SVH x, nextDown(x) )
+ * $(SV $(INFIN), real.max )
+ * $(SV $(PLUSMN)0.0, -real.min_normal*real.epsilon )
+ * $(SV -real.max, -$(INFIN) )
+ * $(SV -$(INFIN), -$(INFIN) )
+ * $(SV $(NAN), $(NAN) )
+ * )
+ */
+real nextDown(real x) @safe pure nothrow @nogc
+{
+ return -nextUp(-x);
+}
+
+/** ditto */
+double nextDown(double x) @safe pure nothrow @nogc
+{
+ return -nextUp(-x);
+}
+
+/** ditto */
+float nextDown(float x) @safe pure nothrow @nogc
+{
+ return -nextUp(-x);
+}
+
+///
+@safe pure nothrow @nogc unittest
+{
+ assert( nextDown(1.0 + real.epsilon) == 1.0);
+}
+
+@safe pure nothrow @nogc unittest
+{
+ static if (floatTraits!(real).realFormat == RealFormat.ieeeExtended)
+ {
+
+ // Tests for 80-bit reals
+ assert(isIdentical(nextUp(NaN(0xABC)), NaN(0xABC)));
+ // negative numbers
+ assert( nextUp(-real.infinity) == -real.max );
+ assert( nextUp(-1.0L-real.epsilon) == -1.0 );
+ assert( nextUp(-2.0L) == -2.0 + real.epsilon);
+ // subnormals and zero
+ assert( nextUp(-real.min_normal) == -real.min_normal*(1-real.epsilon) );
+ assert( nextUp(-real.min_normal*(1-real.epsilon)) == -real.min_normal*(1-2*real.epsilon) );
+ assert( isIdentical(-0.0L, nextUp(-real.min_normal*real.epsilon)) );
+ assert( nextUp(-0.0L) == real.min_normal*real.epsilon );
+ assert( nextUp(0.0L) == real.min_normal*real.epsilon );
+ assert( nextUp(real.min_normal*(1-real.epsilon)) == real.min_normal );
+ assert( nextUp(real.min_normal) == real.min_normal*(1+real.epsilon) );
+ // positive numbers
+ assert( nextUp(1.0L) == 1.0 + real.epsilon );
+ assert( nextUp(2.0L-real.epsilon) == 2.0 );
+ assert( nextUp(real.max) == real.infinity );
+ assert( nextUp(real.infinity)==real.infinity );
+ }
+
+ double n = NaN(0xABC);
+ assert(isIdentical(nextUp(n), n));
+ // negative numbers
+ assert( nextUp(-double.infinity) == -double.max );
+ assert( nextUp(-1-double.epsilon) == -1.0 );
+ assert( nextUp(-2.0) == -2.0 + double.epsilon);
+ // subnormals and zero
+
+ assert( nextUp(-double.min_normal) == -double.min_normal*(1-double.epsilon) );
+ assert( nextUp(-double.min_normal*(1-double.epsilon)) == -double.min_normal*(1-2*double.epsilon) );
+ assert( isIdentical(-0.0, nextUp(-double.min_normal*double.epsilon)) );
+ assert( nextUp(0.0) == double.min_normal*double.epsilon );
+ assert( nextUp(-0.0) == double.min_normal*double.epsilon );
+ assert( nextUp(double.min_normal*(1-double.epsilon)) == double.min_normal );
+ assert( nextUp(double.min_normal) == double.min_normal*(1+double.epsilon) );
+ // positive numbers
+ assert( nextUp(1.0) == 1.0 + double.epsilon );
+ assert( nextUp(2.0-double.epsilon) == 2.0 );
+ assert( nextUp(double.max) == double.infinity );
+
+ float fn = NaN(0xABC);
+ assert(isIdentical(nextUp(fn), fn));
+ float f = -float.min_normal*(1-float.epsilon);
+ float f1 = -float.min_normal;
+ assert( nextUp(f1) == f);
+ f = 1.0f+float.epsilon;
+ f1 = 1.0f;
+ assert( nextUp(f1) == f );
+ f1 = -0.0f;
+ assert( nextUp(f1) == float.min_normal*float.epsilon);
+ assert( nextUp(float.infinity)==float.infinity );
+
+ assert(nextDown(1.0L+real.epsilon)==1.0);
+ assert(nextDown(1.0+double.epsilon)==1.0);
+ f = 1.0f+float.epsilon;
+ assert(nextDown(f)==1.0);
+ assert(nextafter(1.0+real.epsilon, -real.infinity)==1.0);
+}
+
+
+
+/******************************************
+ * Calculates the next representable value after x in the direction of y.
+ *
+ * If y > x, the result will be the next largest floating-point value;
+ * if y < x, the result will be the next smallest value.
+ * If x == y, the result is y.
+ *
+ * Remarks:
+ * This function is not generally very useful; it's almost always better to use
+ * the faster functions nextUp() or nextDown() instead.
+ *
+ * The FE_INEXACT and FE_OVERFLOW exceptions will be raised if x is finite and
+ * the function result is infinite. The FE_INEXACT and FE_UNDERFLOW
+ * exceptions will be raised if the function value is subnormal, and x is
+ * not equal to y.
+ */
+T nextafter(T)(const T x, const T y) @safe pure nothrow @nogc
+{
+ if (x == y) return y;
+ return ((y>x) ? nextUp(x) : nextDown(x));
+}
+
+///
+@safe pure nothrow @nogc unittest
+{
+ float a = 1;
+ assert(is(typeof(nextafter(a, a)) == float));
+ assert(nextafter(a, a.infinity) > a);
+
+ double b = 2;
+ assert(is(typeof(nextafter(b, b)) == double));
+ assert(nextafter(b, b.infinity) > b);
+
+ real c = 3;
+ assert(is(typeof(nextafter(c, c)) == real));
+ assert(nextafter(c, c.infinity) > c);
+}
+
+//real nexttoward(real x, real y) { return core.stdc.math.nexttowardl(x, y); }
+
+/*******************************************
+ * Returns the positive difference between x and y.
+ * Returns:
+ * $(TABLE_SV
+ * $(TR $(TH x, y) $(TH fdim(x, y)))
+ * $(TR $(TD x $(GT) y) $(TD x - y))
+ * $(TR $(TD x $(LT)= y) $(TD +0.0))
+ * )
+ */
+real fdim(real x, real y) @safe pure nothrow @nogc { return (x > y) ? x - y : +0.0; }
+
+/****************************************
+ * Returns the larger of x and y.
+ */
+real fmax(real x, real y) @safe pure nothrow @nogc { return x > y ? x : y; }
+
+/****************************************
+ * Returns the smaller of x and y.
+ */
+real fmin(real x, real y) @safe pure nothrow @nogc { return x < y ? x : y; }
+
+/**************************************
+ * Returns (x * y) + z, rounding only once according to the
+ * current rounding mode.
+ *
+ * BUGS: Not currently implemented - rounds twice.
+ */
+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))
+{
+ import std.traits : Unsigned;
+ real p = 1.0, v = void;
+ Unsigned!(Unqual!G) m = n;
+ if (n < 0)
+ {
+ switch (n)
+ {
+ case -1:
+ return 1 / x;
+ case -2:
+ return 1 / (x * x);
+ default:
+ }
+
+ m = cast(typeof(m))(0 - n);
+ v = p / x;
+ }
+ else
+ {
+ switch (n)
+ {
+ case 0:
+ return 1.0;
+ case 1:
+ return x;
+ case 2:
+ return x * x;
+ default:
+ }
+
+ v = x;
+ }
+
+ while (1)
+ {
+ if (m & 1)
+ p *= v;
+ m >>= 1;
+ if (!m)
+ break;
+ v *= v;
+ }
+ return p;
+}
+
+@safe pure nothrow @nogc unittest
+{
+ // Make sure it instantiates and works properly on immutable values and
+ // with various integer and float types.
+ immutable real x = 46;
+ immutable float xf = x;
+ immutable double xd = x;
+ immutable uint one = 1;
+ immutable ushort two = 2;
+ immutable ubyte three = 3;
+ immutable ulong eight = 8;
+
+ immutable int neg1 = -1;
+ immutable short neg2 = -2;
+ immutable byte neg3 = -3;
+ immutable long neg8 = -8;
+
+
+ assert(pow(x,0) == 1.0);
+ assert(pow(xd,one) == x);
+ assert(pow(xf,two) == x * x);
+ assert(pow(x,three) == x * x * x);
+ assert(pow(x,eight) == (x * x) * (x * x) * (x * x) * (x * x));
+
+ assert(pow(x, neg1) == 1 / x);
+
+ version (X86_64)
+ {
+ pragma(msg, "test disabled on x86_64, see bug 5628");
+ }
+ else version (ARM)
+ {
+ pragma(msg, "test disabled on ARM, see bug 5628");
+ }
+ else
+ {
+ assert(pow(xd, neg2) == 1 / (x * x));
+ assert(pow(xf, neg8) == 1 / ((x * x) * (x * x) * (x * x) * (x * x)));
+ }
+
+ assert(feqrel(pow(x, neg3), 1 / (x * x * x)) >= real.mant_dig - 1);
+}
+
+@system unittest
+{
+ assert(equalsDigit(pow(2.0L, 10.0L), 1024, 19));
+}
+
+/** Compute the value of an integer x, raised to the power of a positive
+ * integer n.
+ *
+ * If both x and n are 0, the result is 1.
+ * If n is negative, an integer divide error will occur at runtime,
+ * 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 (n<0) return x/0; // Only support positive powers
+ typeof(return) p, v = void;
+ Unqual!G m = n;
+
+ switch (m)
+ {
+ case 0:
+ p = 1;
+ break;
+
+ case 1:
+ p = x;
+ break;
+
+ case 2:
+ p = x * x;
+ break;
+
+ default:
+ v = x;
+ p = 1;
+ while (1)
+ {
+ if (m & 1)
+ p *= v;
+ m >>= 1;
+ if (!m)
+ break;
+ v *= v;
+ }
+ break;
+ }
+ return p;
+}
+
+///
+@safe pure nothrow @nogc unittest
+{
+ immutable int one = 1;
+ immutable byte two = 2;
+ immutable ubyte three = 3;
+ immutable short four = 4;
+ immutable long ten = 10;
+
+ assert(pow(two, three) == 8);
+ assert(pow(two, ten) == 1024);
+ assert(pow(one, ten) == 1);
+ assert(pow(ten, four) == 10_000);
+ assert(pow(four, 10) == 1_048_576);
+ assert(pow(three, four) == 81);
+
+}
+
+/**Computes integer to floating point powers.*/
+real pow(I, F)(I x, F y) @nogc @trusted pure nothrow
+if (isIntegral!I && isFloatingPoint!F)
+{
+ return pow(cast(real) x, cast(Unqual!F) y);
+}
+
+/*********************************************
+ * Calculates x$(SUPERSCRIPT y).
+ *
+ * $(TABLE_SV
+ * $(TR $(TH x) $(TH y) $(TH pow(x, y))
+ * $(TH div 0) $(TH invalid?))
+ * $(TR $(TD anything) $(TD $(PLUSMN)0.0) $(TD 1.0)
+ * $(TD no) $(TD no) )
+ * $(TR $(TD |x| $(GT) 1) $(TD +$(INFIN)) $(TD +$(INFIN))
+ * $(TD no) $(TD no) )
+ * $(TR $(TD |x| $(LT) 1) $(TD +$(INFIN)) $(TD +0.0)
+ * $(TD no) $(TD no) )
+ * $(TR $(TD |x| $(GT) 1) $(TD -$(INFIN)) $(TD +0.0)
+ * $(TD no) $(TD no) )
+ * $(TR $(TD |x| $(LT) 1) $(TD -$(INFIN)) $(TD +$(INFIN))
+ * $(TD no) $(TD no) )
+ * $(TR $(TD +$(INFIN)) $(TD $(GT) 0.0) $(TD +$(INFIN))
+ * $(TD no) $(TD no) )
+ * $(TR $(TD +$(INFIN)) $(TD $(LT) 0.0) $(TD +0.0)
+ * $(TD no) $(TD no) )
+ * $(TR $(TD -$(INFIN)) $(TD odd integer $(GT) 0.0) $(TD -$(INFIN))
+ * $(TD no) $(TD no) )
+ * $(TR $(TD -$(INFIN)) $(TD $(GT) 0.0, not odd integer) $(TD +$(INFIN))
+ * $(TD no) $(TD no))
+ * $(TR $(TD -$(INFIN)) $(TD odd integer $(LT) 0.0) $(TD -0.0)
+ * $(TD no) $(TD no) )
+ * $(TR $(TD -$(INFIN)) $(TD $(LT) 0.0, not odd integer) $(TD +0.0)
+ * $(TD no) $(TD no) )
+ * $(TR $(TD $(PLUSMN)1.0) $(TD $(PLUSMN)$(INFIN)) $(TD $(NAN))
+ * $(TD no) $(TD yes) )
+ * $(TR $(TD $(LT) 0.0) $(TD finite, nonintegral) $(TD $(NAN))
+ * $(TD no) $(TD yes))
+ * $(TR $(TD $(PLUSMN)0.0) $(TD odd integer $(LT) 0.0) $(TD $(PLUSMNINF))
+ * $(TD yes) $(TD no) )
+ * $(TR $(TD $(PLUSMN)0.0) $(TD $(LT) 0.0, not odd integer) $(TD +$(INFIN))
+ * $(TD yes) $(TD no))
+ * $(TR $(TD $(PLUSMN)0.0) $(TD odd integer $(GT) 0.0) $(TD $(PLUSMN)0.0)
+ * $(TD no) $(TD no) )
+ * $(TR $(TD $(PLUSMN)0.0) $(TD $(GT) 0.0, not odd integer) $(TD +0.0)
+ * $(TD no) $(TD no) )
+ * )
+ */
+Unqual!(Largest!(F, G)) pow(F, G)(F x, G y) @nogc @trusted pure nothrow
+if (isFloatingPoint!(F) && isFloatingPoint!(G))
+{
+ alias Float = typeof(return);
+
+ static real impl(real x, real y) @nogc pure nothrow
+ {
+ // Special cases.
+ if (isNaN(y))
+ return y;
+ if (isNaN(x) && y != 0.0)
+ return x;
+
+ // Even if x is NaN.
+ if (y == 0.0)
+ return 1.0;
+ if (y == 1.0)
+ return x;
+
+ if (isInfinity(y))
+ {
+ if (fabs(x) > 1)
+ {
+ if (signbit(y))
+ return +0.0;
+ else
+ return F.infinity;
+ }
+ else if (fabs(x) == 1)
+ {
+ return y * 0; // generate NaN.
+ }
+ else // < 1
+ {
+ if (signbit(y))
+ return F.infinity;
+ else
+ return +0.0;
+ }
+ }
+ if (isInfinity(x))
+ {
+ if (signbit(x))
+ {
+ long i = cast(long) y;
+ if (y > 0.0)
+ {
+ if (i == y && i & 1)
+ return -F.infinity;
+ else
+ return F.infinity;
+ }
+ else if (y < 0.0)
+ {
+ if (i == y && i & 1)
+ return -0.0;
+ else
+ return +0.0;
+ }
+ }
+ else
+ {
+ if (y > 0.0)
+ return F.infinity;
+ else if (y < 0.0)
+ return +0.0;
+ }
+ }
+
+ if (x == 0.0)
+ {
+ if (signbit(x))
+ {
+ long i = cast(long) y;
+ if (y > 0.0)
+ {
+ if (i == y && i & 1)
+ return -0.0;
+ else
+ return +0.0;
+ }
+ else if (y < 0.0)
+ {
+ if (i == y && i & 1)
+ return -F.infinity;
+ else
+ return F.infinity;
+ }
+ }
+ else
+ {
+ if (y > 0.0)
+ return +0.0;
+ else if (y < 0.0)
+ return F.infinity;
+ }
+ }
+ if (x == 1.0)
+ return 1.0;
+
+ if (y >= F.max)
+ {
+ if ((x > 0.0 && x < 1.0) || (x > -1.0 && x < 0.0))
+ return 0.0;
+ if (x > 1.0 || x < -1.0)
+ return F.infinity;
+ }
+ if (y <= -F.max)
+ {
+ if ((x > 0.0 && x < 1.0) || (x > -1.0 && x < 0.0))
+ return F.infinity;
+ if (x > 1.0 || x < -1.0)
+ return 0.0;
+ }
+
+ if (x >= F.max)
+ {
+ if (y > 0.0)
+ return F.infinity;
+ else
+ return 0.0;
+ }
+ if (x <= -F.max)
+ {
+ long i = cast(long) y;
+ if (y > 0.0)
+ {
+ if (i == y && i & 1)
+ return -F.infinity;
+ else
+ return F.infinity;
+ }
+ else if (y < 0.0)
+ {
+ if (i == y && i & 1)
+ return -0.0;
+ else
+ return +0.0;
+ }
+ }
+
+ // Integer power of x.
+ long iy = cast(long) y;
+ if (iy == y && fabs(y) < 32_768.0)
+ return pow(x, iy);
+
+ real sign = 1.0;
+ if (x < 0)
+ {
+ // Result is real only if y is an integer
+ // Check for a non-zero fractional part
+ enum maxOdd = pow(2.0L, real.mant_dig) - 1.0L;
+ static if (maxOdd > ulong.max)
+ {
+ // Generic method, for any FP type
+ if (floor(y) != y)
+ return sqrt(x); // Complex result -- create a NaN
+
+ 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;
+ }
+ version (INLINE_YL2X)
+ {
+ // If x > 0, x ^^ y == 2 ^^ ( y * log2(x) )
+ // TODO: This is not accurate in practice. A fast and accurate
+ // (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( core.math.yl2x(x, y) );
+ }
+ else
+ {
+ // If x > 0, x ^^ y == 2 ^^ ( y * log2(x) )
+ // TODO: This is not accurate in practice. A fast and accurate
+ // (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).
+ Float w = exp2(y * log2(x));
+ return sign * w;
+ }
+ }
+ return impl(x, y);
+}
+
+@safe pure nothrow @nogc unittest
+{
+ // Test all the special values. These unittests can be run on Windows
+ // by temporarily changing the version (linux) to version (all).
+ immutable float zero = 0;
+ immutable real one = 1;
+ immutable double two = 2;
+ immutable float three = 3;
+ immutable float fnan = float.nan;
+ immutable double dnan = double.nan;
+ immutable real rnan = real.nan;
+ immutable dinf = double.infinity;
+ immutable rninf = -real.infinity;
+
+ assert(pow(fnan, zero) == 1);
+ assert(pow(dnan, zero) == 1);
+ assert(pow(rnan, zero) == 1);
+
+ assert(pow(two, dinf) == double.infinity);
+ assert(isIdentical(pow(0.2f, dinf), +0.0));
+ assert(pow(0.99999999L, rninf) == real.infinity);
+ assert(isIdentical(pow(1.000000001, rninf), +0.0));
+ assert(pow(dinf, 0.001) == dinf);
+ assert(isIdentical(pow(dinf, -0.001), +0.0));
+ assert(pow(rninf, 3.0L) == rninf);
+ assert(pow(rninf, 2.0L) == real.infinity);
+ assert(isIdentical(pow(rninf, -3.0), -0.0));
+ assert(isIdentical(pow(rninf, -2.0), +0.0));
+
+ // @@@BUG@@@ somewhere
+ version (OSX) {} else assert(isNaN(pow(one, dinf)));
+ version (OSX) {} else assert(isNaN(pow(-one, dinf)));
+ assert(isNaN(pow(-0.2, PI)));
+ // boundary cases. Note that epsilon == 2^^-n for some n,
+ // so 1/epsilon == 2^^n is always even.
+ assert(pow(-1.0L, 1/real.epsilon - 1.0L) == -1.0L);
+ assert(pow(-1.0L, 1/real.epsilon) == 1.0L);
+ assert(isNaN(pow(-1.0L, 1/real.epsilon-0.5L)));
+ assert(isNaN(pow(-1.0L, -1/real.epsilon+0.5L)));
+
+ assert(pow(0.0, -3.0) == double.infinity);
+ assert(pow(-0.0, -3.0) == -double.infinity);
+ assert(pow(0.0, -PI) == double.infinity);
+ assert(pow(-0.0, -PI) == double.infinity);
+ assert(isIdentical(pow(0.0, 5.0), 0.0));
+ assert(isIdentical(pow(-0.0, 5.0), -0.0));
+ 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));
+
+ // Test integer to float power.
+ immutable uint twoI = 2;
+ assert(approxEqual(pow(twoI, three), 8.0));
+}
+
+/**************************************
+ * To what precision is x equal to y?
+ *
+ * Returns: the number of mantissa bits which are equal in x and y.
+ * eg, 0x1.F8p+60 and 0x1.F1p+60 are equal to 5 bits of precision.
+ *
+ * $(TABLE_SV
+ * $(TR $(TH x) $(TH y) $(TH feqrel(x, y)))
+ * $(TR $(TD x) $(TD x) $(TD real.mant_dig))
+ * $(TR $(TD x) $(TD $(GT)= 2*x) $(TD 0))
+ * $(TR $(TD x) $(TD $(LT)= x/2) $(TD 0))
+ * $(TR $(TD $(NAN)) $(TD any) $(TD 0))
+ * $(TR $(TD any) $(TD $(NAN)) $(TD 0))
+ * )
+ */
+int feqrel(X)(const X x, const X y) @trusted pure nothrow @nogc
+if (isFloatingPoint!(X))
+{
+ /* Public Domain. Author: Don Clugston, 18 Aug 2005.
+ */
+ alias F = floatTraits!(X);
+ static if (F.realFormat == RealFormat.ibmExtended)
+ {
+ if (cast(double*)(&x)[MANTISSA_MSB] == cast(double*)(&y)[MANTISSA_MSB])
+ {
+ return double.mant_dig
+ + feqrel(cast(double*)(&x)[MANTISSA_LSB],
+ cast(double*)(&y)[MANTISSA_LSB]);
+ }
+ else
+ {
+ return feqrel(cast(double*)(&x)[MANTISSA_MSB],
+ cast(double*)(&y)[MANTISSA_MSB]);
+ }
+ }
+ else
+ {
+ 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.
+
+ Unqual!X diff = fabs(x - y);
+
+ ushort *pa = cast(ushort *)(&x);
+ ushort *pb = cast(ushort *)(&y);
+ ushort *pd = cast(ushort *)(&diff);
+
+
+ // The difference in abs(exponent) between x or y and abs(x-y)
+ // is equal to the number of significand bits of x which are
+ // equal to y. If negative, x and y have different exponents.
+ // If positive, x and y are equal to 'bitsdiff' bits.
+ // 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,
+ // they could have 0 or 1 bits in common.
+
+ 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] & 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.
+ if (bitsdiff == 0
+ && ((pa[F.EXPPOS_SHORT] ^ pb[F.EXPPOS_SHORT]) & F.EXPMASK) == 0)
+ {
+ return 1;
+ } else return 0;
+ }
+}
+
+@safe pure nothrow @nogc unittest
+{
+ void testFeqrel(F)()
+ {
+ // Exact equality
+ assert(feqrel(F.max, F.max) == F.mant_dig);
+ assert(feqrel!(F)(0.0, 0.0) == F.mant_dig);
+ assert(feqrel(F.infinity, F.infinity) == F.mant_dig);
+
+ // a few bits away from exact equality
+ F w=1;
+ for (int i = 1; i < F.mant_dig - 1; ++i)
+ {
+ assert(feqrel!(F)(1.0 + w * F.epsilon, 1.0) == F.mant_dig-i);
+ assert(feqrel!(F)(1.0 - w * F.epsilon, 1.0) == F.mant_dig-i);
+ assert(feqrel!(F)(1.0, 1 + (w-1) * F.epsilon) == F.mant_dig - i + 1);
+ w*=2;
+ }
+
+ assert(feqrel!(F)(1.5+F.epsilon, 1.5) == F.mant_dig-1);
+ assert(feqrel!(F)(1.5-F.epsilon, 1.5) == F.mant_dig-1);
+ assert(feqrel!(F)(1.5-F.epsilon, 1.5+F.epsilon) == F.mant_dig-2);
+
+
+ // Numbers that are close
+ assert(feqrel!(F)(0x1.Bp+84, 0x1.B8p+84) == 5);
+ assert(feqrel!(F)(0x1.8p+10, 0x1.Cp+10) == 2);
+ assert(feqrel!(F)(1.5 * (1 - F.epsilon), 1.0L) == 2);
+ assert(feqrel!(F)(1.5, 1.0) == 1);
+ assert(feqrel!(F)(2 * (1 - F.epsilon), 1.0L) == 1);
+
+ // Factors of 2
+ assert(feqrel(F.max, F.infinity) == 0);
+ assert(feqrel!(F)(2 * (1 - F.epsilon), 1.0L) == 1);
+ assert(feqrel!(F)(1.0, 2.0) == 0);
+ assert(feqrel!(F)(4.0, 1.0) == 0);
+
+ // Extreme inequality
+ assert(feqrel(F.nan, F.nan) == 0);
+ assert(feqrel!(F)(0.0L, -F.nan) == 0);
+ assert(feqrel(F.nan, F.infinity) == 0);
+ 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;
+ auto Compiles = feqrel(Const, Immutable);
+ }
+
+ assert(feqrel(7.1824L, 7.1824L) == real.mant_dig);
+
+ testFeqrel!(real)();
+ testFeqrel!(double)();
+ testFeqrel!(float)();
+}
+
+package: // Not public yet
+/* Return the value that lies halfway between x and y on the IEEE number line.
+ *
+ * Formally, the result is the arithmetic mean of the binary significands of x
+ * and y, multiplied by the geometric mean of the binary exponents of x and y.
+ * x and y must have the same sign, and must not be NaN.
+ * Note: this function is useful for ensuring O(log n) behaviour in algorithms
+ * involving a 'binary chop'.
+ *
+ * Special cases:
+ * If x and y are within a factor of 2, (ie, feqrel(x, y) > 0), the return value
+ * is the arithmetic mean (x + y) / 2.
+ * If x and y are even powers of 2, the return value is the geometric mean,
+ * ieeeMean(x, y) = sqrt(x * y).
+ *
+ */
+T ieeeMean(T)(const T x, const T y) @trusted pure nothrow @nogc
+in
+{
+ // both x and y must have the same sign, and must not be NaN.
+ assert(signbit(x) == signbit(y));
+ assert(x == x && y == y);
+}
+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;
+
+ // The implementation is simple: cast x and y to integers,
+ // average them (avoiding overflow), and cast the result back to a floating-point number.
+
+ alias F = floatTraits!(T);
+ T u;
+ static if (F.realFormat == RealFormat.ieeeExtended)
+ {
+ // There's slight additional complexity because they are actually
+ // 79-bit reals...
+ ushort *ue = cast(ushort *)&u;
+ ulong *ul = cast(ulong *)&u;
+ ushort *xe = cast(ushort *)&x;
+ ulong *xl = cast(ulong *)&x;
+ ushort *ye = cast(ushort *)&y;
+ ulong *yl = cast(ulong *)&y;
+
+ // Ignore the useless implicit bit. (Bonus: this prevents overflows)
+ ulong m = ((*xl) & 0x7FFF_FFFF_FFFF_FFFFL) + ((*yl) & 0x7FFF_FFFF_FFFF_FFFFL);
+
+ // @@@ BUG? @@@
+ // Cast shouldn't be here
+ ushort e = cast(ushort) ((xe[F.EXPPOS_SHORT] & F.EXPMASK)
+ + (ye[F.EXPPOS_SHORT] & F.EXPMASK));
+ if (m & 0x8000_0000_0000_0000L)
+ {
+ ++e;
+ m &= 0x7FFF_FFFF_FFFF_FFFFL;
+ }
+ // Now do a multi-byte right shift
+ const uint c = e & 1; // carry
+ e >>= 1;
+ m >>>= 1;
+ if (c)
+ m |= 0x4000_0000_0000_0000L; // shift carry into significand
+ if (e)
+ *ul = m | 0x8000_0000_0000_0000L; // set implicit bit...
+ else
+ *ul = m; // ... unless exponent is 0 (subnormal or zero).
+
+ ue[4]= e | (xe[F.EXPPOS_SHORT]& 0x8000); // restore sign bit
+ }
+ else static if (F.realFormat == RealFormat.ieeeQuadruple)
+ {
+ // This would be trivial if 'ucent' were implemented...
+ ulong *ul = cast(ulong *)&u;
+ ulong *xl = cast(ulong *)&x;
+ ulong *yl = cast(ulong *)&y;
+
+ // Multi-byte add, then multi-byte right shift.
+ import core.checkedint : addu;
+ bool carry;
+ ulong ml = addu(xl[MANTISSA_LSB], yl[MANTISSA_LSB], carry);
+
+ ulong mh = carry + (xl[MANTISSA_MSB] & 0x7FFF_FFFF_FFFF_FFFFL) +
+ (yl[MANTISSA_MSB] & 0x7FFF_FFFF_FFFF_FFFFL);
+
+ ul[MANTISSA_MSB] = (mh >>> 1) | (xl[MANTISSA_MSB] & 0x8000_0000_0000_0000);
+ ul[MANTISSA_LSB] = (ml >>> 1) | (mh & 1) << 63;
+ }
+ else static if (F.realFormat == RealFormat.ieeeDouble)
+ {
+ ulong *ul = cast(ulong *)&u;
+ ulong *xl = cast(ulong *)&x;
+ ulong *yl = cast(ulong *)&y;
+ ulong m = (((*xl) & 0x7FFF_FFFF_FFFF_FFFFL)
+ + ((*yl) & 0x7FFF_FFFF_FFFF_FFFFL)) >>> 1;
+ m |= ((*xl) & 0x8000_0000_0000_0000L);
+ *ul = m;
+ }
+ else static if (F.realFormat == RealFormat.ieeeSingle)
+ {
+ uint *ul = cast(uint *)&u;
+ uint *xl = cast(uint *)&x;
+ uint *yl = cast(uint *)&y;
+ uint m = (((*xl) & 0x7FFF_FFFF) + ((*yl) & 0x7FFF_FFFF)) >>> 1;
+ m |= ((*xl) & 0x8000_0000);
+ *ul = m;
+ }
+ else
+ {
+ assert(0, "Not implemented");
+ }
+ return u;
+}
+
+@safe pure nothrow @nogc unittest
+{
+ assert(ieeeMean(-0.0,-1e-20)<0);
+ assert(ieeeMean(0.0,1e-20)>0);
+
+ assert(ieeeMean(1.0L,4.0L)==2L);
+ assert(ieeeMean(2.0*1.013,8.0*1.013)==4*1.013);
+ assert(ieeeMean(-1.0L,-4.0L)==-2L);
+ assert(ieeeMean(-1.0,-4.0)==-2);
+ assert(ieeeMean(-1.0f,-4.0f)==-2f);
+ assert(ieeeMean(-1.0,-2.0)==-1.5);
+ assert(ieeeMean(-1*(1+8*real.epsilon),-2*(1+8*real.epsilon))
+ ==-1.5*(1+5*real.epsilon));
+ assert(ieeeMean(0x1p60,0x1p-10)==0x1p25);
+
+ static if (floatTraits!(real).realFormat == RealFormat.ieeeExtended)
+ {
+ assert(ieeeMean(1.0L,real.infinity)==0x1p8192L);
+ assert(ieeeMean(0.0L,real.infinity)==1.5);
+ }
+ assert(ieeeMean(0.5*real.min_normal*(1-4*real.epsilon),0.5*real.min_normal)
+ == 0.5*real.min_normal*(1-2*real.epsilon));
+}
+
+public:
+
+
+/***********************************
+ * Evaluate polynomial A(x) = $(SUB a, 0) + $(SUB a, 1)x + $(SUB a, 2)$(POWER x,2)
+ * + $(SUB a,3)$(POWER x,3); ...
+ *
+ * Uses Horner's rule A(x) = $(SUB a, 0) + x($(SUB a, 1) + x($(SUB a, 2)
+ * + x($(SUB a, 3) + ...)))
+ * Params:
+ * x = the value to evaluate.
+ * 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)
+in
+{
+ assert(A.length > 0);
+}
+body
+{
+ static if (is(Unqual!T2 == real))
+ {
+ return polyImpl(x, A);
+ }
+ else
+ {
+ return polyImplBase(x, A);
+ }
+}
+
+///
+@safe nothrow @nogc unittest
+{
+ real x = 3.1;
+ static real[] pp = [56.1, 32.7, 6];
+
+ assert(poly(x, pp) == (56.1L + (32.7L + 6.0L * x) * x));
+}
+
+@safe nothrow @nogc unittest
+{
+ double x = 3.1;
+ static double[] pp = [56.1, 32.7, 6];
+ double y = x;
+ y *= 6.0;
+ y += 32.7;
+ y *= x;
+ y += 56.1;
+ assert(poly(x, pp) == y);
+}
+
+@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)
+{
+ ptrdiff_t i = A.length - 1;
+ typeof(return) r = A[i];
+ while (--i >= 0)
+ {
+ r *= x;
+ r += A[i];
+ }
+ return r;
+}
+
+private real polyImpl(real x, in real[] A) @trusted pure nothrow @nogc
+{
+ version (D_InlineAsm_X86)
+ {
+ if (__ctfe)
+ {
+ return polyImplBase(x, A);
+ }
+ version (Windows)
+ {
+ // BUG: This code assumes a frame pointer in EBP.
+ asm pure nothrow @nogc // assembler by W. Bright
+ {
+ // EDX = (A.length - 1) * real.sizeof
+ mov ECX,A[EBP] ; // ECX = A.length
+ dec ECX ;
+ lea EDX,[ECX][ECX*8] ;
+ add EDX,ECX ;
+ add EDX,A+4[EBP] ;
+ fld real ptr [EDX] ; // ST0 = coeff[ECX]
+ jecxz return_ST ;
+ fld x[EBP] ; // ST0 = x
+ fxch ST(1) ; // ST1 = x, ST0 = r
+ align 4 ;
+ L2: fmul ST,ST(1) ; // r *= x
+ fld real ptr -10[EDX] ;
+ sub EDX,10 ; // deg--
+ faddp ST(1),ST ;
+ dec ECX ;
+ jne L2 ;
+ fxch ST(1) ; // ST1 = r, ST0 = x
+ fstp ST(0) ; // dump x
+ align 4 ;
+ return_ST: ;
+ ;
+ }
+ }
+ else version (linux)
+ {
+ asm pure nothrow @nogc // assembler by W. Bright
+ {
+ // EDX = (A.length - 1) * real.sizeof
+ mov ECX,A[EBP] ; // ECX = A.length
+ dec ECX ;
+ lea EDX,[ECX*8] ;
+ lea EDX,[EDX][ECX*4] ;
+ add EDX,A+4[EBP] ;
+ fld real ptr [EDX] ; // ST0 = coeff[ECX]
+ jecxz return_ST ;
+ fld x[EBP] ; // ST0 = x
+ fxch ST(1) ; // ST1 = x, ST0 = r
+ align 4 ;
+ L2: fmul ST,ST(1) ; // r *= x
+ fld real ptr -12[EDX] ;
+ sub EDX,12 ; // deg--
+ faddp ST(1),ST ;
+ dec ECX ;
+ jne L2 ;
+ fxch ST(1) ; // ST1 = r, ST0 = x
+ fstp ST(0) ; // dump x
+ align 4 ;
+ return_ST: ;
+ ;
+ }
+ }
+ else version (OSX)
+ {
+ asm pure nothrow @nogc // assembler by W. Bright
+ {
+ // EDX = (A.length - 1) * real.sizeof
+ mov ECX,A[EBP] ; // ECX = A.length
+ dec ECX ;
+ lea EDX,[ECX*8] ;
+ add EDX,EDX ;
+ add EDX,A+4[EBP] ;
+ fld real ptr [EDX] ; // ST0 = coeff[ECX]
+ jecxz return_ST ;
+ fld x[EBP] ; // ST0 = x
+ fxch ST(1) ; // ST1 = x, ST0 = r
+ align 4 ;
+ L2: fmul ST,ST(1) ; // r *= x
+ fld real ptr -16[EDX] ;
+ sub EDX,16 ; // deg--
+ faddp ST(1),ST ;
+ dec ECX ;
+ jne L2 ;
+ fxch ST(1) ; // ST1 = r, ST0 = x
+ fstp ST(0) ; // dump x
+ align 4 ;
+ return_ST: ;
+ ;
+ }
+ }
+ else version (FreeBSD)
+ {
+ asm pure nothrow @nogc // assembler by W. Bright
+ {
+ // EDX = (A.length - 1) * real.sizeof
+ mov ECX,A[EBP] ; // ECX = A.length
+ dec ECX ;
+ lea EDX,[ECX*8] ;
+ lea EDX,[EDX][ECX*4] ;
+ add EDX,A+4[EBP] ;
+ fld real ptr [EDX] ; // ST0 = coeff[ECX]
+ jecxz return_ST ;
+ fld x[EBP] ; // ST0 = x
+ fxch ST(1) ; // ST1 = x, ST0 = r
+ align 4 ;
+ L2: fmul ST,ST(1) ; // r *= x
+ fld real ptr -12[EDX] ;
+ sub EDX,12 ; // deg--
+ faddp ST(1),ST ;
+ dec ECX ;
+ jne L2 ;
+ fxch ST(1) ; // ST1 = r, ST0 = x
+ fstp ST(0) ; // dump x
+ align 4 ;
+ return_ST: ;
+ ;
+ }
+ }
+ else version (Solaris)
+ {
+ asm pure nothrow @nogc // assembler by W. Bright
+ {
+ // EDX = (A.length - 1) * real.sizeof
+ mov ECX,A[EBP] ; // ECX = A.length
+ dec ECX ;
+ lea EDX,[ECX*8] ;
+ lea EDX,[EDX][ECX*4] ;
+ add EDX,A+4[EBP] ;
+ fld real ptr [EDX] ; // ST0 = coeff[ECX]
+ jecxz return_ST ;
+ fld x[EBP] ; // ST0 = x
+ fxch ST(1) ; // ST1 = x, ST0 = r
+ align 4 ;
+ L2: fmul ST,ST(1) ; // r *= x
+ fld real ptr -12[EDX] ;
+ sub EDX,12 ; // deg--
+ faddp ST(1),ST ;
+ dec ECX ;
+ jne L2 ;
+ fxch ST(1) ; // ST1 = r, ST0 = x
+ fstp ST(0) ; // dump x
+ align 4 ;
+ return_ST: ;
+ ;
+ }
+ }
+ else
+ {
+ static assert(0);
+ }
+ }
+ else
+ {
+ return polyImplBase(x, A);
+ }
+}
+
+
+/**
+ 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.primitives : empty, front, isInputRange, popFront;
+ static if (isInputRange!T)
+ {
+ static if (isInputRange!U)
+ {
+ // Two ranges
+ for (;; lhs.popFront(), rhs.popFront())
+ {
+ if (lhs.empty) return rhs.empty;
+ if (rhs.empty) return lhs.empty;
+ if (!approxEqual(lhs.front, rhs.front, maxRelDiff, maxAbsDiff))
+ 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
+ for (; !lhs.empty; lhs.popFront())
+ {
+ if (!approxEqual(lhs.front, rhs, maxRelDiff, maxAbsDiff))
+ return false;
+ }
+ return true;
+ }
+ }
+ else
+ {
+ static if (isInputRange!U)
+ {
+ // 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
+ {
+ // two numbers
+ //static assert(is(T : real) && is(U : real));
+ if (rhs == 0)
+ {
+ return fabs(lhs) <= maxAbsDiff;
+ }
+ static if (is(typeof(lhs.infinity)) && is(typeof(rhs.infinity)))
+ {
+ if (lhs == lhs.infinity && rhs == rhs.infinity ||
+ lhs == -lhs.infinity && rhs == -rhs.infinity) return true;
+ }
+ return fabs((lhs - rhs) / rhs) <= maxRelDiff
+ || maxAbsDiff != 0 && fabs(lhs - rhs) <= maxAbsDiff;
+ }
+ }
+}
+
+/**
+ Returns $(D approxEqual(lhs, rhs, 1e-2, 1e-5)).
+ */
+bool approxEqual(T, U)(T lhs, U rhs)
+{
+ return approxEqual(lhs, rhs, 1e-2, 1e-5);
+}
+
+///
+@safe pure nothrow unittest
+{
+ assert(approxEqual(1.0, 1.0099));
+ assert(!approxEqual(1.0, 1.011));
+ float[] arr1 = [ 1.0, 2.0, 3.0 ];
+ double[] arr2 = [ 1.001, 1.999, 3 ];
+ assert(approxEqual(arr1, arr2));
+
+ real num = real.infinity;
+ assert(num == real.infinity); // Passes.
+ assert(approxEqual(num, real.infinity)); // Fails.
+ num = -real.infinity;
+ assert(num == -real.infinity); // Passes.
+ assert(approxEqual(num, -real.infinity)); // Fails.
+
+ 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
+{
+ real num = real.infinity;
+ assert(num == real.infinity); // Passes.
+ assert(approxEqual(num, real.infinity)); // Fails.
+}
+
+
+@safe pure nothrow @nogc unittest
+{
+ float f = sqrt(2.0f);
+ assert(fabs(f * f - 2.0f) < .00001);
+
+ double d = sqrt(2.0);
+ assert(fabs(d * d - 2.0) < .00001);
+
+ real r = sqrt(2.0L);
+ assert(fabs(r * r - 2.0) < .00001);
+}
+
+@safe pure nothrow @nogc unittest
+{
+ float f = fabs(-2.0f);
+ assert(f == 2);
+
+ double d = fabs(-2.0);
+ assert(d == 2);
+
+ real r = fabs(-2.0L);
+ assert(r == 2);
+}
+
+@safe pure nothrow @nogc unittest
+{
+ float f = sin(-2.0f);
+ assert(fabs(f - -0.909297f) < .00001);
+
+ double d = sin(-2.0);
+ assert(fabs(d - -0.909297f) < .00001);
+
+ real r = sin(-2.0L);
+ assert(fabs(r - -0.909297f) < .00001);
+}
+
+@safe pure nothrow @nogc unittest
+{
+ float f = cos(-2.0f);
+ assert(fabs(f - -0.416147f) < .00001);
+
+ double d = cos(-2.0);
+ assert(fabs(d - -0.416147f) < .00001);
+
+ real r = cos(-2.0L);
+ assert(fabs(r - -0.416147f) < .00001);
+}
+
+@safe pure nothrow @nogc unittest
+{
+ float f = tan(-2.0f);
+ assert(fabs(f - 2.18504f) < .00001);
+
+ double d = tan(-2.0);
+ assert(fabs(d - 2.18504f) < .00001);
+
+ real r = tan(-2.0L);
+ assert(fabs(r - 2.18504f) < .00001);
+
+ // Verify correct behavior for large inputs
+ assert(!isNaN(tan(0x1p63)));
+ assert(!isNaN(tan(0x1p300L)));
+ assert(!isNaN(tan(-0x1p63)));
+ assert(!isNaN(tan(-0x1p300L)));
+}
+
+@safe pure nothrow unittest
+{
+ // issue 6381: floor/ceil should be usable in pure function.
+ 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 = cast(typeof(var.bits.rem))(-1 - 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(0 - 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/libphobos/src/std/mathspecial.d b/libphobos/src/std/mathspecial.d
new file mode 100644
index 0000000..e35c74c
--- /dev/null
+++ b/libphobos/src/std/mathspecial.d
@@ -0,0 +1,361 @@
+// Written in the D programming language.
+
+/**
+ * Mathematical Special Functions
+ *
+ * The technical term 'Special Functions' includes several families of
+ * transcendental functions, which have important applications in particular
+ * branches of mathematics and physics.
+ *
+ * The gamma and related functions, and the error function are crucial for
+ * mathematical statistics.
+ * The Bessel and related functions arise in problems involving wave propagation
+ * (especially in optics).
+ * Other major categories of special functions include the elliptic integrals
+ * (related to the arc length of an ellipse), and the hypergeometric functions.
+ *
+ * Status:
+ * Many more functions will be added to this module.
+ * The naming convention for the distribution functions (gammaIncomplete, etc)
+ * is not yet finalized and will probably change.
+ *
+ * Macros:
+ * TABLE_SV = <table border="1" cellpadding="4" cellspacing="0">
+ * <caption>Special Values</caption>
+ * $0</table>
+ * SVH = $(TR $(TH $1) $(TH $2))
+ * SV = $(TR $(TD $1) $(TD $2))
+ *
+ * NAN = $(RED NAN)
+ * SUP = <span style="vertical-align:super;font-size:smaller">$0</span>
+ * GAMMA = &#915;
+ * THETA = &theta;
+ * INTEGRAL = &#8747;
+ * INTEGRATE = $(BIG &#8747;<sub>$(SMALL $1)</sub><sup>$2</sup>)
+ * POWER = $1<sup>$2</sup>
+ * SUB = $1<sub>$2</sub>
+ * BIGSUM = $(BIG &Sigma; <sup>$2</sup><sub>$(SMALL $1)</sub>)
+ * CHOOSE = $(BIG &#40;) <sup>$(SMALL $1)</sup><sub>$(SMALL $2)</sub> $(BIG &#41;)
+ * PLUSMN = &plusmn;
+ * INFIN = &infin;
+ * PLUSMNINF = &plusmn;&infin;
+ * PI = &pi;
+ * LT = &lt;
+ * GT = &gt;
+ * SQRT = &radic;
+ * HALF = &frac12;
+ *
+ *
+ * Copyright: Based on the CEPHES math library, which is
+ * Copyright (C) 1994 Stephen L. Moshier (moshier@world.std.com).
+ * 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)
+ */
+module std.mathspecial;
+import std.internal.math.errorfunction;
+import std.internal.math.gammafunction;
+public import std.math;
+
+/* ***********************************************
+ * GAMMA AND RELATED FUNCTIONS *
+ * ***********************************************/
+
+pure:
+nothrow:
+@safe:
+@nogc:
+
+/** The Gamma function, $(GAMMA)(x)
+ *
+ * $(GAMMA)(x) is a generalisation of the factorial function
+ * to real and complex numbers.
+ * Like x!, $(GAMMA)(x+1) = x * $(GAMMA)(x).
+ *
+ * Mathematically, if z.re > 0 then
+ * $(GAMMA)(z) = $(INTEGRATE 0, $(INFIN)) $(POWER t, z-1)$(POWER e, -t) dt
+ *
+ * $(TABLE_SV
+ * $(SVH x, $(GAMMA)(x) )
+ * $(SV $(NAN), $(NAN) )
+ * $(SV $(PLUSMN)0.0, $(PLUSMNINF))
+ * $(SV integer > 0, (x-1)! )
+ * $(SV integer < 0, $(NAN) )
+ * $(SV +$(INFIN), +$(INFIN) )
+ * $(SV -$(INFIN), $(NAN) )
+ * )
+ */
+real gamma(real x)
+{
+ return std.internal.math.gammafunction.gamma(x);
+}
+
+/** Natural logarithm of the gamma function, $(GAMMA)(x)
+ *
+ * Returns the base e (2.718...) logarithm of the absolute
+ * value of the gamma function of the argument.
+ *
+ * For reals, logGamma is equivalent to log(fabs(gamma(x))).
+ *
+ * $(TABLE_SV
+ * $(SVH x, logGamma(x) )
+ * $(SV $(NAN), $(NAN) )
+ * $(SV integer <= 0, +$(INFIN) )
+ * $(SV $(PLUSMNINF), +$(INFIN) )
+ * )
+ */
+real logGamma(real x)
+{
+ return std.internal.math.gammafunction.logGamma(x);
+}
+
+/** The sign of $(GAMMA)(x).
+ *
+ * Returns -1 if $(GAMMA)(x) < 0, +1 if $(GAMMA)(x) > 0,
+ * $(NAN) if sign is indeterminate.
+ *
+ * Note that this function can be used in conjunction with logGamma(x) to
+ * evaluate gamma for very large values of x.
+ */
+real sgnGamma(real x)
+{
+ /* Author: Don Clugston. */
+ if (isNaN(x)) return x;
+ if (x > 0) return 1.0;
+ if (x < -1/real.epsilon)
+ {
+ // Large negatives lose all precision
+ return real.nan;
+ }
+ long n = rndtol(x);
+ if (x == n)
+ {
+ return x == 0 ? copysign(1, x) : real.nan;
+ }
+ return n & 1 ? 1.0 : -1.0;
+}
+
+@safe unittest
+{
+ assert(sgnGamma(5.0) == 1.0);
+ assert(isNaN(sgnGamma(-3.0)));
+ assert(sgnGamma(-0.1) == -1.0);
+ assert(sgnGamma(-55.1) == 1.0);
+ assert(isNaN(sgnGamma(-real.infinity)));
+ assert(isIdentical(sgnGamma(NaN(0xABC)), NaN(0xABC)));
+}
+
+/** Beta function
+ *
+ * The beta function is defined as
+ *
+ * beta(x, y) = ($(GAMMA)(x) * $(GAMMA)(y)) / $(GAMMA)(x + y)
+ */
+real beta(real x, real y)
+{
+ if ((x+y)> MAXGAMMA)
+ {
+ return exp(logGamma(x) + logGamma(y) - logGamma(x+y));
+ } else return gamma(x) * gamma(y) / gamma(x+y);
+}
+
+@safe unittest
+{
+ assert(isIdentical(beta(NaN(0xABC), 4), NaN(0xABC)));
+ assert(isIdentical(beta(2, NaN(0xABC)), NaN(0xABC)));
+}
+
+/** Digamma function
+ *
+ * The digamma function is the logarithmic derivative of the gamma function.
+ *
+ * digamma(x) = d/dx logGamma(x)
+ *
+ * See_Also: $(LREF logmdigamma), $(LREF logmdigammaInverse).
+ */
+real digamma(real x)
+{
+ return std.internal.math.gammafunction.digamma(x);
+}
+
+/** Log Minus Digamma function
+ *
+ * logmdigamma(x) = log(x) - digamma(x)
+ *
+ * See_Also: $(LREF digamma), $(LREF logmdigammaInverse).
+ */
+real logmdigamma(real x)
+{
+ return std.internal.math.gammafunction.logmdigamma(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)
+{
+ return std.internal.math.gammafunction.logmdigammaInverse(x);
+}
+
+/** Incomplete beta integral
+ *
+ * Returns incomplete beta integral of the arguments, evaluated
+ * from zero to x. The regularized incomplete beta function is defined as
+ *
+ * betaIncomplete(a, b, x) = $(GAMMA)(a + b) / ( $(GAMMA)(a) $(GAMMA)(b) ) *
+ * $(INTEGRATE 0, x) $(POWER t, a-1)$(POWER (1-t), b-1) dt
+ *
+ * and is the same as the the cumulative distribution function.
+ *
+ * The domain of definition is 0 <= x <= 1. In this
+ * implementation a and b are restricted to positive values.
+ * The integral from x to 1 may be obtained by the symmetry
+ * relation
+ *
+ * betaIncompleteCompl(a, b, x ) = betaIncomplete( b, a, 1-x )
+ *
+ * The integral is evaluated by a continued fraction expansion
+ * or, when b * x is small, by a power series.
+ */
+real betaIncomplete(real a, real b, real x )
+{
+ return std.internal.math.gammafunction.betaIncomplete(a, b, x);
+}
+
+/** Inverse of incomplete beta integral
+ *
+ * Given y, the function finds x such that
+ *
+ * betaIncomplete(a, b, x) == y
+ *
+ * Newton iterations or interval halving is used.
+ */
+real betaIncompleteInverse(real a, real b, real y )
+{
+ return std.internal.math.gammafunction.betaIncompleteInv(a, b, y);
+}
+
+/** Incomplete gamma integral and its complement
+ *
+ * These functions are defined by
+ *
+ * gammaIncomplete = ( $(INTEGRATE 0, x) $(POWER e, -t) $(POWER t, a-1) dt )/ $(GAMMA)(a)
+ *
+ * gammaIncompleteCompl(a,x) = 1 - gammaIncomplete(a,x)
+ * = ($(INTEGRATE x, $(INFIN)) $(POWER e, -t) $(POWER t, a-1) dt )/ $(GAMMA)(a)
+ *
+ * In this implementation both arguments must be positive.
+ * The integral is evaluated by either a power series or
+ * continued fraction expansion, depending on the relative
+ * values of a and x.
+ */
+real gammaIncomplete(real a, real x )
+in {
+ assert(x >= 0);
+ assert(a > 0);
+}
+body {
+ return std.internal.math.gammafunction.gammaIncomplete(a, x);
+}
+
+/** ditto */
+real gammaIncompleteCompl(real a, real x )
+in {
+ assert(x >= 0);
+ assert(a > 0);
+}
+body {
+ return std.internal.math.gammafunction.gammaIncompleteCompl(a, x);
+}
+
+/** Inverse of complemented incomplete gamma integral
+ *
+ * Given a and p, the function finds x such that
+ *
+ * gammaIncompleteCompl( a, x ) = p.
+ */
+real gammaIncompleteComplInverse(real a, real p)
+in {
+ assert(p >= 0 && p <= 1);
+ assert(a > 0);
+}
+body {
+ return std.internal.math.gammafunction.gammaIncompleteComplInv(a, p);
+}
+
+
+/* ***********************************************
+ * ERROR FUNCTIONS & NORMAL DISTRIBUTION *
+ * ***********************************************/
+
+ /** Error function
+ *
+ * The integral is
+ *
+ * erf(x) = 2/ $(SQRT)($(PI))
+ * $(INTEGRATE 0, x) exp( - $(POWER t, 2)) dt
+ *
+ * The magnitude of x is limited to about 106.56 for IEEE 80-bit
+ * arithmetic; 1 or -1 is returned outside this range.
+ */
+real erf(real x)
+{
+ return std.internal.math.errorfunction.erf(x);
+}
+
+/** Complementary error function
+ *
+ * erfc(x) = 1 - erf(x)
+ * = 2/ $(SQRT)($(PI))
+ * $(INTEGRATE x, $(INFIN)) exp( - $(POWER t, 2)) dt
+ *
+ * This function has high relative accuracy for
+ * values of x far from zero. (For values near zero, use erf(x)).
+ */
+real erfc(real x)
+{
+ return std.internal.math.errorfunction.erfc(x);
+}
+
+
+/** Normal distribution function.
+ *
+ * The normal (or Gaussian, or bell-shaped) distribution is
+ * defined as:
+ *
+ * normalDist(x) = 1/$(SQRT)(2$(PI)) $(INTEGRATE -$(INFIN), x) exp( - $(POWER t, 2)/2) dt
+ * = 0.5 + 0.5 * erf(x/sqrt(2))
+ * = 0.5 * erfc(- x/sqrt(2))
+ *
+ * To maintain accuracy at values of x near 1.0, use
+ * normalDistribution(x) = 1.0 - normalDistribution(-x).
+ *
+ * References:
+ * $(LINK http://www.netlib.org/cephes/ldoubdoc.html),
+ * G. Marsaglia, "Evaluating the Normal Distribution",
+ * Journal of Statistical Software <b>11</b>, (July 2004).
+ */
+real normalDistribution(real x)
+{
+ return std.internal.math.errorfunction.normalDistributionImpl(x);
+}
+
+/** Inverse of Normal distribution function
+ *
+ * Returns the argument, x, for which the area under the
+ * Normal probability density function (integrated from
+ * minus infinity to x) is equal to p.
+ *
+ * Note: This function is only implemented to 80 bit precision.
+ */
+real normalDistributionInverse(real p)
+in {
+ assert(p >= 0.0L && p <= 1.0L, "Domain error");
+}
+body
+{
+ return std.internal.math.errorfunction.normalDistributionInvImpl(p);
+}
diff --git a/libphobos/src/std/meta.d b/libphobos/src/std/meta.d
new file mode 100644
index 0000000..308e50f
--- /dev/null
+++ b/libphobos/src/std/meta.d
@@ -0,0 +1,1679 @@
+// 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)
+ * $(LREF Stride)
+ * ))
+ * $(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.
+ *
+ * In previous versions of Phobos, this was known as `TypeTuple`.
+ */
+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 an `AliasSeq` 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 an `AliasSeq` 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 an `AliasSeq` 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)));
+}
+
+@safe unittest
+{
+ // 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 an `AliasSeq` 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 an `AliasSeq` 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 an `AliasSeq` 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 `AliasSeq` 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.conv : to, octal;
+ import std.range : iota;
+ //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));
+}
+
+/**
+Selects a subset of the argument list by stepping with fixed `stepSize` over the list.
+A negative `stepSize` starts iteration with the last list element.
+
+Params:
+ stepSize = Number of elements to increment on each iteration. Can't be `0`.
+ Args = Template arguments
+
+Returns: A template argument list filtered by the selected stride.
+*/
+template Stride(int stepSize, Args...)
+if (stepSize != 0)
+{
+ static if (Args.length == 0)
+ {
+ alias Stride = AliasSeq!();
+ }
+ else static if (stepSize > 0)
+ {
+ static if (stepSize >= Args.length)
+ alias Stride = AliasSeq!(Args[0]);
+ else
+ alias Stride = AliasSeq!(Args[0], Stride!(stepSize, Args[stepSize .. $]));
+ }
+ else
+ {
+ static if (-stepSize >= Args.length)
+ alias Stride = AliasSeq!(Args[$ - 1]);
+ else
+ alias Stride = AliasSeq!(Args[$ - 1], Stride!(stepSize, Args[0 .. $ + stepSize]));
+ }
+}
+
+///
+@safe unittest
+{
+ static assert(is(Stride!(1, short, int, long) == AliasSeq!(short, int, long)));
+ static assert(is(Stride!(2, short, int, long) == AliasSeq!(short, long)));
+ static assert(is(Stride!(-1, short, int, long) == AliasSeq!(long, int, short)));
+ static assert(is(Stride!(-2, short, int, long) == AliasSeq!(long, short)));
+
+ alias attribs = AliasSeq!(short, int, long, ushort, uint, ulong);
+ static assert(is(Stride!(3, attribs) == AliasSeq!(short, ushort)));
+ static assert(is(Stride!(3, attribs[1 .. $]) == AliasSeq!(int, uint)));
+ static assert(is(Stride!(-3, attribs) == AliasSeq!(ulong, long)));
+}
+
+@safe unittest
+{
+ static assert(Pack!(Stride!(5, int)).equals!(int));
+ static assert(Pack!(Stride!(-5, int)).equals!(int));
+ static assert(!__traits(compiles, Stride!(0, 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) {}
+
+@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/libphobos/src/std/mmfile.d b/libphobos/src/std/mmfile.d
new file mode 100644
index 0000000..453e2ec
--- /dev/null
+++ b/libphobos/src/std/mmfile.d
@@ -0,0 +1,721 @@
+// Written in the D programming language.
+
+/**
+ * Read and write memory mapped files.
+ * Copyright: Copyright Digital Mars 2004 - 2009.
+ * 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.
+ * (See accompanying file LICENSE_1_0.txt or copy at
+ * http://www.boost.org/LICENSE_1_0.txt)
+ */
+module std.mmfile;
+
+import core.stdc.errno;
+import core.stdc.stdio;
+import core.stdc.stdlib;
+import std.conv, std.exception, std.stdio;
+import std.file;
+import std.path;
+import std.string;
+
+import std.internal.cstring;
+
+//debug = MMFILE;
+
+version (Windows)
+{
+ import core.sys.windows.windows;
+ import std.utf;
+ import std.windows.syserror;
+}
+else version (Posix)
+{
+ import core.sys.posix.fcntl;
+ import core.sys.posix.sys.mman;
+ import core.sys.posix.sys.stat;
+ import core.sys.posix.unistd;
+}
+else
+{
+ static assert(0);
+}
+
+/**
+ * MmFile objects control the memory mapped file resource.
+ */
+class MmFile
+{
+ /**
+ * The mode the memory mapped file is opened with.
+ */
+ enum Mode
+ {
+ read, /// Read existing file
+ readWriteNew, /// Delete existing file, write new file
+ readWrite, /// Read/Write existing file, create if not existing
+ readCopyOnWrite, /// Read/Write existing file, copy on write
+ }
+
+ /**
+ * Open memory mapped file filename for reading.
+ * File is closed when the object instance is deleted.
+ * Throws:
+ * std.file.FileException
+ */
+ this(string filename)
+ {
+ this(filename, Mode.read, 0, null);
+ }
+
+ 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);
+ }
+
+ version (linux) private this(int fildes, Mode mode, ulong size,
+ void* address, size_t window)
+ {
+ int oflag;
+ int fmode;
+
+ switch (mode)
+ {
+ case Mode.read:
+ flags = MAP_SHARED;
+ prot = PROT_READ;
+ oflag = O_RDONLY;
+ fmode = 0;
+ break;
+
+ case Mode.readWriteNew:
+ assert(size != 0);
+ flags = MAP_SHARED;
+ prot = PROT_READ | PROT_WRITE;
+ oflag = O_CREAT | O_RDWR | O_TRUNC;
+ fmode = octal!660;
+ break;
+
+ case Mode.readWrite:
+ flags = MAP_SHARED;
+ prot = PROT_READ | PROT_WRITE;
+ oflag = O_CREAT | O_RDWR;
+ fmode = octal!660;
+ break;
+
+ case Mode.readCopyOnWrite:
+ flags = MAP_PRIVATE;
+ prot = PROT_READ | PROT_WRITE;
+ oflag = O_RDWR;
+ fmode = 0;
+ break;
+
+ default:
+ assert(0);
+ }
+
+ fd = fildes;
+
+ // Adjust size
+ stat_t statbuf = void;
+ errnoEnforce(fstat(fd, &statbuf) == 0);
+ if (prot & PROT_WRITE && size > statbuf.st_size)
+ {
+ // Need to make the file size bytes big
+ lseek(fd, cast(off_t)(size - 1), SEEK_SET);
+ char c = 0;
+ core.sys.posix.unistd.write(fd, &c, 1);
+ }
+ else if (prot & PROT_READ && size == 0)
+ size = statbuf.st_size;
+ this.size = size;
+
+ // Map the file into memory!
+ size_t initial_map = (window && 2*window<size)
+ ? 2*window : cast(size_t) size;
+ auto p = mmap(address, initial_map, prot, flags, fd, 0);
+ if (p == MAP_FAILED)
+ {
+ errnoEnforce(false, "Could not map file into memory");
+ }
+ data = p[0 .. initial_map];
+ }
+
+ /**
+ * Open memory mapped file filename in mode.
+ * File is closed when the object instance is deleted.
+ * Params:
+ * filename = name of the file.
+ * If null, an anonymous file mapping is created.
+ * mode = access mode defined above.
+ * size = the size of the file. If 0, it is taken to be the
+ * size of the existing file.
+ * address = the preferred address to map the file to,
+ * although the system is not required to honor it.
+ * If null, the system selects the most convenient address.
+ * window = preferred block size of the amount of data to map at one time
+ * with 0 meaning map the entire file. The window size must be a
+ * multiple of the memory allocation page size.
+ * Throws:
+ * std.file.FileException
+ */
+ this(string filename, Mode mode, ulong size, void* address,
+ size_t window = 0)
+ {
+ this.filename = filename;
+ this.mMode = mode;
+ this.window = window;
+ this.address = address;
+
+ version (Windows)
+ {
+ void* p;
+ uint dwDesiredAccess2;
+ uint dwShareMode;
+ uint dwCreationDisposition;
+ uint flProtect;
+
+ switch (mode)
+ {
+ case Mode.read:
+ dwDesiredAccess2 = GENERIC_READ;
+ dwShareMode = FILE_SHARE_READ;
+ dwCreationDisposition = OPEN_EXISTING;
+ flProtect = PAGE_READONLY;
+ dwDesiredAccess = FILE_MAP_READ;
+ break;
+
+ case Mode.readWriteNew:
+ assert(size != 0);
+ dwDesiredAccess2 = GENERIC_READ | GENERIC_WRITE;
+ dwShareMode = FILE_SHARE_READ | FILE_SHARE_WRITE;
+ dwCreationDisposition = CREATE_ALWAYS;
+ flProtect = PAGE_READWRITE;
+ dwDesiredAccess = FILE_MAP_WRITE;
+ break;
+
+ case Mode.readWrite:
+ dwDesiredAccess2 = GENERIC_READ | GENERIC_WRITE;
+ dwShareMode = FILE_SHARE_READ | FILE_SHARE_WRITE;
+ dwCreationDisposition = OPEN_ALWAYS;
+ flProtect = PAGE_READWRITE;
+ dwDesiredAccess = FILE_MAP_WRITE;
+ break;
+
+ case Mode.readCopyOnWrite:
+ dwDesiredAccess2 = GENERIC_READ | GENERIC_WRITE;
+ dwShareMode = FILE_SHARE_READ | FILE_SHARE_WRITE;
+ dwCreationDisposition = OPEN_EXISTING;
+ flProtect = PAGE_WRITECOPY;
+ dwDesiredAccess = FILE_MAP_COPY;
+ break;
+
+ default:
+ assert(0);
+ }
+
+ if (filename != null)
+ {
+ hFile = CreateFileW(filename.tempCStringW(),
+ dwDesiredAccess2,
+ dwShareMode,
+ null,
+ dwCreationDisposition,
+ FILE_ATTRIBUTE_NORMAL,
+ cast(HANDLE) null);
+ wenforce(hFile != INVALID_HANDLE_VALUE, "CreateFileW");
+ }
+ else
+ hFile = INVALID_HANDLE_VALUE;
+
+ 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);
+ wenforce(hFileMap, "CreateFileMapping");
+ scope(failure)
+ {
+ CloseHandle(hFileMap);
+ hFileMap = null;
+ }
+
+ if (size == 0 && filename != null)
+ {
+ uint sizehi;
+ uint sizelow = GetFileSize(hFile, &sizehi);
+ wenforce(sizelow != INVALID_FILE_SIZE || GetLastError() != ERROR_SUCCESS,
+ "GetFileSize");
+ size = (cast(ulong) sizehi << 32) + sizelow;
+ }
+ this.size = size;
+
+ size_t initial_map = (window && 2*window<size)
+ ? 2*window : cast(size_t) size;
+ p = MapViewOfFileEx(hFileMap, dwDesiredAccess, 0, 0,
+ initial_map, address);
+ wenforce(p, "MapViewOfFileEx");
+ data = p[0 .. initial_map];
+
+ debug (MMFILE) printf("MmFile.this(): p = %p, size = %d\n", p, size);
+ }
+ else version (Posix)
+ {
+ void* p;
+ int oflag;
+ int fmode;
+
+ switch (mode)
+ {
+ case Mode.read:
+ flags = MAP_SHARED;
+ prot = PROT_READ;
+ oflag = O_RDONLY;
+ fmode = 0;
+ break;
+
+ case Mode.readWriteNew:
+ assert(size != 0);
+ flags = MAP_SHARED;
+ prot = PROT_READ | PROT_WRITE;
+ oflag = O_CREAT | O_RDWR | O_TRUNC;
+ fmode = octal!660;
+ break;
+
+ case Mode.readWrite:
+ flags = MAP_SHARED;
+ prot = PROT_READ | PROT_WRITE;
+ oflag = O_CREAT | O_RDWR;
+ fmode = octal!660;
+ break;
+
+ case Mode.readCopyOnWrite:
+ flags = MAP_PRIVATE;
+ prot = PROT_READ | PROT_WRITE;
+ oflag = O_RDWR;
+ fmode = 0;
+ break;
+
+ default:
+ assert(0);
+ }
+
+ if (filename.length)
+ {
+ fd = .open(filename.tempCString(), oflag, fmode);
+ errnoEnforce(fd != -1, "Could not open file "~filename);
+
+ stat_t statbuf;
+ if (fstat(fd, &statbuf))
+ {
+ //printf("\tfstat error, errno = %d\n", errno);
+ .close(fd);
+ fd = -1;
+ errnoEnforce(false, "Could not stat file "~filename);
+ }
+
+ if (prot & PROT_WRITE && size > statbuf.st_size)
+ {
+ // Need to make the file size bytes big
+ .lseek(fd, cast(off_t)(size - 1), SEEK_SET);
+ char c = 0;
+ core.sys.posix.unistd.write(fd, &c, 1);
+ }
+ else if (prot & PROT_READ && size == 0)
+ size = statbuf.st_size;
+ }
+ else
+ {
+ fd = -1;
+ version (CRuntime_Glibc) import core.sys.linux.sys.mman : MAP_ANON;
+ flags |= MAP_ANON;
+ }
+ this.size = size;
+ size_t initial_map = (window && 2*window<size)
+ ? 2*window : cast(size_t) size;
+ p = mmap(address, initial_map, prot, flags, fd, 0);
+ if (p == MAP_FAILED)
+ {
+ if (fd != -1)
+ {
+ .close(fd);
+ fd = -1;
+ }
+ errnoEnforce(false, "Could not map file "~filename);
+ }
+
+ data = p[0 .. initial_map];
+ }
+ else
+ {
+ static assert(0);
+ }
+ }
+
+ /**
+ * Flushes pending output and closes the memory mapped file.
+ */
+ ~this()
+ {
+ debug (MMFILE) printf("MmFile.~this()\n");
+ unmap();
+ data = null;
+ version (Windows)
+ {
+ wenforce(hFileMap == null || CloseHandle(hFileMap) == TRUE,
+ "Could not close file handle");
+ hFileMap = null;
+
+ wenforce(!hFile || hFile == INVALID_HANDLE_VALUE
+ || CloseHandle(hFile) == TRUE,
+ "Could not close handle");
+ hFile = INVALID_HANDLE_VALUE;
+ }
+ else version (Posix)
+ {
+ version (linux)
+ {
+ if (file !is File.init)
+ {
+ // The File destructor will close the file,
+ // if it is the only remaining reference.
+ return;
+ }
+ }
+ errnoEnforce(fd == -1 || fd <= 2
+ || .close(fd) != -1,
+ "Could not close handle");
+ fd = -1;
+ }
+ else
+ {
+ static assert(0);
+ }
+ }
+
+ /* Flush any pending output.
+ */
+ void flush()
+ {
+ debug (MMFILE) printf("MmFile.flush()\n");
+ version (Windows)
+ {
+ FlushViewOfFile(data.ptr, data.length);
+ }
+ else version (Posix)
+ {
+ int i;
+ i = msync(cast(void*) data, data.length, MS_SYNC); // sys/mman.h
+ errnoEnforce(i == 0, "msync failed");
+ }
+ else
+ {
+ static assert(0);
+ }
+ }
+
+ /**
+ * Gives size in bytes of the memory mapped file.
+ */
+ @property ulong length() const
+ {
+ debug (MMFILE) printf("MmFile.length()\n");
+ return size;
+ }
+
+ /**
+ * Read-only property returning the file mode.
+ */
+ Mode mode()
+ {
+ debug (MMFILE) printf("MmFile.mode()\n");
+ return mMode;
+ }
+
+ /**
+ * Returns entire file contents as an array.
+ */
+ void[] opSlice()
+ {
+ debug (MMFILE) printf("MmFile.opSlice()\n");
+ return opSlice(0,size);
+ }
+
+ /**
+ * Returns slice of file contents as an array.
+ */
+ void[] opSlice(ulong i1, ulong i2)
+ {
+ debug (MMFILE) printf("MmFile.opSlice(%lld, %lld)\n", i1, i2);
+ ensureMapped(i1,i2);
+ size_t off1 = cast(size_t)(i1-start);
+ size_t off2 = cast(size_t)(i2-start);
+ return data[off1 .. off2];
+ }
+
+ /**
+ * Returns byte at index i in file.
+ */
+ ubyte opIndex(ulong i)
+ {
+ debug (MMFILE) printf("MmFile.opIndex(%lld)\n", i);
+ ensureMapped(i);
+ size_t off = cast(size_t)(i-start);
+ return (cast(ubyte[]) data)[off];
+ }
+
+ /**
+ * Sets and returns byte at index i in file to value.
+ */
+ ubyte opIndexAssign(ubyte value, ulong i)
+ {
+ debug (MMFILE) printf("MmFile.opIndex(%lld, %d)\n", i, value);
+ ensureMapped(i);
+ size_t off = cast(size_t)(i-start);
+ return (cast(ubyte[]) data)[off] = value;
+ }
+
+
+ // return true if the given position is currently mapped
+ private int mapped(ulong i)
+ {
+ debug (MMFILE) printf("MmFile.mapped(%lld, %lld, %d)\n", i,start,
+ data.length);
+ return i >= start && i < start+data.length;
+ }
+
+ // unmap the current range
+ private void unmap()
+ {
+ debug (MMFILE) printf("MmFile.unmap()\n");
+ version (Windows)
+ {
+ wenforce(!data.ptr || UnmapViewOfFile(data.ptr) != FALSE, "UnmapViewOfFile");
+ }
+ else
+ {
+ errnoEnforce(!data.ptr || munmap(cast(void*) data, data.length) == 0,
+ "munmap failed");
+ }
+ data = null;
+ }
+
+ // map range
+ private void map(ulong start, size_t len)
+ {
+ debug (MMFILE) printf("MmFile.map(%lld, %d)\n", start, len);
+ void* p;
+ if (start+len > 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);
+ wenforce(p, "MapViewOfFileEx");
+ }
+ else
+ {
+ p = mmap(address, len, prot, flags, fd, cast(off_t) start);
+ errnoEnforce(p != MAP_FAILED);
+ }
+ data = p[0 .. len];
+ this.start = start;
+ }
+
+ // ensure a given position is mapped
+ private void ensureMapped(ulong i)
+ {
+ debug (MMFILE) printf("MmFile.ensureMapped(%lld)\n", i);
+ if (!mapped(i))
+ {
+ unmap();
+ if (window == 0)
+ {
+ map(0,cast(size_t) size);
+ }
+ else
+ {
+ ulong block = i/window;
+ if (block == 0)
+ map(0,2*window);
+ else
+ map(window*(block-1),3*window);
+ }
+ }
+ }
+
+ // ensure a given range is mapped
+ private void ensureMapped(ulong i, ulong j)
+ {
+ debug (MMFILE) printf("MmFile.ensureMapped(%lld, %lld)\n", i, j);
+ if (!mapped(i) || !mapped(j-1))
+ {
+ unmap();
+ if (window == 0)
+ {
+ map(0,cast(size_t) size);
+ }
+ else
+ {
+ ulong iblock = i/window;
+ ulong jblock = (j-1)/window;
+ if (iblock == 0)
+ {
+ map(0,cast(size_t)(window*(jblock+2)));
+ }
+ else
+ {
+ map(window*(iblock-1),cast(size_t)(window*(jblock-iblock+3)));
+ }
+ }
+ }
+ }
+
+private:
+ string filename;
+ void[] data;
+ ulong start;
+ size_t window;
+ ulong size;
+ Mode mMode;
+ void* address;
+ version (linux) File file;
+
+ version (Windows)
+ {
+ HANDLE hFile = INVALID_HANDLE_VALUE;
+ HANDLE hFileMap = null;
+ uint dwDesiredAccess;
+ }
+ else version (Posix)
+ {
+ int fd;
+ int prot;
+ int flags;
+ int fmode;
+ }
+ else
+ {
+ static assert(0);
+ }
+
+ // Report error, where errno gives the error number
+ // void errNo()
+ // {
+ // version (Windows)
+ // {
+ // throw new FileException(filename, GetLastError());
+ // }
+ // else version (linux)
+ // {
+ // throw new FileException(filename, errno);
+ // }
+ // else
+ // {
+ // static assert(0);
+ // }
+ // }
+}
+
+@system unittest
+{
+ import core.memory : GC;
+ import std.file : deleteme;
+
+ const size_t K = 1024;
+ size_t win = 64*K; // assume the page size is 64K
+ 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)
+ {
+ // 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];
+ data[] = str[];
+ assert( mf[0 .. 10] == str );
+ data = cast(ubyte[]) mf[50 .. 60];
+ data[] = str[];
+ assert( mf[50 .. 60] == str );
+ 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];
+ assert( data2.length == 79*K );
+ assert( data2[$-1] == 'b' );
+
+ 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.file : deleteme;
+ import std.typecons : scoped;
+
+ // 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/libphobos/src/std/net/curl.d b/libphobos/src/std/net/curl.d
new file mode 100644
index 0000000..3465bdf
--- /dev/null
+++ b/libphobos/src/std/net/curl.d
@@ -0,0 +1,5109 @@
+// Written in the D programming language.
+
+/**
+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.
+
+$(SCRIPT inhibitQuickIndex = 1;)
+
+$(DIVC quickindex,
+$(BOOKTABLE ,
+$(TR $(TH Category) $(TH Functions)
+)
+$(TR $(TDNW High level) $(TD $(MYREF download) $(MYREF upload) $(MYREF get)
+$(MYREF post) $(MYREF put) $(MYREF del) $(MYREF options) $(MYREF trace)
+$(MYREF connect) $(MYREF byLine) $(MYREF byChunk)
+$(MYREF byLineAsync) $(MYREF byChunkAsync) )
+)
+$(TR $(TDNW Low level) $(TD $(MYREF HTTP) $(MYREF FTP) $(MYREF
+SMTP) )
+)
+)
+)
+
+Note:
+You may need to link to the $(B curl) library, e.g. by adding $(D "libs": ["curl"])
+to your $(B dub.json) file if you are using $(LINK2 http://code.dlang.org, DUB).
+
+Windows x86 note:
+A DMD compatible libcurl static library can be downloaded from the dlang.org
+$(LINK2 http://dlang.org/download.html, download page).
+
+Compared to using libcurl directly this module allows simpler client code for
+common uses, requires no unsafe operations, and integrates better with the rest
+of the language. Futhermore it provides <a href="std_range.html">$(D range)</a>
+access to protocols supported by libcurl both synchronously and asynchronously.
+
+A high level and a low level API are available. The high level API is built
+entirely on top of the low level one.
+
+The high level API is for commonly used functionality such as HTTP/FTP get. The
+$(LREF byLineAsync) and $(LREF byChunkAsync) provides asynchronous <a
+href="std_range.html">$(D ranges)</a> that performs the request in another
+thread while handling a line/chunk in the current thread.
+
+The low level API allows for streaming and other advanced features.
+
+$(BOOKTABLE Cheat Sheet,
+$(TR $(TH Function Name) $(TH Description)
+)
+$(LEADINGROW High level)
+$(TR $(TDNW $(LREF download)) $(TD $(D
+download("ftp.digitalmars.com/sieve.ds", "/tmp/downloaded-ftp-file"))
+downloads file from URL to file system.)
+)
+$(TR $(TDNW $(LREF upload)) $(TD $(D
+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 char[] containing the dlang.org web page.)
+)
+$(TR $(TDNW $(LREF put)) $(TD $(D
+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 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 char[] containing the
+dlang.org web page.)
+)
+$(TR $(TDNW $(LREF byChunk)) $(TD $(D
+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 char[] containing the dlang.org web
+ page asynchronously.)
+)
+$(TR $(TDNW $(LREF byChunkAsync)) $(TD $(D
+byChunkAsync("dlang.org", 10)) returns a range of ubyte[10] containing the
+dlang.org web page asynchronously.)
+)
+$(LEADINGROW Low level
+)
+$(TR $(TDNW $(LREF HTTP)) $(TD $(D HTTP) struct for advanced usage))
+$(TR $(TDNW $(LREF FTP)) $(TD $(D FTP) struct for advanced usage))
+$(TR $(TDNW $(LREF SMTP)) $(TD $(D SMTP) struct for advanced usage))
+)
+
+
+Example:
+---
+import std.net.curl, std.stdio;
+
+// Return a char[] containing the content specified by a URL
+auto content = get("dlang.org");
+
+// 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
+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"))
+ writeln(line);
+
+// Get using a line range and proxy settings
+auto client = HTTP();
+client.proxy = "1.2.3.4";
+foreach (line; byLine("dlang.org", client))
+ writeln(line);
+---
+
+For more control than the high level functions provide, use the low level API:
+
+Example:
+---
+import std.net.curl, std.stdio;
+
+// GET with custom data receivers
+auto http = HTTP("dlang.org");
+http.onReceiveHeader =
+ (in char[] key, in char[] value) { writeln(key, ": ", value); };
+http.onReceive = (ubyte[] data) { /+ drop +/ return data.length; };
+http.perform();
+---
+
+First, an instance of the reference-counted HTTP struct is created. Then the
+custom delegates are set. These will be called whenever the HTTP instance
+receives a header and a data buffer, respectively. In this simple example, the
+headers are written to stdout and the data is ignored. If the request should be
+stopped before it has finished then return something less than data.length from
+the onReceive callback. See $(LREF onReceiveHeader)/$(LREF onReceive) for more
+information. Finally the HTTP request is effected by calling perform(), which is
+synchronous.
+
+Source: $(PHOBOSSRC std/net/_curl.d)
+
+Copyright: Copyright Jonas Drewsen 2011-2012
+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 $(HTTP _curl.haxx.se/libcurl, libcurl).
+ LibCurl is licensed under an MIT/X derivative license.
+*/
+/*
+ Copyright Jonas Drewsen 2011 - 2012.
+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.net.curl;
+
+import core.thread;
+import etc.c.curl;
+import std.concurrency;
+import std.encoding;
+import std.exception;
+import std.meta;
+import std.range.primitives;
+import std.socket : InternetAddress;
+import std.traits;
+import std.typecons;
+
+import std.internal.cstring;
+
+public import etc.c.curl : CurlOption;
+
+version (unittest)
+{
+ // Run unit test with the PHOBOS_TEST_ALLOW_NET=1 set in order to
+ // allow net traffic
+ import std.range;
+ import std.stdio;
+
+ 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;
+
+// Default data timeout for Protocols
+private enum _defaultDataTimeout = dur!"minutes"(2);
+
+/**
+Macros:
+
+CALLBACK_PARAMS = $(TABLE ,
+ $(DDOC_PARAM_ROW
+ $(DDOC_PARAM_ID $(DDOC_PARAM dlTotal))
+ $(DDOC_PARAM_DESC total bytes to download)
+ )
+ $(DDOC_PARAM_ROW
+ $(DDOC_PARAM_ID $(DDOC_PARAM dlNow))
+ $(DDOC_PARAM_DESC currently downloaded bytes)
+ )
+ $(DDOC_PARAM_ROW
+ $(DDOC_PARAM_ID $(DDOC_PARAM ulTotal))
+ $(DDOC_PARAM_DESC total bytes to upload)
+ )
+ $(DDOC_PARAM_ROW
+ $(DDOC_PARAM_ID $(DDOC_PARAM ulNow))
+ $(DDOC_PARAM_DESC currently uploaded bytes)
+ )
+)
+*/
+
+/** Connection type used when the URL should be used to auto detect the protocol.
+ *
+ * This struct is used as placeholder for the connection parameter when calling
+ * the high level API and the connection type (HTTP/FTP) should be guessed by
+ * inspecting the URL parameter.
+ *
+ * The rules for guessing the protocol are:
+ * 1, if URL starts with ftp://, ftps:// or ftp. then FTP connection is assumed.
+ * 2, HTTP connection otherwise.
+ *
+ * Example:
+ * ---
+ * import std.net.curl;
+ * // Two requests below will do the same.
+ * string content;
+ *
+ * // Explicit connection provided
+ * content = get!HTTP("dlang.org");
+ *
+ * // 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
+ * content = get("ftp://foo.com/file");
+ * // and will end up detecting FTP from the url and be the same as
+ * content = get!FTP("ftp://foo.com/file");
+ * ---
+ */
+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;
+}
+
+// Is true if the Conn type is a valid Curl Connection type.
+private template isCurlConn(Conn)
+{
+ enum auto isCurlConn = is(Conn : HTTP) ||
+ is(Conn : FTP) || is(Conn : AutoProtocol);
+}
+
+/** HTTP/FTP download to local file system.
+ *
+ * Params:
+ * url = resource to download
+ * saveToPath = path to store the downloaded content on local disk
+ * 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.
+ *
+ * Example:
+ * ----
+ * import std.net.curl;
+ * 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)
+{
+ static if (is(Conn : HTTP) || is(Conn : FTP))
+ {
+ import std.stdio : File;
+ conn.url = url;
+ auto f = File(saveToPath, "wb");
+ conn.onReceive = (ubyte[] data) { f.rawWrite(data); return data.length; };
+ conn.perform();
+ }
+ else
+ {
+ if (isFTPUrl(url))
+ return download!FTP(url, saveToPath, FTP());
+ else
+ return download!HTTP(url, saveToPath, HTTP());
+ }
+}
+
+@system unittest
+{
+ 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.
+ *
+ * Params:
+ * loadFromPath = path load data from local disk.
+ * url = resource to upload 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.
+ *
+ * Example:
+ * ----
+ * import std.net.curl;
+ * upload("/tmp/downloaded-ftp-file", "ftp.digitalmars.com/sieve.ds");
+ * upload("/tmp/downloaded-http-file", "d-lang.appspot.com/testUrl2");
+ * ----
+ */
+void upload(Conn = AutoProtocol)(string loadFromPath, const(char)[] url, Conn conn = Conn())
+if (isCurlConn!Conn)
+{
+ static if (is(Conn : HTTP))
+ {
+ conn.url = url;
+ conn.method = HTTP.Method.put;
+ }
+ else static if (is(Conn : FTP))
+ {
+ conn.url = url;
+ conn.handle.set(CurlOption.upload, 1L);
+ }
+ else
+ {
+ if (isFTPUrl(url))
+ return upload!FTP(loadFromPath, url, FTP());
+ else
+ return upload!HTTP(loadFromPath, url, HTTP());
+ }
+
+ static if (is(Conn : HTTP) || is(Conn : FTP))
+ {
+ 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();
+ }
+}
+
+@system unittest
+{
+ 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.
+ *
+ * Params:
+ * url = resource to get
+ * 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[]). 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:
+ * ----
+ * import std.net.curl;
+ * auto content = get("d-lang.appspot.com/testUrl2");
+ * ----
+ *
+ * Returns:
+ * A T[] range containing the content of the resource pointed to by the URL.
+ *
+ * Throws:
+ *
+ * $(D CurlException) on error.
+ *
+ * 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)) )
+{
+ static if (is(Conn : HTTP))
+ {
+ conn.method = HTTP.Method.get;
+ return _basicHTTP!(T)(url, "", conn);
+
+ }
+ else static if (is(Conn : FTP))
+ {
+ return _basicFTP!(T)(url, "", conn);
+ }
+ else
+ {
+ if (isFTPUrl(url))
+ return get!(FTP,T)(url, FTP());
+ else
+ return get!(HTTP,T)(url, HTTP());
+ }
+}
+
+@system unittest
+{
+ 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
+ * 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.
+ *
+ * Examples:
+ * ----
+ * import std.net.curl;
+ *
+ * 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:
+ * A T[] range containing the content of the resource pointed to by the URL.
+ *
+ * See_Also: $(LREF HTTP.Method)
+ */
+T[] post(T = char, PostUnit)(const(char)[] url, const(PostUnit)[] postData, HTTP conn = HTTP())
+if (is(T == char) || is(T == ubyte))
+{
+ conn.method = HTTP.Method.post;
+ return _basicHTTP!(T)(url, postData, conn);
+}
+
+@system unittest
+{
+ import std.algorithm.searching : canFind;
+
+ foreach (host; [testServer.addr, "http://"~testServer.addr])
+ {
+ 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");
+ }
+}
+
+@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])
+ {
+ 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:
+ * url = resource to put
+ * putData = 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[]). 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:
+ * ----
+ * import std.net.curl;
+ * auto content = put("d-lang.appspot.com/testUrl2",
+ * "Putting this data");
+ * ----
+ *
+ * Returns:
+ * A T[] range containing the content of the resource pointed to by the URL.
+ *
+ * See_Also: $(LREF HTTP.Method)
+ */
+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)) )
+{
+ static if (is(Conn : HTTP))
+ {
+ conn.method = HTTP.Method.put;
+ return _basicHTTP!(T)(url, putData, conn);
+ }
+ else static if (is(Conn : FTP))
+ {
+ return _basicFTP!(T)(url, putData, conn);
+ }
+ else
+ {
+ if (isFTPUrl(url))
+ return put!(FTP,T)(url, putData, FTP());
+ else
+ return put!(HTTP,T)(url, putData, HTTP());
+ }
+}
+
+@system unittest
+{
+ 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");
+ }
+}
+
+
+/** HTTP/FTP delete content.
+ *
+ * Params:
+ * url = resource to delete
+ * 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.
+ *
+ * Example:
+ * ----
+ * import std.net.curl;
+ * del("d-lang.appspot.com/testUrl2");
+ * ----
+ *
+ * See_Also: $(LREF HTTP.Method)
+ */
+void del(Conn = AutoProtocol)(const(char)[] url, Conn conn = Conn())
+if (isCurlConn!Conn)
+{
+ static if (is(Conn : HTTP))
+ {
+ conn.method = HTTP.Method.del;
+ _basicHTTP!char(url, cast(void[]) null, 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;
+ enforce!CurlException(t[0].length > minDomainNameLength,
+ text("Invalid FTP URL for delete ", url));
+ conn.url = t[0];
+
+ enforce!CurlException(!t[1].empty,
+ text("No filename specified to delete for URL ", url));
+ conn.addCommand("DELE " ~ t[1]);
+ conn.perform();
+ }
+ else
+ {
+ if (isFTPUrl(url))
+ return del!FTP(url, FTP());
+ else
+ return del!HTTP(url, HTTP());
+ }
+}
+
+@system unittest
+{
+ 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");
+ }
+}
+
+
+/** HTTP options request.
+ *
+ * Params:
+ * url = resource make a option call 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.
+ *
+ * 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:
+ * ----
+ * import std.net.curl;
+ * auto http = HTTP();
+ * options("d-lang.appspot.com/testUrl2", http);
+ * writeln("Allow set to " ~ http.responseHeaders["Allow"]);
+ * ----
+ *
+ * Returns:
+ * A T[] range containing the options of the resource pointed to by the URL.
+ *
+ * See_Also: $(LREF HTTP.Method)
+ */
+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, null, conn);
+}
+
+@system unittest
+{
+ 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");
+}
+
+
+/** HTTP trace request.
+ *
+ * Params:
+ * url = resource make a trace call 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.
+ *
+ * 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:
+ * ----
+ * import std.net.curl;
+ * trace("d-lang.appspot.com/testUrl1");
+ * ----
+ *
+ * Returns:
+ * A T[] range containing the trace info of the resource pointed to by the URL.
+ *
+ * See_Also: $(LREF HTTP.Method)
+ */
+T[] trace(T = char)(const(char)[] url, HTTP conn = HTTP())
+if (is(T == char) || is(T == ubyte))
+{
+ conn.method = HTTP.Method.trace;
+ return _basicHTTP!(T)(url, cast(void[]) null, conn);
+}
+
+@system unittest
+{
+ 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");
+}
+
+
+/** HTTP connect request.
+ *
+ * Params:
+ * url = resource make a connect to
+ * 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:
+ * ----
+ * import std.net.curl;
+ * connect("d-lang.appspot.com/testUrl1");
+ * ----
+ *
+ * Returns:
+ * A T[] range containing the connect info of the resource pointed to by the URL.
+ *
+ * See_Also: $(LREF HTTP.Method)
+ */
+T[] connect(T = char)(const(char)[] url, HTTP conn = HTTP())
+if (is(T == char) || is(T == ubyte))
+{
+ conn.method = HTTP.Method.connect;
+ return _basicHTTP!(T)(url, cast(void[]) null, conn);
+}
+
+@system unittest
+{
+ 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");
+}
+
+
+/*
+ * Helper function for the high level interface.
+ *
+ * It performs an HTTP request using the client which must have
+ * been setup correctly before calling this function.
+ */
+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 (doSend)
+ {
+ client.onSend = null;
+ client.handle.onSeek = null;
+ client.contentLength = 0;
+ }
+ }
+ client.url = url;
+ HTTP.StatusLine statusLine;
+ import std.array : appender;
+ auto content = appender!(ubyte[])();
+ client.onReceive = (ubyte[] data)
+ {
+ content ~= data;
+ return data.length;
+ };
+
+ if (doSend)
+ {
+ client.contentLength = sendData.length;
+ auto remainingData = sendData;
+ client.onSend = delegate size_t(void[] buf)
+ {
+ size_t minLen = min(buf.length, remainingData.length);
+ if (minLen == 0) return 0;
+ buf[0 .. minLen] = remainingData[0 .. minLen];
+ remainingData = remainingData[minLen..$];
+ return minLen;
+ };
+ client.handle.onSeek = delegate(long offset, CurlSeekPos mode)
+ {
+ switch (mode)
+ {
+ case CurlSeekPos.set:
+ remainingData = sendData[cast(size_t) offset..$];
+ return CurlSeek.ok;
+ default:
+ // As of curl 7.18.0, libcurl will not pass
+ // anything other than CurlSeekPos.set.
+ return CurlSeek.cantseek;
+ }
+ };
+ }
+
+ client.onReceiveHeader = (in char[] key,
+ in char[] value)
+ {
+ if (key == "content-length")
+ {
+ import std.conv : to;
+ content.reserve(value.to!size_t);
+ }
+ };
+ client.onReceiveStatusLine = (HTTP.StatusLine l) { statusLine = l; };
+ client.perform();
+ enforce(statusLine.code / 100 == 2, new HTTPStatusException(statusLine.code,
+ format("HTTP request returned status code %d (%s)", statusLine.code, statusLine.reason)));
+
+ 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!HTTPStatusException(get(testServer.addr ~ "/path"));
+ assert(e.msg == "HTTP request returned status code 404 (Not Found)");
+ assert(e.status == 404);
+}
+
+// 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");
+}
+
+/*
+ * Helper function for the high level interface.
+ *
+ * It performs an FTP request using the client which must have
+ * been setup correctly before calling this function.
+ */
+private auto _basicFTP(T)(const(char)[] url, const(void)[] sendData, FTP client)
+{
+ import std.algorithm.comparison : min;
+
+ scope (exit)
+ {
+ client.onReceive = null;
+ if (!sendData.empty)
+ client.onSend = null;
+ }
+
+ ubyte[] content;
+
+ if (client.encoding.empty)
+ client.encoding = "ISO-8859-1";
+
+ client.url = url;
+ client.onReceive = (ubyte[] data)
+ {
+ content ~= data;
+ return data.length;
+ };
+
+ if (!sendData.empty)
+ {
+ client.handle.set(CurlOption.upload, 1L);
+ client.onSend = delegate size_t(void[] buf)
+ {
+ size_t minLen = min(buf.length, sendData.length);
+ if (minLen == 0) return 0;
+ buf[0 .. minLen] = sendData[0 .. minLen];
+ sendData = sendData[minLen..$];
+ return minLen;
+ };
+ }
+
+ client.perform();
+
+ return _decodeContent!T(content, client.encoding);
+}
+
+/* Used by _basicHTTP() and _basicFTP() to decode ubyte[] to
+ * correct string format
+ */
+private auto _decodeContent(T)(ubyte[] content, string encoding)
+{
+ static if (is(T == ubyte))
+ {
+ return content;
+ }
+ else
+ {
+ import std.format : format;
+
+ // Optimally just return the utf8 encoded content
+ if (encoding == "UTF-8")
+ return cast(char[])(content);
+
+ // The content has to be re-encoded to utf8
+ auto scheme = EncodingScheme.create(encoding);
+ enforce!CurlException(scheme !is null,
+ format("Unknown encoding '%s'", encoding));
+
+ auto strInfo = decodeString(content, scheme);
+ enforce!CurlException(strInfo[0] != size_t.max,
+ format("Invalid encoding sequence for encoding '%s'",
+ encoding));
+
+ return strInfo[1];
+ }
+}
+
+alias KeepTerminator = Flag!"keepTerminator";
+/+
+struct ByLineBuffer(Char)
+{
+ bool linePresent;
+ bool EOF;
+ Char[] buffer;
+ ubyte[] decodeRemainder;
+
+ bool append(const(ubyte)[] data)
+ {
+ byLineBuffer ~= data;
+ }
+
+ @property bool linePresent()
+ {
+ return byLinePresent;
+ }
+
+ Char[] get()
+ {
+ if (!linePresent)
+ {
+ // Decode ubyte[] into Char[] until a Terminator is found.
+ // If not Terminator is found and EOF is false then raise an
+ // exception.
+ }
+ return byLineBuffer;
+ }
+
+}
+++/
+/** HTTP/FTP fetch content as a range of lines.
+ *
+ * A range of lines is returned when the request is complete. If the method or
+ * other request properties is to be customized then set the $(D conn) parameter
+ * with a HTTP/FTP instance that has these properties set.
+ *
+ * Example:
+ * ----
+ * import std.net.curl, std.stdio;
+ * foreach (line; byLine("dlang.org"))
+ * writeln(line);
+ * ----
+ *
+ * Params:
+ * url = The url to receive content from
+ * 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.
+ *
+ * Returns:
+ * 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 = No.keepTerminator,
+ Terminator terminator = '\n', Conn conn = Conn())
+if (isCurlConn!Conn && isSomeChar!Char && isSomeChar!Terminator)
+{
+ static struct SyncLineInputRange
+ {
+
+ private Char[] lines;
+ private Char[] current;
+ private bool currentValid;
+ private bool keepTerminator;
+ private Terminator terminator;
+
+ this(Char[] lines, bool kt, Terminator terminator)
+ {
+ this.lines = lines;
+ this.keepTerminator = kt;
+ this.terminator = terminator;
+ currentValid = true;
+ popFront();
+ }
+
+ @property @safe bool empty()
+ {
+ return !currentValid;
+ }
+
+ @property @safe Char[] front()
+ {
+ enforce!CurlException(currentValid, "Cannot call front() on empty range");
+ return current;
+ }
+
+ void popFront()
+ {
+ import std.algorithm.searching : findSplitAfter, findSplit;
+
+ enforce!CurlException(currentValid, "Cannot call popFront() on empty range");
+ if (lines.empty)
+ {
+ currentValid = false;
+ return;
+ }
+
+ if (keepTerminator)
+ {
+ auto r = findSplitAfter(lines, [ terminator ]);
+ if (r[0].empty)
+ {
+ current = r[1];
+ lines = r[0];
+ }
+ else
+ {
+ current = r[0];
+ lines = r[1];
+ }
+ }
+ else
+ {
+ auto r = findSplit(lines, [ terminator ]);
+ current = r[0];
+ lines = r[2];
+ }
+ }
+ }
+
+ auto result = _getForRange!Char(url, conn);
+ return SyncLineInputRange(result, keepTerminator == Yes.keepTerminator, terminator);
+}
+
+@system unittest
+{
+ 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.
+ *
+ * A range of chunks is returned when the request is complete. If the method or
+ * other request properties is to be customized then set the $(D conn) parameter
+ * with a HTTP/FTP instance that has these properties set.
+ *
+ * Example:
+ * ----
+ * import std.net.curl, std.stdio;
+ * foreach (chunk; byChunk("dlang.org", 100))
+ * writeln(chunk); // chunk is ubyte[100]
+ * ----
+ *
+ * Params:
+ * url = The url to receive content from
+ * chunkSize = The size of each chunk
+ * conn = The connection to use e.g. HTTP or FTP.
+ *
+ * Returns:
+ * A range of ubyte[chunkSize] with the content of the resource pointer to by the URL
+ */
+auto byChunk(Conn = AutoProtocol)
+ (const(char)[] url, size_t chunkSize = 1024, Conn conn = Conn())
+if (isCurlConn!(Conn))
+{
+ static struct SyncChunkInputRange
+ {
+ private size_t chunkSize;
+ private ubyte[] _bytes;
+ private size_t offset;
+
+ this(ubyte[] bytes, size_t chunkSize)
+ {
+ this._bytes = bytes;
+ this.chunkSize = chunkSize;
+ }
+
+ @property @safe auto empty()
+ {
+ return offset == _bytes.length;
+ }
+
+ @property ubyte[] front()
+ {
+ size_t nextOffset = offset + chunkSize;
+ if (nextOffset > _bytes.length) nextOffset = _bytes.length;
+ return _bytes[offset .. nextOffset];
+ }
+
+ @safe void popFront()
+ {
+ offset += chunkSize;
+ if (offset > _bytes.length) offset = _bytes.length;
+ }
+ }
+
+ auto result = _getForRange!ubyte(url, conn);
+ return SyncChunkInputRange(result, chunkSize);
+}
+
+@system unittest
+{
+ 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(byChunk(host, 2).equal([[0, 1], [2, 3], [4, 5]]));
+ }
+}
+
+private T[] _getForRange(T,Conn)(const(char)[] url, Conn conn)
+{
+ static if (is(Conn : HTTP))
+ {
+ conn.method = conn.method == HTTP.Method.undefined ? HTTP.Method.get : conn.method;
+ return _basicHTTP!(T)(url, null, conn);
+ }
+ else static if (is(Conn : FTP))
+ {
+ return _basicFTP!(T)(url, null, conn);
+ }
+ else
+ {
+ if (isFTPUrl(url))
+ return get!(FTP,T)(url, FTP());
+ else
+ return get!(HTTP,T)(url, HTTP());
+ }
+}
+
+/*
+ Main thread part of the message passing protocol used for all async
+ curl protocols.
+ */
+private mixin template WorkerThreadProtocol(Unit, alias units)
+{
+ @property bool empty()
+ {
+ tryEnsureUnits();
+ return state == State.done;
+ }
+
+ @property Unit[] front()
+ {
+ import std.format : format;
+ tryEnsureUnits();
+ assert(state == State.gotUnits,
+ format("Expected %s but got $s",
+ State.gotUnits, state));
+ return units;
+ }
+
+ void popFront()
+ {
+ import std.format : format;
+ tryEnsureUnits();
+ assert(state == State.gotUnits,
+ format("Expected %s but got $s",
+ State.gotUnits, state));
+ state = State.needUnits;
+ // Send to worker thread for buffer reuse
+ workerTid.send(cast(immutable(Unit)[]) units);
+ units = null;
+ }
+
+ /** Wait for duration or until data is available and return true if data is
+ available
+ */
+ bool wait(Duration d)
+ {
+ import std.datetime.stopwatch : StopWatch;
+
+ if (state == State.gotUnits)
+ return true;
+
+ enum noDur = dur!"hnsecs"(0);
+ StopWatch sw;
+ sw.start();
+ while (state != State.gotUnits && d > noDur)
+ {
+ final switch (state)
+ {
+ case State.needUnits:
+ receiveTimeout(d,
+ (Tid origin, CurlMessage!(immutable(Unit)[]) _data)
+ {
+ if (origin != workerTid)
+ return false;
+ units = cast(Unit[]) _data.data;
+ state = State.gotUnits;
+ return true;
+ },
+ (Tid origin, CurlMessage!bool f)
+ {
+ if (origin != workerTid)
+ return false;
+ state = state.done;
+ return true;
+ }
+ );
+ break;
+ case State.gotUnits: return true;
+ case State.done:
+ return false;
+ }
+ d -= sw.peek();
+ sw.reset();
+ }
+ return state == State.gotUnits;
+ }
+
+ enum State
+ {
+ needUnits,
+ gotUnits,
+ done
+ }
+ State state;
+
+ void tryEnsureUnits()
+ {
+ while (true)
+ {
+ final switch (state)
+ {
+ case State.needUnits:
+ receive(
+ (Tid origin, CurlMessage!(immutable(Unit)[]) _data)
+ {
+ if (origin != workerTid)
+ return false;
+ units = cast(Unit[]) _data.data;
+ state = State.gotUnits;
+ return true;
+ },
+ (Tid origin, CurlMessage!bool f)
+ {
+ if (origin != workerTid)
+ return false;
+ state = state.done;
+ return true;
+ }
+ );
+ break;
+ case State.gotUnits: return;
+ case State.done:
+ return;
+ }
+ }
+ }
+}
+
+// @@@@BUG 15831@@@@
+// this should be inside byLineAsync
+// Range that reads one line at a time asynchronously.
+private static struct AsyncLineInputRange(Char)
+{
+ private Char[] line;
+ mixin WorkerThreadProtocol!(Char, line);
+
+ private Tid workerTid;
+ private State running;
+
+ private this(Tid tid, size_t transmitBuffers, size_t bufferSize)
+ {
+ workerTid = tid;
+ state = State.needUnits;
+
+ // 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)
+ {
+ auto arr = new Char[](bufferSize);
+ workerTid.send(cast(immutable(Char[]))arr);
+ }
+ }
+}
+
+/** HTTP/FTP fetch content as a range of lines asynchronously.
+ *
+ * A range of lines is returned immediately and the request that fetches the
+ * lines is performed in another thread. If the method or other request
+ * properties is to be customized then set the $(D conn) parameter with a
+ * HTTP/FTP instance that has these properties set.
+ *
+ * If $(D postData) is non-_null the method will be set to $(D post) for HTTP
+ * requests.
+ *
+ * The background thread will buffer up to transmitBuffers number of lines
+ * before it stops receiving data from network. When the main thread reads the
+ * lines from the range it frees up buffers and allows for the background thread
+ * to receive more data from the network.
+ *
+ * If no data is available and the main thread accesses the range it will block
+ * until data becomes available. An exception to this is the $(D wait(Duration)) method on
+ * the $(LREF AsyncLineInputRange). This method will wait at maximum for the
+ * specified duration and return true if data is available.
+ *
+ * Example:
+ * ----
+ * import std.net.curl, std.stdio;
+ * // Get some pages in the background
+ * auto range1 = byLineAsync("www.google.com");
+ * auto range2 = byLineAsync("www.wikipedia.org");
+ * foreach (line; byLineAsync("dlang.org"))
+ * writeln(line);
+ *
+ * // Lines already fetched in the background and ready
+ * foreach (line; range1) writeln(line);
+ * foreach (line; range2) writeln(line);
+ * ----
+ *
+ * ----
+ * import std.net.curl, std.stdio;
+ * // 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 (range3.wait(dur!"seconds"(2)))
+ * writeln(range3.front);
+ * else
+ * writeln("No line received after 2 seconds!");
+ * ----
+ *
+ * Params:
+ * url = The url to receive content from
+ * postData = Data to HTTP Post
+ * 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
+ * conn = The connection to use e.g. HTTP or FTP.
+ *
+ * Returns:
+ * A range of Char[] with the content of the resource pointer to by the
+ * URL.
+ */
+auto byLineAsync(Conn = AutoProtocol, Terminator = char, Char = char, PostUnit)
+ (const(char)[] url, const(PostUnit)[] postData,
+ KeepTerminator keepTerminator = No.keepTerminator,
+ Terminator terminator = '\n',
+ size_t transmitBuffers = 10, Conn conn = Conn())
+if (isCurlConn!Conn && isSomeChar!Char && isSomeChar!Terminator)
+{
+ static if (is(Conn : AutoProtocol))
+ {
+ if (isFTPUrl(url))
+ return byLineAsync(url, postData, keepTerminator,
+ terminator, transmitBuffers, FTP());
+ else
+ return byLineAsync(url, postData, keepTerminator,
+ terminator, transmitBuffers, HTTP());
+ }
+ else
+ {
+ // 50 is just an arbitrary number for now
+ setMaxMailboxSize(thisTid, 50, OnCrowding.block);
+ auto tid = spawn(&_spawnAsync!(Conn, Char, Terminator));
+ tid.send(thisTid);
+ tid.send(terminator);
+ tid.send(keepTerminator == Yes.keepTerminator);
+
+ _asyncDuplicateConnection(url, conn, postData, tid);
+
+ return AsyncLineInputRange!Char(tid, transmitBuffers,
+ Conn.defaultAsyncStringBufferSize);
+ }
+}
+
+/// ditto
+auto byLineAsync(Conn = AutoProtocol, Terminator = char, Char = char)
+ (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,
+ terminator, transmitBuffers, FTP());
+ else
+ return byLineAsync(url, cast(void[]) null, keepTerminator,
+ terminator, transmitBuffers, HTTP());
+ }
+ else
+ {
+ return byLineAsync(url, cast(void[]) null, keepTerminator,
+ terminator, transmitBuffers, conn);
+ }
+}
+
+@system unittest
+{
+ 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"]));
+ }
+}
+
+// @@@@BUG 15831@@@@
+// this should be inside byLineAsync
+// Range that reads one chunk at a time asynchronously.
+private static struct AsyncChunkInputRange
+{
+ private ubyte[] chunk;
+ mixin WorkerThreadProtocol!(ubyte, chunk);
+
+ private Tid workerTid;
+ private State running;
+
+ private this(Tid tid, size_t transmitBuffers, size_t chunkSize)
+ {
+ workerTid = tid;
+ state = State.needUnits;
+
+ // 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)
+ {
+ ubyte[] arr = new ubyte[](chunkSize);
+ workerTid.send(cast(immutable(ubyte[]))arr);
+ }
+ }
+}
+
+/** HTTP/FTP fetch content as a range of chunks asynchronously.
+ *
+ * A range of chunks is returned immediately and the request that fetches the
+ * chunks is performed in another thread. If the method or other request
+ * properties is to be customized then set the $(D conn) parameter with a
+ * HTTP/FTP instance that has these properties set.
+ *
+ * If $(D postData) is non-_null the method will be set to $(D post) for HTTP
+ * requests.
+ *
+ * The background thread will buffer up to transmitBuffers number of chunks
+ * before is stops receiving data from network. When the main thread reads the
+ * chunks from the range it frees up buffers and allows for the background
+ * thread to receive more data from the network.
+ *
+ * If no data is available and the main thread access the range it will block
+ * until data becomes available. An exception to this is the $(D wait(Duration))
+ * method on the $(LREF AsyncChunkInputRange). This method will wait at maximum for the specified
+ * duration and return true if data is available.
+ *
+ * Example:
+ * ----
+ * import std.net.curl, std.stdio;
+ * // Get some pages in the background
+ * auto range1 = byChunkAsync("www.google.com", 100);
+ * auto range2 = byChunkAsync("www.wikipedia.org");
+ * foreach (chunk; byChunkAsync("dlang.org"))
+ * writeln(chunk); // chunk is ubyte[100]
+ *
+ * // Chunks already fetched in the background and ready
+ * foreach (chunk; range1) writeln(chunk);
+ * foreach (chunk; range2) writeln(chunk);
+ * ----
+ *
+ * ----
+ * import std.net.curl, std.stdio;
+ * // 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 (range3.wait(dur!"seconds"(2)))
+ * writeln(range3.front);
+ * else
+ * writeln("No chunk received after 2 seconds!");
+ * ----
+ *
+ * Params:
+ * url = The url to receive content from
+ * postData = Data to HTTP Post
+ * chunkSize = The size of the chunks
+ * transmitBuffers = The number of chunks buffered asynchronously
+ * conn = The connection to use e.g. HTTP or FTP.
+ *
+ * Returns:
+ * A range of ubyte[chunkSize] with the content of the resource pointer to by
+ * the URL.
+ */
+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))
+{
+ static if (is(Conn : AutoProtocol))
+ {
+ if (isFTPUrl(url))
+ return byChunkAsync(url, postData, chunkSize,
+ transmitBuffers, FTP());
+ else
+ return byChunkAsync(url, postData, chunkSize,
+ transmitBuffers, HTTP());
+ }
+ else
+ {
+ // 50 is just an arbitrary number for now
+ setMaxMailboxSize(thisTid, 50, OnCrowding.block);
+ auto tid = spawn(&_spawnAsync!(Conn, ubyte));
+ tid.send(thisTid);
+
+ _asyncDuplicateConnection(url, conn, postData, tid);
+
+ return AsyncChunkInputRange(tid, transmitBuffers, chunkSize);
+ }
+}
+
+/// ditto
+auto byChunkAsync(Conn = AutoProtocol)
+ (const(char)[] url,
+ size_t chunkSize = 1024, size_t transmitBuffers = 10,
+ Conn conn = Conn())
+if (isCurlConn!(Conn))
+{
+ static if (is(Conn : AutoProtocol))
+ {
+ if (isFTPUrl(url))
+ return byChunkAsync(url, cast(void[]) null, chunkSize,
+ transmitBuffers, FTP());
+ else
+ return byChunkAsync(url, cast(void[]) null, chunkSize,
+ transmitBuffers, HTTP());
+ }
+ else
+ {
+ return byChunkAsync(url, cast(void[]) null, chunkSize,
+ transmitBuffers, conn);
+ }
+}
+
+@system unittest
+{
+ 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]]));
+ }
+}
+
+
+/* Used by byLineAsync/byChunkAsync to duplicate an existing connection
+ * that can be used exclusively in a spawned thread.
+ */
+private void _asyncDuplicateConnection(Conn, PostData)
+ (const(char)[] url, Conn conn, PostData postData, Tid tid)
+{
+ // no move semantic available in std.concurrency ie. must use casting.
+ auto connDup = conn.dup();
+ connDup.url = url;
+
+ static if ( is(Conn : HTTP) )
+ {
+ connDup.p.headersOut = null;
+ connDup.method = conn.method == HTTP.Method.undefined ?
+ HTTP.Method.get : conn.method;
+ if (postData !is null)
+ {
+ if (connDup.method == HTTP.Method.put)
+ {
+ connDup.handle.set(CurlOption.infilesize_large,
+ postData.length);
+ }
+ else
+ {
+ // post
+ connDup.method = HTTP.Method.post;
+ connDup.handle.set(CurlOption.postfieldsize_large,
+ postData.length);
+ }
+ connDup.handle.set(CurlOption.copypostfields,
+ cast(void*) postData.ptr);
+ }
+ 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(HTTP.Method.undefined);
+ }
+ connDup.p.curl.handle = null; // make sure handle is not freed
+}
+
+/*
+ Mixin template for all supported curl protocols. This is the commom
+ functionallity such as timeouts and network interface settings. This should
+ really be in the HTTP/FTP/SMTP structs but the documentation tool does not
+ support a mixin to put its doc strings where a mixin is done. Therefore docs
+ in this template is copied into each of HTTP/FTP/SMTP below.
+*/
+private mixin template Protocol()
+{
+
+ /// Value to return from $(D onSend)/$(D onReceive) delegates in order to
+ /// pause a request
+ alias requestPause = CurlReadFunc.pause;
+
+ /// Value to return from onSend delegate in order to abort a request
+ alias requestAbort = CurlReadFunc.abort;
+
+ static uint defaultAsyncStringBufferSize = 100;
+
+ /**
+ The curl handle used by this connection.
+ */
+ @property ref Curl handle() return
+ {
+ return p.curl;
+ }
+
+ /**
+ True if the instance is stopped. A stopped instance is not usable.
+ */
+ @property bool isStopped()
+ {
+ return p.curl.stopped;
+ }
+
+ /// Stop and invalidate this instance.
+ void shutdown()
+ {
+ p.curl.shutdown();
+ }
+
+ /** Set verbose.
+ This will print request information to stderr.
+ */
+ @property void verbose(bool on)
+ {
+ p.curl.set(CurlOption.verbose, on ? 1L : 0L);
+ }
+
+ // Connection settings
+
+ /// Set timeout for activity on connection.
+ @property void dataTimeout(Duration d)
+ {
+ p.curl.set(CurlOption.low_speed_limit, 1);
+ p.curl.set(CurlOption.low_speed_time, d.total!"seconds");
+ }
+
+ /** Set maximum time an operation is allowed to take.
+ This includes dns resolution, connecting, data transfer, etc.
+ */
+ @property void operationTimeout(Duration d)
+ {
+ p.curl.set(CurlOption.timeout_ms, d.total!"msecs");
+ }
+
+ /// Set timeout for connecting.
+ @property void connectTimeout(Duration d)
+ {
+ p.curl.set(CurlOption.connecttimeout_ms, d.total!"msecs");
+ }
+
+ // Network settings
+
+ /** Proxy
+ * See: $(HTTP curl.haxx.se/libcurl/c/curl_easy_setopt.html#CURLOPTPROXY, _proxy)
+ */
+ @property void proxy(const(char)[] host)
+ {
+ p.curl.set(CurlOption.proxy, host);
+ }
+
+ /** Proxy port
+ * See: $(HTTP curl.haxx.se/libcurl/c/curl_easy_setopt.html#CURLOPTPROXYPORT, _proxy_port)
+ */
+ @property void proxyPort(ushort port)
+ {
+ p.curl.set(CurlOption.proxyport, cast(long) port);
+ }
+
+ /// Type of proxy
+ alias CurlProxy = etc.c.curl.CurlProxy;
+
+ /** Proxy type
+ * See: $(HTTP curl.haxx.se/libcurl/c/curl_easy_setopt.html#CURLOPTPROXY, _proxy_type)
+ */
+ @property void proxyType(CurlProxy type)
+ {
+ p.curl.set(CurlOption.proxytype, cast(long) type);
+ }
+
+ /// DNS lookup timeout.
+ @property void dnsTimeout(Duration d)
+ {
+ p.curl.set(CurlOption.dns_cache_timeout, d.total!"msecs");
+ }
+
+ /**
+ * The network interface to use in form of the the IP of the interface.
+ *
+ * Example:
+ * ----
+ * theprotocol.netInterface = "192.168.1.32";
+ * theprotocol.netInterface = [ 192, 168, 1, 32 ];
+ * ----
+ *
+ * See: $(REF InternetAddress, std,socket)
+ */
+ @property void netInterface(const(char)[] i)
+ {
+ p.curl.set(CurlOption.intrface, i);
+ }
+
+ /// ditto
+ @property void netInterface(const(ubyte)[4] i)
+ {
+ import std.format : format;
+ const str = format("%d.%d.%d.%d", i[0], i[1], i[2], i[3]);
+ netInterface = str;
+ }
+
+ /// ditto
+ @property void netInterface(InternetAddress i)
+ {
+ netInterface = i.toAddrString();
+ }
+
+ /**
+ Set the local outgoing port to use.
+ Params:
+ port = the first outgoing port number to try and use
+ */
+ @property void localPort(ushort port)
+ {
+ p.curl.set(CurlOption.localport, cast(long) port);
+ }
+
+ /**
+ Set the no proxy flag for the specified host names.
+ Params:
+ test = a list of comma host names that do not require
+ proxy to get reached
+ */
+ void setNoProxy(string hosts)
+ {
+ p.curl.set(CurlOption.noproxy, hosts);
+ }
+
+ /**
+ Set the local outgoing port range to use.
+ This can be used together with the localPort property.
+ Params:
+ range = if the first port is occupied then try this many
+ port number forwards
+ */
+ @property void localPortRange(ushort range)
+ {
+ p.curl.set(CurlOption.localportrange, cast(long) range);
+ }
+
+ /** Set the tcp no-delay socket option on or off.
+ See: $(HTTP curl.haxx.se/libcurl/c/curl_easy_setopt.html#CURLOPTTCPNODELAY, nodelay)
+ */
+ @property void tcpNoDelay(bool on)
+ {
+ p.curl.set(CurlOption.tcp_nodelay, cast(long) (on ? 1 : 0) );
+ }
+
+ /** Sets whether SSL peer certificates should be verified.
+ See: $(HTTP curl.haxx.se/libcurl/c/curl_easy_setopt.html#CURLOPTSSLVERIFYPEER, verifypeer)
+ */
+ @property void verifyPeer(bool on)
+ {
+ p.curl.set(CurlOption.ssl_verifypeer, on ? 1 : 0);
+ }
+
+ /** Sets whether the host within an SSL certificate should be verified.
+ See: $(HTTP curl.haxx.se/libcurl/c/curl_easy_setopt.html#CURLOPTSSLVERIFYHOST, verifypeer)
+ */
+ @property void verifyHost(bool on)
+ {
+ p.curl.set(CurlOption.ssl_verifyhost, on ? 2 : 0);
+ }
+
+ // Authentication settings
+
+ /**
+ Set the user name, password and optionally domain for authentication
+ purposes.
+
+ Some protocols may need authentication in some cases. Use this
+ function to provide credentials.
+
+ Params:
+ username = the username
+ password = the password
+ domain = used for NTLM authentication only and is set to the NTLM domain
+ name
+ */
+ 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));
+ }
+
+ @system unittest
+ {
+ 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("user", "pass");
+ http.perform();
+
+ // Bugzilla 17540
+ http.setNoProxy("www.example.com");
+ }
+
+ /**
+ Set the user name and password for proxy authentication.
+
+ Params:
+ username = the username
+ password = the password
+ */
+ 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"),
+ password.replace(":", "%3A"))
+ );
+ }
+
+ /**
+ * The event handler that gets called when data is needed for sending. The
+ * length of the $(D void[]) specifies the maximum number of bytes that can
+ * be sent.
+ *
+ * Returns:
+ * The callback returns the number of elements in the buffer that have been
+ * filled and are ready to send.
+ * The special value $(D .abortRequest) can be returned in order to abort the
+ * current request.
+ * The special value $(D .pauseRequest) can be returned in order to pause the
+ * current request.
+ *
+ * Example:
+ * ----
+ * import std.net.curl;
+ * string msg = "Hello world";
+ * auto client = HTTP("dlang.org");
+ * client.onSend = delegate size_t(void[] data)
+ * {
+ * 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];
+ * msg = msg[length..$];
+ * return length;
+ * };
+ * client.perform();
+ * ----
+ */
+ @property void onSend(size_t delegate(void[]) callback)
+ {
+ p.curl.clear(CurlOption.postfields); // cannot specify data when using callback
+ p.curl.onSend = callback;
+ }
+
+ /**
+ * The event handler that receives incoming data. Be sure to copy the
+ * incoming ubyte[] since it is not guaranteed to be valid after the
+ * callback returns.
+ *
+ * Returns:
+ * The callback returns the number of incoming bytes read. If the entire array is
+ * not read the request will abort.
+ * The special value .pauseRequest can be returned in order to pause the
+ * current request.
+ *
+ * Example:
+ * ----
+ * import std.net.curl, std.stdio;
+ * auto client = HTTP("dlang.org");
+ * client.onReceive = (ubyte[] data)
+ * {
+ * writeln("Got data", to!(const(char)[])(data));
+ * return data.length;
+ * };
+ * client.perform();
+ * ----
+ */
+ @property void onReceive(size_t delegate(ubyte[]) callback)
+ {
+ p.curl.onReceive = callback;
+ }
+
+ /**
+ * The event handler that gets called to inform of upload/download progress.
+ *
+ * Params:
+ * dlTotal = total bytes to download
+ * dlNow = currently downloaded bytes
+ * ulTotal = total bytes to upload
+ * ulNow = currently uploaded bytes
+ *
+ * Returns:
+ * Return 0 from the callback to signal success, return non-zero to abort
+ * transfer
+ *
+ * Example:
+ * ----
+ * import std.net.curl, std.stdio;
+ * auto client = HTTP("dlang.org");
+ * client.onProgress = delegate int(size_t dl, size_t dln, size_t ul, size_t ult)
+ * {
+ * writeln("Progress: downloaded ", dln, " of ", dl);
+ * writeln("Progress: uploaded ", uln, " of ", ul);
+ * };
+ * client.perform();
+ * ----
+ */
+ @property void onProgress(int delegate(size_t dlTotal, size_t dlNow,
+ size_t ulTotal, size_t ulNow) callback)
+ {
+ p.curl.onProgress = callback;
+ }
+}
+
+/*
+ Decode $(D ubyte[]) array using the provided EncodingScheme up to maxChars
+ Returns: Tuple of ubytes read and the $(D Char[]) characters decoded.
+ Not all ubytes are guaranteed to be read in case of decoding error.
+*/
+private Tuple!(size_t,Char[])
+decodeString(Char = char)(const(ubyte)[] data,
+ EncodingScheme scheme,
+ size_t maxChars = size_t.max)
+{
+ Char[] res;
+ immutable startLen = data.length;
+ size_t charsDecoded = 0;
+ while (data.length && charsDecoded < maxChars)
+ {
+ immutable dchar dc = scheme.safeDecode(data);
+ if (dc == INVALID_SEQUENCE)
+ {
+ return typeof(return)(size_t.max, cast(Char[]) null);
+ }
+ charsDecoded++;
+ res ~= dc;
+ }
+ return typeof(return)(startLen-data.length, res);
+}
+
+/*
+ Decode $(D ubyte[]) array using the provided $(D EncodingScheme) until a the
+ line terminator specified is found. The basesrc parameter is effectively
+ prepended to src as the first thing.
+
+ This function is used for decoding as much of the src buffer as
+ possible until either the terminator is found or decoding fails. If
+ it fails as the last data in the src it may mean that the src buffer
+ were missing some bytes in order to represent a correct code
+ point. Upon the next call to this function more bytes have been
+ received from net and the failing bytes should be given as the
+ basesrc parameter. It is done this way to minimize data copying.
+
+ Returns: true if a terminator was found
+ Not all ubytes are guaranteed to be read in case of decoding error.
+ any decoded chars will be inserted into dst.
+*/
+private bool decodeLineInto(Terminator, Char = char)(ref const(ubyte)[] basesrc,
+ ref const(ubyte)[] src,
+ ref Char[] dst,
+ EncodingScheme scheme,
+ Terminator terminator)
+{
+ 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.
+ immutable blen = basesrc.length;
+ immutable len = (basesrc.length + src.length) >= 4 ?
+ 4 : basesrc.length + src.length;
+ basesrc.length = len;
+
+ immutable dchar dc = scheme.safeDecode(basesrc);
+ if (dc == INVALID_SEQUENCE)
+ {
+ enforce!CurlException(len != 4, "Invalid code sequence");
+ return false;
+ }
+ dst ~= dc;
+ src = src[len-basesrc.length-blen .. $]; // remove used ubytes from src
+ basesrc.length = 0;
+ }
+
+ while (src.length)
+ {
+ const lsrc = src;
+ dchar dc = scheme.safeDecode(src);
+ if (dc == INVALID_SEQUENCE)
+ {
+ if (src.empty)
+ {
+ // The invalid sequence was in the end of the src. Maybe there
+ // just need to be more bytes available so these last bytes are
+ // put back to src for later use.
+ src = lsrc;
+ return false;
+ }
+ dc = '?';
+ }
+ dst ~= dc;
+
+ if (dst.endsWith(terminator))
+ return true;
+ }
+ return false; // no terminator found
+}
+
+/**
+ * HTTP client functionality.
+ *
+ * Example:
+ * ---
+ * import std.net.curl, std.stdio;
+ *
+ * // Get with custom data receivers
+ * auto http = HTTP("dlang.org");
+ * http.onReceiveHeader =
+ * (in char[] key, in char[] value) { writeln(key ~ ": " ~ value); };
+ * http.onReceive = (ubyte[] data) { /+ drop +/ return data.length; };
+ * http.perform();
+ *
+ * // Put with data senders
+ * auto msg = "Hello world";
+ * http.contentLength = msg.length;
+ * http.onSend = (void[] data)
+ * {
+ * 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];
+ * msg = msg[len..$];
+ * return len;
+ * };
+ * http.perform();
+ *
+ * // Track progress
+ * http.method = HTTP.Method.get;
+ * http.url = "http://upload.wikimedia.org/wikipedia/commons/"
+ * "5/53/Wikipedia-logo-en-big.png";
+ * http.onReceive = (ubyte[] data) { return data.length; };
+ * http.onProgress = (size_t dltotal, size_t dlnow,
+ * size_t ultotal, size_t ulnow)
+ * {
+ * writeln("Progress ", dltotal, ", ", dlnow, ", ", ultotal, ", ", ulnow);
+ * return 0;
+ * };
+ * http.perform();
+ * ---
+ *
+ * See_Also: $(_HTTP www.ietf.org/rfc/rfc2616.txt, RFC2616)
+ *
+ */
+struct HTTP
+{
+ mixin Protocol;
+
+ import std.datetime.systime : SysTime;
+
+ /// Authentication method equal to $(REF CurlAuth, etc,c,curl)
+ alias AuthMethod = CurlAuth;
+
+ static private uint defaultMaxRedirects = 10;
+
+ private struct Impl
+ {
+ ~this()
+ {
+ if (headersOut !is null)
+ Curl.curl.slist_free_all(headersOut);
+ if (curl.handle !is null) // work around RefCounted/emplace bug
+ curl.shutdown();
+ }
+ Curl curl;
+ curl_slist* headersOut;
+ string[string] headersIn;
+ string charset;
+
+ /// The status line of the final sub-request in a request.
+ StatusLine status;
+ private void delegate(StatusLine) onReceiveStatusLine;
+
+ /// The HTTP method to use.
+ Method method = Method.undefined;
+
+ @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.
+ auto dg = (in char[] header)
+ {
+ import std.utf : UTFException;
+ try
+ {
+ if (header.empty)
+ {
+ // header delimiter
+ return;
+ }
+ if (header.startsWith("HTTP/"))
+ {
+ headersIn.clear();
+
+ const m = match(header, regex(r"^HTTP/(\d+)\.(\d+) (\d+) (.*)$"));
+ if (m.empty)
+ {
+ // Invalid status line
+ }
+ else
+ {
+ status.majorVersion = to!ushort(m.captures[1]);
+ status.minorVersion = to!ushort(m.captures[2]);
+ status.code = to!ushort(m.captures[3]);
+ status.reason = m.captures[4].idup;
+ if (onReceiveStatusLine != null)
+ onReceiveStatusLine(status);
+ }
+ return;
+ }
+
+ // Normal http header
+ auto m = match(cast(char[]) header, regex("(.*?): (.*)$"));
+
+ auto fieldName = m.captures[1].toLower().idup;
+ if (fieldName == "content-type")
+ {
+ auto mct = match(cast(char[]) m.captures[2],
+ regex("charset=([^;]*)", "i"));
+ if (!mct.empty && mct.captures.length > 1)
+ charset = mct.captures[1].idup;
+ }
+
+ if (!m.empty && callback !is null)
+ callback(fieldName, m.captures[2]);
+ headersIn[fieldName] = m.captures[2].idup;
+ }
+ catch (UTFException e)
+ {
+ //munch it - a header should be all ASCII, any "wrong UTF" is broken header
+ }
+ };
+
+ curl.onReceiveHeader = dg;
+ }
+ }
+
+ private RefCounted!Impl p;
+
+ /** Time condition enumeration as an alias of $(REF CurlTimeCond, etc,c,curl)
+
+ $(HTTP www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.25, _RFC2616 Section 14.25)
+ */
+ alias TimeCond = CurlTimeCond;
+
+ /**
+ Constructor taking the url as parameter.
+ */
+ static HTTP opCall(const(char)[] url)
+ {
+ HTTP http;
+ http.initialize();
+ http.url = url;
+ return http;
+ }
+
+ ///
+ static HTTP opCall()
+ {
+ HTTP http;
+ http.initialize();
+ return http;
+ }
+
+ ///
+ HTTP dup()
+ {
+ HTTP copy;
+ copy.initialize();
+ copy.p.method = p.method;
+ curl_slist* cur = p.headersOut;
+ curl_slist* newlist = null;
+ while (cur)
+ {
+ newlist = Curl.curl.slist_append(newlist, cur.data);
+ cur = cur.next;
+ }
+ copy.p.headersOut = newlist;
+ copy.p.curl.set(CurlOption.httpheader, copy.p.headersOut);
+ copy.p.curl = p.curl.dup();
+ copy.dataTimeout = _defaultDataTimeout;
+ copy.onReceiveHeader = null;
+ return copy;
+ }
+
+ private void initialize()
+ {
+ p.curl.initialize();
+ maxRedirects = HTTP.defaultMaxRedirects;
+ p.charset = "ISO-8859-1"; // Default charset defined in HTTP RFC
+ p.method = Method.undefined;
+ setUserAgent(HTTP.defaultUserAgent);
+ dataTimeout = _defaultDataTimeout;
+ onReceiveHeader = null;
+ verifyPeer = true;
+ verifyHost = true;
+ }
+
+ /**
+ Perform a http request.
+
+ After the HTTP client has been setup and possibly assigned callbacks the
+ $(D perform()) method will start performing the request towards the
+ specified server.
+
+ Params:
+ throwOnError = whether to throw an exception or return a CurlCode on error
+ */
+ 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);
+ }
+
+ /// Set the CA certificate bundle file to use for SSL peer verification
+ @property void caInfo(const(char)[] caFile)
+ {
+ p.curl.set(CurlOption.cainfo, caFile);
+ }
+
+ // This is a workaround for mixed in content not having its
+ // docs mixed in.
+ version (StdDdoc)
+ {
+ /// Value to return from $(D onSend)/$(D onReceive) delegates in order to
+ /// pause a request
+ alias requestPause = CurlReadFunc.pause;
+
+ /// Value to return from onSend delegate in order to abort a request
+ alias requestAbort = CurlReadFunc.abort;
+
+ /**
+ True if the instance is stopped. A stopped instance is not usable.
+ */
+ @property bool isStopped();
+
+ /// Stop and invalidate this instance.
+ void shutdown();
+
+ /** Set verbose.
+ This will print request information to stderr.
+ */
+ @property void verbose(bool on);
+
+ // Connection settings
+
+ /// Set timeout for activity on connection.
+ @property void dataTimeout(Duration d);
+
+ /** Set maximum time an operation is allowed to take.
+ This includes dns resolution, connecting, data transfer, etc.
+ */
+ @property void operationTimeout(Duration d);
+
+ /// Set timeout for connecting.
+ @property void connectTimeout(Duration d);
+
+ // Network settings
+
+ /** Proxy
+ * See: $(HTTP curl.haxx.se/libcurl/c/curl_easy_setopt.html#CURLOPTPROXY, _proxy)
+ */
+ @property void proxy(const(char)[] host);
+
+ /** Proxy port
+ * See: $(HTTP curl.haxx.se/libcurl/c/curl_easy_setopt.html#CURLOPTPROXYPORT, _proxy_port)
+ */
+ @property void proxyPort(ushort port);
+
+ /// Type of proxy
+ alias CurlProxy = etc.c.curl.CurlProxy;
+
+ /** Proxy type
+ * See: $(HTTP curl.haxx.se/libcurl/c/curl_easy_setopt.html#CURLOPTPROXY, _proxy_type)
+ */
+ @property void proxyType(CurlProxy type);
+
+ /// DNS lookup timeout.
+ @property void dnsTimeout(Duration d);
+
+ /**
+ * The network interface to use in form of the the IP of the interface.
+ *
+ * Example:
+ * ----
+ * theprotocol.netInterface = "192.168.1.32";
+ * theprotocol.netInterface = [ 192, 168, 1, 32 ];
+ * ----
+ *
+ * See: $(REF InternetAddress, std,socket)
+ */
+ @property void netInterface(const(char)[] i);
+
+ /// ditto
+ @property void netInterface(const(ubyte)[4] i);
+
+ /// ditto
+ @property void netInterface(InternetAddress i);
+
+ /**
+ Set the local outgoing port to use.
+ Params:
+ port = the first outgoing port number to try and use
+ */
+ @property void localPort(ushort port);
+
+ /**
+ Set the local outgoing port range to use.
+ This can be used together with the localPort property.
+ Params:
+ range = if the first port is occupied then try this many
+ port number forwards
+ */
+ @property void localPortRange(ushort range);
+
+ /** Set the tcp no-delay socket option on or off.
+ See: $(HTTP curl.haxx.se/libcurl/c/curl_easy_setopt.html#CURLOPTTCPNODELAY, nodelay)
+ */
+ @property void tcpNoDelay(bool on);
+
+ // Authentication settings
+
+ /**
+ Set the user name, password and optionally domain for authentication
+ purposes.
+
+ Some protocols may need authentication in some cases. Use this
+ function to provide credentials.
+
+ Params:
+ username = the username
+ password = the password
+ domain = used for NTLM authentication only and is set to the NTLM domain
+ name
+ */
+ void setAuthentication(const(char)[] username, const(char)[] password,
+ const(char)[] domain = "");
+
+ /**
+ Set the user name and password for proxy authentication.
+
+ Params:
+ username = the username
+ password = the password
+ */
+ void setProxyAuthentication(const(char)[] username, const(char)[] password);
+
+ /**
+ * The event handler that gets called when data is needed for sending. The
+ * length of the $(D void[]) specifies the maximum number of bytes that can
+ * be sent.
+ *
+ * Returns:
+ * The callback returns the number of elements in the buffer that have been
+ * filled and are ready to send.
+ * The special value $(D .abortRequest) can be returned in order to abort the
+ * current request.
+ * The special value $(D .pauseRequest) can be returned in order to pause the
+ * current request.
+ *
+ * Example:
+ * ----
+ * import std.net.curl;
+ * string msg = "Hello world";
+ * auto client = HTTP("dlang.org");
+ * client.onSend = delegate size_t(void[] data)
+ * {
+ * 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];
+ * msg = msg[length..$];
+ * return length;
+ * };
+ * client.perform();
+ * ----
+ */
+ @property void onSend(size_t delegate(void[]) callback);
+
+ /**
+ * The event handler that receives incoming data. Be sure to copy the
+ * incoming ubyte[] since it is not guaranteed to be valid after the
+ * callback returns.
+ *
+ * Returns:
+ * The callback returns the incoming bytes read. If not the entire array is
+ * the request will abort.
+ * The special value .pauseRequest can be returned in order to pause the
+ * current request.
+ *
+ * Example:
+ * ----
+ * import std.net.curl, std.stdio;
+ * auto client = HTTP("dlang.org");
+ * client.onReceive = (ubyte[] data)
+ * {
+ * writeln("Got data", to!(const(char)[])(data));
+ * return data.length;
+ * };
+ * client.perform();
+ * ----
+ */
+ @property void onReceive(size_t delegate(ubyte[]) callback);
+
+ /**
+ * Register an event handler that gets called to inform of
+ * upload/download progress.
+ *
+ * Callback_parameters:
+ * $(CALLBACK_PARAMS)
+ *
+ * Callback_returns: Return 0 to signal success, return non-zero to
+ * abort transfer.
+ *
+ * Example:
+ * ----
+ * import std.net.curl, std.stdio;
+ * auto client = HTTP("dlang.org");
+ * client.onProgress = delegate int(size_t dl, size_t dln, size_t ul, size_t ult)
+ * {
+ * writeln("Progress: downloaded ", dln, " of ", dl);
+ * writeln("Progress: uploaded ", uln, " of ", ul);
+ * };
+ * client.perform();
+ * ----
+ */
+ @property void onProgress(int delegate(size_t dlTotal, size_t dlNow,
+ size_t ulTotal, size_t ulNow) callback);
+ }
+
+ /** Clear all outgoing headers.
+ */
+ void clearRequestHeaders()
+ {
+ if (p.headersOut !is null)
+ Curl.curl.slist_free_all(p.headersOut);
+ p.headersOut = null;
+ p.curl.clear(CurlOption.httpheader);
+ }
+
+ /** Add a header e.g. "X-CustomField: Something is fishy".
+ *
+ * There is no remove header functionality. Do a $(LREF clearRequestHeaders)
+ * and set the needed headers instead.
+ *
+ * Example:
+ * ---
+ * import std.net.curl;
+ * auto client = HTTP();
+ * client.addRequestHeader("X-Custom-ABC", "This is the custom value");
+ * 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.curl.slist_append(p.headersOut,
+ nv.tempCString().buffPtr);
+ p.curl.set(CurlOption.httpheader, p.headersOut);
+ }
+
+ /**
+ * 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 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;
+
+ static char[maxLen] buf = void;
+ static string userAgent;
+
+ 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.
+ *
+ * By default a request has it's "User-Agent" field set to $(LREF
+ * defaultUserAgent) even if $(D setUserAgent) was never called. Pass
+ * an empty string to suppress the "User-Agent" field altogether.
+ */
+ void setUserAgent(const(char)[] userAgent)
+ {
+ 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.
+ *
+ */
+ @property string[string] responseHeaders()
+ {
+ return p.headersIn;
+ }
+
+ /// HTTP method used.
+ @property void method(Method m)
+ {
+ p.method = m;
+ }
+
+ /// ditto
+ @property Method method()
+ {
+ return p.method;
+ }
+
+ /**
+ HTTP status line of last response. One call to perform may
+ result in several requests because of redirection.
+ */
+ @property StatusLine statusLine()
+ {
+ return p.status;
+ }
+
+ /// Set the active cookie string e.g. "name1=value1;name2=value2"
+ void setCookie(const(char)[] cookie)
+ {
+ p.curl.set(CurlOption.cookie, cookie);
+ }
+
+ /// Set a file path to where a cookie jar should be read/stored.
+ void setCookieJar(const(char)[] path)
+ {
+ p.curl.set(CurlOption.cookiefile, path);
+ if (path.length)
+ p.curl.set(CurlOption.cookiejar, path);
+ }
+
+ /// Flush cookie jar to disk.
+ void flushCookieJar()
+ {
+ p.curl.set(CurlOption.cookielist, "FLUSH");
+ }
+
+ /// Clear session cookies.
+ void clearSessionCookies()
+ {
+ p.curl.set(CurlOption.cookielist, "SESS");
+ }
+
+ /// Clear all cookies.
+ void clearAllCookies()
+ {
+ p.curl.set(CurlOption.cookielist, "ALL");
+ }
+
+ /**
+ Set time condition on the request.
+
+ Params:
+ cond = $(D CurlTimeCond.{none,ifmodsince,ifunmodsince,lastmod})
+ timestamp = Timestamp for the condition
+
+ $(HTTP www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.25, _RFC2616 Section 14.25)
+ */
+ void setTimeCondition(HTTP.TimeCond cond, SysTime timestamp)
+ {
+ p.curl.set(CurlOption.timecondition, cond);
+ p.curl.set(CurlOption.timevalue, timestamp.toUnixTime());
+ }
+
+ /** Specifying data to post when not using the onSend callback.
+ *
+ * The data is NOT copied by the library. Content-Type will default to
+ * application/octet-stream. Data is not converted or encoded by this
+ * method.
+ *
+ * Example:
+ * ----
+ * import std.net.curl, std.stdio;
+ * auto http = HTTP("http://www.mydomain.com");
+ * http.onReceive = (ubyte[] data) { writeln(to!(const(char)[])(data)); return data.length; };
+ * http.postData = [1,2,3,4,5];
+ * http.perform();
+ * ----
+ */
+ @property void postData(const(void)[] data)
+ {
+ setPostData(data, "application/octet-stream");
+ }
+
+ /** Specifying data to post when not using the onSend callback.
+ *
+ * The data is NOT copied by the library. Content-Type will default to
+ * text/plain. Data is not converted or encoded by this method.
+ *
+ * Example:
+ * ----
+ * import std.net.curl, std.stdio;
+ * auto http = HTTP("http://www.mydomain.com");
+ * http.onReceive = (ubyte[] data) { writeln(to!(const(char)[])(data)); return data.length; };
+ * http.postData = "The quick....";
+ * http.perform();
+ * ----
+ */
+ @property void postData(const(char)[] data)
+ {
+ setPostData(data, "text/plain");
+ }
+
+ /**
+ * Specify data to post when not using the onSend callback, with
+ * user-specified Content-Type.
+ * Params:
+ * data = Data to post.
+ * contentType = MIME type of the data, for example, "text/plain" or
+ * "application/octet-stream". See also:
+ * $(LINK2 http://en.wikipedia.org/wiki/Internet_media_type,
+ * Internet media type) on Wikipedia.
+ * -----
+ * import std.net.curl;
+ * auto http = HTTP("http://onlineform.example.com");
+ * auto data = "app=login&username=bob&password=s00perS3kret";
+ * http.setPostData(data, "application/x-www-form-urlencoded");
+ * http.onReceive = (ubyte[] data) { return data.length; };
+ * http.perform();
+ * -----
+ */
+ void setPostData(const(void)[] data, string contentType)
+ {
+ // 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.postfieldsize, data.length);
+ if (method == Method.undefined)
+ method = Method.post;
+ }
+
+ @system unittest
+ {
+ 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[] res;
+ http.onReceive = (data) { res ~= data; return data.length; };
+ http.perform();
+ assert(res == cast(ubyte[])[17, 27, 35, 41]);
+ }
+
+ /**
+ * Set the event handler that receives incoming headers.
+ *
+ * The callback will receive a header field key, value as parameter. The
+ * $(D const(char)[]) arrays are not valid after the delegate has returned.
+ *
+ * Example:
+ * ----
+ * import std.net.curl, std.stdio;
+ * auto http = HTTP("dlang.org");
+ * http.onReceive = (ubyte[] data) { writeln(to!(const(char)[])(data)); return data.length; };
+ * http.onReceiveHeader = (in char[] key, in char[] value) { writeln(key, " = ", value); };
+ * http.perform();
+ * ----
+ */
+ @property void onReceiveHeader(void delegate(in char[] key,
+ in char[] value) callback)
+ {
+ p.onReceiveHeader = callback;
+ }
+
+ /**
+ Callback for each received StatusLine.
+
+ Notice that several callbacks can be done for each call to
+ $(D perform()) due to redirections.
+
+ See_Also: $(LREF StatusLine)
+ */
+ @property void onReceiveStatusLine(void delegate(StatusLine) callback)
+ {
+ p.onReceiveStatusLine = callback;
+ }
+
+ /**
+ 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 ulong.max to reset to chunked transfer.
+ */
+ @property void contentLength(ulong len)
+ {
+ import std.conv : to;
+
+ CurlOption lenOpt;
+
+ // Force post if necessary
+ if (p.method != Method.put && p.method != Method.post &&
+ p.method != Method.patch)
+ p.method = Method.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 == ulong.max)
+ {
+ // HTTP 1.1 supports requests with no length header set.
+ addRequestHeader("Transfer-Encoding", "chunked");
+ addRequestHeader("Expect", "100-continue");
+ }
+ else
+ {
+ p.curl.set(lenOpt, to!curl_off_t(len));
+ }
+ }
+
+ /**
+ Authentication method as specified in $(LREF AuthMethod).
+ */
+ @property void authenticationMethod(AuthMethod authMethod)
+ {
+ p.curl.set(CurlOption.httpauth, cast(long) authMethod);
+ }
+
+ /**
+ Set max allowed redirections using the location header.
+ uint.max for infinite.
+ */
+ @property void maxRedirects(uint maxRedirs)
+ {
+ if (maxRedirs == uint.max)
+ {
+ // Disable
+ p.curl.set(CurlOption.followlocation, 0);
+ }
+ else
+ {
+ p.curl.set(CurlOption.followlocation, 1);
+ p.curl.set(CurlOption.maxredirs, maxRedirs);
+ }
+ }
+
+ /** <a name="HTTP.Method"/>The standard HTTP methods :
+ * $(HTTP www.w3.org/Protocols/rfc2616/rfc2616-sec5.html#sec5.1.1, _RFC2616 Section 5.1.1)
+ */
+ enum Method
+ {
+ undefined,
+ head, ///
+ get, ///
+ post, ///
+ put, ///
+ del, ///
+ options, ///
+ trace, ///
+ connect, ///
+ patch, ///
+ }
+
+ /**
+ HTTP status line ie. the first line returned in an HTTP response.
+
+ If authentication or redirections are done then the status will be for
+ the last response received.
+ */
+ struct StatusLine
+ {
+ ushort majorVersion; /// Major HTTP version ie. 1 in HTTP/1.0.
+ ushort minorVersion; /// Minor HTTP version ie. 0 in HTTP/1.0.
+ ushort code; /// HTTP status line code e.g. 200.
+ string reason; /// HTTP status line reason string.
+
+ /// Reset this status line
+ @safe void reset()
+ {
+ majorVersion = 0;
+ minorVersion = 0;
+ code = 0;
+ reason = "";
+ }
+
+ ///
+ string toString() const
+ {
+ import std.format : format;
+ return format("%s %s (%s.%s)",
+ code, reason, majorVersion, minorVersion);
+ }
+ }
+
+} // 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: $(HTTP tools.ietf.org/html/rfc959, RFC959)
+*/
+struct FTP
+{
+
+ mixin Protocol;
+
+ private struct Impl
+ {
+ ~this()
+ {
+ if (commands !is null)
+ Curl.curl.slist_free_all(commands);
+ if (curl.handle !is null) // work around RefCounted/emplace bug
+ curl.shutdown();
+ }
+ curl_slist* commands;
+ Curl curl;
+ string encoding;
+ }
+
+ private RefCounted!Impl p;
+
+ /**
+ FTP access to the specified url.
+ */
+ static FTP opCall(const(char)[] url)
+ {
+ FTP ftp;
+ ftp.initialize();
+ ftp.url = url;
+ return ftp;
+ }
+
+ ///
+ static FTP opCall()
+ {
+ FTP ftp;
+ ftp.initialize();
+ return ftp;
+ }
+
+ ///
+ FTP dup()
+ {
+ FTP copy = FTP();
+ copy.initialize();
+ copy.p.encoding = p.encoding;
+ copy.p.curl = p.curl.dup();
+ curl_slist* cur = p.commands;
+ curl_slist* newlist = null;
+ while (cur)
+ {
+ newlist = Curl.curl.slist_append(newlist, cur.data);
+ cur = cur.next;
+ }
+ copy.p.commands = newlist;
+ copy.p.curl.set(CurlOption.postquote, copy.p.commands);
+ copy.dataTimeout = _defaultDataTimeout;
+ return copy;
+ }
+
+ private void initialize()
+ {
+ p.curl.initialize();
+ p.encoding = "ISO-8859-1";
+ dataTimeout = _defaultDataTimeout;
+ }
+
+ /**
+ Performs the ftp request as it has been configured.
+
+ After a FTP client has been setup and possibly assigned callbacks the $(D
+ perform()) method will start performing the actual communication with the
+ server.
+
+ Params:
+ throwOnError = whether to throw an exception or return a CurlCode on error
+ */
+ CurlCode perform(ThrowOnError throwOnError = Yes.throwOnError)
+ {
+ 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(), "ftp://", "ftps://"))
+ url = "ftp://" ~ url;
+ p.curl.set(CurlOption.url, url);
+ }
+
+ // This is a workaround for mixed in content not having its
+ // docs mixed in.
+ version (StdDdoc)
+ {
+ /// Value to return from $(D onSend)/$(D onReceive) delegates in order to
+ /// pause a request
+ alias requestPause = CurlReadFunc.pause;
+
+ /// Value to return from onSend delegate in order to abort a request
+ alias requestAbort = CurlReadFunc.abort;
+
+ /**
+ True if the instance is stopped. A stopped instance is not usable.
+ */
+ @property bool isStopped();
+
+ /// Stop and invalidate this instance.
+ void shutdown();
+
+ /** Set verbose.
+ This will print request information to stderr.
+ */
+ @property void verbose(bool on);
+
+ // Connection settings
+
+ /// Set timeout for activity on connection.
+ @property void dataTimeout(Duration d);
+
+ /** Set maximum time an operation is allowed to take.
+ This includes dns resolution, connecting, data transfer, etc.
+ */
+ @property void operationTimeout(Duration d);
+
+ /// Set timeout for connecting.
+ @property void connectTimeout(Duration d);
+
+ // Network settings
+
+ /** Proxy
+ * See: $(HTTP curl.haxx.se/libcurl/c/curl_easy_setopt.html#CURLOPTPROXY, _proxy)
+ */
+ @property void proxy(const(char)[] host);
+
+ /** Proxy port
+ * See: $(HTTP curl.haxx.se/libcurl/c/curl_easy_setopt.html#CURLOPTPROXYPORT, _proxy_port)
+ */
+ @property void proxyPort(ushort port);
+
+ /// Type of proxy
+ alias CurlProxy = etc.c.curl.CurlProxy;
+
+ /** Proxy type
+ * See: $(HTTP curl.haxx.se/libcurl/c/curl_easy_setopt.html#CURLOPTPROXY, _proxy_type)
+ */
+ @property void proxyType(CurlProxy type);
+
+ /// DNS lookup timeout.
+ @property void dnsTimeout(Duration d);
+
+ /**
+ * The network interface to use in form of the the IP of the interface.
+ *
+ * Example:
+ * ----
+ * theprotocol.netInterface = "192.168.1.32";
+ * theprotocol.netInterface = [ 192, 168, 1, 32 ];
+ * ----
+ *
+ * See: $(REF InternetAddress, std,socket)
+ */
+ @property void netInterface(const(char)[] i);
+
+ /// ditto
+ @property void netInterface(const(ubyte)[4] i);
+
+ /// ditto
+ @property void netInterface(InternetAddress i);
+
+ /**
+ Set the local outgoing port to use.
+ Params:
+ port = the first outgoing port number to try and use
+ */
+ @property void localPort(ushort port);
+
+ /**
+ Set the local outgoing port range to use.
+ This can be used together with the localPort property.
+ Params:
+ range = if the first port is occupied then try this many
+ port number forwards
+ */
+ @property void localPortRange(ushort range);
+
+ /** Set the tcp no-delay socket option on or off.
+ See: $(HTTP curl.haxx.se/libcurl/c/curl_easy_setopt.html#CURLOPTTCPNODELAY, nodelay)
+ */
+ @property void tcpNoDelay(bool on);
+
+ // Authentication settings
+
+ /**
+ Set the user name, password and optionally domain for authentication
+ purposes.
+
+ Some protocols may need authentication in some cases. Use this
+ function to provide credentials.
+
+ Params:
+ username = the username
+ password = the password
+ domain = used for NTLM authentication only and is set to the NTLM domain
+ name
+ */
+ void setAuthentication(const(char)[] username, const(char)[] password,
+ const(char)[] domain = "");
+
+ /**
+ Set the user name and password for proxy authentication.
+
+ Params:
+ username = the username
+ password = the password
+ */
+ void setProxyAuthentication(const(char)[] username, const(char)[] password);
+
+ /**
+ * The event handler that gets called when data is needed for sending. The
+ * length of the $(D void[]) specifies the maximum number of bytes that can
+ * be sent.
+ *
+ * Returns:
+ * The callback returns the number of elements in the buffer that have been
+ * filled and are ready to send.
+ * The special value $(D .abortRequest) can be returned in order to abort the
+ * current request.
+ * The special value $(D .pauseRequest) can be returned in order to pause the
+ * current request.
+ *
+ */
+ @property void onSend(size_t delegate(void[]) callback);
+
+ /**
+ * The event handler that receives incoming data. Be sure to copy the
+ * incoming ubyte[] since it is not guaranteed to be valid after the
+ * callback returns.
+ *
+ * Returns:
+ * The callback returns the incoming bytes read. If not the entire array is
+ * the request will abort.
+ * The special value .pauseRequest can be returned in order to pause the
+ * current request.
+ *
+ */
+ @property void onReceive(size_t delegate(ubyte[]) callback);
+
+ /**
+ * The event handler that gets called to inform of upload/download progress.
+ *
+ * Callback_parameters:
+ * $(CALLBACK_PARAMS)
+ *
+ * Callback_returns:
+ * Return 0 from the callback to signal success, return non-zero to
+ * abort transfer.
+ */
+ @property void onProgress(int delegate(size_t dlTotal, size_t dlNow,
+ size_t ulTotal, size_t ulNow) callback);
+ }
+
+ /** Clear all commands send to ftp server.
+ */
+ void clearCommands()
+ {
+ if (p.commands !is null)
+ Curl.curl.slist_free_all(p.commands);
+ p.commands = null;
+ p.curl.clear(CurlOption.postquote);
+ }
+
+ /** Add a command to send to ftp server.
+ *
+ * There is no remove command functionality. Do a $(LREF clearCommands) and
+ * set the needed commands instead.
+ *
+ * Example:
+ * ---
+ * import std.net.curl;
+ * auto client = FTP();
+ * client.addCommand("RNFR my_file.txt");
+ * client.addCommand("RNTO my_renamed_file.txt");
+ * upload("my_file.txt", "ftp.digitalmars.com", client);
+ * ---
+ */
+ void addCommand(const(char)[] command)
+ {
+ p.commands = Curl.curl.slist_append(p.commands,
+ command.tempCString().buffPtr);
+ p.curl.set(CurlOption.postquote, p.commands);
+ }
+
+ /// Connection encoding. Defaults to ISO-8859-1.
+ @property void encoding(string name)
+ {
+ p.encoding = name;
+ }
+
+ /// ditto
+ @property string encoding()
+ {
+ return p.encoding;
+ }
+
+ /**
+ The content length in bytes of the ftp data.
+ */
+ @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
+ {
+ 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);
+ }
+}
+
+/**
+ * Basic SMTP protocol support.
+ *
+ * Example:
+ * ---
+ * import std.net.curl;
+ *
+ * // Send an email with SMTPS
+ * auto smtp = SMTP("smtps://smtp.gmail.com");
+ * smtp.setAuthentication("from.addr@gmail.com", "password");
+ * smtp.mailTo = ["<to.addr@gmail.com>"];
+ * smtp.mailFrom = "<from.addr@gmail.com>";
+ * smtp.message = "Example Message";
+ * smtp.perform();
+ * ---
+ *
+ * See_Also: $(HTTP www.ietf.org/rfc/rfc2821.txt, RFC2821)
+ */
+struct SMTP
+{
+ mixin Protocol;
+
+ private struct Impl
+ {
+ ~this()
+ {
+ if (curl.handle !is null) // work around RefCounted/emplace bug
+ curl.shutdown();
+ }
+ Curl curl;
+
+ @property void message(string msg)
+ {
+ import std.algorithm.comparison : min;
+
+ auto _message = msg;
+ /**
+ This delegate reads the message text and copies it.
+ */
+ curl.onSend = delegate size_t(void[] data)
+ {
+ if (!msg.length) return 0;
+ size_t to_copy = min(data.length, _message.length);
+ data[0 .. to_copy] = (cast(void[])_message)[0 .. to_copy];
+ _message = _message[to_copy..$];
+ return to_copy;
+ };
+ }
+ }
+
+ private RefCounted!Impl p;
+
+ /**
+ Sets to the URL of the SMTP server.
+ */
+ static SMTP opCall(const(char)[] url)
+ {
+ SMTP smtp;
+ smtp.initialize();
+ smtp.url = url;
+ return smtp;
+ }
+
+ ///
+ static SMTP opCall()
+ {
+ SMTP smtp;
+ smtp.initialize();
+ return smtp;
+ }
+
+ /+ TODO: The other structs have this function.
+ SMTP dup()
+ {
+ SMTP copy = SMTP();
+ copy.initialize();
+ copy.p.encoding = p.encoding;
+ copy.p.curl = p.curl.dup();
+ curl_slist* cur = p.commands;
+ curl_slist* newlist = null;
+ while (cur)
+ {
+ newlist = Curl.curl.slist_append(newlist, cur.data);
+ cur = cur.next;
+ }
+ copy.p.commands = newlist;
+ copy.p.curl.set(CurlOption.postquote, copy.p.commands);
+ copy.dataTimeout = _defaultDataTimeout;
+ return copy;
+ }
+ +/
+
+ /**
+ Performs the request as configured.
+ Params:
+ throwOnError = whether to throw an exception or return a CurlCode on error
+ */
+ CurlCode perform(ThrowOnError throwOnError = Yes.throwOnError)
+ {
+ 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;
+
+ auto lowered = url.toLower();
+
+ if (lowered.startsWith("smtps://"))
+ {
+ p.curl.set(CurlOption.use_ssl, CurlUseSSL.all);
+ }
+ else
+ {
+ enforce!CurlException(lowered.startsWith("smtp://"),
+ "The url must be for the smtp protocol.");
+ }
+ p.curl.set(CurlOption.url, url);
+ }
+
+ private void initialize()
+ {
+ p.curl.initialize();
+ p.curl.set(CurlOption.upload, 1L);
+ dataTimeout = _defaultDataTimeout;
+ verifyPeer = true;
+ verifyHost = true;
+ }
+
+ // This is a workaround for mixed in content not having its
+ // docs mixed in.
+ version (StdDdoc)
+ {
+ /// Value to return from $(D onSend)/$(D onReceive) delegates in order to
+ /// pause a request
+ alias requestPause = CurlReadFunc.pause;
+
+ /// Value to return from onSend delegate in order to abort a request
+ alias requestAbort = CurlReadFunc.abort;
+
+ /**
+ True if the instance is stopped. A stopped instance is not usable.
+ */
+ @property bool isStopped();
+
+ /// Stop and invalidate this instance.
+ void shutdown();
+
+ /** Set verbose.
+ This will print request information to stderr.
+ */
+ @property void verbose(bool on);
+
+ // Connection settings
+
+ /// Set timeout for activity on connection.
+ @property void dataTimeout(Duration d);
+
+ /** Set maximum time an operation is allowed to take.
+ This includes dns resolution, connecting, data transfer, etc.
+ */
+ @property void operationTimeout(Duration d);
+
+ /// Set timeout for connecting.
+ @property void connectTimeout(Duration d);
+
+ // Network settings
+
+ /** Proxy
+ * See: $(HTTP curl.haxx.se/libcurl/c/curl_easy_setopt.html#CURLOPTPROXY, _proxy)
+ */
+ @property void proxy(const(char)[] host);
+
+ /** Proxy port
+ * See: $(HTTP curl.haxx.se/libcurl/c/curl_easy_setopt.html#CURLOPTPROXYPORT, _proxy_port)
+ */
+ @property void proxyPort(ushort port);
+
+ /// Type of proxy
+ alias CurlProxy = etc.c.curl.CurlProxy;
+
+ /** Proxy type
+ * See: $(HTTP curl.haxx.se/libcurl/c/curl_easy_setopt.html#CURLOPTPROXY, _proxy_type)
+ */
+ @property void proxyType(CurlProxy type);
+
+ /// DNS lookup timeout.
+ @property void dnsTimeout(Duration d);
+
+ /**
+ * The network interface to use in form of the the IP of the interface.
+ *
+ * Example:
+ * ----
+ * theprotocol.netInterface = "192.168.1.32";
+ * theprotocol.netInterface = [ 192, 168, 1, 32 ];
+ * ----
+ *
+ * See: $(REF InternetAddress, std,socket)
+ */
+ @property void netInterface(const(char)[] i);
+
+ /// ditto
+ @property void netInterface(const(ubyte)[4] i);
+
+ /// ditto
+ @property void netInterface(InternetAddress i);
+
+ /**
+ Set the local outgoing port to use.
+ Params:
+ port = the first outgoing port number to try and use
+ */
+ @property void localPort(ushort port);
+
+ /**
+ Set the local outgoing port range to use.
+ This can be used together with the localPort property.
+ Params:
+ range = if the first port is occupied then try this many
+ port number forwards
+ */
+ @property void localPortRange(ushort range);
+
+ /** Set the tcp no-delay socket option on or off.
+ See: $(HTTP curl.haxx.se/libcurl/c/curl_easy_setopt.html#CURLOPTTCPNODELAY, nodelay)
+ */
+ @property void tcpNoDelay(bool on);
+
+ // Authentication settings
+
+ /**
+ Set the user name, password and optionally domain for authentication
+ purposes.
+
+ Some protocols may need authentication in some cases. Use this
+ function to provide credentials.
+
+ Params:
+ username = the username
+ password = the password
+ domain = used for NTLM authentication only and is set to the NTLM domain
+ name
+ */
+ void setAuthentication(const(char)[] username, const(char)[] password,
+ const(char)[] domain = "");
+
+ /**
+ Set the user name and password for proxy authentication.
+
+ Params:
+ username = the username
+ password = the password
+ */
+ void setProxyAuthentication(const(char)[] username, const(char)[] password);
+
+ /**
+ * The event handler that gets called when data is needed for sending. The
+ * length of the $(D void[]) specifies the maximum number of bytes that can
+ * be sent.
+ *
+ * Returns:
+ * The callback returns the number of elements in the buffer that have been
+ * filled and are ready to send.
+ * The special value $(D .abortRequest) can be returned in order to abort the
+ * current request.
+ * The special value $(D .pauseRequest) can be returned in order to pause the
+ * current request.
+ */
+ @property void onSend(size_t delegate(void[]) callback);
+
+ /**
+ * The event handler that receives incoming data. Be sure to copy the
+ * incoming ubyte[] since it is not guaranteed to be valid after the
+ * callback returns.
+ *
+ * Returns:
+ * The callback returns the incoming bytes read. If not the entire array is
+ * the request will abort.
+ * The special value .pauseRequest can be returned in order to pause the
+ * current request.
+ */
+ @property void onReceive(size_t delegate(ubyte[]) callback);
+
+ /**
+ * The event handler that gets called to inform of upload/download progress.
+ *
+ * Callback_parameters:
+ * $(CALLBACK_PARAMS)
+ *
+ * Callback_returns:
+ * Return 0 from the callback to signal success, return non-zero to
+ * abort transfer.
+ */
+ @property void onProgress(int delegate(size_t dlTotal, size_t dlNow,
+ size_t ulTotal, size_t ulNow) callback);
+ }
+
+ /**
+ Setter for the sender's email address.
+ */
+ @property void mailFrom()(const(char)[] sender)
+ {
+ assert(!sender.empty, "Sender must not be empty");
+ p.curl.set(CurlOption.mail_from, sender);
+ }
+
+ /**
+ Setter for the recipient email addresses.
+ */
+ void mailTo()(const(char)[][] recipients...)
+ {
+ assert(!recipients.empty, "Recipient must not be empty");
+ curl_slist* recipients_list = null;
+ foreach (recipient; recipients)
+ {
+ recipients_list =
+ Curl.curl.slist_append(recipients_list,
+ recipient.tempCString().buffPtr);
+ }
+ p.curl.set(CurlOption.mail_rcpt, recipients_list);
+ }
+
+ /**
+ Sets the message body text.
+ */
+
+ @property void message(string msg)
+ {
+ p.message = msg;
+ }
+}
+
+/++
+ Exception thrown on errors in std.net.curl functions.
++/
+class CurlException : 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.
+ +/
+ @safe pure nothrow
+ this(string msg,
+ string file = __FILE__,
+ size_t line = __LINE__,
+ Throwable next = null)
+ {
+ super(msg, file, line, next);
+ }
+}
+
+/++
+ Exception thrown on timeout errors in std.net.curl functions.
++/
+class CurlTimeoutException : CurlException
+{
+ /++
+ 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.
+ +/
+ @safe pure nothrow
+ this(string msg,
+ string file = __FILE__,
+ size_t line = __LINE__,
+ Throwable next = null)
+ {
+ super(msg, file, line, next);
+ }
+}
+
+/++
+ Exception thrown on HTTP request failures, e.g. 404 Not Found.
++/
+class HTTPStatusException : CurlException
+{
+ /++
+ Params:
+ status = The HTTP status code.
+ 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.
+ +/
+ @safe pure nothrow
+ this(int status,
+ string msg,
+ string file = __FILE__,
+ size_t line = __LINE__,
+ Throwable next = null)
+ {
+ super(msg, file, line, next);
+ this.status = status;
+ }
+
+ immutable int status; /// The HTTP status code
+}
+
+/// Equal to $(REF CURLcode, etc,c,curl)
+alias CurlCode = CURLcode;
+
+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");
+
+ static extern(C) void cleanup()
+ {
+ 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;
+ }
+
+ import core.stdc.stdlib : atexit;
+ atexit(&cleanup);
+
+ return handle;
+ }
+}
+
+/**
+ 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
+ raw access to libcurl is needed.
+
+ Warning: This struct uses interior pointers for callbacks. Only allocate it
+ on the stack if you never move or copy it. This also means passing by reference
+ when passing Curl to other functions. Otherwise always allocate on
+ the heap.
+*/
+struct Curl
+{
+ alias OutData = void[];
+ alias InData = ubyte[];
+ 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;
+
+ // May also return $(D CURL_READFUNC_ABORT) or $(D CURL_READFUNC_PAUSE)
+ private size_t delegate(OutData) _onSend;
+ private size_t delegate(InData) _onReceive;
+ private void delegate(in char[]) _onReceiveHeader;
+ private CurlSeek delegate(long,CurlSeekPos) _onSeek;
+ private int delegate(curl_socket_t,CurlSockType) _onSocketOption;
+ private int delegate(size_t dltotal, size_t dlnow,
+ size_t ultotal, size_t ulnow) _onProgress;
+
+ alias requestPause = CurlReadFunc.pause;
+ alias requestAbort = CurlReadFunc.abort;
+
+ /**
+ Initialize the instance by creating a working curl handle.
+ */
+ void initialize()
+ {
+ enforce!CurlException(!handle, "Curl instance already initialized");
+ handle = curl.easy_init();
+ enforce!CurlException(handle, "Curl instance couldn't be initialized");
+ _stopped = false;
+ set(CurlOption.nosignal, 1);
+ }
+
+ ///
+ @property bool stopped() const
+ {
+ return _stopped;
+ }
+
+ /**
+ Duplicate this handle.
+
+ The new handle will have all options set as the one it was duplicated
+ from. An exception to this is that all options that cannot be shared
+ across threads are reset thereby making it safe to use the duplicate
+ in a new thread.
+ */
+ Curl dup()
+ {
+ Curl copy;
+ copy.handle = curl.easy_duphandle(handle);
+ copy._stopped = false;
+
+ with (CurlOption) {
+ 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);
+ }
+
+ // The options are only supported by libcurl when it has been built
+ // against certain versions of OpenSSL - if your libcurl uses an old
+ // OpenSSL, or uses an entirely different SSL engine, attempting to
+ // clear these normally will raise an exception
+ copy.clearIfSupported(CurlOption.ssl_ctx_function);
+ copy.clearIfSupported(CurlOption.ssh_keydata);
+
+ // Enable for curl version > 7.21.7
+ static if (LIBCURL_VERSION_MAJOR >= 7 &&
+ LIBCURL_VERSION_MINOR >= 21 &&
+ LIBCURL_VERSION_PATCH >= 7)
+ {
+ copy.clear(CurlOption.closesocketdata);
+ copy.clear(CurlOption.closesocketfunction);
+ }
+
+ copy.set(CurlOption.nosignal, 1);
+
+ // copy.clear(CurlOption.ssl_ctx_data); Let ssl function be shared
+ // copy.clear(CurlOption.ssh_keyfunction); Let key function be shared
+
+ /*
+ Allow sharing of conv functions
+ copy.clear(CurlOption.conv_to_network_function);
+ copy.clear(CurlOption.conv_from_network_function);
+ copy.clear(CurlOption.conv_from_utf8_function);
+ */
+
+ return copy;
+ }
+
+ private void _check(CurlCode code)
+ {
+ enforce!CurlTimeoutException(code != CurlError.operation_timedout,
+ errorString(code));
+
+ enforce!CurlException(code == CurlError.ok,
+ errorString(code));
+ }
+
+ private string errorString(CurlCode code)
+ {
+ import core.stdc.string : strlen;
+ import std.format : format;
+
+ 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 .. strlen(msgZ)], handle);
+ }
+
+ private void throwOnStopped(string message = null)
+ {
+ auto def = "Curl instance called after being cleaned up";
+ enforce!CurlException(!stopped,
+ message == null ? def : message);
+ }
+
+ /**
+ Stop and invalidate this curl instance.
+ Warning: Do not call this from inside a callback handler e.g. $(D onReceive).
+ */
+ void shutdown()
+ {
+ throwOnStopped();
+ _stopped = true;
+ curl.easy_cleanup(this.handle);
+ this.handle = null;
+ }
+
+ /**
+ Pausing and continuing transfers.
+ */
+ void pause(bool sendingPaused, bool receivingPaused)
+ {
+ throwOnStopped();
+ _check(curl.easy_pause(this.handle,
+ (sendingPaused ? CurlPause.send_cont : CurlPause.send) |
+ (receivingPaused ? CurlPause.recv_cont : CurlPause.recv)));
+ }
+
+ /**
+ Set a string curl option.
+ Params:
+ 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));
+ }
+
+ /**
+ Set a long curl option.
+ Params:
+ 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));
+ }
+
+ /**
+ Set a void* curl option.
+ Params:
+ 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));
+ }
+
+ /**
+ Clear a pointer option.
+ Params:
+ 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));
+ }
+
+ /**
+ Clear a pointer option. Does not raise an exception if the underlying
+ libcurl does not support the option. Use sparingly.
+ Params:
+ 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_option && rval != CurlError.not_built_in)
+ _check(rval);
+ }
+
+ /**
+ perform the curl request by doing the HTTP,FTP etc. as it has
+ been setup beforehand.
+
+ Params:
+ throwOnError = whether to throw an exception or return a CurlCode on error
+ */
+ CurlCode perform(ThrowOnError throwOnError = Yes.throwOnError)
+ {
+ throwOnStopped();
+ CurlCode code = curl.easy_perform(this.handle);
+ if (throwOnError)
+ _check(code);
+ return code;
+ }
+
+ /**
+ 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)
+ {
+ CurlCode code;
+ code = curl.easy_getinfo(handle, timing, &val);
+ return code;
+ }
+
+ /**
+ * The event handler that receives incoming data.
+ *
+ * Params:
+ * callback = the callback that receives the $(D ubyte[]) data.
+ * Be sure to copy the incoming data and not store
+ * a slice.
+ *
+ * Returns:
+ * The callback returns the incoming bytes read. If not the entire array is
+ * the request will abort.
+ * The special value HTTP.pauseRequest can be returned in order to pause the
+ * current request.
+ *
+ * Example:
+ * ----
+ * import std.net.curl, std.stdio;
+ * Curl curl;
+ * curl.initialize();
+ * curl.set(CurlOption.url, "http://dlang.org");
+ * curl.onReceive = (ubyte[] data) { writeln("Got data", to!(const(char)[])(data)); return data.length;};
+ * curl.perform();
+ * ----
+ */
+ @property void onReceive(size_t delegate(InData) callback)
+ {
+ _onReceive = (InData id)
+ {
+ throwOnStopped("Receive callback called on cleaned up Curl instance");
+ return callback(id);
+ };
+ set(CurlOption.file, cast(void*) &this);
+ set(CurlOption.writefunction, cast(void*) &Curl._receiveCallback);
+ }
+
+ /**
+ * The event handler that receives incoming headers for protocols
+ * that uses headers.
+ *
+ * Params:
+ * callback = the callback that receives the header string.
+ * Make sure the callback copies the incoming params if
+ * it needs to store it because they are references into
+ * the backend and may very likely change.
+ *
+ * Example:
+ * ----
+ * import std.net.curl, std.stdio;
+ * Curl curl;
+ * curl.initialize();
+ * curl.set(CurlOption.url, "http://dlang.org");
+ * curl.onReceiveHeader = (in char[] header) { writeln(header); };
+ * curl.perform();
+ * ----
+ */
+ @property void onReceiveHeader(void delegate(in char[]) callback)
+ {
+ _onReceiveHeader = (in char[] od)
+ {
+ throwOnStopped("Receive header callback called on "~
+ "cleaned up Curl instance");
+ callback(od);
+ };
+ set(CurlOption.writeheader, cast(void*) &this);
+ set(CurlOption.headerfunction,
+ cast(void*) &Curl._receiveHeaderCallback);
+ }
+
+ /**
+ * The event handler that gets called when data is needed for sending.
+ *
+ * Params:
+ * callback = the callback that has a $(D void[]) buffer to be filled
+ *
+ * Returns:
+ * The callback returns the number of elements in the buffer that have been
+ * filled and are ready to send.
+ * The special value $(D Curl.abortRequest) can be returned in
+ * order to abort the current request.
+ * The special value $(D Curl.pauseRequest) can be returned in order to
+ * pause the current request.
+ *
+ * Example:
+ * ----
+ * import std.net.curl;
+ * Curl curl;
+ * curl.initialize();
+ * curl.set(CurlOption.url, "http://dlang.org");
+ *
+ * string msg = "Hello world";
+ * curl.onSend = (void[] data)
+ * {
+ * 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];
+ * msg = msg[length..$];
+ * return length;
+ * };
+ * curl.perform();
+ * ----
+ */
+ @property void onSend(size_t delegate(OutData) callback)
+ {
+ _onSend = (OutData od)
+ {
+ throwOnStopped("Send callback called on cleaned up Curl instance");
+ return callback(od);
+ };
+ set(CurlOption.infile, cast(void*) &this);
+ set(CurlOption.readfunction, cast(void*) &Curl._sendCallback);
+ }
+
+ /**
+ * The event handler that gets called when the curl backend needs to seek
+ * the data to be sent.
+ *
+ * Params:
+ * callback = the callback that receives a seek offset and a seek position
+ * $(REF CurlSeekPos, etc,c,curl)
+ *
+ * Returns:
+ * The callback returns the success state of the seeking
+ * $(REF CurlSeek, etc,c,curl)
+ *
+ * Example:
+ * ----
+ * import std.net.curl;
+ * Curl curl;
+ * curl.initialize();
+ * curl.set(CurlOption.url, "http://dlang.org");
+ * curl.onSeek = (long p, CurlSeekPos sp)
+ * {
+ * return CurlSeek.cantseek;
+ * };
+ * curl.perform();
+ * ----
+ */
+ @property void onSeek(CurlSeek delegate(long, CurlSeekPos) callback)
+ {
+ _onSeek = (long ofs, CurlSeekPos sp)
+ {
+ throwOnStopped("Seek callback called on cleaned up Curl instance");
+ return callback(ofs, sp);
+ };
+ set(CurlOption.seekdata, cast(void*) &this);
+ set(CurlOption.seekfunction, cast(void*) &Curl._seekCallback);
+ }
+
+ /**
+ * The event handler that gets called when the net socket has been created
+ * but a $(D connect()) call has not yet been done. This makes it possible to set
+ * misc. socket options.
+ *
+ * Params:
+ * callback = the callback that receives the socket and socket type
+ * $(REF CurlSockType, etc,c,curl)
+ *
+ * Returns:
+ * Return 0 from the callback to signal success, return 1 to signal error
+ * and make curl close the socket
+ *
+ * Example:
+ * ----
+ * import std.net.curl;
+ * Curl curl;
+ * curl.initialize();
+ * curl.set(CurlOption.url, "http://dlang.org");
+ * curl.onSocketOption = delegate int(curl_socket_t s, CurlSockType t) { /+ do stuff +/ };
+ * curl.perform();
+ * ----
+ */
+ @property void onSocketOption(int delegate(curl_socket_t,
+ CurlSockType) callback)
+ {
+ _onSocketOption = (curl_socket_t sock, CurlSockType st)
+ {
+ throwOnStopped("Socket option callback called on "~
+ "cleaned up Curl instance");
+ return callback(sock, st);
+ };
+ set(CurlOption.sockoptdata, cast(void*) &this);
+ set(CurlOption.sockoptfunction,
+ cast(void*) &Curl._socketOptionCallback);
+ }
+
+ /**
+ * The event handler that gets called to inform of upload/download progress.
+ *
+ * Params:
+ * callback = the callback that receives the (total bytes to download,
+ * currently downloaded bytes, total bytes to upload, currently uploaded
+ * bytes).
+ *
+ * Returns:
+ * Return 0 from the callback to signal success, return non-zero to abort
+ * transfer
+ *
+ * Example:
+ * ----
+ * import std.net.curl;
+ * Curl curl;
+ * curl.initialize();
+ * curl.set(CurlOption.url, "http://dlang.org");
+ * curl.onProgress = delegate int(size_t dltotal, size_t dlnow, size_t ultotal, size_t uln)
+ * {
+ * writeln("Progress: downloaded bytes ", dlnow, " of ", dltotal);
+ * writeln("Progress: uploaded bytes ", ulnow, " of ", ultotal);
+ * curl.perform();
+ * };
+ * ----
+ */
+ @property void onProgress(int delegate(size_t dlTotal,
+ size_t dlNow,
+ size_t ulTotal,
+ size_t ulNow) callback)
+ {
+ _onProgress = (size_t dlt, size_t dln, size_t ult, size_t uln)
+ {
+ throwOnStopped("Progress callback called on cleaned "~
+ "up Curl instance");
+ return callback(dlt, dln, ult, uln);
+ };
+ set(CurlOption.noprogress, 0);
+ set(CurlOption.progressdata, cast(void*) &this);
+ set(CurlOption.progressfunction, cast(void*) &Curl._progressCallback);
+ }
+
+ // Internal C callbacks to register with libcurl
+ extern (C) private static
+ size_t _receiveCallback(const char* str,
+ size_t size, size_t nmemb, void* ptr)
+ {
+ auto b = cast(Curl*) ptr;
+ if (b._onReceive != null)
+ return b._onReceive(cast(InData)(str[0 .. size*nmemb]));
+ return size*nmemb;
+ }
+
+ extern (C) private static
+ 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();
+ if (b._onReceiveHeader != null)
+ b._onReceiveHeader(s);
+
+ return size*nmemb;
+ }
+
+ extern (C) private static
+ 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];
+ if (b._onSend == null)
+ return 0;
+ return b._onSend(a);
+ }
+
+ extern (C) private static
+ int _seekCallback(void *ptr, curl_off_t offset, int origin)
+ {
+ auto b = cast(Curl*) ptr;
+ if (b._onSeek == null)
+ return CurlSeek.cantseek;
+
+ // origin: CurlSeekPos.set/current/end
+ // return: CurlSeek.ok/fail/cantseek
+ return b._onSeek(cast(long) offset, cast(CurlSeekPos) origin);
+ }
+
+ extern (C) private static
+ int _socketOptionCallback(void *ptr,
+ curl_socket_t curlfd, curlsocktype purpose)
+ {
+ auto b = cast(Curl*) ptr;
+ if (b._onSocketOption == null)
+ return 0;
+
+ // return: 0 ok, 1 fail
+ return b._onSocketOption(curlfd, cast(CurlSockType) purpose);
+ }
+
+ extern (C) private static
+ int _progressCallback(void *ptr,
+ double dltotal, double dlnow,
+ double ultotal, double ulnow)
+ {
+ auto b = cast(Curl*) ptr;
+ if (b._onProgress == null)
+ 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);
+ }
+
+}
+
+// Internal messages send between threads.
+// The data is wrapped in this struct in order to ensure that
+// other std.concurrency.receive calls does not pick up our messages
+// by accident.
+private struct CurlMessage(T)
+{
+ public T data;
+}
+
+private static CurlMessage!T curlMessage(T)(T data)
+{
+ return CurlMessage!T(data);
+}
+
+// Pool of to be used for reusing buffers
+private struct Pool(Data)
+{
+ private struct Entry
+ {
+ Data data;
+ Entry* next;
+ }
+ private Entry* root;
+ private Entry* freeList;
+
+ @safe @property bool empty()
+ {
+ return root == null;
+ }
+
+ @safe nothrow void push(Data d)
+ {
+ if (freeList == null)
+ {
+ // Allocate new Entry since there is no one
+ // available in the freeList
+ freeList = new Entry;
+ }
+ freeList.data = d;
+ Entry* oldroot = root;
+ root = freeList;
+ freeList = freeList.next;
+ root.next = oldroot;
+ }
+
+ @safe Data pop()
+ {
+ enforce!Exception(root != null, "pop() called on empty pool");
+ auto d = root.data;
+ auto n = root.next;
+ root.next = freeList;
+ freeList = root;
+ root = n;
+ return d;
+ }
+}
+
+// Shared function for reading incoming chunks of data and
+// sending the to a parent thread
+private static size_t _receiveAsyncChunks(ubyte[] data, ref ubyte[] outdata,
+ Pool!(ubyte[]) freeBuffers,
+ ref ubyte[] buffer, Tid fromTid,
+ ref bool aborted)
+{
+ immutable datalen = data.length;
+
+ // Copy data to fill active buffer
+ while (!data.empty)
+ {
+
+ // Make sure a buffer is present
+ while ( outdata.empty && freeBuffers.empty)
+ {
+ // Active buffer is invalid and there are no
+ // available buffers in the pool. Wait for buffers
+ // to return from main thread in order to reuse
+ // them.
+ receive((immutable(ubyte)[] buf)
+ {
+ buffer = cast(ubyte[]) buf;
+ outdata = buffer[];
+ },
+ (bool flag) { aborted = true; }
+ );
+ if (aborted) return cast(size_t) 0;
+ }
+ if (outdata.empty)
+ {
+ buffer = freeBuffers.pop();
+ outdata = buffer[];
+ }
+
+ // Copy data
+ auto copyBytes = outdata.length < data.length ?
+ outdata.length : data.length;
+
+ outdata[0 .. copyBytes] = data[0 .. copyBytes];
+ outdata = outdata[copyBytes..$];
+ data = data[copyBytes..$];
+
+ if (outdata.empty)
+ fromTid.send(thisTid, curlMessage(cast(immutable(ubyte)[])buffer));
+ }
+
+ return datalen;
+}
+
+// ditto
+private static void _finalizeAsyncChunks(ubyte[] outdata, ref ubyte[] buffer,
+ Tid fromTid)
+{
+ if (!outdata.empty)
+ {
+ // Resize the last buffer
+ buffer.length = buffer.length - outdata.length;
+ fromTid.send(thisTid, curlMessage(cast(immutable(ubyte)[])buffer));
+ }
+}
+
+
+// Shared function for reading incoming lines of data and sending the to a
+// parent thread
+private static size_t _receiveAsyncLines(Terminator, Unit)
+ (const(ubyte)[] data, ref EncodingScheme encodingScheme,
+ bool keepTerminator, Terminator terminator,
+ ref const(ubyte)[] leftOverBytes, ref bool bufferValid,
+ ref Pool!(Unit[]) freeBuffers, ref Unit[] buffer,
+ Tid fromTid, ref bool aborted)
+{
+ import std.format : format;
+
+ immutable datalen = data.length;
+
+ // Terminator is specified and buffers should be resized as determined by
+ // the terminator
+
+ // Copy data to active buffer until terminator is found.
+
+ // Decode as many lines as possible
+ while (true)
+ {
+
+ // Make sure a buffer is present
+ while (!bufferValid && freeBuffers.empty)
+ {
+ // Active buffer is invalid and there are no available buffers in
+ // the pool. Wait for buffers to return from main thread in order to
+ // reuse them.
+ receive((immutable(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 (!bufferValid)
+ {
+ buffer = freeBuffers.pop();
+ bufferValid = true;
+ }
+
+ // Try to read a line from left over bytes from last onReceive plus the
+ // newly received bytes.
+ try
+ {
+ if (decodeLineInto(leftOverBytes, data, buffer,
+ encodingScheme, terminator))
+ {
+ if (keepTerminator)
+ {
+ fromTid.send(thisTid,
+ curlMessage(cast(immutable(Unit)[])buffer));
+ }
+ else
+ {
+ static if (isArray!Terminator)
+ fromTid.send(thisTid,
+ curlMessage(cast(immutable(Unit)[])
+ buffer[0..$-terminator.length]));
+ else
+ fromTid.send(thisTid,
+ curlMessage(cast(immutable(Unit)[])
+ buffer[0..$-1]));
+ }
+ bufferValid = false;
+ }
+ else
+ {
+ // Could not decode an entire line. Save
+ // bytes left in data for next call to
+ // onReceive. Can be up to a max of 4 bytes.
+ enforce!CurlException(data.length <= 4,
+ format(
+ "Too many bytes left not decoded %s"~
+ " > 4. Maybe the charset specified in"~
+ " headers does not match "~
+ "the actual content downloaded?",
+ data.length));
+ leftOverBytes ~= data;
+ break;
+ }
+ }
+ catch (CurlException ex)
+ {
+ prioritySend(fromTid, cast(immutable(CurlException))ex);
+ return cast(size_t) 0;
+ }
+ }
+ return datalen;
+}
+
+// ditto
+private static
+void _finalizeAsyncLines(Unit)(bool bufferValid, Unit[] buffer, Tid fromTid)
+{
+ if (bufferValid && buffer.length != 0)
+ fromTid.send(thisTid, curlMessage(cast(immutable(Unit)[])buffer[0..$]));
+}
+
+
+// Spawn a thread for handling the reading of incoming data in the
+// background while the delegate is executing. This will optimize
+// throughput by allowing simultaneous input (this struct) and
+// output (e.g. AsyncHTTPLineOutputRange).
+private static void _spawnAsync(Conn, Unit, Terminator = void)()
+{
+ Tid fromTid = receiveOnly!Tid();
+
+ // Get buffer to read into
+ Pool!(Unit[]) freeBuffers; // Free list of buffer objects
+
+ // Number of bytes filled into active buffer
+ Unit[] buffer;
+ bool aborted = false;
+
+ EncodingScheme encodingScheme;
+ static if ( !is(Terminator == void))
+ {
+ // Only lines reading will receive a terminator
+ 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
+ // decode a code point in the supported encodings.
+ auto leftOverBytes = new const(ubyte)[4];
+ leftOverBytes.length = 0;
+ auto bufferValid = false;
+ }
+ else
+ {
+ Unit[] outdata;
+ }
+
+ // no move semantic available in std.concurrency ie. must use casting.
+ auto connDup = cast(CURL*) receiveOnly!ulong();
+ auto client = Conn();
+ client.p.curl.handle = connDup;
+
+ // receive a method for both ftp and http but just use it for http
+ auto method = receiveOnly!(HTTP.Method)();
+
+ client.onReceive = (ubyte[] data)
+ {
+ // If no terminator is specified the chunk size is fixed.
+ static if ( is(Terminator == void) )
+ return _receiveAsyncChunks(data, outdata, freeBuffers, buffer,
+ fromTid, aborted);
+ else
+ return _receiveAsyncLines(data, encodingScheme,
+ keepTerminator, terminator, leftOverBytes,
+ bufferValid, freeBuffers, buffer,
+ fromTid, aborted);
+ };
+
+ static if ( is(Conn == HTTP) )
+ {
+ client.method = method;
+ // register dummy header handler
+ client.onReceiveHeader = (in char[] key, in char[] value)
+ {
+ if (key == "content-type")
+ encodingScheme = EncodingScheme.create(client.p.charset);
+ };
+ }
+ else
+ {
+ encodingScheme = EncodingScheme.create(client.encoding);
+ }
+
+ // Start the request
+ CurlCode code;
+ try
+ {
+ code = client.perform(No.throwOnError);
+ }
+ catch (Exception ex)
+ {
+ prioritySend(fromTid, cast(immutable(Exception)) ex);
+ fromTid.send(thisTid, curlMessage(true)); // signal done
+ return;
+ }
+
+ if (code != CurlError.ok)
+ {
+ if (aborted && (code == CurlError.aborted_by_callback ||
+ code == CurlError.write_error))
+ {
+ fromTid.send(thisTid, curlMessage(true)); // signal done
+ return;
+ }
+ prioritySend(fromTid, cast(immutable(CurlException))
+ new CurlException(client.p.curl.errorString(code)));
+
+ fromTid.send(thisTid, curlMessage(true)); // signal done
+ return;
+ }
+
+ // Send remaining data that is not a full chunk size
+ static if ( is(Terminator == void) )
+ _finalizeAsyncChunks(outdata, buffer, fromTid);
+ else
+ _finalizeAsyncLines(bufferValid, buffer, fromTid);
+
+ fromTid.send(thisTid, curlMessage(true)); // signal done
+}
diff --git a/libphobos/src/std/net/isemail.d b/libphobos/src/std/net/isemail.d
new file mode 100644
index 0000000..cd6aa41
--- /dev/null
+++ b/libphobos/src/std/net/isemail.d
@@ -0,0 +1,1864 @@
+/**
+ * Validates an email address according to RFCs 5321, 5322 and others.
+ *
+ * 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)
+ * Version: 3.0.13 - Version 3.0 of the original PHP implementation: $(LINK http://www.dominicsayers.com/isemail)
+ *
+ * Standards:
+ * $(UL
+ * $(LI RFC 5321)
+ * $(LI RFC 5322)
+ * )
+ *
+ * References:
+ * $(UL
+ * $(LI $(LINK http://www.dominicsayers.com/isemail))
+ * $(LI $(LINK http://tools.ietf.org/html/rfc5321))
+ * $(LI $(LINK http://tools.ietf.org/html/rfc5322))
+ * )
+ *
+ * Source: $(PHOBOSSRC std/net/_isemail.d)
+ */
+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.
+ *
+ * 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 $(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.
+ * Thus the calling program can simply look for EmailStatusCode.valid
+ * if it is only interested in whether an address is valid or not. The
+ * $(D_PARAM errorLevel) will determine how "picky" isEmail() is about
+ * the address.
+ *
+ * If omitted or passed as EmailStatusCode.none then isEmail() will
+ * not perform any finer grained error checking and an address is
+ * either considered valid or not. Email status code will either be
+ * EmailStatusCode.valid or EmailStatusCode.error.
+ *
+ * Returns:
+ * An $(LREF EmailStatus), indicating the status of the email address.
+ */
+EmailStatus isEmail(Char)(const(Char)[] email, CheckDns checkDNS = No.checkDns,
+EmailStatusCode errorLevel = EmailStatusCode.none)
+if (isSomeChar!(Char))
+{
+ import std.algorithm.iteration : uniq, filter, map;
+ import std.algorithm.searching : canFind, maxElement;
+ import std.array : array, split;
+ import std.conv : to;
+ import std.exception : enforce;
+ 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)
+ {
+ threshold = EmailStatusCode.valid;
+ diagnose = true;
+ }
+
+ else if (errorLevel == EmailStatusCode.none)
+ threshold = defaultThreshold;
+
+ else
+ {
+ diagnose = true;
+
+ switch (errorLevel)
+ {
+ case EmailStatusCode.warning: threshold = defaultThreshold; break;
+ case EmailStatusCode.error: threshold = EmailStatusCode.valid; break;
+ default: threshold = errorLevel;
+ }
+ }
+
+ auto returnStatus = [EmailStatusCode.valid];
+ auto context = EmailPart.componentLocalPart;
+ auto contextStack = [context];
+ auto contextPrior = context;
+ tstring token = "";
+ tstring tokenPrior = "";
+ tstring[EmailPart] parseData = [EmailPart.componentLocalPart : "", EmailPart.componentDomain : ""];
+ tstring[][EmailPart] atomList = [EmailPart.componentLocalPart : [""], EmailPart.componentDomain : [""]];
+ auto elementCount = 0;
+ auto elementLength = 0;
+ auto hyphenFlag = false;
+ auto endOrDie = false;
+ auto crlfCount = int.min; // int.min == not defined
+
+ foreach (ref i, e ; email)
+ {
+ token = email.get(i, e);
+
+ switch (context)
+ {
+ case EmailPart.componentLocalPart:
+ switch (token)
+ {
+ case Token.openParenthesis:
+ if (elementLength == 0)
+ returnStatus ~= elementCount == 0 ? EmailStatusCode.comment :
+ EmailStatusCode.deprecatedComment;
+
+ else
+ {
+ returnStatus ~= EmailStatusCode.comment;
+ endOrDie = true;
+ }
+
+ contextStack ~= context;
+ context = EmailPart.contextComment;
+ break;
+
+ case Token.dot:
+ if (elementLength == 0)
+ returnStatus ~= elementCount == 0 ? EmailStatusCode.errorDotStart :
+ EmailStatusCode.errorConsecutiveDots;
+
+ else
+ {
+ if (endOrDie)
+ returnStatus ~= EmailStatusCode.deprecatedLocalPart;
+ }
+
+ endOrDie = false;
+ elementLength = 0;
+ elementCount++;
+ parseData[EmailPart.componentLocalPart] ~= token;
+
+ if (elementCount >= atomList[EmailPart.componentLocalPart].length)
+ atomList[EmailPart.componentLocalPart] ~= "";
+
+ else
+ atomList[EmailPart.componentLocalPart][elementCount] = "";
+ break;
+
+ case Token.doubleQuote:
+ if (elementLength == 0)
+ {
+ returnStatus ~= elementCount == 0 ? EmailStatusCode.rfc5321QuotedString :
+ EmailStatusCode.deprecatedLocalPart;
+
+ parseData[EmailPart.componentLocalPart] ~= token;
+ atomList[EmailPart.componentLocalPart][elementCount] ~= token;
+ elementLength++;
+ endOrDie = true;
+ contextStack ~= context;
+ context = EmailPart.contextQuotedString;
+ }
+
+ else
+ returnStatus ~= EmailStatusCode.errorExpectingText;
+ break;
+
+ case Token.cr:
+ case Token.space:
+ case Token.tab:
+ if ((token == Token.cr) && ((++i == email.length) || (email.get(i, e) != Token.lf)))
+ {
+ returnStatus ~= EmailStatusCode.errorCrNoLf;
+ break;
+ }
+
+ if (elementLength == 0)
+ returnStatus ~= elementCount == 0 ? EmailStatusCode.foldingWhitespace :
+ EmailStatusCode.deprecatedFoldingWhitespace;
+
+ else
+ endOrDie = true;
+
+ contextStack ~= context;
+ context = EmailPart.contextFoldingWhitespace;
+ tokenPrior = token;
+ break;
+
+ case Token.at:
+ enforce(contextStack.length == 1, "Unexpected item on context stack");
+
+ if (parseData[EmailPart.componentLocalPart] == "")
+ returnStatus ~= EmailStatusCode.errorNoLocalPart;
+
+ else if (elementLength == 0)
+ returnStatus ~= EmailStatusCode.errorDotEnd;
+
+ else if (parseData[EmailPart.componentLocalPart].length > 64)
+ returnStatus ~= EmailStatusCode.rfc5322LocalTooLong;
+
+ else if (contextPrior == EmailPart.contextComment ||
+ contextPrior == EmailPart.contextFoldingWhitespace)
+ returnStatus ~= EmailStatusCode.deprecatedCommentFoldingWhitespaceNearAt;
+
+ context = EmailPart.componentDomain;
+ contextStack = [context];
+ elementCount = 0;
+ elementLength = 0;
+ endOrDie = false;
+ break;
+
+ default:
+ if (endOrDie)
+ {
+ switch (contextPrior)
+ {
+ case EmailPart.contextComment:
+ case EmailPart.contextFoldingWhitespace:
+ returnStatus ~= EmailStatusCode.errorTextAfterCommentFoldingWhitespace;
+ break;
+
+ case EmailPart.contextQuotedString:
+ returnStatus ~= EmailStatusCode.errorTextAfterQuotedString;
+ break;
+
+ default:
+ throw new Exception("More text found where none is allowed, but "
+ ~"unrecognised prior context: " ~ to!(string)(contextPrior));
+ }
+ }
+
+ else
+ {
+ contextPrior = context;
+ immutable c = token.front;
+
+ if (c < '!' || c > '~' || c == '\n' || Token.specials.canFind(token))
+ returnStatus ~= EmailStatusCode.errorExpectingText;
+
+ parseData[EmailPart.componentLocalPart] ~= token;
+ atomList[EmailPart.componentLocalPart][elementCount] ~= token;
+ elementLength++;
+ }
+ }
+ break;
+
+ case EmailPart.componentDomain:
+ switch (token)
+ {
+ case Token.openParenthesis:
+ if (elementLength == 0)
+ {
+ returnStatus ~= elementCount == 0 ?
+ EmailStatusCode.deprecatedCommentFoldingWhitespaceNearAt
+ : EmailStatusCode.deprecatedComment;
+ }
+ else
+ {
+ returnStatus ~= EmailStatusCode.comment;
+ endOrDie = true;
+ }
+
+ contextStack ~= context;
+ context = EmailPart.contextComment;
+ break;
+
+ case Token.dot:
+ if (elementLength == 0)
+ returnStatus ~= elementCount == 0 ? EmailStatusCode.errorDotStart :
+ EmailStatusCode.errorConsecutiveDots;
+
+ else if (hyphenFlag)
+ returnStatus ~= EmailStatusCode.errorDomainHyphenEnd;
+
+ else
+ {
+ if (elementLength > 63)
+ returnStatus ~= EmailStatusCode.rfc5322LabelTooLong;
+ }
+
+ endOrDie = false;
+ elementLength = 0;
+ elementCount++;
+
+ //atomList[EmailPart.componentDomain][elementCount] = "";
+ atomList[EmailPart.componentDomain] ~= "";
+ parseData[EmailPart.componentDomain] ~= token;
+ break;
+
+ case Token.openBracket:
+ if (parseData[EmailPart.componentDomain] == "")
+ {
+ endOrDie = true;
+ elementLength++;
+ contextStack ~= context;
+ context = EmailPart.componentLiteral;
+ parseData[EmailPart.componentDomain] ~= token;
+ atomList[EmailPart.componentDomain][elementCount] ~= token;
+ parseData[EmailPart.componentLiteral] = "";
+ }
+
+ else
+ returnStatus ~= EmailStatusCode.errorExpectingText;
+ break;
+
+ case Token.cr:
+ case Token.space:
+ case Token.tab:
+ if (token == Token.cr && (++i == email.length || email.get(i, e) != Token.lf))
+ {
+ returnStatus ~= EmailStatusCode.errorCrNoLf;
+ break;
+ }
+
+ if (elementLength == 0)
+ {
+ returnStatus ~= elementCount == 0 ?
+ EmailStatusCode.deprecatedCommentFoldingWhitespaceNearAt
+ : EmailStatusCode.deprecatedFoldingWhitespace;
+ }
+ else
+ {
+ returnStatus ~= EmailStatusCode.foldingWhitespace;
+ endOrDie = true;
+ }
+
+ contextStack ~= context;
+ context = EmailPart.contextFoldingWhitespace;
+ tokenPrior = token;
+ break;
+
+ default:
+ if (endOrDie)
+ {
+ switch (contextPrior)
+ {
+ case EmailPart.contextComment:
+ case EmailPart.contextFoldingWhitespace:
+ returnStatus ~= EmailStatusCode.errorTextAfterCommentFoldingWhitespace;
+ break;
+
+ case EmailPart.componentLiteral:
+ returnStatus ~= EmailStatusCode.errorTextAfterDomainLiteral;
+ break;
+
+ default:
+ throw new Exception("More text found where none is allowed, but "
+ ~"unrecognised prior context: " ~ to!(string)(contextPrior));
+ }
+
+ }
+
+ immutable c = token.front;
+ hyphenFlag = false;
+
+ if (c < '!' || c > '~' || Token.specials.canFind(token))
+ returnStatus ~= EmailStatusCode.errorExpectingText;
+
+ else if (token == Token.hyphen)
+ {
+ if (elementLength == 0)
+ returnStatus ~= EmailStatusCode.errorDomainHyphenStart;
+
+ hyphenFlag = true;
+ }
+
+ else if (!((c > '/' && c < ':') || (c > '@' && c < '[') || (c > '`' && c < '{')))
+ returnStatus ~= EmailStatusCode.rfc5322Domain;
+
+ parseData[EmailPart.componentDomain] ~= token;
+ atomList[EmailPart.componentDomain][elementCount] ~= token;
+ elementLength++;
+ }
+ break;
+
+ case EmailPart.componentLiteral:
+ switch (token)
+ {
+ case Token.closeBracket:
+ if (returnStatus.maxElement() < EmailStatusCode.deprecated_)
+ {
+ auto maxGroups = 8;
+ size_t index = -1;
+ auto addressLiteral = parseData[EmailPart.componentLiteral];
+ auto matchesIp = addressLiteral.matchAll(ipRegex).map!(a => a.hit).array;
+
+ if (!matchesIp.empty)
+ {
+ index = addressLiteral.lastIndexOf(matchesIp.front);
+
+ if (index != 0)
+ addressLiteral = addressLiteral[0 .. index] ~ "0:0";
+ }
+
+ if (index == 0)
+ returnStatus ~= EmailStatusCode.rfc5321AddressLiteral;
+
+ else if (addressLiteral.compareFirstN(Token.ipV6Tag, 5))
+ returnStatus ~= EmailStatusCode.rfc5322DomainLiteral;
+
+ else
+ {
+ auto ipV6 = addressLiteral[5 .. $];
+ matchesIp = ipV6.split(Token.colon);
+ immutable groupCount = matchesIp.length;
+ index = ipV6.indexOf(Token.doubleColon);
+
+ if (index == -1)
+ {
+ if (groupCount != maxGroups)
+ returnStatus ~= EmailStatusCode.rfc5322IpV6GroupCount;
+ }
+
+ else
+ {
+ if (index != ipV6.lastIndexOf(Token.doubleColon))
+ returnStatus ~= EmailStatusCode.rfc5322IpV6TooManyDoubleColons;
+
+ else
+ {
+ if (index == 0 || index == (ipV6.length - 2))
+ maxGroups++;
+
+ if (groupCount > maxGroups)
+ returnStatus ~= EmailStatusCode.rfc5322IpV6MaxGroups;
+
+ else if (groupCount == maxGroups)
+ returnStatus ~= EmailStatusCode.rfc5321IpV6Deprecated;
+ }
+ }
+
+ if (ipV6[0 .. 1] == Token.colon && ipV6[1 .. 2] != Token.colon)
+ returnStatus ~= EmailStatusCode.rfc5322IpV6ColonStart;
+
+ else if (ipV6[$ - 1 .. $] == Token.colon && ipV6[$ - 2 .. $ - 1] != Token.colon)
+ returnStatus ~= EmailStatusCode.rfc5322IpV6ColonEnd;
+
+ else if (!matchesIp
+ .filter!(a => a.matchFirst(fourChars).empty)
+ .empty)
+ returnStatus ~= EmailStatusCode.rfc5322IpV6BadChar;
+
+ else
+ returnStatus ~= EmailStatusCode.rfc5321AddressLiteral;
+ }
+ }
+
+ else
+ returnStatus ~= EmailStatusCode.rfc5322DomainLiteral;
+
+ parseData[EmailPart.componentDomain] ~= token;
+ atomList[EmailPart.componentDomain][elementCount] ~= token;
+ elementLength++;
+ contextPrior = context;
+ context = contextStack.pop();
+ break;
+
+ case Token.backslash:
+ returnStatus ~= EmailStatusCode.rfc5322DomainLiteralObsoleteText;
+ contextStack ~= context;
+ context = EmailPart.contextQuotedPair;
+ break;
+
+ case Token.cr:
+ case Token.space:
+ case Token.tab:
+ if (token == Token.cr && (++i == email.length || email.get(i, e) != Token.lf))
+ {
+ returnStatus ~= EmailStatusCode.errorCrNoLf;
+ break;
+ }
+
+ returnStatus ~= EmailStatusCode.foldingWhitespace;
+ contextStack ~= context;
+ context = EmailPart.contextFoldingWhitespace;
+ tokenPrior = token;
+ break;
+
+ default:
+ immutable c = token.front;
+
+ if (c > AsciiToken.delete_ || c == '\0' || token == Token.openBracket)
+ {
+ returnStatus ~= EmailStatusCode.errorExpectingDomainText;
+ break;
+ }
+
+ else if (c < '!' || c == AsciiToken.delete_ )
+ returnStatus ~= EmailStatusCode.rfc5322DomainLiteralObsoleteText;
+
+ parseData[EmailPart.componentLiteral] ~= token;
+ parseData[EmailPart.componentDomain] ~= token;
+ atomList[EmailPart.componentDomain][elementCount] ~= token;
+ elementLength++;
+ }
+ break;
+
+ case EmailPart.contextQuotedString:
+ switch (token)
+ {
+ case Token.backslash:
+ contextStack ~= context;
+ context = EmailPart.contextQuotedPair;
+ break;
+
+ case Token.cr:
+ case Token.tab:
+ if (token == Token.cr && (++i == email.length || email.get(i, e) != Token.lf))
+ {
+ returnStatus ~= EmailStatusCode.errorCrNoLf;
+ break;
+ }
+
+ parseData[EmailPart.componentLocalPart] ~= Token.space;
+ atomList[EmailPart.componentLocalPart][elementCount] ~= Token.space;
+ elementLength++;
+
+ returnStatus ~= EmailStatusCode.foldingWhitespace;
+ contextStack ~= context;
+ context = EmailPart.contextFoldingWhitespace;
+ tokenPrior = token;
+ break;
+
+ case Token.doubleQuote:
+ parseData[EmailPart.componentLocalPart] ~= token;
+ atomList[EmailPart.componentLocalPart][elementCount] ~= token;
+ elementLength++;
+ contextPrior = context;
+ context = contextStack.pop();
+ break;
+
+ default:
+ immutable c = token.front;
+
+ if (c > AsciiToken.delete_ || c == '\0' || c == '\n')
+ returnStatus ~= EmailStatusCode.errorExpectingQuotedText;
+
+ else if (c < ' ' || c == AsciiToken.delete_)
+ returnStatus ~= EmailStatusCode.deprecatedQuotedText;
+
+ parseData[EmailPart.componentLocalPart] ~= token;
+ atomList[EmailPart.componentLocalPart][elementCount] ~= token;
+ elementLength++;
+ }
+ break;
+
+ case EmailPart.contextQuotedPair:
+ immutable c = token.front;
+
+ if (c > AsciiToken.delete_)
+ returnStatus ~= EmailStatusCode.errorExpectingQuotedPair;
+
+ else if (c < AsciiToken.unitSeparator && c != AsciiToken.horizontalTab || c == AsciiToken.delete_)
+ returnStatus ~= EmailStatusCode.deprecatedQuotedPair;
+
+ contextPrior = context;
+ context = contextStack.pop();
+ token = Token.backslash ~ token;
+
+ switch (context)
+ {
+ case EmailPart.contextComment: break;
+
+ case EmailPart.contextQuotedString:
+ parseData[EmailPart.componentLocalPart] ~= token;
+ atomList[EmailPart.componentLocalPart][elementCount] ~= token;
+ elementLength += 2;
+ break;
+
+ case EmailPart.componentLiteral:
+ parseData[EmailPart.componentDomain] ~= token;
+ atomList[EmailPart.componentDomain][elementCount] ~= token;
+ elementLength += 2;
+ break;
+
+ default:
+ throw new Exception("Quoted pair logic invoked in an invalid context: " ~ to!(string)(context));
+ }
+ break;
+
+ case EmailPart.contextComment:
+ switch (token)
+ {
+ case Token.openParenthesis:
+ contextStack ~= context;
+ context = EmailPart.contextComment;
+ break;
+
+ case Token.closeParenthesis:
+ contextPrior = context;
+ context = contextStack.pop();
+ break;
+
+ case Token.backslash:
+ contextStack ~= context;
+ context = EmailPart.contextQuotedPair;
+ break;
+
+ case Token.cr:
+ case Token.space:
+ case Token.tab:
+ if (token == Token.cr && (++i == email.length || email.get(i, e) != Token.lf))
+ {
+ returnStatus ~= EmailStatusCode.errorCrNoLf;
+ break;
+ }
+
+ returnStatus ~= EmailStatusCode.foldingWhitespace;
+
+ contextStack ~= context;
+ context = EmailPart.contextFoldingWhitespace;
+ tokenPrior = token;
+ break;
+
+ default:
+ immutable c = token.front;
+
+ if (c > AsciiToken.delete_ || c == '\0' || c == '\n')
+ {
+ returnStatus ~= EmailStatusCode.errorExpectingCommentText;
+ break;
+ }
+
+ else if (c < ' ' || c == AsciiToken.delete_)
+ returnStatus ~= EmailStatusCode.deprecatedCommentText;
+ }
+ break;
+
+ case EmailPart.contextFoldingWhitespace:
+ if (tokenPrior == Token.cr)
+ {
+ if (token == Token.cr)
+ {
+ returnStatus ~= EmailStatusCode.errorFoldingWhitespaceCrflX2;
+ break;
+ }
+
+ if (crlfCount != int.min) // int.min == not defined
+ {
+ if (++crlfCount > 1)
+ returnStatus ~= EmailStatusCode.deprecatedFoldingWhitespace;
+ }
+
+ else
+ crlfCount = 1;
+ }
+
+ switch (token)
+ {
+ case Token.cr:
+ if (++i == email.length || email.get(i, e) != Token.lf)
+ returnStatus ~= EmailStatusCode.errorCrNoLf;
+ break;
+
+ case Token.space:
+ case Token.tab:
+ break;
+
+ default:
+ if (tokenPrior == Token.cr)
+ {
+ returnStatus ~= EmailStatusCode.errorFoldingWhitespaceCrLfEnd;
+ break;
+ }
+
+ crlfCount = int.min; // int.min == not defined
+ contextPrior = context;
+ context = contextStack.pop();
+ i--;
+ break;
+ }
+
+ tokenPrior = token;
+ break;
+
+ default:
+ throw new Exception("Unkown context: " ~ to!(string)(context));
+ }
+
+ if (returnStatus.maxElement() > EmailStatusCode.rfc5322)
+ break;
+ }
+
+ if (returnStatus.maxElement() < EmailStatusCode.rfc5322)
+ {
+ if (context == EmailPart.contextQuotedString)
+ returnStatus ~= EmailStatusCode.errorUnclosedQuotedString;
+
+ else if (context == EmailPart.contextQuotedPair)
+ returnStatus ~= EmailStatusCode.errorBackslashEnd;
+
+ else if (context == EmailPart.contextComment)
+ returnStatus ~= EmailStatusCode.errorUnclosedComment;
+
+ else if (context == EmailPart.componentLiteral)
+ returnStatus ~= EmailStatusCode.errorUnclosedDomainLiteral;
+
+ else if (token == Token.cr)
+ returnStatus ~= EmailStatusCode.errorFoldingWhitespaceCrLfEnd;
+
+ else if (parseData[EmailPart.componentDomain] == "")
+ returnStatus ~= EmailStatusCode.errorNoDomain;
+
+ else if (elementLength == 0)
+ returnStatus ~= EmailStatusCode.errorDotEnd;
+
+ else if (hyphenFlag)
+ returnStatus ~= EmailStatusCode.errorDomainHyphenEnd;
+
+ else if (parseData[EmailPart.componentDomain].length > 255)
+ returnStatus ~= EmailStatusCode.rfc5322DomainTooLong;
+
+ else if ((parseData[EmailPart.componentLocalPart] ~ Token.at ~ parseData[EmailPart.componentDomain]).length >
+ 254)
+ returnStatus ~= EmailStatusCode.rfc5322TooLong;
+
+ else if (elementLength > 63)
+ returnStatus ~= EmailStatusCode.rfc5322LabelTooLong;
+ }
+
+ auto dnsChecked = false;
+
+ if (checkDNS == Yes.checkDns && returnStatus.maxElement() < EmailStatusCode.dnsWarning)
+ {
+ assert(false, "DNS check is currently not implemented");
+ }
+
+ if (!dnsChecked && returnStatus.maxElement() < EmailStatusCode.dnsWarning)
+ {
+ if (elementCount == 0)
+ returnStatus ~= EmailStatusCode.rfc5321TopLevelDomain;
+
+ if (isNumber(atomList[EmailPart.componentDomain][elementCount].front))
+ returnStatus ~= EmailStatusCode.rfc5321TopLevelDomainNumeric;
+ }
+
+ returnStatus = array(uniq(returnStatus));
+ auto finalStatus = returnStatus.maxElement();
+
+ if (returnStatus.length != 1)
+ returnStatus.popFront();
+
+ parseData[EmailPart.status] = to!(tstring)(returnStatus);
+
+ if (finalStatus < threshold)
+ finalStatus = EmailStatusCode.valid;
+
+ if (!diagnose)
+ finalStatus = finalStatus < threshold ? EmailStatusCode.valid : EmailStatusCode.error;
+
+ auto valid = finalStatus == EmailStatusCode.valid;
+ tstring localPart = "";
+ tstring domainPart = "";
+
+ if (auto value = EmailPart.componentLocalPart in parseData)
+ localPart = *value;
+
+ if (auto value = EmailPart.componentDomain in parseData)
+ domainPart = *value;
+
+ return EmailStatus(valid, to!(string)(localPart), to!(string)(domainPart), finalStatus);
+}
+
+@safe unittest
+{
+ 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@[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(No.checkDns, EmailStatusCode.any).statusCode == EmailStatusCode.errorNoLocalPart,
+ `io. currently has an MX-record (Feb 2011)`);
+
+ 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(No.checkDns, EmailStatusCode.any).statusCode == EmailStatusCode.dnsWarningNoRecord);
+ // DNS check is currently not implemented
+
+ //assert(`test@iana.a`.isEmail(No.checkDns, EmailStatusCode.any).statusCode == EmailStatusCode.dnsWarningNoRecord);
+ // DNS check is currently not implemented
+
+ 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(No.checkDns, EmailStatusCode.any).statusCode ==
+ EmailStatusCode.errorConsecutiveDots);
+
+ 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(No.checkDns, EmailStatusCode.any).statusCode ==
+ EmailStatusCode.errorExpectingText);
+
+ 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(No.checkDns, EmailStatusCode.any).statusCode ==
+ EmailStatusCode.rfc5321TopLevelDomainNumeric);
+ assert(`test@255.255.255.255`.isEmail(No.checkDns, EmailStatusCode.any).statusCode ==
+ EmailStatusCode.rfc5321TopLevelDomainNumeric);
+
+ assert(`abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghiklm@iana.org`.isEmail(No.checkDns,
+ EmailStatusCode.any).statusCode == EmailStatusCode.valid);
+
+ assert(`abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghiklmn@iana.org`.isEmail(No.checkDns,
+ EmailStatusCode.any).statusCode == EmailStatusCode.rfc5322LocalTooLong);
+
+ // assert(`test@abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghikl.com`.isEmail(No.checkDns,
+ // EmailStatusCode.any).statusCode == EmailStatusCode.dnsWarningNoRecord);
+ // DNS check is currently not implemented
+
+ assert(`test@abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghiklm.com`.isEmail(No.checkDns,
+ EmailStatusCode.any).statusCode == EmailStatusCode.rfc5322LabelTooLong);
+
+ assert(`test@mason-dixon.com`.isEmail(No.checkDns, EmailStatusCode.any).statusCode == EmailStatusCode.valid);
+
+ assert(`test@-iana.org`.isEmail(No.checkDns, EmailStatusCode.any).statusCode ==
+ EmailStatusCode.errorDomainHyphenStart);
+
+ assert(`test@iana-.com`.isEmail(No.checkDns, EmailStatusCode.any).statusCode ==
+ EmailStatusCode.errorDomainHyphenEnd);
+
+ assert(`test@g--a.com`.isEmail(No.checkDns, EmailStatusCode.any).statusCode == EmailStatusCode.valid);
+
+ //assert(`test@iana.co-uk`.isEmail(No.checkDns, EmailStatusCode.any).statusCode ==
+ //EmailStatusCode.dnsWarningNoRecord); // DNS check is currently not implemented
+
+ 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(No.checkDns, EmailStatusCode.any).statusCode ==
+ // EmailStatusCode.dnsWarningNoRecord); // DNS check is currently not implemented
+
+ // assert(`abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghiklm@abcdefghijklmnopqrstuvwxyz`
+ // `abcdefghijklmnopqrstuvwxyzabcdefghikl.abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghikl.`
+ // `abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghi`.isEmail(No.checkDns,
+ // EmailStatusCode.any).statusCode == EmailStatusCode.dnsWarningNoRecord);
+ // DNS check is currently not implemented
+
+ assert((`abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghiklm@abcdefghijklmnopqrstuvwxyz`~
+ `abcdefghijklmnopqrstuvwxyzabcdefghikl.abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghikl.`~
+ `abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghij`).isEmail(No.checkDns,
+ EmailStatusCode.any).statusCode == EmailStatusCode.rfc5322TooLong);
+
+ assert((`a@abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghikl.abcdefghijklmnopqrstuvwxyz`~
+ `abcdefghijklmnopqrstuvwxyzabcdefghikl.abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghikl.`~
+ `abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefg.hij`).isEmail(No.checkDns,
+ EmailStatusCode.any).statusCode == EmailStatusCode.rfc5322TooLong);
+
+ assert((`a@abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghikl.abcdefghijklmnopqrstuvwxyz`~
+ `abcdefghijklmnopqrstuvwxyzabcdefghikl.abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghikl.`~
+ `abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefg.hijk`).isEmail(No.checkDns,
+ EmailStatusCode.any).statusCode == EmailStatusCode.rfc5322DomainTooLong);
+
+ assert(`"test"@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(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(No.checkDns, EmailStatusCode.any).statusCode ==
+ EmailStatusCode.errorUnclosedQuotedString);
+
+ 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(No.checkDns, EmailStatusCode.any).statusCode ==
+ EmailStatusCode.errorUnclosedQuotedString);
+
+ assert(`"test"test@iana.org`.isEmail(No.checkDns, EmailStatusCode.any).statusCode ==
+ EmailStatusCode.errorTextAfterQuotedString);
+
+ assert(`test"text"@iana.org`.isEmail(No.checkDns, EmailStatusCode.any).statusCode ==
+ EmailStatusCode.errorExpectingText);
+
+ assert(`"test""test"@iana.org`.isEmail(No.checkDns, EmailStatusCode.any).statusCode ==
+ EmailStatusCode.errorExpectingText);
+
+ assert(`"test"."test"@iana.org`.isEmail(No.checkDns, EmailStatusCode.any).statusCode ==
+ EmailStatusCode.deprecatedLocalPart);
+
+ assert(`"test\ test"@iana.org`.isEmail(No.checkDns, EmailStatusCode.any).statusCode ==
+ EmailStatusCode.rfc5321QuotedString);
+
+ assert(`"test".test@iana.org`.isEmail(No.checkDns, EmailStatusCode.any).statusCode ==
+ EmailStatusCode.deprecatedLocalPart);
+
+ assert("\"test\u0000\"@iana.org".isEmail(No.checkDns, EmailStatusCode.any).statusCode ==
+ EmailStatusCode.errorExpectingQuotedText);
+
+ assert("\"test\\\u0000\"@iana.org".isEmail(No.checkDns, EmailStatusCode.any).statusCode ==
+ EmailStatusCode.deprecatedQuotedPair);
+
+ 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(No.checkDns,
+ EmailStatusCode.any).statusCode == EmailStatusCode.rfc5322LocalTooLong,
+ `Quoted pair is still part of the length restriction`);
+
+ assert(`test@[255.255.255.255]`.isEmail(No.checkDns, EmailStatusCode.any).statusCode ==
+ EmailStatusCode.rfc5321AddressLiteral);
+
+ assert(`test@a[255.255.255.255]`.isEmail(No.checkDns, EmailStatusCode.any).statusCode ==
+ EmailStatusCode.errorExpectingText);
+
+ assert(`test@[255.255.255]`.isEmail(No.checkDns, EmailStatusCode.any).statusCode ==
+ EmailStatusCode.rfc5322DomainLiteral);
+
+ assert(`test@[255.255.255.255.255]`.isEmail(No.checkDns, EmailStatusCode.any).statusCode ==
+ EmailStatusCode.rfc5322DomainLiteral);
+
+ 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(No.checkDns, EmailStatusCode.any).statusCode ==
+ EmailStatusCode.rfc5322DomainLiteral);
+
+ 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(No.checkDns, EmailStatusCode.any).statusCode
+ == EmailStatusCode.rfc5321AddressLiteral);
+
+ 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(No.checkDns,
+ EmailStatusCode.any).statusCode == EmailStatusCode.rfc5322IpV6BadChar);
+
+ 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(No.checkDns, EmailStatusCode.any).statusCode ==
+ EmailStatusCode.rfc5321AddressLiteral);
+
+ 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(No.checkDns, EmailStatusCode.any).statusCode ==
+ EmailStatusCode.rfc5322IpV6ColonStart);
+
+ 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(No.checkDns, EmailStatusCode.any).statusCode ==
+ EmailStatusCode.rfc5322IpV6TooManyDoubleColons);
+
+ assert(`test@[IPv6:::]`.isEmail(No.checkDns, EmailStatusCode.any).statusCode ==
+ EmailStatusCode.rfc5321AddressLiteral);
+
+ 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(No.checkDns,
+ EmailStatusCode.any).statusCode == EmailStatusCode.rfc5321AddressLiteral);
+
+ 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(No.checkDns,
+ EmailStatusCode.any).statusCode == EmailStatusCode.rfc5321AddressLiteral);
+
+ 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(No.checkDns, EmailStatusCode.any).statusCode
+ == EmailStatusCode.rfc5322IpV6TooManyDoubleColons);
+
+ assert(`test@[IPv6::255.255.255.255]`.isEmail(No.checkDns, EmailStatusCode.any).statusCode ==
+ EmailStatusCode.rfc5322IpV6ColonStart);
+
+ assert(` test @iana.org`.isEmail(No.checkDns, EmailStatusCode.any).statusCode ==
+ EmailStatusCode.deprecatedCommentFoldingWhitespaceNearAt);
+
+ assert(`test@ iana .com`.isEmail(No.checkDns, EmailStatusCode.any).statusCode ==
+ EmailStatusCode.deprecatedCommentFoldingWhitespaceNearAt);
+
+ assert(`test . test@iana.org`.isEmail(No.checkDns, EmailStatusCode.any).statusCode ==
+ EmailStatusCode.deprecatedFoldingWhitespace);
+
+ 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(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(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(No.checkDns, EmailStatusCode.any).statusCode ==
+ EmailStatusCode.comment);
+
+ assert(`test@(comment)iana.org`.isEmail(No.checkDns, EmailStatusCode.any).statusCode ==
+ EmailStatusCode.deprecatedCommentFoldingWhitespaceNearAt);
+
+ assert(`test(comment)test@iana.org`.isEmail(No.checkDns, EmailStatusCode.any).statusCode ==
+ EmailStatusCode.errorTextAfterCommentFoldingWhitespace);
+
+ assert(`test@(comment)[255.255.255.255]`.isEmail(No.checkDns, EmailStatusCode.any).statusCode ==
+ EmailStatusCode.deprecatedCommentFoldingWhitespaceNearAt);
+
+ assert(`(comment)abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghiklm@iana.org`.isEmail(No.checkDns,
+ EmailStatusCode.any).statusCode == EmailStatusCode.comment);
+
+ assert(`test@(comment)abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghikl.com`.isEmail(No.checkDns,
+ EmailStatusCode.any).statusCode == EmailStatusCode.deprecatedCommentFoldingWhitespaceNearAt);
+
+ assert((`(comment)test@abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghik.abcdefghijklmnopqrstuvwxyz`~
+ `abcdefghijklmnopqrstuvwxyzabcdefghik.abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijk.`~
+ `abcdefghijklmnopqrstuvwxyzabcdefghijk.abcdefghijklmnopqrstu`).isEmail(No.checkDns,
+ EmailStatusCode.any).statusCode == EmailStatusCode.comment);
+
+ assert("test@iana.org\u000A".isEmail(No.checkDns, EmailStatusCode.any).statusCode ==
+ EmailStatusCode.errorExpectingText);
+
+ assert(`test@xn--hxajbheg2az3al.xn--jxalpdlp`.isEmail(No.checkDns, EmailStatusCode.any).statusCode ==
+ EmailStatusCode.valid, `A valid IDN from ICANN's <a href="http://idn.icann.org/#The_example.test_names">`~
+ `IDN TLD evaluation gateway</a>`);
+
+ 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(No.checkDns, EmailStatusCode.any).statusCode ==
+ EmailStatusCode.errorDomainHyphenEnd);
+
+ assert(`"test@iana.org`.isEmail(No.checkDns, EmailStatusCode.any).statusCode ==
+ EmailStatusCode.errorUnclosedQuotedString);
+
+ assert(`(test@iana.org`.isEmail(No.checkDns, EmailStatusCode.any).statusCode ==
+ EmailStatusCode.errorUnclosedComment);
+
+ assert(`test@(iana.org`.isEmail(No.checkDns, EmailStatusCode.any).statusCode ==
+ EmailStatusCode.errorUnclosedComment);
+
+ assert(`test@[1.2.3.4`.isEmail(No.checkDns, EmailStatusCode.any).statusCode ==
+ EmailStatusCode.errorUnclosedDomainLiteral);
+
+ assert(`"test\"@iana.org`.isEmail(No.checkDns, EmailStatusCode.any).statusCode ==
+ EmailStatusCode.errorUnclosedQuotedString);
+
+ assert(`(comment\)test@iana.org`.isEmail(No.checkDns, EmailStatusCode.any).statusCode ==
+ EmailStatusCode.errorUnclosedComment);
+
+ assert(`test@iana.org(comment\)`.isEmail(No.checkDns, EmailStatusCode.any).statusCode ==
+ EmailStatusCode.errorUnclosedComment);
+
+ assert(`test@iana.org(comment\`.isEmail(No.checkDns, EmailStatusCode.any).statusCode ==
+ EmailStatusCode.errorBackslashEnd);
+
+ assert(`test@[RFC-5322-domain-literal]`.isEmail(No.checkDns, EmailStatusCode.any).statusCode ==
+ EmailStatusCode.rfc5322DomainLiteral);
+
+ assert(`test@[RFC-5322]-domain-literal]`.isEmail(No.checkDns, EmailStatusCode.any).statusCode ==
+ EmailStatusCode.errorTextAfterDomainLiteral);
+
+ assert(`test@[RFC-5322-[domain-literal]`.isEmail(No.checkDns, EmailStatusCode.any).statusCode ==
+ EmailStatusCode.errorExpectingDomainText);
+
+ assert("test@[RFC-5322-\\\u0007-domain-literal]".isEmail(No.checkDns, EmailStatusCode.any).statusCode ==
+ EmailStatusCode.rfc5322DomainLiteralObsoleteText, `obs-dtext <strong>and</strong> obs-qp`);
+
+ assert("test@[RFC-5322-\\\u0009-domain-literal]".isEmail(No.checkDns, EmailStatusCode.any).statusCode ==
+ EmailStatusCode.rfc5322DomainLiteralObsoleteText);
+
+ assert(`test@[RFC-5322-\]-domain-literal]`.isEmail(No.checkDns, EmailStatusCode.any).statusCode ==
+ EmailStatusCode.rfc5322DomainLiteralObsoleteText);
+
+ assert(`test@[RFC-5322-domain-literal\]`.isEmail(No.checkDns, EmailStatusCode.any).statusCode ==
+ EmailStatusCode.errorUnclosedDomainLiteral);
+
+ assert(`test@[RFC-5322-domain-literal\`.isEmail(No.checkDns, EmailStatusCode.any).statusCode ==
+ EmailStatusCode.errorBackslashEnd);
+
+ 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(No.checkDns, EmailStatusCode.any).statusCode ==
+ EmailStatusCode.rfc5322DomainLiteral);
+
+ 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(No.checkDns, EmailStatusCode.any).statusCode ==
+ EmailStatusCode.deprecatedQuotedPair);
+
+ assert("(\u007F)test@iana.org".isEmail(No.checkDns, EmailStatusCode.any).statusCode ==
+ EmailStatusCode.deprecatedCommentText);
+
+ assert("test@iana.org\u000D".isEmail(No.checkDns, 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("\"\u000Dtest\"@iana.org".isEmail(No.checkDns, EmailStatusCode.any).statusCode ==
+ EmailStatusCode.errorCrNoLf, `No LF after the CR`);
+
+ assert("(\u000D)test@iana.org".isEmail(No.checkDns, EmailStatusCode.any).statusCode == EmailStatusCode.errorCrNoLf,
+ `No LF after the CR`);
+
+ assert("(\u000D".isEmail(No.checkDns, EmailStatusCode.any).statusCode == EmailStatusCode.errorCrNoLf,
+ `No LF after the CR`);
+
+ assert("test@iana.org(\u000D)".isEmail(No.checkDns, EmailStatusCode.any).statusCode == EmailStatusCode.errorCrNoLf,
+ `No LF after the CR`);
+
+ assert("\u000Atest@iana.org".isEmail(No.checkDns, EmailStatusCode.any).statusCode ==
+ EmailStatusCode.errorExpectingText);
+
+ assert("\"\u000A\"@iana.org".isEmail(No.checkDns, EmailStatusCode.any).statusCode ==
+ EmailStatusCode.errorExpectingQuotedText);
+
+ assert("\"\\\u000A\"@iana.org".isEmail(No.checkDns, EmailStatusCode.any).statusCode ==
+ EmailStatusCode.deprecatedQuotedPair);
+
+ assert("(\u000A)test@iana.org".isEmail(No.checkDns, EmailStatusCode.any).statusCode ==
+ EmailStatusCode.errorExpectingCommentText);
+
+ assert("\u0007@iana.org".isEmail(No.checkDns, EmailStatusCode.any).statusCode ==
+ EmailStatusCode.errorExpectingText);
+
+ assert("test@\u0007.org".isEmail(No.checkDns, EmailStatusCode.any).statusCode ==
+ EmailStatusCode.errorExpectingText);
+
+ assert("\"\u0007\"@iana.org".isEmail(No.checkDns, EmailStatusCode.any).statusCode ==
+ EmailStatusCode.deprecatedQuotedText);
+
+ assert("\"\\\u0007\"@iana.org".isEmail(No.checkDns, EmailStatusCode.any).statusCode ==
+ EmailStatusCode.deprecatedQuotedPair);
+
+ assert("(\u0007)test@iana.org".isEmail(No.checkDns, EmailStatusCode.any).statusCode ==
+ EmailStatusCode.deprecatedCommentText);
+
+ 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(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(No.checkDns, EmailStatusCode.any).statusCode ==
+ EmailStatusCode.errorFoldingWhitespaceCrLfEnd, `Not FWS because no white space after the fold`);
+
+ assert(" \u000D\u000A test@iana.org".isEmail(No.checkDns, EmailStatusCode.any).statusCode ==
+ EmailStatusCode.foldingWhitespace, `FWS`);
+
+ 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(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(No.checkDns, EmailStatusCode.any).statusCode ==
+ EmailStatusCode.errorFoldingWhitespaceCrflX2, `Not FWS because no white space after the first fold`);
+
+ assert("test@iana.org\u000D\u000A ".isEmail(No.checkDns, EmailStatusCode.any).statusCode ==
+ EmailStatusCode.foldingWhitespace, `FWS`);
+
+ 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(No.checkDns, EmailStatusCode.any).statusCode ==
+ EmailStatusCode.errorFoldingWhitespaceCrLfEnd, `Not FWS because no actual white space`);
+
+ 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(No.checkDns, EmailStatusCode.any).statusCode ==
+ EmailStatusCode.errorFoldingWhitespaceCrLfEnd, `Not FWS because no white space after the fold`);
+
+ assert("test@iana.org \u000D\u000A ".isEmail(No.checkDns, EmailStatusCode.any).statusCode ==
+ EmailStatusCode.foldingWhitespace, `FWS`);
+
+ 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(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(No.checkDns, EmailStatusCode.any).statusCode ==
+ EmailStatusCode.errorFoldingWhitespaceCrflX2, `Not FWS because no white space after the first fold`);
+
+ 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(No.checkDns, EmailStatusCode.any).statusCode ==
+ EmailStatusCode.rfc5322IpV6ColonEnd);
+
+ assert("\"test\\\u00A9\"@iana.org".isEmail(No.checkDns, EmailStatusCode.any).statusCode ==
+ EmailStatusCode.errorExpectingQuotedPair);
+
+ assert(`test@iana/icann.org`.isEmail(No.checkDns, EmailStatusCode.any).statusCode == EmailStatusCode.rfc5322Domain);
+
+ assert(`test.(comment)test@iana.org`.isEmail(No.checkDns, EmailStatusCode.any).statusCode ==
+ EmailStatusCode.deprecatedComment);
+
+ assert(`test@org`.isEmail(No.checkDns, EmailStatusCode.any).statusCode == EmailStatusCode.rfc5321TopLevelDomain);
+
+ // 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(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
+}
+
+// https://issues.dlang.org/show_bug.cgi?id=17217
+@safe unittest
+{
+ 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);
+}
+
+/**
+ * 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
+ {
+ bool valid_;
+ string localPart_;
+ string domainPart_;
+ EmailStatusCode statusCode_;
+ }
+
+ /// Self aliases to a `bool` representing if the email is valid or not
+ alias valid this;
+
+ /*
+ * Params:
+ * valid = indicates if the email address is valid or not
+ * localPart = the local part of the email address
+ * domainPart = the domain part of the email address
+ * statusCode = the status code
+ */
+ private this (bool valid, string localPart, string domainPart, EmailStatusCode statusCode) @safe @nogc pure nothrow
+ {
+ this.valid_ = valid;
+ this.localPart_ = localPart;
+ this.domainPart_ = domainPart;
+ this.statusCode_ = statusCode;
+ }
+
+ /// Returns: If the email address is valid or not.
+ @property bool valid() const @safe @nogc pure nothrow
+ {
+ return valid_;
+ }
+
+ /// 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_;
+ }
+
+ /// 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_;
+ }
+
+ /// 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 @safe @nogc pure nothrow
+ {
+ return statusCodeDescription(statusCode_);
+ }
+
+ /// 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,
+ localPart, domainPart, 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)
+ {
+ // Categories
+ case EmailStatusCode.validCategory: return "Address is valid";
+ case EmailStatusCode.dnsWarning: return "Address is valid but a DNS check was not successful";
+ case EmailStatusCode.rfc5321: return "Address is valid for SMTP but has unusual elements";
+
+ case EmailStatusCode.cFoldingWhitespace: return "Address is valid within the message but cannot be used"~
+ " unmodified for the envelope";
+
+ case EmailStatusCode.deprecated_: return "Address contains deprecated elements but may still be valid in"~
+ " restricted contexts";
+
+ case EmailStatusCode.rfc5322: return "The address is only valid according to the broad definition of RFC 5322."~
+ " It is otherwise invalid";
+
+ case EmailStatusCode.any: return "";
+ case EmailStatusCode.none: return "";
+ case EmailStatusCode.warning: return "";
+ case EmailStatusCode.error: return "Address is invalid for any purpose";
+
+ // Diagnoses
+ case EmailStatusCode.valid: return "Address is valid";
+
+ // Address is valid but a DNS check was not successful
+ case EmailStatusCode.dnsWarningNoMXRecord: return "Could not find an MX record for this domain but an A-record"~
+ " does exist";
+
+ case EmailStatusCode.dnsWarningNoRecord: return "Could not find an MX record or an A-record for this domain";
+
+ // Address is valid for SMTP but has unusual elements
+ case EmailStatusCode.rfc5321TopLevelDomain: return "Address is valid but at a Top Level Domain";
+
+ case EmailStatusCode.rfc5321TopLevelDomainNumeric: return "Address is valid but the Top Level Domain begins"~
+ " with a number";
+
+ case EmailStatusCode.rfc5321QuotedString: return "Address is valid but contains a quoted string";
+ case EmailStatusCode.rfc5321AddressLiteral: return "Address is valid but at a literal address not a domain";
+
+ case EmailStatusCode.rfc5321IpV6Deprecated: return "Address is valid but contains a :: that only elides one"~
+ " zero group";
+
+
+ // Address is valid within the message but cannot be used unmodified for the envelope
+ case EmailStatusCode.comment: return "Address contains comments";
+ case EmailStatusCode.foldingWhitespace: return "Address contains Folding White Space";
+
+ // Address contains deprecated elements but may still be valid in restricted contexts
+ case EmailStatusCode.deprecatedLocalPart: return "The local part is in a deprecated form";
+
+ case EmailStatusCode.deprecatedFoldingWhitespace: return "Address contains an obsolete form of"~
+ " Folding White Space";
+
+ case EmailStatusCode.deprecatedQuotedText: return "A quoted string contains a deprecated character";
+ case EmailStatusCode.deprecatedQuotedPair: return "A quoted pair contains a deprecated character";
+ case EmailStatusCode.deprecatedComment: return "Address contains a comment in a position that is deprecated";
+ case EmailStatusCode.deprecatedCommentText: return "A comment contains a deprecated character";
+
+ case EmailStatusCode.deprecatedCommentFoldingWhitespaceNearAt: return "Address contains a comment or"~
+ " Folding White Space around the @ sign";
+
+ // The address is only valid according to the broad definition of RFC 5322
+ case EmailStatusCode.rfc5322Domain: return "Address is RFC 5322 compliant but contains domain characters that"~
+ " are not allowed by DNS";
+
+ case EmailStatusCode.rfc5322TooLong: return "Address is too long";
+ case EmailStatusCode.rfc5322LocalTooLong: return "The local part of the address is too long";
+ case EmailStatusCode.rfc5322DomainTooLong: return "The domain part is too long";
+ case EmailStatusCode.rfc5322LabelTooLong: return "The domain part contains an element that is too long";
+ case EmailStatusCode.rfc5322DomainLiteral: return "The domain literal is not a valid RFC 5321 address literal";
+
+ case EmailStatusCode.rfc5322DomainLiteralObsoleteText: return "The domain literal is not a valid RFC 5321"~
+ " address literal and it contains obsolete characters";
+
+ case EmailStatusCode.rfc5322IpV6GroupCount:
+ return "The IPv6 literal address contains the wrong number of groups";
+
+ case EmailStatusCode.rfc5322IpV6TooManyDoubleColons:
+ return "The IPv6 literal address contains too many :: sequences";
+
+ case EmailStatusCode.rfc5322IpV6BadChar: return "The IPv6 address contains an illegal group of characters";
+ case EmailStatusCode.rfc5322IpV6MaxGroups: return "The IPv6 address has too many groups";
+ case EmailStatusCode.rfc5322IpV6ColonStart: return "IPv6 address starts with a single colon";
+ case EmailStatusCode.rfc5322IpV6ColonEnd: return "IPv6 address ends with a single colon";
+
+ // Address is invalid for any purpose
+ case EmailStatusCode.errorExpectingDomainText:
+ return "A domain literal contains a character that is not allowed";
+
+ case EmailStatusCode.errorNoLocalPart: return "Address has no local part";
+ case EmailStatusCode.errorNoDomain: return "Address has no domain part";
+ case EmailStatusCode.errorConsecutiveDots: return "The address may not contain consecutive dots";
+
+ case EmailStatusCode.errorTextAfterCommentFoldingWhitespace:
+ return "Address contains text after a comment or Folding White Space";
+
+ case EmailStatusCode.errorTextAfterQuotedString: return "Address contains text after a quoted string";
+
+ case EmailStatusCode.errorTextAfterDomainLiteral: return "Extra characters were found after the end of"~
+ " the domain literal";
+
+ case EmailStatusCode.errorExpectingQuotedPair:
+ return "The address contains a character that is not allowed in a quoted pair";
+
+ case EmailStatusCode.errorExpectingText: return "Address contains a character that is not allowed";
+
+ case EmailStatusCode.errorExpectingQuotedText:
+ return "A quoted string contains a character that is not allowed";
+
+ case EmailStatusCode.errorExpectingCommentText: return "A comment contains a character that is not allowed";
+ case EmailStatusCode.errorBackslashEnd: return "The address cannot end with a backslash";
+ case EmailStatusCode.errorDotStart: return "Neither part of the address may begin with a dot";
+ case EmailStatusCode.errorDotEnd: return "Neither part of the address may end with a dot";
+ case EmailStatusCode.errorDomainHyphenStart: return "A domain or subdomain cannot begin with a hyphen";
+ case EmailStatusCode.errorDomainHyphenEnd: return "A domain or subdomain cannot end with a hyphen";
+ case EmailStatusCode.errorUnclosedQuotedString: return "Unclosed quoted string";
+ case EmailStatusCode.errorUnclosedComment: return "Unclosed comment";
+ case EmailStatusCode.errorUnclosedDomainLiteral: return "Domain literal is missing its closing bracket";
+
+ case EmailStatusCode.errorFoldingWhitespaceCrflX2:
+ return "Folding White Space contains consecutive CRLF sequences";
+
+ case EmailStatusCode.errorFoldingWhitespaceCrLfEnd: return "Folding White Space ends with a CRLF sequence";
+
+ case EmailStatusCode.errorCrNoLf:
+ return "Address contains a carriage return that is not followed by a line feed";
+ }
+}
+
+/**
+ * An email status code, indicating if an email address is valid or not.
+ * If it is invalid it also indicates why.
+ */
+enum EmailStatusCode
+{
+ // Categories
+
+ /// Address is valid
+ validCategory = 1,
+
+ /// Address is valid but a DNS check was not successful
+ dnsWarning = 7,
+
+ /// Address is valid for SMTP but has unusual elements
+ rfc5321 = 15,
+
+ /// Address is valid within the message but cannot be used unmodified for the envelope
+ cFoldingWhitespace = 31,
+
+ /// Address contains deprecated elements but may still be valid in restricted contexts
+ deprecated_ = 63,
+
+ /// The address is only valid according to the broad definition of RFC 5322. It is otherwise invalid
+ rfc5322 = 127,
+
+ /**
+ * All finer grained error checking is turned on. Address containing errors or
+ * warnings is considered invalid. A specific email status code will be
+ * returned indicating the error/warning of the address.
+ */
+ any = 252,
+
+ /**
+ * Address is either considered valid or not, no finer grained error checking
+ * is performed. Returned email status code will be either Error or Valid.
+ */
+ none = 253,
+
+ /**
+ * Address containing warnings is considered valid, that is,
+ * any status code below 16 is considered valid.
+ */
+ warning = 254,
+
+ /// Address is invalid for any purpose
+ error = 255,
+
+
+
+ // Diagnoses
+
+ /// Address is valid
+ valid = 0,
+
+ // Address is valid but a DNS check was not successful
+
+ /// Could not find an MX record for this domain but an A-record does exist
+ dnsWarningNoMXRecord = 5,
+
+ /// Could not find an MX record or an A-record for this domain
+ dnsWarningNoRecord = 6,
+
+
+
+ // Address is valid for SMTP but has unusual elements
+
+ /// Address is valid but at a Top Level Domain
+ rfc5321TopLevelDomain = 9,
+
+ /// Address is valid but the Top Level Domain begins with a number
+ rfc5321TopLevelDomainNumeric = 10,
+
+ /// Address is valid but contains a quoted string
+ rfc5321QuotedString = 11,
+
+ /// Address is valid but at a literal address not a domain
+ rfc5321AddressLiteral = 12,
+
+ /// Address is valid but contains a :: that only elides one zero group
+ rfc5321IpV6Deprecated = 13,
+
+
+
+ // Address is valid within the message but cannot be used unmodified for the envelope
+
+ /// Address contains comments
+ comment = 17,
+
+ /// Address contains Folding White Space
+ foldingWhitespace = 18,
+
+
+
+ // Address contains deprecated elements but may still be valid in restricted contexts
+
+ /// The local part is in a deprecated form
+ deprecatedLocalPart = 33,
+
+ /// Address contains an obsolete form of Folding White Space
+ deprecatedFoldingWhitespace = 34,
+
+ /// A quoted string contains a deprecated character
+ deprecatedQuotedText = 35,
+
+ /// A quoted pair contains a deprecated character
+ deprecatedQuotedPair = 36,
+
+ /// Address contains a comment in a position that is deprecated
+ deprecatedComment = 37,
+
+ /// A comment contains a deprecated character
+ deprecatedCommentText = 38,
+
+ /// Address contains a comment or Folding White Space around the @ sign
+ deprecatedCommentFoldingWhitespaceNearAt = 49,
+
+
+
+ // The address is only valid according to the broad definition of RFC 5322
+
+ /// Address is RFC 5322 compliant but contains domain characters that are not allowed by DNS
+ rfc5322Domain = 65,
+
+ /// Address is too long
+ rfc5322TooLong = 66,
+
+ /// The local part of the address is too long
+ rfc5322LocalTooLong = 67,
+
+ /// The domain part is too long
+ rfc5322DomainTooLong = 68,
+
+ /// The domain part contains an element that is too long
+ rfc5322LabelTooLong = 69,
+
+ /// The domain literal is not a valid RFC 5321 address literal
+ rfc5322DomainLiteral = 70,
+
+ /// The domain literal is not a valid RFC 5321 address literal and it contains obsolete characters
+ rfc5322DomainLiteralObsoleteText = 71,
+
+ /// The IPv6 literal address contains the wrong number of groups
+ rfc5322IpV6GroupCount = 72,
+
+ /// The IPv6 literal address contains too many :: sequences
+ rfc5322IpV6TooManyDoubleColons = 73,
+
+ /// The IPv6 address contains an illegal group of characters
+ rfc5322IpV6BadChar = 74,
+
+ /// The IPv6 address has too many groups
+ rfc5322IpV6MaxGroups = 75,
+
+ /// IPv6 address starts with a single colon
+ rfc5322IpV6ColonStart = 76,
+
+ /// IPv6 address ends with a single colon
+ rfc5322IpV6ColonEnd = 77,
+
+
+
+ // Address is invalid for any purpose
+
+ /// A domain literal contains a character that is not allowed
+ errorExpectingDomainText = 129,
+
+ /// Address has no local part
+ errorNoLocalPart = 130,
+
+ /// Address has no domain part
+ errorNoDomain = 131,
+
+ /// The address may not contain consecutive dots
+ errorConsecutiveDots = 132,
+
+ /// Address contains text after a comment or Folding White Space
+ errorTextAfterCommentFoldingWhitespace = 133,
+
+ /// Address contains text after a quoted string
+ errorTextAfterQuotedString = 134,
+
+ /// Extra characters were found after the end of the domain literal
+ errorTextAfterDomainLiteral = 135,
+
+ /// The address contains a character that is not allowed in a quoted pair
+ errorExpectingQuotedPair = 136,
+
+ /// Address contains a character that is not allowed
+ errorExpectingText = 137,
+
+ /// A quoted string contains a character that is not allowed
+ errorExpectingQuotedText = 138,
+
+ /// A comment contains a character that is not allowed
+ errorExpectingCommentText = 139,
+
+ /// The address cannot end with a backslash
+ errorBackslashEnd = 140,
+
+ /// Neither part of the address may begin with a dot
+ errorDotStart = 141,
+
+ /// Neither part of the address may end with a dot
+ errorDotEnd = 142,
+
+ /// A domain or subdomain cannot begin with a hyphen
+ errorDomainHyphenStart = 143,
+
+ /// A domain or subdomain cannot end with a hyphen
+ errorDomainHyphenEnd = 144,
+
+ /// Unclosed quoted string
+ errorUnclosedQuotedString = 145,
+
+ /// Unclosed comment
+ errorUnclosedComment = 146,
+
+ /// Domain literal is missing its closing bracket
+ errorUnclosedDomainLiteral = 147,
+
+ /// Folding White Space contains consecutive CRLF sequences
+ errorFoldingWhitespaceCrflX2 = 148,
+
+ /// Folding White Space ends with a CRLF sequence
+ errorFoldingWhitespaceCrLfEnd = 149,
+
+ /// Address contains a carriage return that is not followed by a line feed
+ errorCrNoLf = 150,
+}
+
+private:
+
+// Email parts for the isEmail function
+enum EmailPart
+{
+ // The local part of the email address, that is, the part before the @ sign
+ componentLocalPart,
+
+ // The domain part of the email address, that is, the part after the @ sign.
+ componentDomain,
+
+ componentLiteral,
+ contextComment,
+ contextFoldingWhitespace,
+ contextQuotedString,
+ contextQuotedPair,
+ status
+}
+
+// Miscellaneous string constants
+struct TokenImpl(Char)
+{
+ enum : const(Char)[]
+ {
+ at = "@",
+ backslash = `\`,
+ dot = ".",
+ doubleQuote = `"`,
+ openParenthesis = "(",
+ closeParenthesis = ")",
+ openBracket = "[",
+ closeBracket = "]",
+ hyphen = "-",
+ colon = ":",
+ doubleColon = "::",
+ space = " ",
+ tab = "\t",
+ cr = "\r",
+ lf = "\n",
+ ipV6Tag = "IPV6:",
+
+ // US-ASCII visible characters not valid for atext (http://tools.ietf.org/html/rfc5322#section-3.2.3)
+ specials = `()<>[]:;@\\,."`
+ }
+}
+
+enum AsciiToken
+{
+ horizontalTab = 9,
+ unitSeparator = 31,
+ delete_ = 127
+}
+
+/*
+ * Compare the two given strings lexicographically. An upper limit of the number of
+ * characters, that will be used in the comparison, can be specified. Supports both
+ * case-sensitive and case-insensitive comparison.
+ *
+ * Params:
+ * s1 = the first string to be compared
+ * s2 = the second string to be compared
+ * length = the length of strings to be used in the comparison.
+ * caseInsensitive = if true, a case-insensitive comparison will be made,
+ * otherwise a case-sensitive comparison will be made
+ *
+ * Returns: (for $(D pred = "a < b")):
+ *
+ * $(BOOKTABLE,
+ * $(TR $(TD $(D < 0)) $(TD $(D s1 < s2) ))
+ * $(TR $(TD $(D = 0)) $(TD $(D s1 == s2)))
+ * $(TR $(TD $(D > 0)) $(TD $(D s1 > s2)))
+ * )
+ */
+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;
+ 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 slice1.icmp(slice2);
+}
+
+@safe unittest
+{
+ assert("abc".compareFirstN("abcdef", 3) == 0);
+ assert("abc".compareFirstN("Abc", 3) == 0);
+ assert("abc".compareFirstN("abcdef", 6) < 0);
+ assert("abcdef".compareFirstN("abc", 6) > 0);
+}
+
+/*
+ * Pops the last element of the given range and returns the element.
+ *
+ * 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[]))
+{
+ auto e = a.back;
+ a.popBack();
+ return e;
+}
+
+@safe unittest
+{
+ auto array = [0, 1, 2, 3];
+ auto result = array.pop();
+
+ assert(array == [0, 1, 2]);
+ assert(result == 3);
+}
+
+/*
+ * Returns the character at the given index as a string. The returned string will be a
+ * slice of the original string.
+ *
+ * Params:
+ * str = the string to get the character from
+ * index = the index of the character to get
+ * c = the character to return, or any other of the same length
+ *
+ * Returns: the character at the given index as a string
+ */
+const(T)[] get (T) (const(T)[] str, size_t index, dchar c)
+{
+ import std.utf : codeLength;
+ return str[index .. index + codeLength!(T)(c)];
+}
+
+@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/libphobos/src/std/numeric.d b/libphobos/src/std/numeric.d
new file mode 100644
index 0000000..307406e
--- /dev/null
+++ b/libphobos/src/std/numeric.d
@@ -0,0 +1,3467 @@
+// Written in the D programming language.
+
+/**
+This module is a port of a growing fragment of the $(D_PARAM numeric)
+header in Alexander Stepanov's $(LINK2 http://sgi.com/tech/stl,
+Standard Template Library), with a few additions.
+
+Macros:
+Copyright: Copyright Andrei Alexandrescu 2008 - 2009.
+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)
+*/
+/*
+ Copyright Andrei Alexandrescu 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.numeric;
+
+import std.complex;
+import std.math;
+import std.range.primitives;
+import std.traits;
+import std.typecons;
+
+version (unittest)
+{
+ import std.stdio;
+}
+/// Format flags for CustomFloat.
+public enum CustomFloatFlags
+{
+ /// Adds a sign bit to allow for signed numbers.
+ signed = 1,
+
+ /**
+ * 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 $(LINK2 https://en.wikipedia.org/wiki/IEEE_floating_point, IEE754) types
+ */
+ storeNormalized = 2,
+
+ /**
+ * 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 $(LINK2 https://en.wikipedia.org/wiki/IEEE_754-1985#Positive_and_negative_infinity,
+ * IEEE754 _infinity) values.
+ */
+ infinity = 8,
+
+ /// Allows the storage of $(LINK2 https://en.wikipedia.org/wiki/NaN, IEEE754 Not a Number) values.
+ nan = 16,
+
+ /**
+ * If set, select an exponent bias such that max_exp = 1.
+ * i.e. so that the maximum value is >= 1.0 and < 2.0.
+ * Ignored if the exponent bias is manually specified.
+ */
+ probability = 32,
+
+ /// If set, unsigned custom floats are assumed to be negative.
+ negativeUnsigned = 64,
+
+ /**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 $(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
+}
+
+private template CustomFloatParams(uint bits)
+{
+ enum CustomFloatFlags flags = CustomFloatFlags.ieee
+ ^ ((bits == 80) ? CustomFloatFlags.storeNormalized : CustomFloatFlags.none);
+ static if (bits == 8) alias CustomFloatParams = CustomFloatParams!( 4, 3, flags);
+ static if (bits == 16) alias CustomFloatParams = CustomFloatParams!(10, 5, flags);
+ static if (bits == 32) alias CustomFloatParams = CustomFloatParams!(23, 8, flags);
+ static if (bits == 64) alias CustomFloatParams = CustomFloatParams!(52, 11, flags);
+ static if (bits == 80) alias CustomFloatParams = CustomFloatParams!(64, 15, flags);
+}
+
+private template CustomFloatParams(uint precision, uint exponentWidth, CustomFloatFlags flags)
+{
+ import std.meta : AliasSeq;
+ alias CustomFloatParams =
+ AliasSeq!(
+ precision,
+ exponentWidth,
+ flags,
+ (1 << (exponentWidth - ((flags & flags.probability) == 0)))
+ - ((flags & (flags.nan | flags.infinity)) != 0) - ((flags & flags.probability) != 0)
+ ); // ((flags & CustomFloatFlags.probability) == 0)
+}
+
+/**
+ * Allows user code to define custom floating-point formats. These formats are
+ * for storage only; all operations on them are performed by first implicitly
+ * extracting them to $(D real) first. After the operation is completed the
+ * 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)
+{
+ 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)
+{
+ alias CustomFloat = CustomFloat!(CustomFloatParams!(precision, exponentWidth, flags));
+}
+
+///
+@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
+ CustomFloat!(10, 5,CustomFloatFlags.ieee) z; // Using the precision, exponent width and format flags
+ CustomFloat!(10, 5,CustomFloatFlags.ieee, 15) w; // Using the precision, exponent width, format flags and exponent offset bias
+
+ // Use the 16-bit floats mostly like normal numbers
+ w = x*y - 1;
+
+ // 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
+
+ // Define a 8-bit custom float for storing probabilities
+ alias Probability = CustomFloat!(4, 4, CustomFloatFlags.ieee^CustomFloatFlags.probability^CustomFloatFlags.signed );
+ auto p = Probability(0.5);
+}
+
+/// ditto
+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)
+{
+ 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;
+ 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;
+ else alias sType = long;
+ }
+
+ alias T_sig = uType!precision;
+ alias T_exp = uType!exponentWidth;
+ alias T_signed_exp = sType!exponentWidth;
+
+ alias Flags = CustomFloatFlags;
+
+ // Facilitate converting numeric types to custom float
+ union ToBinary(F)
+ if (is(typeof(CustomFloatParams!(F.sizeof*8))) || is(F == real))
+ {
+ F set;
+
+ // If on Linux or Mac, where 80-bit reals are padded, ignore the
+ // padding.
+ import std.algorithm.comparison : min;
+ CustomFloat!(CustomFloatParams!(min(F.sizeof*8, 80))) get;
+
+ // Convert F to the correct binary type.
+ static typeof(get) opCall(F value)
+ {
+ ToBinary r;
+ r.set = value;
+ return r.get;
+ }
+ alias get this;
+ }
+
+ // Perform IEEE rounding with round to nearest detection
+ void roundedShift(T,U)(ref T sig, U shift)
+ {
+ if (sig << (T.sizeof*8 - shift) == cast(T) 1uL << (T.sizeof*8 - 1))
+ {
+ // round to even
+ sig >>= shift;
+ sig += sig & 1;
+ }
+ else
+ {
+ sig >>= shift - 1;
+ sig += sig & 1;
+ // Perform standard rounding
+ sig >>= 1;
+ }
+ }
+
+ // Convert the current value to signed exponent, normalized form
+ void toNormalized(T,U)(ref T sig, ref U exp)
+ {
+ sig = significand;
+ auto shift = (T.sizeof*8) - precision;
+ exp = exponent;
+ static if (flags&(Flags.infinity|Flags.nan))
+ {
+ // Handle inf or nan
+ if (exp == exponent_max)
+ {
+ exp = exp.max;
+ sig <<= shift;
+ static if (flags&Flags.storeNormalized)
+ {
+ // Save inf/nan in denormalized format
+ sig >>= 1;
+ sig += cast(T) 1uL << (T.sizeof*8 - 1);
+ }
+ return;
+ }
+ }
+ if ((~flags&Flags.storeNormalized) ||
+ // Convert denormalized form to normalized form
+ ((flags&Flags.allowDenorm) && exp == 0))
+ {
+ if (sig > 0)
+ {
+ import core.bitop : bsr;
+ auto shift2 = precision - bsr(sig);
+ exp -= shift2-1;
+ shift += shift2;
+ }
+ else // value = 0.0
+ {
+ exp = exp.min;
+ return;
+ }
+ }
+ sig <<= shift;
+ exp -= bias;
+ }
+
+ // Set the current value from signed exponent, normalized form
+ void fromNormalized(T,U)(ref T sig, ref U exp)
+ {
+ auto shift = (T.sizeof*8) - precision;
+ if (exp == exp.max)
+ {
+ // infinity or nan
+ exp = exponent_max;
+ static if (flags & Flags.storeNormalized)
+ sig <<= 1;
+
+ // convert back to normalized form
+ static if (~flags & Flags.infinity)
+ // 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?
+ assert(sig == 0, "NaN floating point value assigned to a " ~
+ typeof(this).stringof ~ " (no nan support).");
+ sig >>= shift;
+ return;
+ }
+ if (exp == exp.min) // 0.0
+ {
+ exp = 0;
+ sig = 0;
+ return;
+ }
+
+ exp += bias;
+ if (exp <= 0)
+ {
+ static if ((flags&Flags.allowDenorm) ||
+ // Convert from normalized form to denormalized
+ (~flags&Flags.storeNormalized))
+ {
+ shift += -exp;
+ roundedShift(sig,1);
+ sig += cast(T) 1uL << (T.sizeof*8 - 1);
+ // Add the leading 1
+ exp = 0;
+ }
+ else
+ assert((flags&Flags.storeNormalized) && exp == 0,
+ "Underflow occured assigning to a " ~
+ typeof(this).stringof ~ " (no denormal support).");
+ }
+ else
+ {
+ static if (~flags&Flags.storeNormalized)
+ {
+ // Convert from normalized form to denormalized
+ roundedShift(sig,1);
+ sig += cast(T) 1uL << (T.sizeof*8 - 1);
+ // Add the leading 1
+ }
+ }
+
+ if (shift > 0)
+ roundedShift(sig,shift);
+ if (sig > significand_max)
+ {
+ // handle significand overflow (should only be 1 bit)
+ static if (~flags&Flags.storeNormalized)
+ {
+ sig >>= 1;
+ }
+ else
+ sig &= significand_max;
+ exp++;
+ }
+ static if ((flags&Flags.allowDenormZeroOnly)==Flags.allowDenormZeroOnly)
+ {
+ // disallow non-zero denormals
+ if (exp == 0)
+ {
+ sig <<= 1;
+ if (sig > significand_max && (sig&significand_max) > 0)
+ // Check and round to even
+ exp++;
+ sig = 0;
+ }
+ }
+
+ if (exp >= exponent_max)
+ {
+ static if (flags&(Flags.infinity|Flags.nan))
+ {
+ sig = 0;
+ exp = exponent_max;
+ static if (~flags&(Flags.infinity))
+ assert(0, "Overflow occured assigning to a " ~
+ typeof(this).stringof ~ " (no infinity support).");
+ }
+ else
+ assert(exp == exponent_max, "Overflow occured assigning to a "
+ ~ typeof(this).stringof ~ " (no infinity support).");
+ }
+ }
+
+public:
+ static if (precision == 64) // CustomFloat!80 support hack
+ {
+ ulong significand;
+ enum ulong significand_max = ulong.max;
+ mixin(bitfields!(
+ T_exp , "exponent", exponentWidth,
+ bool , "sign" , flags & flags.signed ));
+ }
+ else
+ {
+ mixin(bitfields!(
+ T_sig, "significand", precision,
+ T_exp, "exponent" , exponentWidth,
+ bool , "sign" , flags & flags.signed ));
+ }
+
+ /// Returns: infinity value
+ static if (flags & Flags.infinity)
+ static @property CustomFloat infinity()
+ {
+ CustomFloat value;
+ static if (flags & Flags.signed)
+ value.sign = 0;
+ value.significand = 0;
+ value.exponent = exponent_max;
+ return value;
+ }
+
+ /// Returns: NaN value
+ static if (flags & Flags.nan)
+ static @property CustomFloat nan()
+ {
+ CustomFloat value;
+ static if (flags & Flags.signed)
+ value.sign = 0;
+ value.significand = cast(typeof(significand_max)) 1L << (precision-1);
+ value.exponent = exponent_max;
+ return value;
+ }
+
+ /// Returns: number of decimal digits of precision
+ static @property size_t dig()
+ {
+ auto shiftcnt = precision - ((flags&Flags.storeNormalized) != 0);
+ immutable x = (shiftcnt == 64) ? 0 : 1uL << shiftcnt;
+ return cast(size_t) log10(x);
+ }
+
+ /// Returns: smallest increment to the value 1
+ static @property CustomFloat epsilon()
+ {
+ CustomFloat value;
+ static if (flags & Flags.signed)
+ value.sign = 0;
+ T_signed_exp exp = -precision;
+ T_sig sig = 0;
+
+ value.fromNormalized(sig,exp);
+ if (exp == 0 && sig == 0) // underflowed to zero
+ {
+ static if ((flags&Flags.allowDenorm) ||
+ (~flags&Flags.storeNormalized))
+ sig = 1;
+ else
+ sig = cast(T) 1uL << (precision - 1);
+ }
+ value.exponent = cast(value.T_exp) exp;
+ value.significand = cast(value.T_sig) sig;
+ return value;
+ }
+
+ /// the number of bits in mantissa
+ enum mant_dig = precision + ((flags&Flags.storeNormalized) != 0);
+
+ /// Returns: maximum int value such that 10<sup>max_10_exp</sup> is representable
+ static @property int max_10_exp(){ return cast(int) log10( +max ); }
+
+ /// maximum int value such that 2<sup>max_exp-1</sup> is representable
+ enum max_exp = exponent_max-bias+((~flags&(Flags.infinity|flags.nan))!=0);
+
+ /// Returns: minimum int value such that 10<sup>min_10_exp</sup> is representable
+ static @property int min_10_exp(){ return cast(int) log10( +min_normal ); }
+
+ /// minimum int value such that 2<sup>min_exp-1</sup> is representable as a normalized value
+ enum min_exp = cast(T_signed_exp)-bias +1+ ((flags&Flags.allowDenorm)!=0);
+
+ /// Returns: largest representable value that's not infinity
+ static @property CustomFloat max()
+ {
+ CustomFloat value;
+ static if (flags & Flags.signed)
+ value.sign = 0;
+ value.exponent = exponent_max - ((flags&(flags.infinity|flags.nan)) != 0);
+ value.significand = significand_max;
+ return value;
+ }
+
+ /// Returns: smallest representable normalized value that's not 0
+ static @property CustomFloat min_normal() {
+ CustomFloat value;
+ static if (flags & Flags.signed)
+ value.sign = 0;
+ value.exponent = 1;
+ static if (flags&Flags.storeNormalized)
+ value.significand = 0;
+ else
+ value.significand = cast(T_sig) 1uL << (precision - 1);
+ return value;
+ }
+
+ /// Returns: real part
+ @property CustomFloat re() { return this; }
+
+ /// Returns: imaginary part
+ 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 = input;
+ }
+
+ /// Self assignment
+ void opAssign(F:CustomFloat)(F input)
+ {
+ static if (flags & Flags.signed)
+ sign = input.sign;
+ exponent = input.exponent;
+ significand = input.significand;
+ }
+
+ /// Assigns from any $(D real) compatible type.
+ void opAssign(F)(F input)
+ if (__traits(compiles, cast(real) input))
+ {
+ import std.conv : text;
+
+ static if (staticIndexOf!(Unqual!F, float, double, real) >= 0)
+ auto value = ToBinary!(Unqual!F)(input);
+ else
+ auto value = ToBinary!(real )(input);
+
+ // Assign the sign bit
+ static if (~flags & Flags.signed)
+ assert((!value.sign) ^ ((flags&flags.negativeUnsigned) > 0),
+ "Incorrectly signed floating point value assigned to a " ~
+ typeof(this).stringof ~ " (no sign support).");
+ else
+ sign = value.sign;
+
+ CommonType!(T_signed_exp ,value.T_signed_exp) exp = value.exponent;
+ CommonType!(T_sig, value.T_sig ) sig = value.significand;
+
+ value.toNormalized(sig,exp);
+ fromNormalized(sig,exp);
+
+ assert(exp <= exponent_max, text(typeof(this).stringof ~
+ " exponent too large: " ,exp," > ",exponent_max, "\t",input,"\t",sig));
+ assert(sig <= significand_max, text(typeof(this).stringof ~
+ " significand too large: ",sig," > ",significand_max,
+ "\t",input,"\t",exp," ",exponent_max));
+ exponent = cast(T_exp) exp;
+ significand = cast(T_sig) sig;
+ }
+
+ /// Fetches the stored value either as a $(D float), $(D double) or $(D real).
+ @property F get(F)()
+ if (staticIndexOf!(Unqual!F, float, double, real) >= 0)
+ {
+ import std.conv : text;
+
+ ToBinary!F result;
+
+ static if (flags&Flags.signed)
+ result.sign = sign;
+ else
+ result.sign = (flags&flags.negativeUnsigned) > 0;
+
+ CommonType!(T_signed_exp ,result.get.T_signed_exp ) exp = exponent; // Assign the exponent and fraction
+ CommonType!(T_sig, result.get.T_sig ) sig = significand;
+
+ toNormalized(sig,exp);
+ result.fromNormalized(sig,exp);
+ assert(exp <= result.exponent_max, text("get exponent too large: " ,exp," > ",result.exponent_max) );
+ assert(sig <= result.significand_max, text("get significand too large: ",sig," > ",result.significand_max) );
+ result.exponent = cast(result.get.T_exp) exp;
+ result.significand = cast(result.get.T_sig) sig;
+ return result.set;
+ }
+
+ ///ditto
+ T opCast(T)() if (__traits(compiles, get!T )) { return get!T; }
+
+ /// Convert the CustomFloat to a real and perform the relavent operator on the result
+ real opUnary(string op)()
+ if (__traits(compiles, mixin(op~`(get!real)`)) || op=="++" || op=="--")
+ {
+ static if (op=="++" || op=="--")
+ {
+ auto result = get!real;
+ this = mixin(op~`result`);
+ return result;
+ }
+ else
+ return mixin(op~`get!real`);
+ }
+
+ /// ditto
+ real opBinary(string op,T)(T b)
+ if (__traits(compiles, mixin(`get!real`~op~`b`)))
+ {
+ return mixin(`get!real`~op~`b`);
+ }
+
+ /// ditto
+ real opBinaryRight(string op,T)(T a)
+ if ( __traits(compiles, mixin(`a`~op~`get!real`)) &&
+ !__traits(compiles, mixin(`get!real`~op~`b`)))
+ {
+ return mixin(`a`~op~`get!real`);
+ }
+
+ /// ditto
+ int opCmp(T)(auto ref T b)
+ if (__traits(compiles, cast(real) b))
+ {
+ auto x = get!real;
+ auto y = cast(real) b;
+ 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`)))
+ {
+ return mixin(`this = this `~op~` cast(real) b`);
+ }
+
+ /// ditto
+ template toString()
+ {
+ import std.format : FormatSpec, formatValue;
+ // Needs to be a template because of DMD @@BUG@@ 13737.
+ void toString()(scope void delegate(const(char)[]) sink, FormatSpec!char fmt)
+ {
+ sink.formatValue(get!real, fmt);
+ }
+ }
+}
+
+@safe unittest
+{
+ import std.meta;
+ alias FPTypes =
+ AliasSeq!(
+ CustomFloat!(5, 10),
+ CustomFloat!(5, 11, CustomFloatFlags.ieee ^ CustomFloatFlags.signed),
+ CustomFloat!(1, 15, CustomFloatFlags.ieee ^ CustomFloatFlags.signed),
+ CustomFloat!(4, 3, CustomFloatFlags.ieee | CustomFloatFlags.probability ^ CustomFloatFlags.signed)
+ );
+
+ foreach (F; FPTypes)
+ {
+ auto x = F(0.125);
+ assert(x.get!float == 0.125F);
+ assert(x.get!double == 0.125);
+
+ x -= 0.0625;
+ assert(x.get!float == 0.0625F);
+ assert(x.get!double == 0.0625);
+
+ x *= 2;
+ assert(x.get!float == 0.125F);
+ assert(x.get!double == 0.125);
+
+ x /= 4;
+ assert(x.get!float == 0.03125);
+ assert(x.get!double == 0.03125);
+
+ x = 0.5;
+ x ^^= 4;
+ assert(x.get!float == 1 / 16.0F);
+ assert(x.get!double == 1 / 16.0);
+ }
+}
+
+@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");
+}
+
+/**
+Defines the fastest type to use when storing temporaries of a
+calculation intended to ultimately yield a result of type $(D F)
+(where $(D F) must be one of $(D float), $(D double), or $(D
+real)). When doing a multi-step computation, you may want to store
+intermediate results as $(D FPTemporary!F).
+
+The necessity of $(D FPTemporary) stems from the optimized
+floating-point operations and registers present in virtually all
+processors. When adding numbers in the example above, the addition may
+in fact be done in $(D real) precision internally. In that case,
+storing the intermediate $(D result) in $(D double format) is not only
+less precise, it is also (surprisingly) slower, because a conversion
+from $(D real) to $(D double) is performed every pass through the
+loop. This being a lose-lose situation, $(D FPTemporary!F) has been
+defined as the $(I fastest) type to use for calculations at precision
+$(D F). There is no need to define a type for the $(I most accurate)
+calculations, as that is always $(D real).
+
+Finally, there is no guarantee that using $(D FPTemporary!F) will
+always be fastest, as the speed of floating-point calculations depends
+on very many factors.
+ */
+template FPTemporary(F)
+if (isFloatingPoint!F)
+{
+ version (X86)
+ alias FPTemporary = real;
+ else
+ alias FPTemporary = Unqual!F;
+}
+
+///
+@safe unittest
+{
+ import std.math : approxEqual;
+
+ // Average numbers in an array
+ double avg(in double[] a)
+ {
+ if (a.length == 0) return 0;
+ FPTemporary!double result = 0;
+ foreach (e; a) result += e;
+ return result / a.length;
+ }
+
+ auto a = [1.0, 2.0, 3.0];
+ assert(approxEqual(avg(a), 2));
+}
+
+/**
+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).
+*/
+template secantMethod(alias fun)
+{
+ import std.functional : unaryFun;
+ Num secantMethod(Num)(Num xn_1, Num xn)
+ {
+ auto fxn = unaryFun!(fun)(xn_1), d = xn_1 - xn;
+ typeof(fxn) fxn_1;
+
+ xn = xn_1;
+ while (!approxEqual(d, 0) && isFinite(d))
+ {
+ xn_1 = xn;
+ xn -= d;
+ fxn_1 = fxn;
+ fxn = unaryFun!(fun)(xn);
+ d *= -fxn / (fxn - fxn_1);
+ }
+ return xn;
+ }
+}
+
+///
+@safe unittest
+{
+ import std.math : approxEqual, cos;
+
+ float f(float x)
+ {
+ return cos(x) - x*x*x;
+ }
+ auto x = secantMethod!(f)(0f, 1f);
+ assert(approxEqual(x, 0.865474));
+}
+
+@system unittest
+{
+ // @system because of __gshared stderr
+ scope(failure) stderr.writeln("Failure testing secantMethod");
+ float f(float x)
+ {
+ return cos(x) - x*x*x;
+ }
+ immutable x = secantMethod!(f)(0f, 1f);
+ assert(approxEqual(x, 0.865474));
+ auto d = &f;
+ immutable y = secantMethod!(d)(0f, 1f);
+ assert(approxEqual(y, 0.865474));
+}
+
+
+/**
+ * Return true if a and b have opposite sign.
+ */
+private bool oppositeSigns(T1, T2)(T1 a, T2 b)
+{
+ return signbit(a) != signbit(b);
+}
+
+public:
+
+/** Find a real root of a real function f(x) via bracketing.
+ *
+ * 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 `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
+ * interpolation whenever possible, otherwise reverting to parabolic
+ * 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 `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 $(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
+ )
+{
+ immutable fa = f(a);
+ if (fa == 0)
+ return a;
+ immutable fb = f(b);
+ if (fb == 0)
+ return b;
+ immutable r = findRoot(f, a, b, fa, fb, tolerance);
+ // Return the first value if it is smaller or NaN
+ return !(fabs(r[2]) > fabs(r[3])) ? r[0] : r[1];
+}
+
+///ditto
+T findRoot(T, DF)(scope DF f, in T a, in T b)
+{
+ return findRoot(f, a, b, (T a, T b) => false);
+}
+
+/** Find root of a real function f(x) by bracketing, allowing the
+ * termination condition to be specified.
+ *
+ * Params:
+ *
+ * f = Function to be analyzed
+ *
+ * ax = Left bound of initial range of `f` known to contain the
+ * root.
+ *
+ * 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.
+ * ($(D f(ax)) and $(D f(bx)) are commonly known in advance.)
+ *
+ *
+ * tolerance = Defines an early termination condition. Receives the
+ * current upper and lower bounds on the root. The
+ * delegate must return $(D true) when these bounds are
+ * acceptable. If this function always returns $(D false),
+ * full machine precision will be achieved.
+ *
+ * Returns:
+ *
+ * A tuple consisting of two ranges. The first two elements are the
+ * 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
+ )
+in
+{
+ assert(!ax.isNaN() && !bx.isNaN(), "Limits must not be NaN");
+ assert(signbit(fax) != signbit(fbx), "Parameters must bracket the root.");
+}
+body
+{
+ // Author: Don Clugston. This code is (heavily) modified from TOMS748
+ // (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.
+ R fa, fb, fd; // Values of f at a, b, d.
+ bool done = false; // Has a root been found?
+
+ // Allow ax and bx to be provided in reverse order
+ if (ax <= bx)
+ {
+ a = ax; fa = fax;
+ b = bx; fb = fbx;
+ }
+ else
+ {
+ a = bx; fa = fbx;
+ b = ax; fb = fax;
+ }
+
+ // Test the function at point c; update brackets accordingly
+ void bracket(T c)
+ {
+ R fc = f(c);
+ if (fc == 0 || fc.isNaN()) // Exact solution, or NaN
+ {
+ a = c;
+ fa = fc;
+ d = c;
+ fd = fc;
+ done = true;
+ return;
+ }
+
+ // Determine new enclosing interval
+ if (signbit(fa) != signbit(fc))
+ {
+ d = b;
+ fd = fb;
+ b = c;
+ fb = fc;
+ }
+ else
+ {
+ d = a;
+ fd = fa;
+ a = c;
+ fa = fc;
+ }
+ }
+
+ /* Perform a secant interpolation. If the result would lie on a or b, or if
+ a and b differ so wildly in magnitude that the result would be meaningless,
+ perform a bisection instead.
+ */
+ static T secant_interpolate(T a, T b, R fa, R fb)
+ {
+ if (( ((a - b) == a) && b != 0) || (a != 0 && ((b - a) == b)))
+ {
+ // Catastrophic cancellation
+ if (a == 0)
+ a = copysign(T(0), b);
+ else if (b == 0)
+ b = copysign(T(0), a);
+ else if (signbit(a) != signbit(b))
+ return 0;
+ T c = ieeeMean(a, b);
+ return c;
+ }
+ // avoid overflow
+ if (b - a > T.max)
+ return b / 2 + a / 2;
+ if (fb - fa > R.max)
+ return a - (b - a) / 2;
+ T c = a - (fa / (fb - fa)) * (b - a);
+ if (c == a || c == b)
+ return (a + b) / 2;
+ return c;
+ }
+
+ /* 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.
+ */
+ T newtonQuadratic(int numsteps)
+ {
+ // Find the coefficients of the quadratic polynomial.
+ immutable T a0 = fa;
+ immutable T a1 = (fb - fa)/(b - a);
+ immutable T a2 = ((fd - fb)/(d - b) - a1)/(d - a);
+
+ // Determine the starting point of newton steps.
+ T c = oppositeSigns(a2, fa) ? a : b;
+
+ // start the safeguarded newton steps.
+ foreach (int i; 0 .. numsteps)
+ {
+ immutable T pc = a0 + (a1 + a2 * (c - b))*(c - a);
+ immutable T pdc = a1 + a2*((2 * c) - (a + b));
+ if (pdc == 0)
+ return a - a0 / a1;
+ else
+ c = c - pc / pdc;
+ }
+ return c;
+ }
+
+ // On the first iteration we take a secant step:
+ if (fa == 0 || fa.isNaN())
+ {
+ done = true;
+ b = a;
+ fb = fa;
+ }
+ else if (fb == 0 || fb.isNaN())
+ {
+ done = true;
+ a = b;
+ fa = fb;
+ }
+ else
+ {
+ bracket(secant_interpolate(a, b, fa, fb));
+ }
+
+ // Starting with the second iteration, higher-order interpolation can
+ // be used.
+ int itnum = 1; // Iteration number
+ int baditer = 1; // Num bisections to take if an iteration is bad.
+ T c, e; // e is our fourth best guess
+ R fe;
+
+whileloop:
+ while (!done && (b != nextUp(a)) && !tolerance(a, b))
+ {
+ T a0 = a, b0 = b; // record the brackets
+
+ // Do two higher-order (cubic or parabolic) interpolation steps.
+ foreach (int QQ; 0 .. 2)
+ {
+ // Cubic inverse interpolation requires that
+ // all four function values fa, fb, fd, and fe are distinct;
+ // otherwise use quadratic interpolation.
+ bool distinct = (fa != fb) && (fa != fd) && (fa != fe)
+ && (fb != fd) && (fb != fe) && (fd != fe);
+ // The first time, cubic interpolation is impossible.
+ if (itnum<2) distinct = false;
+ bool ok = distinct;
+ if (distinct)
+ {
+ // Cubic inverse interpolation of f(x) at a, b, d, and e
+ immutable q11 = (d - e) * fd / (fe - fd);
+ immutable q21 = (b - d) * fb / (fd - fb);
+ immutable q31 = (a - b) * fa / (fb - fa);
+ immutable d21 = (b - d) * fd / (fd - fb);
+ immutable d31 = (a - b) * fb / (fb - fa);
+
+ immutable q22 = (d21 - q11) * fb / (fe - fb);
+ immutable q32 = (d31 - q21) * fa / (fd - fa);
+ immutable d32 = (d31 - q21) * fd / (fd - fa);
+ immutable q33 = (d32 - q22) * fa / (fe - fa);
+ c = a + (q31 + q32 + q33);
+ if (c.isNaN() || (c <= a) || (c >= b))
+ {
+ // DAC: If the interpolation predicts a or b, it's
+ // probable that it's the actual root. Only allow this if
+ // we're already close to the root.
+ if (c == a && a - b != a)
+ {
+ c = nextUp(a);
+ }
+ else if (c == b && a - b != -b)
+ {
+ c = nextDown(b);
+ }
+ else
+ {
+ ok = false;
+ }
+ }
+ }
+ if (!ok)
+ {
+ // DAC: Alefeld doesn't explain why the number of newton steps
+ // should vary.
+ c = newtonQuadratic(distinct ? 3 : 2);
+ if (c.isNaN() || (c <= a) || (c >= b))
+ {
+ // Failure, try a secant step:
+ c = secant_interpolate(a, b, fa, fb);
+ }
+ }
+ ++itnum;
+ e = d;
+ fe = fd;
+ bracket(c);
+ if (done || ( b == nextUp(a)) || tolerance(a, b))
+ break whileloop;
+ if (itnum == 2)
+ continue whileloop;
+ }
+
+ // Now we take a double-length secant step:
+ T u;
+ R fu;
+ if (fabs(fa) < fabs(fb))
+ {
+ u = a;
+ fu = fa;
+ }
+ else
+ {
+ u = b;
+ fu = fb;
+ }
+ c = u - 2 * (fu / (fb - fa)) * (b - a);
+
+ // 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 ((a-b) == a || (b-a) == b)
+ {
+ if ((a>0 && b<0) || (a<0 && b>0))
+ c = 0;
+ else
+ {
+ if (a == 0)
+ c = ieeeMean(copysign(T(0), b), b);
+ else if (b == 0)
+ c = ieeeMean(copysign(T(0), a), a);
+ else
+ c = ieeeMean(a, b);
+ }
+ }
+ else
+ {
+ c = a + (b - a) / 2;
+ }
+ }
+ e = d;
+ fe = fd;
+ bracket(c);
+ if (done || (b == nextUp(a)) || tolerance(a, b))
+ break;
+
+ // IMPROVE THE WORST-CASE PERFORMANCE
+ // We must ensure that the bounds reduce by a factor of 2
+ // in binary space! every iteration. If we haven't achieved this
+ // yet, or if we don't yet know what the exponent is,
+ // perform a binary chop.
+
+ 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))
+ {
+ baditer = 1;
+ continue;
+ }
+
+ // DAC: If this happens on consecutive iterations, we probably have a
+ // pathological function. Perform a number of bisections equal to the
+ // total number of consecutive bad iterations.
+
+ if ((b - a) < T(0.25) * (b0 - a0))
+ baditer = 1;
+ foreach (int QQ; 0 .. baditer)
+ {
+ e = d;
+ fe = fd;
+
+ T w;
+ if ((a>0 && b<0) || (a<0 && b>0))
+ w = 0;
+ else
+ {
+ T usea = a;
+ T useb = b;
+ if (a == 0)
+ usea = copysign(T(0), b);
+ else if (b == 0)
+ useb = copysign(T(0), a);
+ w = ieeeMean(usea, useb);
+ }
+ bracket(w);
+ }
+ ++baditer;
+ }
+ return Tuple!(T, T, R, R)(a, b, fa, fb);
+}
+
+///ditto
+Tuple!(T, T, R, R) findRoot(T, R, DF)(scope DF f, in T ax, in T bx, in R fax, in R fbx)
+{
+ return findRoot(f, ax, bx, fax, fbx, (T a, T b) => false);
+}
+
+///ditto
+T findRoot(T, R)(scope R delegate(T) f, in T a, in T b,
+ scope bool delegate(T lo, T hi) tolerance = (T a, T b) => false)
+{
+ return findRoot!(T, R delegate(T), bool delegate(T lo, T hi))(f, a, b, tolerance);
+}
+
+@safe nothrow unittest
+{
+ int numProblems = 0;
+ int numCalls;
+
+ void testFindRoot(real delegate(real) @nogc @safe nothrow pure f , real x1, real x2) @nogc @safe nothrow pure
+ {
+ //numCalls=0;
+ //++numProblems;
+ assert(!x1.isNaN() && !x2.isNaN());
+ assert(signbit(x1) != signbit(x2));
+ auto result = findRoot(f, x1, x2, f(x1), f(x2),
+ (real lo, real hi) { return false; });
+
+ auto flo = f(result[0]);
+ auto fhi = f(result[1]);
+ if (flo != 0)
+ {
+ assert(oppositeSigns(flo, fhi));
+ }
+ }
+
+ // Test functions
+ real cubicfn(real x) @nogc @safe nothrow pure
+ {
+ //++numCalls;
+ if (x>float.max)
+ x = float.max;
+ if (x<-double.max)
+ x = -double.max;
+ // This has a single real root at -59.286543284815
+ return 0.386*x*x*x + 23*x*x + 15.7*x + 525.2;
+ }
+ // Test a function with more than one root.
+ real multisine(real x) { ++numCalls; return sin(x); }
+ //testFindRoot( &multisine, 6, 90);
+ //testFindRoot(&cubicfn, -100, 100);
+ //testFindRoot( &cubicfn, -double.max, real.max);
+
+
+/* Tests from the paper:
+ * "On Enclosing Simple Roots of Nonlinear Equations", G. Alefeld, F.A. Potra,
+ * Yixun Shi, Mathematics of Computation 61, pp733-744 (1993).
+ */
+ // Parameters common to many alefeld tests.
+ int n;
+ real ale_a, ale_b;
+
+ int powercalls = 0;
+
+ real power(real x)
+ {
+ ++powercalls;
+ ++numCalls;
+ return pow(x, n) + double.min_normal;
+ }
+ int [] power_nvals = [3, 5, 7, 9, 19, 25];
+ // Alefeld paper states that pow(x,n) is a very poor case, where bisection
+ // outperforms his method, and gives total numcalls =
+ // 921 for bisection (2.4 calls per bit), 1830 for Alefeld (4.76/bit),
+ // 2624 for brent (6.8/bit)
+ // ... but that is for double, not real80.
+ // This poor performance seems mainly due to catastrophic cancellation,
+ // which is avoided here by the use of ieeeMean().
+ // I get: 231 (0.48/bit).
+ // IE this is 10X faster in Alefeld's worst case
+ numProblems=0;
+ foreach (k; power_nvals)
+ {
+ n = k;
+ //testFindRoot(&power, -1, 10);
+ }
+
+ int powerProblems = numProblems;
+
+ // Tests from Alefeld paper
+
+ int [9] alefeldSums;
+ real alefeld0(real x)
+ {
+ ++alefeldSums[0];
+ ++numCalls;
+ real q = sin(x) - x/2;
+ for (int i=1; i<20; ++i)
+ q+=(2*i-5.0)*(2*i-5.0)/((x-i*i)*(x-i*i)*(x-i*i));
+ return q;
+ }
+ real alefeld1(real x)
+ {
+ ++numCalls;
+ ++alefeldSums[1];
+ return ale_a*x + exp(ale_b * x);
+ }
+ real alefeld2(real x)
+ {
+ ++numCalls;
+ ++alefeldSums[2];
+ return pow(x, n) - ale_a;
+ }
+ real alefeld3(real x)
+ {
+ ++numCalls;
+ ++alefeldSums[3];
+ return (1.0 +pow(1.0L-n, 2))*x - pow(1.0L-n*x, 2);
+ }
+ real alefeld4(real x)
+ {
+ ++numCalls;
+ ++alefeldSums[4];
+ return x*x - pow(1-x, n);
+ }
+ real alefeld5(real x)
+ {
+ ++numCalls;
+ ++alefeldSums[5];
+ return (1+pow(1.0L-n, 4))*x - pow(1.0L-n*x, 4);
+ }
+ real alefeld6(real x)
+ {
+ ++numCalls;
+ ++alefeldSums[6];
+ return exp(-n*x)*(x-1.01L) + pow(x, n);
+ }
+ real alefeld7(real x)
+ {
+ ++numCalls;
+ ++alefeldSums[7];
+ return (n*x-1)/((n-1)*x);
+ }
+
+ numProblems=0;
+ //testFindRoot(&alefeld0, PI_2, PI);
+ for (n=1; n <= 10; ++n)
+ {
+ //testFindRoot(&alefeld0, n*n+1e-9L, (n+1)*(n+1)-1e-9L);
+ }
+ ale_a = -40; ale_b = -1;
+ //testFindRoot(&alefeld1, -9, 31);
+ ale_a = -100; ale_b = -2;
+ //testFindRoot(&alefeld1, -9, 31);
+ ale_a = -200; ale_b = -3;
+ //testFindRoot(&alefeld1, -9, 31);
+ int [] nvals_3 = [1, 2, 5, 10, 15, 20];
+ int [] nvals_5 = [1, 2, 4, 5, 8, 15, 20];
+ int [] nvals_6 = [1, 5, 10, 15, 20];
+ int [] nvals_7 = [2, 5, 15, 20];
+
+ for (int i=4; i<12; i+=2)
+ {
+ n = i;
+ ale_a = 0.2;
+ //testFindRoot(&alefeld2, 0, 5);
+ ale_a=1;
+ //testFindRoot(&alefeld2, 0.95, 4.05);
+ //testFindRoot(&alefeld2, 0, 1.5);
+ }
+ foreach (i; nvals_3)
+ {
+ n=i;
+ //testFindRoot(&alefeld3, 0, 1);
+ }
+ foreach (i; nvals_3)
+ {
+ n=i;
+ //testFindRoot(&alefeld4, 0, 1);
+ }
+ foreach (i; nvals_5)
+ {
+ n=i;
+ //testFindRoot(&alefeld5, 0, 1);
+ }
+ foreach (i; nvals_6)
+ {
+ n=i;
+ //testFindRoot(&alefeld6, 0, 1);
+ }
+ foreach (i; nvals_7)
+ {
+ n=i;
+ //testFindRoot(&alefeld7, 0.01L, 1);
+ }
+ real worstcase(real x)
+ {
+ ++numCalls;
+ return x<0.3*real.max? -0.999e-3 : 1.0;
+ }
+ //testFindRoot(&worstcase, -real.max, real.max);
+
+ // just check that the double + float cases compile
+ //findRoot((double x){ return 0.0; }, -double.max, double.max);
+ //findRoot((float x){ return 0.0f; }, -float.max, float.max);
+
+/*
+ int grandtotal=0;
+ foreach (calls; alefeldSums)
+ {
+ grandtotal+=calls;
+ }
+ grandtotal-=2*numProblems;
+ printf("\nALEFELD TOTAL = %d avg = %f (alefeld avg=19.3 for double)\n",
+ grandtotal, (1.0*grandtotal)/numProblems);
+ powercalls -= 2*powerProblems;
+ printf("POWER TOTAL = %d avg = %f ", powercalls,
+ (1.0*powercalls)/powerProblems);
+*/
+ //Issue 14231
+ auto xp = findRoot((float x) => x, 0f, 1f);
+ auto xn = findRoot((float x) => x, -1f, -0f);
+}
+
+//regression control
+@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
+{
+ 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 $(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
+distance is sought).
+ */
+CommonType!(ElementType!(Range1), ElementType!(Range2))
+euclideanDistance(Range1, Range2)(Range1 a, Range2 b)
+if (isInputRange!(Range1) && isInputRange!(Range2))
+{
+ enum bool haveLen = hasLength!(Range1) && hasLength!(Range2);
+ static if (haveLen) assert(a.length == b.length);
+ Unqual!(typeof(return)) result = 0;
+ for (; !a.empty; a.popFront(), b.popFront())
+ {
+ immutable t = a.front - b.front;
+ result += t * t;
+ }
+ 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))
+{
+ limit *= limit;
+ enum bool haveLen = hasLength!(Range1) && hasLength!(Range2);
+ static if (haveLen) assert(a.length == b.length);
+ Unqual!(typeof(return)) result = 0;
+ for (; ; a.popFront(), b.popFront())
+ {
+ if (a.empty)
+ {
+ static if (!haveLen) assert(b.empty);
+ break;
+ }
+ immutable t = a.front - b.front;
+ result += t * t;
+ if (result >= limit) break;
+ }
+ return sqrt(result);
+}
+
+@safe unittest
+{
+ import std.meta : AliasSeq;
+ foreach (T; AliasSeq!(double, const double, immutable double))
+ {
+ T[] a = [ 1.0, 2.0, ];
+ T[] b = [ 4.0, 6.0, ];
+ assert(euclideanDistance(a, b) == 5);
+ assert(euclideanDistance(a, b, 5) == 5);
+ assert(euclideanDistance(a, b, 4) == 5);
+ assert(euclideanDistance(a, b, 2) == 3);
+ }
+}
+
+/**
+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)))
+{
+ enum bool haveLen = hasLength!(Range1) && hasLength!(Range2);
+ 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) assert(b.empty);
+ return result;
+}
+
+/// Ditto
+CommonType!(F1, F2)
+dotProduct(F1, F2)(in F1[] avector, in F2[] bvector)
+{
+ immutable n = avector.length;
+ assert(n == bvector.length);
+ auto avec = avector.ptr, bvec = bvector.ptr;
+ Unqual!(typeof(return)) sum0 = 0, sum1 = 0;
+
+ const all_endp = avec + n;
+ const smallblock_endp = avec + (n & ~3);
+ const bigblock_endp = avec + (n & ~15);
+
+ for (; avec != bigblock_endp; avec += 16, bvec += 16)
+ {
+ sum0 += avec[0] * bvec[0];
+ sum1 += avec[1] * bvec[1];
+ sum0 += avec[2] * bvec[2];
+ sum1 += avec[3] * bvec[3];
+ sum0 += avec[4] * bvec[4];
+ sum1 += avec[5] * bvec[5];
+ sum0 += avec[6] * bvec[6];
+ sum1 += avec[7] * bvec[7];
+ sum0 += avec[8] * bvec[8];
+ sum1 += avec[9] * bvec[9];
+ sum0 += avec[10] * bvec[10];
+ sum1 += avec[11] * bvec[11];
+ sum0 += avec[12] * bvec[12];
+ sum1 += avec[13] * bvec[13];
+ sum0 += avec[14] * bvec[14];
+ sum1 += avec[15] * bvec[15];
+ }
+
+ for (; avec != smallblock_endp; avec += 4, bvec += 4)
+ {
+ sum0 += avec[0] * bvec[0];
+ sum1 += avec[1] * bvec[1];
+ sum0 += avec[2] * bvec[2];
+ sum1 += avec[3] * bvec[3];
+ }
+
+ sum0 += sum1;
+
+ /* Do trailing portion in naive loop. */
+ while (avec != all_endp)
+ {
+ sum0 += *avec * *bvec;
+ ++avec;
+ ++bvec;
+ }
+
+ return sum0;
+}
+
+@system unittest
+{
+ // @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, ];
+ assert(dotProduct(a, b) == 16);
+ assert(dotProduct([1, 3, -5], [4, -2, -1]) == 3);
+ }
+
+ // Make sure the unrolled loop codepath gets tested.
+ static const x =
+ [1.0, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18];
+ static const y =
+ [2.0, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19];
+ assertCTFEable!({ assert(dotProduct(x, y) == 2280); });
+}
+
+/**
+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))
+{
+ enum bool haveLen = hasLength!(Range1) && hasLength!(Range2);
+ static if (haveLen) assert(a.length == b.length);
+ Unqual!(typeof(return)) norma = 0, normb = 0, dotprod = 0;
+ for (; !a.empty; a.popFront(), b.popFront())
+ {
+ immutable t1 = a.front, t2 = b.front;
+ norma += t1 * t1;
+ normb += t2 * t2;
+ dotprod += t1 * t2;
+ }
+ static if (!haveLen) assert(b.empty);
+ if (norma == 0 || normb == 0) return 0;
+ return dotprod / sqrt(norma * normb);
+}
+
+@safe unittest
+{
+ import std.meta : AliasSeq;
+ foreach (T; AliasSeq!(double, const double, immutable double))
+ {
+ T[] a = [ 1.0, 2.0, ];
+ T[] b = [ 4.0, 3.0, ];
+ assert(approxEqual(
+ cosineSimilarity(a, b), 10.0 / sqrt(5.0 * 25),
+ 0.01));
+ }
+}
+
+/**
+Normalizes values in $(D range) by multiplying each element with a
+number chosen such that values sum up to $(D sum). If elements in $(D
+range) sum to zero, assigns $(D sum / range.length) to
+all. Normalization makes sense only if all elements in $(D range) are
+positive. $(D normalize) assumes that is the case without checking it.
+
+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))
+{
+ ElementType!(R) s = 0;
+ // Step 1: Compute sum and length of the range
+ static if (hasLength!(R))
+ {
+ const length = range.length;
+ foreach (e; range)
+ {
+ s += e;
+ }
+ }
+ else
+ {
+ uint length = 0;
+ foreach (e; range)
+ {
+ s += e;
+ ++length;
+ }
+ }
+ // Step 2: perform normalization
+ if (s == 0)
+ {
+ if (length)
+ {
+ immutable f = sum / range.length;
+ foreach (ref e; range) e = f;
+ }
+ return false;
+ }
+ // The path most traveled
+ assert(s >= 0);
+ immutable f = sum / s;
+ foreach (ref e; range)
+ e *= f;
+ return true;
+}
+
+///
+@safe unittest
+{
+ double[] a = [];
+ assert(!normalize(a));
+ a = [ 1.0, 3.0 ];
+ assert(normalize(a));
+ assert(a == [ 0.25, 0.75 ]);
+ a = [ 0.0, 0.0 ];
+ assert(!normalize(a));
+ assert(a == [ 0.5, 0.5 ]);
+}
+
+/**
+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))
+{
+ long exp = 0;
+ Unqual!(typeof(return)) x = 1;
+ foreach (e; r)
+ {
+ if (e < 0)
+ return typeof(return).nan;
+ int lexp = void;
+ x *= frexp(e, lexp);
+ exp += lexp;
+ if (x < 0.5)
+ {
+ x *= 2;
+ exp--;
+ }
+ }
+ return exp + log2(x);
+}
+
+///
+@safe unittest
+{
+ import std.math : isNaN;
+
+ assert(sumOfLog2s(new double[0]) == 0);
+ assert(sumOfLog2s([0.0L]) == -real.infinity);
+ assert(sumOfLog2s([-0.0L]) == -real.infinity);
+ assert(sumOfLog2s([2.0L]) == 1);
+ assert(sumOfLog2s([-2.0L]).isNaN());
+ assert(sumOfLog2s([real.nan]).isNaN());
+ assert(sumOfLog2s([-real.nan]).isNaN());
+ assert(sumOfLog2s([real.infinity]) == real.infinity);
+ assert(sumOfLog2s([-real.infinity]).isNaN());
+ assert(sumOfLog2s([ 0.25, 0.25, 0.25, 0.125 ]) == -9);
+}
+
+/**
+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)
+{
+ Unqual!(typeof(return)) result = 0.0;
+ for (;!r.empty; r.popFront)
+ {
+ if (!r.front) continue;
+ result -= r.front * log2(r.front);
+ }
+ return result;
+}
+
+/// Ditto
+ElementType!Range entropy(Range, F)(Range r, F max)
+if (isInputRange!Range &&
+ !is(CommonType!(ElementType!Range, F) == void))
+{
+ Unqual!(typeof(return)) result = 0.0;
+ for (;!r.empty; r.popFront)
+ {
+ if (!r.front) continue;
+ result -= r.front * log2(r.front);
+ if (result >= max) break;
+ }
+ return result;
+}
+
+@safe unittest
+{
+ import std.meta : AliasSeq;
+ foreach (T; AliasSeq!(double, const double, immutable double))
+ {
+ T[] p = [ 0.0, 0, 0, 1 ];
+ assert(entropy(p) == 0);
+ p = [ 0.25, 0.25, 0.25, 0.25 ];
+ assert(entropy(p) == 2);
+ assert(entropy(p, 1) == 1);
+ }
+}
+
+/**
+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,
+but this is not required or checked by $(D
+kullbackLeiblerDivergence). If any element $(D bi) is zero and the
+corresponding element $(D ai) nonzero, returns infinity. (Otherwise,
+if $(D ai == 0 && bi == 0), the term $(D ai * log(ai / bi)) is
+considered zero.) If the inputs are normalized, the result is
+positive.
+ */
+CommonType!(ElementType!Range1, ElementType!Range2)
+kullbackLeiblerDivergence(Range1, Range2)(Range1 a, Range2 b)
+if (isInputRange!(Range1) && isInputRange!(Range2))
+{
+ enum bool haveLen = hasLength!(Range1) && hasLength!(Range2);
+ static if (haveLen) assert(a.length == b.length);
+ Unqual!(typeof(return)) result = 0;
+ for (; !a.empty; a.popFront(), b.popFront())
+ {
+ immutable t1 = a.front;
+ if (t1 == 0) continue;
+ immutable t2 = b.front;
+ if (t2 == 0) return result.infinity;
+ assert(t1 > 0 && t2 > 0);
+ result += t1 * log2(t1 / t2);
+ }
+ static if (!haveLen) assert(b.empty);
+ return result;
+}
+
+///
+@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 ];
+ assert(kullbackLeiblerDivergence(p1, p1) == 0);
+ assert(kullbackLeiblerDivergence(p, p1) == 2);
+ assert(kullbackLeiblerDivergence(p1, p) == double.infinity);
+ double[] p2 = [ 0.2, 0.2, 0.2, 0.4 ];
+ assert(approxEqual(kullbackLeiblerDivergence(p1, p2), 0.0719281));
+ assert(approxEqual(kullbackLeiblerDivergence(p2, p1), 0.0780719));
+}
+
+/**
+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
+normalized probability distributions, but this is not required or
+checked by $(D jensenShannonDivergence). If the inputs are normalized,
+the result is bounded within $(D [0, 1]). The three-parameter version
+stops evaluations as soon as the intermediate result is greater than
+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)))
+{
+ enum bool haveLen = hasLength!(Range1) && hasLength!(Range2);
+ static if (haveLen) assert(a.length == b.length);
+ Unqual!(typeof(return)) result = 0;
+ for (; !a.empty; a.popFront(), b.popFront())
+ {
+ immutable t1 = a.front;
+ immutable t2 = b.front;
+ immutable avg = (t1 + t2) / 2;
+ if (t1 != 0)
+ {
+ result += t1 * log2(t1 / avg);
+ }
+ if (t2 != 0)
+ {
+ result += t2 * log2(t2 / avg);
+ }
+ }
+ 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))
+{
+ enum bool haveLen = hasLength!(Range1) && hasLength!(Range2);
+ static if (haveLen) assert(a.length == b.length);
+ Unqual!(typeof(return)) result = 0;
+ limit *= 2;
+ for (; !a.empty; a.popFront(), b.popFront())
+ {
+ immutable t1 = a.front;
+ immutable t2 = b.front;
+ immutable avg = (t1 + t2) / 2;
+ if (t1 != 0)
+ {
+ result += t1 * log2(t1 / avg);
+ }
+ if (t2 != 0)
+ {
+ result += t2 * log2(t2 / avg);
+ }
+ if (result >= limit) break;
+ }
+ static if (!haveLen) assert(b.empty);
+ return result / 2;
+}
+
+///
+@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 ];
+ assert(jensenShannonDivergence(p1, p1) == 0);
+ assert(approxEqual(jensenShannonDivergence(p1, p), 0.548795));
+ double[] p2 = [ 0.2, 0.2, 0.2, 0.4 ];
+ assert(approxEqual(jensenShannonDivergence(p1, p2), 0.0186218));
+ assert(approxEqual(jensenShannonDivergence(p2, p1), 0.0186218));
+ assert(approxEqual(jensenShannonDivergence(p2, p1, 0.005), 0.00602366));
+}
+
+/**
+The so-called "all-lengths gap-weighted string kernel" computes a
+similarity measure between $(D s) and $(D t) based on all of their
+common subsequences of all lengths. Gapped subsequences are also
+included.
+
+To understand what $(D gapWeightedSimilarity(s, t, lambda)) computes,
+consider first the case $(D lambda = 1) and the strings $(D s =
+["Hello", "brave", "new", "world"]) and $(D t = ["Hello", "new",
+"world"]). In that case, $(D gapWeightedSimilarity) counts the
+following matches:
+
+$(OL $(LI three matches of length 1, namely $(D "Hello"), $(D "new"),
+and $(D "world");) $(LI three matches of length 2, namely ($(D
+"Hello", "new")), ($(D "Hello", "world")), and ($(D "new", "world"));)
+$(LI one match of length 3, namely ($(D "Hello", "new", "world")).))
+
+The call $(D gapWeightedSimilarity(s, t, 1)) simply counts all of
+these matches and adds them up, returning 7.
+
+----
+string[] s = ["Hello", "brave", "new", "world"];
+string[] t = ["Hello", "new", "world"];
+assert(gapWeightedSimilarity(s, t, 1) == 7);
+----
+
+Note how the gaps in matching are simply ignored, for example ($(D
+"Hello", "new")) is deemed as good a match as ($(D "new",
+"world")). This may be too permissive for some applications. To
+eliminate gapped matches entirely, use $(D lambda = 0):
+
+----
+string[] s = ["Hello", "brave", "new", "world"];
+string[] t = ["Hello", "new", "world"];
+assert(gapWeightedSimilarity(s, t, 0) == 4);
+----
+
+The call above eliminated the gapped matches ($(D "Hello", "new")),
+($(D "Hello", "world")), and ($(D "Hello", "new", "world")) from the
+tally. That leaves only 4 matches.
+
+The most interesting case is when gapped matches still participate in
+the result, but not as strongly as ungapped matches. The result will
+be a smooth, fine-grained similarity measure between the input
+strings. This is where values of $(D lambda) between 0 and 1 enter
+into play: gapped matches are $(I exponentially penalized with the
+number of gaps) with base $(D lambda). This means that an ungapped
+match adds 1 to the return value; a match with one gap in either
+string adds $(D lambda) to the return value; ...; a match with a total
+of $(D n) gaps in both strings adds $(D pow(lambda, n)) to the return
+value. In the example above, we have 4 matches without gaps, 2 matches
+with one gap, and 1 match with three gaps. The latter match is ($(D
+"Hello", "world")), which has two gaps in the first string and one gap
+in the second string, totaling to three gaps. Summing these up we get
+$(D 4 + 2 * lambda + pow(lambda, 3)).
+
+----
+string[] s = ["Hello", "brave", "new", "world"];
+string[] t = ["Hello", "new", "world"];
+assert(gapWeightedSimilarity(s, t, 0.5) == 4 + 0.5 * 2 + 0.125);
+----
+
+$(D gapWeightedSimilarity) is useful wherever a smooth similarity
+measure between sequences allowing for approximate matches is
+needed. The examples above are given with words, but any sequences
+with elements comparable for equality are allowed, e.g. characters or
+numbers. $(D gapWeightedSimilarity) uses a highly optimized dynamic
+programming implementation that needs $(D 16 * min(s.length,
+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))
+{
+ import core.exception : onOutOfMemoryError;
+ import core.stdc.stdlib : malloc, free;
+ import std.algorithm.mutation : swap;
+ import std.functional : binaryFun;
+
+ if (s.length < t.length) return gapWeightedSimilarity(t, s, lambda);
+ if (!t.length) return 0;
+
+ 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;
+ dpvi1[0] = 0;
+ immutable lambda2 = lambda * lambda;
+
+ F result = 0;
+ foreach (i; 0 .. s.length)
+ {
+ const si = s[i];
+ for (size_t j = 0;;)
+ {
+ F dpsij = void;
+ if (binaryFun!(comp)(si, t[j]))
+ {
+ dpsij = 1 + dpvi[j];
+ result += dpsij;
+ }
+ else
+ {
+ dpsij = 0;
+ }
+ immutable j1 = j + 1;
+ if (j1 == t.length) break;
+ dpvi1[j1] = dpsij + lambda * (dpvi1[j] + dpvi[j1]) -
+ lambda2 * dpvi[j];
+ j = j1;
+ }
+ swap(dpvi, dpvi1);
+ }
+ return result;
+}
+
+@system unittest
+{
+ string[] s = ["Hello", "brave", "new", "world"];
+ string[] t = ["Hello", "new", "world"];
+ assert(gapWeightedSimilarity(s, t, 1) == 7);
+ assert(gapWeightedSimilarity(s, t, 0) == 4);
+ assert(gapWeightedSimilarity(s, t, 0.5) == 4 + 2 * 0.5 + 0.125);
+}
+
+/**
+The similarity per $(D gapWeightedSimilarity) has an issue in that it
+grows with the lengths of the two strings, even though the strings are
+not actually very similar. For example, the range $(D ["Hello",
+"world"]) is increasingly similar with the range $(D ["Hello",
+"world", "world", "world",...]) as more instances of $(D "world") are
+appended. To prevent that, $(D gapWeightedSimilarityNormalized)
+computes a normalized version of the similarity that is computed as
+$(D gapWeightedSimilarity(s, t, lambda) /
+sqrt(gapWeightedSimilarity(s, t, lambda) * gapWeightedSimilarity(s, t,
+lambda))). The function $(D gapWeightedSimilarityNormalized) (a
+so-called normalized kernel) is bounded in $(D [0, 1]), reaches $(D 0)
+only for ranges that don't match in any position, and $(D 1) only for
+identical ranges.
+
+The optional parameters $(D sSelfSim) and $(D tSelfSim) are meant for
+avoiding duplicate computation. Many applications may have already
+computed $(D gapWeightedSimilarity(s, s, lambda)) and/or $(D
+gapWeightedSimilarity(t, t, lambda)). In that case, they can be passed
+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))
+{
+ static bool uncomputed(F n)
+ {
+ static if (isFloatingPoint!(F))
+ return isNaN(n);
+ else
+ return n == n.init;
+ }
+ if (uncomputed(sSelfSim))
+ sSelfSim = gapWeightedSimilarity!(comp)(s, s, lambda);
+ if (sSelfSim == 0) return 0;
+ if (uncomputed(tSelfSim))
+ tSelfSim = gapWeightedSimilarity!(comp)(t, t, lambda);
+ if (tSelfSim == 0) return 0;
+
+ return gapWeightedSimilarity!(comp)(s, t, lambda) /
+ sqrt(cast(typeof(return)) sSelfSim * tSelfSim);
+}
+
+///
+@system unittest
+{
+ import std.math : approxEqual, sqrt;
+
+ string[] s = ["Hello", "brave", "new", "world"];
+ string[] t = ["Hello", "new", "world"];
+ assert(gapWeightedSimilarity(s, s, 1) == 15);
+ assert(gapWeightedSimilarity(t, t, 1) == 7);
+ assert(gapWeightedSimilarity(s, t, 1) == 7);
+ assert(approxEqual(gapWeightedSimilarityNormalized(s, t, 1),
+ 7.0 / sqrt(15.0 * 7), 0.01));
+}
+
+/**
+Similar to $(D gapWeightedSimilarity), just works in an incremental
+manner by first revealing the matches of length 1, then gapped matches
+of length 2, and so on. The memory requirement is $(BIGOH s.length *
+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
+$(HTTP jmlr.csail.mit.edu/papers/volume6/rousu05a/rousu05a.pdf,
+"Efficient 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))
+{
+ import core.stdc.stdlib : malloc, realloc, alloca, free;
+
+private:
+ Range s, t;
+ F currentValue = 0;
+ F* kl;
+ size_t gram = void;
+ F lambda = void, lambda2 = void;
+
+public:
+/**
+Constructs an object given two ranges $(D s) and $(D t) and a penalty
+$(D lambda). Constructor completes in $(BIGOH s.length * t.length)
+time and computes all matches of length 1.
+ */
+ this(Range s, Range t, F lambda)
+ {
+ import core.exception : onOutOfMemoryError;
+
+ assert(lambda > 0);
+ this.gram = 0;
+ this.lambda = lambda;
+ this.lambda2 = lambda * lambda; // for efficiency only
+
+ size_t iMin = size_t.max, jMin = size_t.max,
+ iMax = 0, jMax = 0;
+ /* initialize */
+ Tuple!(size_t, size_t) * k0;
+ size_t k0len;
+ scope(exit) free(k0);
+ currentValue = 0;
+ foreach (i, si; s)
+ {
+ foreach (j; 0 .. t.length)
+ {
+ if (si != t[j]) continue;
+ k0 = cast(typeof(k0)) realloc(k0, ++k0len * (*k0).sizeof);
+ with (k0[k0len - 1])
+ {
+ field[0] = i;
+ field[1] = j;
+ }
+ // Maintain the minimum and maximum i and j
+ if (iMin > i) iMin = i;
+ if (iMax < i) iMax = i;
+ if (jMin > j) jMin = j;
+ if (jMax < j) jMax = j;
+ }
+ }
+
+ if (iMin > iMax) return;
+ assert(k0len);
+
+ currentValue = k0len;
+ // Chop strings down to the useful sizes
+ s = s[iMin .. iMax + 1];
+ t = t[jMin .. jMax + 1];
+ this.s = s;
+ this.t = t;
+
+ kl = cast(F*) malloc(s.length * t.length * F.sizeof);
+ if (!kl)
+ onOutOfMemoryError();
+
+ kl[0 .. s.length * t.length] = 0;
+ foreach (pos; 0 .. k0len)
+ {
+ with (k0[pos])
+ {
+ kl[(field[0] - iMin) * t.length + field[1] -jMin] = lambda2;
+ }
+ }
+ }
+
+ /**
+ Returns: $(D this).
+ */
+ ref GapWeightedSimilarityIncremental opSlice()
+ {
+ return this;
+ }
+
+ /**
+ Computes the match of the popFront length. Completes in $(BIGOH s.length *
+ t.length) time.
+ */
+ void popFront()
+ {
+ 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
+ // similarity at the gram level is 0 as well.
+ if (empty) return;
+
+ // Now attempt to match gapped substrings of length `gram'
+ ++gram;
+ currentValue = 0;
+
+ auto Si = cast(F*) alloca(t.length * F.sizeof);
+ Si[0 .. t.length] = 0;
+ foreach (i; 0 .. s.length)
+ {
+ const si = s[i];
+ F Sij_1 = 0;
+ F Si_1j_1 = 0;
+ auto kli = kl + i * t.length;
+ for (size_t j = 0;;)
+ {
+ const klij = kli[j];
+ const Si_1j = Si[j];
+ const tmp = klij + lambda * (Si_1j + Sij_1) - lambda2 * Si_1j_1;
+ // now update kl and currentValue
+ if (si == t[j])
+ currentValue += kli[j] = lambda2 * Si_1j_1;
+ else
+ kli[j] = 0;
+ // commit to Si
+ Si[j] = tmp;
+ if (++j == t.length) break;
+ // get ready for the popFront step; virtually increment j,
+ // so essentially stuffj_1 <-- stuffj
+ Si_1j_1 = Si_1j;
+ Sij_1 = tmp;
+ }
+ }
+ currentValue /= pow(lambda, 2 * (gram + 1));
+
+ version (none)
+ {
+ Si_1[0 .. t.length] = 0;
+ kl[0 .. min(t.length, maxPerimeter + 1)] = 0;
+ foreach (i; 1 .. min(s.length, maxPerimeter + 1))
+ {
+ auto kli = kl + i * t.length;
+ assert(s.length > i);
+ const si = s[i];
+ auto kl_1i_1 = kl_1 + (i - 1) * t.length;
+ kli[0] = 0;
+ F lastS = 0;
+ foreach (j; 1 .. min(maxPerimeter - i + 1, t.length))
+ {
+ immutable j_1 = j - 1;
+ immutable tmp = kl_1i_1[j_1]
+ + lambda * (Si_1[j] + lastS)
+ - lambda2 * Si_1[j_1];
+ kl_1i_1[j_1] = float.nan;
+ Si_1[j_1] = lastS;
+ lastS = tmp;
+ if (si == t[j])
+ {
+ currentValue += kli[j] = lambda2 * lastS;
+ }
+ else
+ {
+ kli[j] = 0;
+ }
+ }
+ Si_1[t.length - 1] = lastS;
+ }
+ currentValue /= pow(lambda, 2 * (gram + 1));
+ // get ready for the popFront computation
+ swap(kl, kl_1);
+ }
+ }
+
+ /**
+ Returns: The gapped similarity at the current match length (initially
+ 1, grows with each call to $(D popFront)).
+ */
+ @property F front() { return currentValue; }
+
+ /**
+ Returns: Whether there are more matches.
+ */
+ @property bool empty()
+ {
+ if (currentValue) return false;
+ if (kl)
+ {
+ free(kl);
+ kl = null;
+ }
+ return true;
+ }
+}
+
+/**
+Ditto
+ */
+GapWeightedSimilarityIncremental!(R, F) gapWeightedSimilarityIncremental(R, F)
+(R r1, R r2, F penalty)
+{
+ return typeof(return)(r1, r2, penalty);
+}
+
+///
+@system unittest
+{
+ string[] s = ["Hello", "brave", "new", "world"];
+ string[] t = ["Hello", "new", "world"];
+ auto simIter = gapWeightedSimilarityIncremental(s, t, 1.0);
+ assert(simIter.front == 3); // three 1-length matches
+ simIter.popFront();
+ assert(simIter.front == 3); // three 2-length matches
+ simIter.popFront();
+ assert(simIter.front == 1); // one 3-length match
+ simIter.popFront();
+ assert(simIter.empty); // no more match
+}
+
+@system unittest
+{
+ import std.conv : text;
+ string[] s = ["Hello", "brave", "new", "world"];
+ string[] t = ["Hello", "new", "world"];
+ auto simIter = gapWeightedSimilarityIncremental(s, t, 1.0);
+ //foreach (e; simIter) writeln(e);
+ assert(simIter.front == 3); // three 1-length matches
+ simIter.popFront();
+ assert(simIter.front == 3, text(simIter.front)); // three 2-length matches
+ simIter.popFront();
+ assert(simIter.front == 1); // one 3-length matches
+ simIter.popFront();
+ assert(simIter.empty); // no more match
+
+ s = ["Hello"];
+ t = ["bye"];
+ simIter = gapWeightedSimilarityIncremental(s, t, 0.5);
+ assert(simIter.empty);
+
+ s = ["Hello"];
+ t = ["Hello"];
+ simIter = gapWeightedSimilarityIncremental(s, t, 0.5);
+ assert(simIter.front == 1); // one match
+ simIter.popFront();
+ assert(simIter.empty);
+
+ s = ["Hello", "world"];
+ t = ["Hello"];
+ simIter = gapWeightedSimilarityIncremental(s, t, 0.5);
+ assert(simIter.front == 1); // one match
+ simIter.popFront();
+ assert(simIter.empty);
+
+ s = ["Hello", "world"];
+ t = ["Hello", "yah", "world"];
+ simIter = gapWeightedSimilarityIncremental(s, t, 0.5);
+ assert(simIter.front == 2); // two 1-gram matches
+ simIter.popFront();
+ assert(simIter.front == 0.5, text(simIter.front)); // one 2-gram match, 1 gap
+}
+
+@system unittest
+{
+ GapWeightedSimilarityIncremental!(string[]) sim =
+ GapWeightedSimilarityIncremental!(string[])(
+ ["nyuk", "I", "have", "no", "chocolate", "giba"],
+ ["wyda", "I", "have", "I", "have", "have", "I", "have", "hehe"],
+ 0.5);
+ double[] witness = [ 7.0, 4.03125, 0, 0 ];
+ foreach (e; sim)
+ {
+ //writeln(e);
+ assert(e == witness.front);
+ witness.popFront();
+ }
+ witness = [ 3.0, 1.3125, 0.25 ];
+ sim = GapWeightedSimilarityIncremental!(string[])(
+ ["I", "have", "no", "chocolate"],
+ ["I", "have", "some", "chocolate"],
+ 0.5);
+ foreach (e; sim)
+ {
+ //writeln(e);
+ assert(e == witness.front);
+ witness.popFront();
+ }
+ assert(witness.empty);
+}
+
+/**
+Computes the greatest common divisor of $(D a) and $(D b) by using
+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 version (DigitalMars)
+ {
+ static if (T.min < 0)
+ {
+ assert(a >= 0 && b >= 0);
+ }
+ while (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;
+ }
+}
+
+///
+@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
+// size 2 ^^ 22.
+private alias lookup_t = float;
+
+/**A class for performing fast Fourier transforms of power of two sizes.
+ * This class encapsulates a large amount of state that is reusable when
+ * performing multiple FFTs of sizes smaller than or equal to that specified
+ * in the constructor. This results in substantial speedups when performing
+ * multiple FFTs with a known maximum size. However,
+ * a free function API is provided for convenience if you need to perform a
+ * one-off FFT.
+ *
+ * References:
+ * $(HTTP en.wikipedia.org/wiki/Cooley%E2%80%93Tukey_FFT_algorithm)
+ */
+final class Fft
+{
+ import core.bitop : bsf;
+ import std.algorithm.iteration : map;
+ import std.array : uninitializedArray;
+
+private:
+ immutable lookup_t[][] negSinLookup;
+
+ void enforceSize(R)(R range) const
+ {
+ import std.conv : text;
+ assert(range.length <= size, text(
+ "FFT size mismatch. Expected ", size, ", got ", range.length));
+ }
+
+ void fftImpl(Ret, R)(Stride!R range, Ret buf) const
+ in
+ {
+ assert(range.length >= 4);
+ assert(isPowerOf2(range.length));
+ }
+ body
+ {
+ auto recurseRange = range;
+ recurseRange.doubleSteps();
+
+ if (buf.length > 4)
+ {
+ fftImpl(recurseRange, buf[0..$ / 2]);
+ recurseRange.popHalf();
+ fftImpl(recurseRange, buf[$ / 2..$]);
+ }
+ else
+ {
+ // Do this here instead of in another recursion to save on
+ // recursion overhead.
+ slowFourier2(recurseRange, buf[0..$ / 2]);
+ recurseRange.popHalf();
+ slowFourier2(recurseRange, buf[$ / 2..$]);
+ }
+
+ butterfly(buf);
+ }
+
+ // This algorithm works by performing the even and odd parts of our FFT
+ // using the "two for the price of one" method mentioned at
+ // http://www.engineeringproductivitytools.com/stuff/T0001/PT10.HTM#Head521
+ // by making the odd terms into the imaginary components of our new FFT,
+ // and then using symmetry to recombine them.
+ void fftImplPureReal(Ret, R)(R range, Ret buf) const
+ in
+ {
+ assert(range.length >= 4);
+ assert(isPowerOf2(range.length));
+ }
+ body
+ {
+ alias E = ElementType!R;
+
+ // 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)
+ {
+ // Then the memory layout of complex numbers provides a dirt
+ // cheap way to convert. This is a common case, so take advantage.
+ auto oddsImag = cast(Complex!E[]) range;
+ }
+ else
+ {
+ // General case: Use a higher order range. We can assume
+ // source.length is even because it has to be a power of 2.
+ static struct OddToImaginary
+ {
+ R source;
+ alias C = Complex!(CommonType!(E, typeof(buf[0].re)));
+
+ @property
+ {
+ C front()
+ {
+ return C(source[0], source[1]);
+ }
+
+ C back()
+ {
+ immutable n = source.length;
+ return C(source[n - 2], source[n - 1]);
+ }
+
+ typeof(this) save()
+ {
+ return typeof(this)(source.save);
+ }
+
+ bool empty()
+ {
+ return source.empty;
+ }
+
+ size_t length()
+ {
+ return source.length / 2;
+ }
+ }
+
+ void popFront()
+ {
+ source.popFront();
+ source.popFront();
+ }
+
+ void popBack()
+ {
+ source.popBack();
+ source.popBack();
+ }
+
+ C opIndex(size_t index)
+ {
+ return C(source[index * 2], source[index * 2 + 1]);
+ }
+
+ typeof(this) opSlice(size_t lower, size_t upper)
+ {
+ return typeof(this)(source[lower * 2 .. upper * 2]);
+ }
+ }
+
+ auto oddsImag = OddToImaginary(range);
+ }
+
+ fft(oddsImag, buf[0..$ / 2]);
+ auto evenFft = buf[0..$ / 2];
+ auto oddFft = buf[$ / 2..$];
+ immutable halfN = evenFft.length;
+ oddFft[0].re = buf[0].im;
+ oddFft[0].im = 0;
+ 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)
+ {
+ immutable bufk = buf[k];
+ immutable bufnk = buf[buf.length / 2 - k];
+ evenFft[k].re = 0.5 * (bufk.re + bufnk.re);
+ evenFft[halfN - k].re = evenFft[k].re;
+ evenFft[k].im = 0.5 * (bufk.im - bufnk.im);
+ evenFft[halfN - k].im = -evenFft[k].im;
+
+ oddFft[k].re = 0.5 * (bufk.im + bufnk.im);
+ oddFft[halfN - k].re = oddFft[k].re;
+ oddFft[k].im = 0.5 * (bufnk.re - bufk.re);
+ oddFft[halfN - k].im = -oddFft[k].im;
+ }
+
+ butterfly(buf);
+ }
+
+ void butterfly(R)(R buf) const
+ in
+ {
+ assert(isPowerOf2(buf.length));
+ }
+ body
+ {
+ immutable n = buf.length;
+ immutable localLookup = negSinLookup[bsf(n)];
+ assert(localLookup.length == n);
+
+ immutable cosMask = n - 1;
+ immutable cosAdd = n / 4 * 3;
+
+ lookup_t negSinFromLookup(size_t index) pure nothrow
+ {
+ return localLookup[index];
+ }
+
+ lookup_t cosFromLookup(size_t index) pure nothrow
+ {
+ // cos is just -sin shifted by PI * 3 / 2.
+ return localLookup[(index + cosAdd) & cosMask];
+ }
+
+ immutable halfLen = n / 2;
+
+ // This loop is unrolled and the two iterations are interleaved
+ // relative to the textbook FFT to increase ILP. This gives roughly 5%
+ // speedups on DMD.
+ for (size_t k = 0; k < halfLen; k += 2)
+ {
+ immutable cosTwiddle1 = cosFromLookup(k);
+ immutable sinTwiddle1 = negSinFromLookup(k);
+ immutable cosTwiddle2 = cosFromLookup(k + 1);
+ immutable sinTwiddle2 = negSinFromLookup(k + 1);
+
+ immutable realLower1 = buf[k].re;
+ immutable imagLower1 = buf[k].im;
+ immutable realLower2 = buf[k + 1].re;
+ immutable imagLower2 = buf[k + 1].im;
+
+ immutable upperIndex1 = k + halfLen;
+ immutable upperIndex2 = upperIndex1 + 1;
+ immutable realUpper1 = buf[upperIndex1].re;
+ immutable imagUpper1 = buf[upperIndex1].im;
+ immutable realUpper2 = buf[upperIndex2].re;
+ immutable imagUpper2 = buf[upperIndex2].im;
+
+ immutable realAdd1 = cosTwiddle1 * realUpper1
+ - sinTwiddle1 * imagUpper1;
+ immutable imagAdd1 = sinTwiddle1 * realUpper1
+ + cosTwiddle1 * imagUpper1;
+ immutable realAdd2 = cosTwiddle2 * realUpper2
+ - sinTwiddle2 * imagUpper2;
+ immutable imagAdd2 = sinTwiddle2 * realUpper2
+ + cosTwiddle2 * imagUpper2;
+
+ buf[k].re += realAdd1;
+ buf[k].im += imagAdd1;
+ buf[k + 1].re += realAdd2;
+ buf[k + 1].im += imagAdd2;
+
+ buf[upperIndex1].re = realLower1 - realAdd1;
+ buf[upperIndex1].im = imagLower1 - imagAdd1;
+ buf[upperIndex2].re = realLower2 - realAdd2;
+ buf[upperIndex2].im = imagLower2 - imagAdd2;
+ }
+ }
+
+ // This constructor is used within this module for allocating the
+ // buffer space elsewhere besides the GC heap. It's definitely **NOT**
+ // part of the public API and definitely **IS** subject to change.
+ //
+ // Also, this is unsafe because the memSpace buffer will be cast
+ // to immutable.
+ public this(lookup_t[] memSpace) // Public b/c of bug 4636.
+ {
+ immutable size = memSpace.length / 2;
+
+ /* Create a lookup table of all negative sine values at a resolution of
+ * size and all smaller power of two resolutions. This may seem
+ * 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)
+ {
+ return;
+ }
+
+ 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];
+
+ auto lastRow = table[$ - 1];
+ lastRow[0] = 0; // -sin(0) == 0.
+ 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.
+
+ if (i == size / 4)
+ lastRow[i] = -1; // -sin(pi / 2) == -1.
+ else if (i == size / 2)
+ lastRow[i] = 0; // -sin(pi) == 0.
+ else if (i == size * 3 / 4)
+ lastRow[i] = 1; // -sin(pi * 3 / 2) == 1
+ else
+ lastRow[i] = -sin(i * 2.0L * PI / size);
+ }
+
+ // Fill in all the other rows with strided versions.
+ foreach (i; 1 .. table.length - 1)
+ {
+ immutable strideLength = size / (2 ^^ i);
+ auto strided = Stride!(lookup_t[])(lastRow, strideLength);
+ table[i] = memSpace[$ - strided.length..$];
+ memSpace = memSpace[0..$ - strided.length];
+
+ size_t copyIndex;
+ foreach (elem; strided)
+ {
+ table[i][copyIndex++] = elem;
+ }
+ }
+
+ negSinLookup = cast(immutable) table;
+ }
+
+public:
+ /**Create an $(D Fft) object for computing fast Fourier transforms of
+ * power of two sizes of $(D size) or smaller. $(D size) must be a
+ * power of two.
+ */
+ this(size_t size)
+ {
+ // Allocate all twiddle factor buffers in one contiguous block so that,
+ // when one is done being used, the next one is next in cache.
+ auto memSpace = uninitializedArray!(lookup_t[])(2 * size);
+ this(memSpace);
+ }
+
+ @property size_t size() const
+ {
+ return (negSinLookup is null) ? 0 : negSinLookup[$ - 1].length;
+ }
+
+ /**Compute the Fourier transform of range using the $(BIGOH N log N)
+ * Cooley-Tukey Algorithm. $(D range) must be a random-access range with
+ * slicing and a length equal to $(D size) as provided at the construction of
+ * this object. The contents of range can be either numeric types,
+ * which will be interpreted as pure real values, or complex types with
+ * properties or members $(D .re) and $(D .im) that can be read.
+ *
+ * Note: Pure real FFTs are automatically detected and the relevant
+ * optimizations are performed.
+ *
+ * Returns: An array of complex numbers representing the transformed data in
+ * the frequency domain.
+ *
+ * Conventions: The exponent is negative and the factor is one,
+ * i.e., output[j] := sum[ exp(-2 PI i j k / N) input[k] ].
+ */
+ Complex!F[] fft(F = double, R)(R range) const
+ if (isFloatingPoint!F && isRandomAccessRange!R)
+ {
+ enforceSize(range);
+ Complex!F[] ret;
+ if (range.length == 0)
+ {
+ return ret;
+ }
+
+ // Don't waste time initializing the memory for ret.
+ ret = uninitializedArray!(Complex!F[])(range.length);
+
+ fft(range, ret);
+ return ret;
+ }
+
+ /**Same as the overload, but allows for the results to be stored in a user-
+ * provided buffer. The buffer must be of the same length as range, must be
+ * a random-access range, must have slicing, and must contain elements that are
+ * complex-like. This means that they must have a .re and a .im member or
+ * 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)
+ {
+ assert(buf.length == range.length);
+ enforceSize(range);
+
+ if (range.length == 0)
+ {
+ return;
+ }
+ else if (range.length == 1)
+ {
+ buf[0] = range[0];
+ return;
+ }
+ else if (range.length == 2)
+ {
+ slowFourier2(range, buf);
+ return;
+ }
+ else
+ {
+ alias E = ElementType!R;
+ static if (is(E : real))
+ {
+ return fftImplPureReal(range, buf);
+ }
+ else
+ {
+ static if (is(R : Stride!R))
+ return fftImpl(range, buf);
+ else
+ return fftImpl(Stride!R(range, 1), buf);
+ }
+ }
+ }
+
+ /**
+ * Computes the inverse Fourier transform of a range. The range must be a
+ * random access range with slicing, have a length equal to the size
+ * provided at construction of this object, and contain elements that are
+ * either of type std.complex.Complex or have essentially
+ * the same compile-time interface.
+ *
+ * Returns: The time-domain signal.
+ *
+ * Conventions: The exponent is positive and the factor is 1/N, i.e.,
+ * output[j] := (1 / N) sum[ exp(+2 PI i j k / N) input[k] ].
+ */
+ Complex!F[] inverseFft(F = double, R)(R range) const
+ if (isRandomAccessRange!R && isComplexLike!(ElementType!R) && isFloatingPoint!F)
+ {
+ enforceSize(range);
+ Complex!F[] ret;
+ if (range.length == 0)
+ {
+ return ret;
+ }
+
+ // Don't waste time initializing the memory for ret.
+ ret = uninitializedArray!(Complex!F[])(range.length);
+
+ inverseFft(range, ret);
+ return ret;
+ }
+
+ /**
+ * Inverse FFT that allows a user-supplied buffer to be provided. The buffer
+ * must be a random access range with slicing, and its elements
+ * must be some complex-like type.
+ */
+ void inverseFft(Ret, R)(R range, Ret buf) const
+ if (isRandomAccessRange!Ret && isComplexLike!(ElementType!Ret) && hasSlicing!Ret)
+ {
+ enforceSize(range);
+
+ auto swapped = map!swapRealImag(range);
+ fft(swapped, buf);
+
+ immutable lenNeg1 = 1.0 / buf.length;
+ foreach (ref elem; buf)
+ {
+ immutable temp = elem.re * lenNeg1;
+ elem.re = elem.im * lenNeg1;
+ elem.im = temp;
+ }
+ }
+}
+
+// This mixin creates an Fft object in the scope it's mixed into such that all
+// memory owned by the object is deterministically destroyed at the end of that
+// scope.
+private enum string MakeLocalFft = q{
+ import core.stdc.stdlib;
+ import core.exception : onOutOfMemoryError;
+
+ auto lookupBuf = (cast(lookup_t*) malloc(range.length * 2 * lookup_t.sizeof))
+ [0 .. 2 * range.length];
+ if (!lookupBuf.ptr)
+ onOutOfMemoryError();
+
+ scope(exit) free(cast(void*) lookupBuf.ptr);
+ auto fftObj = scoped!Fft(lookupBuf);
+};
+
+/**Convenience functions that create an $(D Fft) object, run the FFT or inverse
+ * FFT and return the result. Useful for one-off FFTs.
+ *
+ * Note: In addition to convenience, these functions are slightly more
+ * efficient than manually creating an Fft object for a single use,
+ * as the Fft object is deterministically destroyed before these
+ * functions return.
+ */
+Complex!F[] fft(F = double, R)(R range)
+{
+ mixin(MakeLocalFft);
+ return fftObj.fft!(F, R)(range);
+}
+
+/// ditto
+void fft(Ret, R)(R range, Ret buf)
+{
+ mixin(MakeLocalFft);
+ return fftObj.fft!(Ret, R)(range, buf);
+}
+
+/// ditto
+Complex!F[] inverseFft(F = double, R)(R range)
+{
+ mixin(MakeLocalFft);
+ return fftObj.inverseFft!(F, R)(range);
+}
+
+/// ditto
+void inverseFft(Ret, R)(R range, Ret buf)
+{
+ mixin(MakeLocalFft);
+ return fftObj.inverseFft!(Ret, R)(range, buf);
+}
+
+@system unittest
+{
+ import std.algorithm;
+ import std.conv;
+ import std.range;
+ // Test values from R and Octave.
+ auto arr = [1,2,3,4,5,6,7,8];
+ auto fft1 = fft(arr);
+ assert(approxEqual(map!"a.re"(fft1),
+ [36.0, -4, -4, -4, -4, -4, -4, -4]));
+ assert(approxEqual(map!"a.im"(fft1),
+ [0, 9.6568, 4, 1.6568, 0, -1.6568, -4, -9.6568]));
+
+ auto fft1Retro = fft(retro(arr));
+ assert(approxEqual(map!"a.re"(fft1Retro),
+ [36.0, 4, 4, 4, 4, 4, 4, 4]));
+ assert(approxEqual(map!"a.im"(fft1Retro),
+ [0, -9.6568, -4, -1.6568, 0, 1.6568, 4, 9.6568]));
+
+ auto fft1Float = fft(to!(float[])(arr));
+ assert(approxEqual(map!"a.re"(fft1), map!"a.re"(fft1Float)));
+ assert(approxEqual(map!"a.im"(fft1), map!"a.im"(fft1Float)));
+
+ alias C = Complex!float;
+ auto arr2 = [C(1,2), C(3,4), C(5,6), C(7,8), C(9,10),
+ C(11,12), C(13,14), C(15,16)];
+ auto fft2 = fft(arr2);
+ assert(approxEqual(map!"a.re"(fft2),
+ [64.0, -27.3137, -16, -11.3137, -8, -4.6862, 0, 11.3137]));
+ assert(approxEqual(map!"a.im"(fft2),
+ [72, 11.3137, 0, -4.686, -8, -11.3137, -16, -27.3137]));
+
+ auto inv1 = inverseFft(fft1);
+ assert(approxEqual(map!"a.re"(inv1), arr));
+ assert(reduce!max(map!"a.im"(inv1)) < 1e-10);
+
+ auto inv2 = inverseFft(fft2);
+ assert(approxEqual(map!"a.re"(inv2), map!"a.re"(arr2)));
+ assert(approxEqual(map!"a.im"(inv2), map!"a.im"(arr2)));
+
+ // FFTs of size 0, 1 and 2 are handled as special cases. Test them here.
+ ushort[] empty;
+ assert(fft(empty) == null);
+ assert(inverseFft(fft(empty)) == null);
+
+ real[] oneElem = [4.5L];
+ auto oneFft = fft(oneElem);
+ assert(oneFft.length == 1);
+ assert(oneFft[0].re == 4.5L);
+ assert(oneFft[0].im == 0);
+
+ auto oneInv = inverseFft(oneFft);
+ assert(oneInv.length == 1);
+ assert(approxEqual(oneInv[0].re, 4.5));
+ assert(approxEqual(oneInv[0].im, 0));
+
+ long[2] twoElems = [8, 4];
+ auto twoFft = fft(twoElems[]);
+ assert(twoFft.length == 2);
+ assert(approxEqual(twoFft[0].re, 12));
+ assert(approxEqual(twoFft[0].im, 0));
+ assert(approxEqual(twoFft[1].re, 4));
+ assert(approxEqual(twoFft[1].im, 0));
+ auto twoInv = inverseFft(twoFft);
+ assert(approxEqual(twoInv[0].re, 8));
+ assert(approxEqual(twoInv[0].im, 0));
+ assert(approxEqual(twoInv[1].re, 4));
+ assert(approxEqual(twoInv[1].im, 0));
+}
+
+// Swaps the real and imaginary parts of a complex number. This is useful
+// for inverse FFTs.
+C swapRealImag(C)(C input)
+{
+ return C(input.im, input.re);
+}
+
+private:
+// The reasons I couldn't use std.algorithm were b/c its stride length isn't
+// modifiable on the fly and because range has grown some performance hacks
+// for powers of 2.
+struct Stride(R)
+{
+ import core.bitop : bsf;
+ Unqual!R range;
+ size_t _nSteps;
+ size_t _length;
+ alias E = ElementType!(R);
+
+ this(R range, size_t nStepsIn)
+ {
+ this.range = range;
+ _nSteps = nStepsIn;
+ _length = (range.length + _nSteps - 1) / nSteps;
+ }
+
+ size_t length() const @property
+ {
+ return _length;
+ }
+
+ typeof(this) save() @property
+ {
+ auto ret = this;
+ ret.range = ret.range.save;
+ return ret;
+ }
+
+ E opIndex(size_t index)
+ {
+ return range[index * _nSteps];
+ }
+
+ E front() @property
+ {
+ return range[0];
+ }
+
+ void popFront()
+ {
+ if (range.length >= _nSteps)
+ {
+ range = range[_nSteps .. range.length];
+ _length--;
+ }
+ else
+ {
+ range = range[0 .. 0];
+ _length = 0;
+ }
+ }
+
+ // Pops half the range's stride.
+ void popHalf()
+ {
+ range = range[_nSteps / 2 .. range.length];
+ }
+
+ bool empty() const @property
+ {
+ return length == 0;
+ }
+
+ size_t nSteps() const @property
+ {
+ return _nSteps;
+ }
+
+ void doubleSteps()
+ {
+ _nSteps *= 2;
+ _length /= 2;
+ }
+
+ size_t nSteps(size_t newVal) @property
+ {
+ _nSteps = newVal;
+
+ // Using >> bsf(nSteps) is a few cycles faster than / nSteps.
+ _length = (range.length + _nSteps - 1) >> bsf(nSteps);
+ return newVal;
+ }
+}
+
+// Hard-coded base case for FFT of size 2. This is actually a TON faster than
+// using a generic slow DFT. This seems to be the best base case. (Size 1
+// can be coded inline as buf[0] = range[0]).
+void slowFourier2(Ret, R)(R range, Ret buf)
+{
+ assert(range.length == 2);
+ assert(buf.length == 2);
+ buf[0] = range[0] + range[1];
+ buf[1] = range[0] - range[1];
+}
+
+// Hard-coded base case for FFT of size 4. Doesn't work as well as the size
+// 2 case.
+void slowFourier4(Ret, R)(R range, Ret buf)
+{
+ alias C = ElementType!Ret;
+
+ assert(range.length == 4);
+ assert(buf.length == 4);
+ buf[0] = range[0] + range[1] + range[2] + range[3];
+ buf[1] = range[0] - range[1] * C(0, 1) - range[2] + range[3] * C(0, 1);
+ buf[2] = range[0] - range[1] + range[2] - range[3];
+ buf[3] = range[0] + range[1] * C(0, 1) - range[2] - range[3] * C(0, 1);
+}
+
+N roundDownToPowerOf2(N)(N num)
+if (isScalarType!N && !isFloatingPoint!N)
+{
+ import core.bitop : bsr;
+ return num & (cast(N) 1 << bsr(num));
+}
+
+@safe unittest
+{
+ assert(roundDownToPowerOf2(7) == 4);
+ assert(roundDownToPowerOf2(4) == 4);
+}
+
+template isComplexLike(T)
+{
+ enum bool isComplexLike = is(typeof(T.init.re)) &&
+ is(typeof(T.init.im));
+}
+
+@safe unittest
+{
+ static assert(isComplexLike!(Complex!double));
+ static assert(!isComplexLike!(uint));
+}
diff --git a/libphobos/src/std/outbuffer.d b/libphobos/src/std/outbuffer.d
new file mode 100644
index 0000000..1d59498
--- /dev/null
+++ b/libphobos/src/std/outbuffer.d
@@ -0,0 +1,418 @@
+// Written in the D programming language.
+
+/**
+Serialize data to $(D ubyte) arrays.
+
+ * Copyright: Copyright Digital Mars 2000 - 2015.
+ * 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;
+
+import core.stdc.stdarg; // : va_list;
+
+/*********************************************
+ * OutBuffer provides a way to build up an array of bytes out
+ * of raw data. It is useful for things like preparing an
+ * array of bytes to write out to a file.
+ * 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. 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
+{
+ ubyte[] data;
+ size_t offset;
+
+ invariant()
+ {
+ assert(offset <= data.length);
+ }
+
+ pure nothrow @safe
+ {
+ /*********************************
+ * Convert to array of bytes.
+ */
+ ubyte[] toBytes() { return data[0 .. offset]; }
+
+ /***********************************
+ * Preallocate nbytes more to the size of the internal buffer.
+ *
+ * This is a
+ * 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
+ {
+ assert(offset + nbytes >= offset);
+ }
+ out
+ {
+ assert(offset + nbytes <= data.length);
+ }
+ body
+ {
+ if (data.length < offset + nbytes)
+ {
+ 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 put = write;
+
+ /*************************************
+ * Append data to the internal buffer.
+ */
+
+ void write(const(ubyte)[] bytes)
+ {
+ reserve(bytes.length);
+ data[offset .. offset + bytes.length] = bytes[];
+ offset += bytes.length;
+ }
+
+ void write(in wchar[] chars) @trusted
+ {
+ write(cast(ubyte[]) chars);
+ }
+
+ void write(const(dchar)[] chars) @trusted
+ {
+ write(cast(ubyte[]) chars);
+ }
+
+ void write(ubyte b) /// ditto
+ {
+ reserve(ubyte.sizeof);
+ this.data[offset] = b;
+ 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(ushort w) @trusted /// ditto
+ {
+ reserve(ushort.sizeof);
+ *cast(ushort *)&data[offset] = w;
+ offset += ushort.sizeof;
+ }
+
+ void write(short s) { write(cast(ushort) s); } /// ditto
+
+ void write(wchar c) @trusted /// ditto
+ {
+ reserve(wchar.sizeof);
+ *cast(wchar *)&data[offset] = c;
+ offset += wchar.sizeof;
+ }
+
+ void write(uint w) @trusted /// ditto
+ {
+ reserve(uint.sizeof);
+ *cast(uint *)&data[offset] = w;
+ offset += uint.sizeof;
+ }
+
+ void write(int i) { write(cast(uint) i); } /// ditto
+
+ void write(ulong l) @trusted /// ditto
+ {
+ reserve(ulong.sizeof);
+ *cast(ulong *)&data[offset] = l;
+ offset += ulong.sizeof;
+ }
+
+ void write(long l) { write(cast(ulong) l); } /// ditto
+
+ void write(float f) @trusted /// ditto
+ {
+ reserve(float.sizeof);
+ *cast(float *)&data[offset] = f;
+ offset += float.sizeof;
+ }
+
+ void write(double f) @trusted /// ditto
+ {
+ reserve(double.sizeof);
+ *cast(double *)&data[offset] = f;
+ offset += double.sizeof;
+ }
+
+ void write(real f) @trusted /// ditto
+ {
+ reserve(real.sizeof);
+ *cast(real *)&data[offset] = f;
+ offset += real.sizeof;
+ }
+
+ void write(in char[] s) @trusted /// ditto
+ {
+ write(cast(ubyte[]) s);
+ }
+
+ void write(OutBuffer buf) /// ditto
+ {
+ write(buf.toBytes());
+ }
+
+ /****************************************
+ * Append nbytes of 0 to the internal buffer.
+ */
+
+ void fill0(size_t nbytes)
+ {
+ reserve(nbytes);
+ data[offset .. offset + nbytes] = 0;
+ offset += nbytes;
+ }
+
+ /**********************************
+ * 0-fill to align on power of 2 boundary.
+ */
+
+ void alignSize(size_t alignsize)
+ in
+ {
+ assert(alignsize && (alignsize & (alignsize - 1)) == 0);
+ }
+ out
+ {
+ assert((offset & (alignsize - 1)) == 0);
+ }
+ body
+ {
+ auto nbytes = offset & (alignsize - 1);
+ if (nbytes)
+ fill0(alignsize - nbytes);
+ }
+
+ /// Clear the data in the buffer
+ void clear()
+ {
+ offset = 0;
+ }
+
+ /****************************************
+ * Optimize common special case alignSize(2)
+ */
+
+ void align2()
+ {
+ if (offset & 1)
+ write(cast(byte) 0);
+ }
+
+ /****************************************
+ * Optimize common special case alignSize(4)
+ */
+
+ void align4()
+ {
+ if (offset & 3)
+ { auto nbytes = (4 - offset) & 3;
+ fill0(nbytes);
+ }
+ }
+
+ /**************************************
+ * Convert internal buffer to array of chars.
+ */
+
+ override string toString() const
+ {
+ //printf("OutBuffer.toString()\n");
+ return cast(string) data[0 .. offset].idup;
+ }
+ }
+
+ /*****************************************
+ * Append output of C's vprintf() to internal buffer.
+ */
+
+ void vprintf(string format, va_list args) @trusted nothrow
+ {
+ import core.stdc.stdio : vsnprintf;
+ import core.stdc.stdlib : alloca;
+ import std.string : toStringz;
+
+ 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:
+ // "cannot mix core.std.stdlib.alloca() and exception handling".
+ auto f = toStringz(format);
+ auto p = buffer.ptr;
+ auto psize = buffer.length;
+ for (;;)
+ {
+ va_list args2;
+ va_copy(args2, args);
+ count = vsnprintf(p, psize, f, args2);
+ va_end(args2);
+ if (count == -1)
+ {
+ if (psize > psize.max / 2) assert(0); // overflow check
+ psize *= 2;
+ }
+ else if (count >= psize)
+ {
+ if (count == count.max) assert(0); // overflow check
+ psize = count + 1;
+ }
+ else
+ break;
+
+ p = cast(char *) alloca(psize); // buffer too small, try again with larger size
+ }
+ write(cast(ubyte[]) p[0 .. count]);
+ }
+
+ /*****************************************
+ * Append output of C's printf() to internal buffer.
+ */
+
+ void printf(string format, ...) @trusted
+ {
+ va_list ap;
+ va_start(ap, format);
+ vprintf(format, ap);
+ va_end(ap);
+ }
+
+ /**
+ * Formats and writes its arguments in text format to the OutBuffer.
+ *
+ * Params:
+ * fmt = format string as described in $(REF formattedWrite, std,format)
+ * args = arguments to be formatted
+ *
+ * See_Also:
+ * $(REF _writef, std,stdio);
+ * $(REF formattedWrite, std,format);
+ */
+ void writef(Char, A...)(in Char[] fmt, A args)
+ {
+ import std.format : formattedWrite;
+ formattedWrite(this, fmt, args);
+ }
+
+ ///
+ @safe unittest
+ {
+ OutBuffer b = new OutBuffer();
+ b.writef("a%sb", 16);
+ assert(b.toString() == "a16b");
+ }
+
+ /**
+ * Formats and writes its arguments in text format to the OutBuffer,
+ * followed by a newline.
+ *
+ * Params:
+ * fmt = format string as described in $(REF formattedWrite, std,format)
+ * args = arguments to be formatted
+ *
+ * See_Also:
+ * $(REF _writefln, std,stdio);
+ * $(REF formattedWrite, std,format);
+ */
+ void writefln(Char, A...)(in Char[] fmt, A args)
+ {
+ import std.format : formattedWrite;
+ formattedWrite(this, fmt, args);
+ put('\n');
+ }
+
+ ///
+ @safe unittest
+ {
+ OutBuffer b = new OutBuffer();
+ b.writefln("a%sb", 16);
+ assert(b.toString() == "a16b\n");
+ }
+
+ /*****************************************
+ * At offset index into buffer, create nbytes of space by shifting upwards
+ * all data past index.
+ */
+
+ void spread(size_t index, size_t nbytes) pure nothrow @safe
+ in
+ {
+ assert(index <= offset);
+ }
+ body
+ {
+ reserve(nbytes);
+
+ // This is an overlapping copy - should use memmove()
+ for (size_t i = offset; i > index; )
+ {
+ --i;
+ data[i + nbytes] = data[i];
+ }
+ offset += nbytes;
+ }
+}
+
+///
+@safe unittest
+{
+ import std.string : cmp;
+
+ OutBuffer buf = new OutBuffer();
+
+ assert(buf.offset == 0);
+ buf.write("hello");
+ buf.write(cast(byte) 0x20);
+ buf.write("world");
+ buf.printf(" %d", 62665);
+ assert(cmp(buf.toString(), "hello world 62665") == 0);
+
+ buf.clear();
+ assert(cmp(buf.toString(), "") == 0);
+ buf.write("New data");
+ assert(cmp(buf.toString(),"New data") == 0);
+}
+
+@safe unittest
+{
+ import std.range;
+ static assert(isOutputRange!(OutBuffer, char));
+
+ import std.algorithm;
+ {
+ OutBuffer buf = new OutBuffer();
+ "hello".copy(buf);
+ assert(buf.toBytes() == "hello");
+ }
+ {
+ OutBuffer buf = new OutBuffer();
+ "hello"w.copy(buf);
+ assert(buf.toBytes() == "h\x00e\x00l\x00l\x00o\x00");
+ }
+ {
+ OutBuffer buf = new OutBuffer();
+ "hello"d.copy(buf);
+ assert(buf.toBytes() == "h\x00\x00\x00e\x00\x00\x00l\x00\x00\x00l\x00\x00\x00o\x00\x00\x00");
+ }
+}
diff --git a/libphobos/src/std/parallelism.d b/libphobos/src/std/parallelism.d
new file mode 100644
index 0000000..df07baf
--- /dev/null
+++ b/libphobos/src/std/parallelism.d
@@ -0,0 +1,4636 @@
+/**
+$(D std._parallelism) implements high-level primitives for SMP _parallelism.
+These include parallel foreach, parallel reduce, parallel eager map, pipelining
+and future/promise _parallelism. $(D std._parallelism) is recommended when the
+same operation is to be executed in parallel on different data, or when a
+function is to be executed in a background thread and its result returned to a
+well-defined main thread. For communication between arbitrary threads, see
+$(D std.concurrency).
+
+$(D std._parallelism) is based on the concept of a $(D Task). A $(D Task) is an
+object that represents the fundamental unit of work in this library and may be
+executed in parallel with any other $(D Task). Using $(D Task)
+directly allows programming with a future/promise paradigm. All other
+supported _parallelism paradigms (parallel foreach, map, reduce, pipelining)
+represent an additional level of abstraction over $(D Task). They
+automatically create one or more $(D Task) objects, or closely related types
+that are conceptually identical but not part of the public API.
+
+After creation, a $(D Task) may be executed in a new thread, or submitted
+to a $(D TaskPool) for execution. A $(D TaskPool) encapsulates a task queue
+and its worker threads. Its purpose is to efficiently map a large
+number of $(D Task)s onto a smaller number of threads. A task queue is a
+FIFO queue of $(D Task) objects that have been submitted to the
+$(D TaskPool) and are awaiting execution. A worker thread is a thread that
+is associated with exactly one task queue. It executes the $(D Task) at the
+front of its queue when the queue has work available, or sleeps when
+no work is available. Each task queue is associated with zero or
+more worker threads. If the result of a $(D Task) is needed before execution
+by a worker thread has begun, the $(D Task) can be removed from the task queue
+and executed immediately in the thread where the result is needed.
+
+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.
+
+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;
+
+///
+@system unittest
+{
+ import std.algorithm.iteration : map;
+ import std.math : approxEqual;
+ import std.parallelism : taskPool;
+ import std.range : iota;
+
+ // Parallel reduce can be combined with
+ // 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 Intel i5-3450 quad core machine
+ // for n = 1_000_000_000:
+ //
+ // TaskPool.reduce: 1.067 s
+ // std.algorithm.reduce: 4.011 s
+
+ enum n = 1_000_000;
+ enum delta = 1.0 / n;
+
+ alias getTerm = (int i)
+ {
+ immutable x = ( i - 0.5 ) * delta;
+ return delta / ( 1.0 + x * x ) ;
+ };
+
+ immutable pi = 4.0 * taskPool.reduce!"a + b"(n.iota.map!getTerm);
+
+ assert(pi.approxEqual(3.1415926));
+}
+
+import core.atomic;
+import core.memory;
+import core.sync.condition;
+import core.thread;
+
+import std.functional;
+import std.meta;
+import std.range.primitives;
+import std.traits;
+
+version (OSX)
+{
+ version = useSysctlbyname;
+}
+else version (FreeBSD)
+{
+ version = useSysctlbyname;
+}
+else version (NetBSD)
+{
+ version = useSysctlbyname;
+}
+
+
+version (Windows)
+{
+ // BUGS: Only works on Windows 2000 and above.
+ 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);
+ }
+
+}
+else version (linux)
+{
+ shared static this()
+ {
+ import core.sys.posix.unistd : _SC_NPROCESSORS_ONLN, sysconf;
+ totalCPUs = cast(uint) sysconf(_SC_NPROCESSORS_ONLN);
+ }
+}
+else version (Solaris)
+{
+ shared static this()
+ {
+ import core.sys.posix.unistd : _SC_NPROCESSORS_ONLN, sysconf;
+ totalCPUs = cast(uint) sysconf(_SC_NPROCESSORS_ONLN);
+ }
+}
+else version (useSysctlbyname)
+{
+ extern(C) int sysctlbyname(
+ const char *, void *, size_t *, void *, size_t
+ );
+
+ shared static this()
+ {
+ version (OSX)
+ {
+ auto nameStr = "machdep.cpu.core_count\0".ptr;
+ }
+ else version (FreeBSD)
+ {
+ auto nameStr = "hw.ncpu\0".ptr;
+ }
+ else version (NetBSD)
+ {
+ auto nameStr = "hw.ncpu\0".ptr;
+ }
+
+ uint ans;
+ size_t len = uint.sizeof;
+ sysctlbyname(nameStr, &ans, &len, null, 0);
+ totalCPUs = ans;
+ }
+
+}
+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:
+
+ 1. They used to actually contain ASM code and I don' want to have to change
+ to directly calling core.atomic in a zillion different places.
+
+ 2. core.atomic has some misc. issues that make my use cases difficult
+ without wrapping it. If I didn't wrap it, casts would be required
+ basically everywhere.
+*/
+private void atomicSetUbyte(T)(ref T stuff, T newVal)
+if (__traits(isIntegral, T) && is(T : ubyte))
+{
+ //core.atomic.cas(cast(shared) &stuff, stuff, newVal);
+ atomicStore(*(cast(shared) &stuff), newVal);
+}
+
+private ubyte atomicReadUbyte(T)(ref T val)
+if (__traits(isIntegral, T) && is(T : ubyte))
+{
+ return atomicLoad(*(cast(shared) &val));
+}
+
+// This gets rid of the need for a lot of annoying casts in other parts of the
+// code, when enums are involved.
+private bool atomicCasUbyte(T)(ref T stuff, T testVal, T newVal)
+if (__traits(isIntegral, T) && is(T : ubyte))
+{
+ return core.atomic.cas(cast(shared) &stuff, testVal, newVal);
+}
+
+/*--------------------- Generic helper functions, etc.------------------------*/
+private template MapType(R, functions...)
+{
+ static assert(functions.length);
+
+ ElementType!R e = void;
+ alias MapType =
+ typeof(adjoin!(staticMap!(unaryFun, functions))(e));
+}
+
+private template ReduceType(alias fun, R, E)
+{
+ alias ReduceType = typeof(binaryFun!fun(E.init, ElementType!R.init));
+}
+
+private template noUnsharedAliasing(T)
+{
+ enum bool noUnsharedAliasing = !hasUnsharedAliasing!T;
+}
+
+// This template tests whether a function may be executed in parallel from
+// @safe code via Task.executeInNewThread(). There is an additional
+// requirement for executing it via a TaskPool. (See isSafeReturn).
+private template isSafeTask(F)
+{
+ enum bool isSafeTask =
+ (functionAttributes!F & (FunctionAttribute.safe | FunctionAttribute.trusted)) != 0 &&
+ (functionAttributes!F & FunctionAttribute.ref_) == 0 &&
+ (isFunctionPointer!F || !hasUnsharedAliasing!F) &&
+ allSatisfy!(noUnsharedAliasing, Parameters!F);
+}
+
+@safe unittest
+{
+ alias F1 = void function() @safe;
+ alias F2 = void function();
+ alias F3 = void function(uint, string) @trusted;
+ alias F4 = void function(uint, char[]);
+
+ static assert( isSafeTask!F1);
+ static assert(!isSafeTask!F2);
+ static assert( isSafeTask!F3);
+ static assert(!isSafeTask!F4);
+
+ alias F5 = uint[] function(uint, string) pure @trusted;
+ static assert( isSafeTask!F5);
+}
+
+// This function decides whether Tasks that meet all of the other requirements
+// for being executed from @safe code can be executed on a TaskPool.
+// When executing via TaskPool, it's theoretically possible
+// to return a value that is also pointed to by a worker thread's thread local
+// storage. When executing from executeInNewThread(), the thread that executed
+// the Task is terminated by the time the return value is visible in the calling
+// thread, so this is a non-issue. It's also a non-issue for pure functions
+// since they can't read global state.
+private template isSafeReturn(T)
+{
+ static if (!hasUnsharedAliasing!(T.ReturnType))
+ {
+ enum isSafeReturn = true;
+ }
+ else static if (T.isPure)
+ {
+ enum isSafeReturn = true;
+ }
+ else
+ {
+ enum isSafeReturn = false;
+ }
+}
+
+private template randAssignable(R)
+{
+ enum randAssignable = isRandomAccessRange!R && hasAssignableElements!R;
+}
+
+private enum TaskStatus : ubyte
+{
+ notStarted,
+ inProgress,
+ done
+}
+
+private template AliasReturn(alias fun, T...)
+{
+ alias AliasReturn = typeof({ T args; return fun(args); });
+}
+
+// Should be private, but std.algorithm.reduce is used in the zero-thread case
+// and won't work w/ private.
+template reduceAdjoin(functions...)
+{
+ static if (functions.length == 1)
+ {
+ alias reduceAdjoin = binaryFun!(functions[0]);
+ }
+ else
+ {
+ T reduceAdjoin(T, U)(T lhs, U rhs)
+ {
+ alias funs = staticMap!(binaryFun, functions);
+
+ foreach (i, Unused; typeof(lhs.expand))
+ {
+ lhs.expand[i] = funs[i](lhs.expand[i], rhs);
+ }
+
+ return lhs;
+ }
+ }
+}
+
+private template reduceFinish(functions...)
+{
+ static if (functions.length == 1)
+ {
+ alias reduceFinish = binaryFun!(functions[0]);
+ }
+ else
+ {
+ T reduceFinish(T)(T lhs, T rhs)
+ {
+ alias funs = staticMap!(binaryFun, functions);
+
+ foreach (i, Unused; typeof(lhs.expand))
+ {
+ lhs.expand[i] = funs[i](lhs.expand[i], rhs.expand[i]);
+ }
+
+ return lhs;
+ }
+ }
+}
+
+private template isRoundRobin(R : RoundRobinBuffer!(C1, C2), C1, C2)
+{
+ enum isRoundRobin = true;
+}
+
+private template isRoundRobin(T)
+{
+ enum isRoundRobin = false;
+}
+
+@safe unittest
+{
+ static assert( isRoundRobin!(RoundRobinBuffer!(void delegate(char[]), bool delegate())));
+ static assert(!isRoundRobin!(uint));
+}
+
+// This is the base "class" for all of the other tasks. Using C-style
+// polymorphism to allow more direct control over memory allocation, etc.
+private struct AbstractTask
+{
+ AbstractTask* prev;
+ AbstractTask* next;
+
+ // Pointer to a function that executes this task.
+ void function(void*) runTask;
+
+ Throwable exception;
+ ubyte taskStatus = TaskStatus.notStarted;
+
+ bool done() @property
+ {
+ if (atomicReadUbyte(taskStatus) == TaskStatus.done)
+ {
+ if (exception)
+ {
+ throw exception;
+ }
+
+ return true;
+ }
+
+ return false;
+ }
+
+ void job()
+ {
+ runTask(&this);
+ }
+}
+
+/**
+$(D Task) represents the fundamental unit of work. A $(D Task) may be
+executed in parallel with any other $(D Task). Using this struct directly
+allows future/promise _parallelism. In this paradigm, a function (or delegate
+or other callable) is executed in a thread other than the one it was called
+from. The calling thread does not block while the function is being executed.
+A call to $(D workForce), $(D yieldForce), or $(D spinForce) is used to
+ensure that the $(D Task) has finished executing and to obtain the return
+value, if any. These functions and $(D done) also act as full memory barriers,
+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 $(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
+$(D workForce) by ref. If $(D fun) returns by ref, the reference will point
+to the returned reference of $(D fun). Otherwise it will point to a
+field in this struct.
+
+Copying of this struct is disabled, since it would provide no useful semantics.
+If you want to pass this struct around, you should do so by reference or
+pointer.
+
+Bugs: Changes to $(D ref) and $(D out) arguments are not propagated to the
+ call site, only to $(D args) in this struct.
+*/
+struct Task(alias fun, Args...)
+{
+ AbstractTask base = {runTask : &impl};
+ alias base this;
+
+ private @property AbstractTask* basePtr()
+ {
+ return &base;
+ }
+
+ private static void impl(void* myTask)
+ {
+ import std.algorithm.internal : addressOf;
+
+ Task* myCastedTask = cast(typeof(this)*) myTask;
+ static if (is(ReturnType == void))
+ {
+ fun(myCastedTask._args);
+ }
+ else static if (is(typeof(addressOf(fun(myCastedTask._args)))))
+ {
+ myCastedTask.returnVal = addressOf(fun(myCastedTask._args));
+ }
+ else
+ {
+ myCastedTask.returnVal = fun(myCastedTask._args);
+ }
+ }
+
+ private TaskPool pool;
+ private bool isScoped; // True if created with scopedTask.
+
+ Args _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))
+ {
+ alias args = _args[1..$];
+ }
+ else
+ {
+ alias args = _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 (isFunctionPointer!(_args[0]))
+ {
+ private enum bool isPure =
+ functionAttributes!(Args[0]) & FunctionAttribute.pure_;
+ }
+ else
+ {
+ // BUG: Should check this for delegates too, but std.traits
+ // apparently doesn't allow this. isPure is irrelevant
+ // for delegates, at least for now since shared delegates
+ // don't work.
+ private enum bool isPure = false;
+ }
+
+ }
+ else
+ {
+ // We already know that we can't execute aliases in @safe code, so
+ // just put a dummy value here.
+ private enum bool isPure = false;
+ }
+
+
+ /**
+ The return type of the function called by this $(D Task). This can be
+ $(D void).
+ */
+ alias ReturnType = typeof(fun(_args));
+
+ static if (!is(ReturnType == void))
+ {
+ static if (is(typeof(&fun(_args))))
+ {
+ // Ref return.
+ ReturnType* returnVal;
+
+ ref ReturnType fixRef(ReturnType* val)
+ {
+ return *val;
+ }
+
+ }
+ else
+ {
+ ReturnType returnVal;
+
+ ref ReturnType fixRef(ref ReturnType val)
+ {
+ return val;
+ }
+ }
+ }
+
+ private void enforcePool()
+ {
+ import std.exception : enforce;
+ enforce(this.pool !is null, "Job not submitted yet.");
+ }
+
+ static if (Args.length > 0)
+ {
+ private this(Args args)
+ {
+ _args = args;
+ }
+ }
+
+ // Work around DMD bug 6588, allow immutable elements.
+ static if (allSatisfy!(isAssignable, Args))
+ {
+ typeof(this) opAssign(typeof(this) rhs)
+ {
+ foreach (i, Type; typeof(this.tupleof))
+ {
+ this.tupleof[i] = rhs.tupleof[i];
+ }
+ return this;
+ }
+ }
+ else
+ {
+ @disable typeof(this) opAssign(typeof(this) rhs)
+ {
+ assert(0);
+ }
+ }
+
+ /**
+ If the $(D Task) isn't started yet, execute it in the current thread.
+ If it's done, return its return value, if any. If it's in progress,
+ busy spin until it's done, then return the return value. If it threw
+ an exception, rethrow that exception.
+
+ This function should be used when you expect the result of the
+ $(D Task) to be available on a timescale shorter than that of an OS
+ context switch.
+ */
+ @property ref ReturnType spinForce() @trusted
+ {
+ enforcePool();
+
+ this.pool.tryDeleteExecute(basePtr);
+
+ while (atomicReadUbyte(this.taskStatus) != TaskStatus.done) {}
+
+ if (exception)
+ {
+ throw exception;
+ }
+
+ static if (!is(ReturnType == void))
+ {
+ return fixRef(this.returnVal);
+ }
+ }
+
+ /**
+ If the $(D Task) isn't started yet, execute it in the current thread.
+ If it's done, return its return value, if any. If it's in progress,
+ wait on a condition variable. If it threw an exception, rethrow that
+ exception.
+
+ This function should be used for expensive functions, as waiting on a
+ condition variable introduces latency, but avoids wasted CPU cycles.
+ */
+ @property ref ReturnType yieldForce() @trusted
+ {
+ enforcePool();
+ this.pool.tryDeleteExecute(basePtr);
+
+ if (done)
+ {
+ static if (is(ReturnType == void))
+ {
+ return;
+ }
+ else
+ {
+ return fixRef(this.returnVal);
+ }
+ }
+
+ pool.waiterLock();
+ scope(exit) pool.waiterUnlock();
+
+ while (atomicReadUbyte(this.taskStatus) != TaskStatus.done)
+ {
+ pool.waitUntilCompletion();
+ }
+
+ if (exception)
+ {
+ throw exception; // nocoverage
+ }
+
+ static if (!is(ReturnType == void))
+ {
+ return fixRef(this.returnVal);
+ }
+ }
+
+ /**
+ If this $(D Task) was not started yet, execute it in the current
+ thread. If it is finished, return its result. If it is in progress,
+ execute any other $(D Task) from the $(D TaskPool) instance that
+ this $(D Task) was submitted to until this one
+ is finished. If it threw an exception, rethrow that exception.
+ If no other tasks are available or this $(D Task) was executed using
+ $(D executeInNewThread), wait on a condition variable.
+ */
+ @property ref ReturnType workForce() @trusted
+ {
+ enforcePool();
+ this.pool.tryDeleteExecute(basePtr);
+
+ while (true)
+ {
+ if (done) // done() implicitly checks for exceptions.
+ {
+ static if (is(ReturnType == void))
+ {
+ return;
+ }
+ else
+ {
+ return fixRef(this.returnVal);
+ }
+ }
+
+ AbstractTask* job;
+ {
+ // Locking explicitly and calling popNoSync() because
+ // pop() waits on a condition variable if there are no Tasks
+ // in the queue.
+
+ pool.queueLock();
+ scope(exit) pool.queueUnlock();
+ job = pool.popNoSync();
+ }
+
+
+ if (job !is null)
+ {
+
+ version (verboseUnittest)
+ {
+ stderr.writeln("Doing workForce work.");
+ }
+
+ pool.doJob(job);
+
+ if (done)
+ {
+ static if (is(ReturnType == void))
+ {
+ return;
+ }
+ else
+ {
+ return fixRef(this.returnVal);
+ }
+ }
+ }
+ else
+ {
+ version (verboseUnittest)
+ {
+ stderr.writeln("Yield from workForce.");
+ }
+
+ return yieldForce;
+ }
+ }
+ }
+
+ /**
+ Returns $(D true) if the $(D Task) is finished executing.
+
+ Throws: Rethrows any exception thrown during the execution of the
+ $(D Task).
+ */
+ @property bool done() @trusted
+ {
+ // Explicitly forwarded for documentation purposes.
+ return base.done;
+ }
+
+ /**
+ Create a new thread for executing this $(D Task), execute it in the
+ 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 $(REF task, std,parallelism) for
+ usage example.
+ */
+ void executeInNewThread() @trusted
+ {
+ pool = new TaskPool(basePtr);
+ }
+
+ /// Ditto
+ void executeInNewThread(int priority) @trusted
+ {
+ pool = new TaskPool(basePtr, priority);
+ }
+
+ @safe ~this()
+ {
+ if (isScoped && pool !is null && taskStatus != TaskStatus.done)
+ {
+ yieldForce;
+ }
+ }
+
+ // When this is uncommented, it somehow gets called on returning from
+ // scopedTask even though the struct shouldn't be getting copied.
+ //@disable this(this) {}
+}
+
+// Calls $(D fpOrDelegate) with $(D args). This is an
+// adapter that makes $(D Task) work with delegates, function pointers and
+// functors instead of just aliases.
+ReturnType!F run(F, Args...)(F fpOrDelegate, ref Args args)
+{
+ return fpOrDelegate(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
+$(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).
+
+Example:
+---
+// Read two files into memory at the same time.
+import std.file;
+
+void main()
+{
+ // Create and execute a Task for reading
+ // foo.txt.
+ auto file1Task = task!read("foo.txt");
+ file1Task.executeInNewThread();
+
+ // Read bar.txt in parallel.
+ auto file2Data = read("bar.txt");
+
+ // Get the results of reading foo.txt.
+ auto file1Data = file1Task.yieldForce;
+}
+---
+
+---
+// Sorts an array using a parallel quick sort algorithm.
+// The first partition is done serially. Both recursion
+// branches are then executed in parallel.
+//
+// Timings for sorting an array of 1,000,000 doubles on
+// an Athlon 64 X2 dual core machine:
+//
+// This implementation: 176 milliseconds.
+// Equivalent serial implementation: 280 milliseconds
+void parallelSort(T)(T[] data)
+{
+ // Sort small subarrays serially.
+ if (data.length < 100)
+ {
+ std.algorithm.sort(data);
+ return;
+ }
+
+ // Partition the array.
+ swap(data[$ / 2], data[$ - 1]);
+ auto pivot = data[$ - 1];
+ bool lessThanPivot(T elem) { return elem < pivot; }
+
+ auto greaterEqual = partition!lessThanPivot(data[0..$ - 1]);
+ swap(data[$ - greaterEqual.length - 1], data[$ - 1]);
+
+ auto less = data[0..$ - greaterEqual.length - 1];
+ greaterEqual = data[$ - greaterEqual.length..$];
+
+ // Execute both recursion branches in parallel.
+ auto recurseTask = task!parallelSort(greaterEqual);
+ taskPool.put(recurseTask);
+ parallelSort(less);
+ recurseTask.yieldForce;
+}
+---
+*/
+auto task(alias fun, Args...)(Args args)
+{
+ return new Task!(fun, Args)(args);
+}
+
+/**
+Creates a $(D Task) on the GC heap that calls a function pointer, delegate, or
+class/struct with overloaded opCall.
+
+Example:
+---
+// Read two files in at the same time again,
+// but this time use a function pointer instead
+// of an alias to represent std.file.read.
+import std.file;
+
+void main()
+{
+ // Create and execute a Task for reading
+ // foo.txt.
+ auto file1Task = task(&read, "foo.txt");
+ file1Task.executeInNewThread();
+
+ // Read bar.txt in parallel.
+ auto file2Data = read("bar.txt");
+
+ // Get the results of reading foo.txt.
+ auto file1Data = file1Task.yieldForce;
+}
+---
+
+Notes: This function takes a non-scope delegate, meaning it can be
+ used with closures. If you can't allocate a closure due to objects
+ on the stack that have scoped destruction, see $(D scopedTask), which
+ takes a scope delegate.
+ */
+auto task(F, Args...)(F delegateOrFp, Args args)
+if (is(typeof(delegateOrFp(args))) && !isSafeTask!F)
+{
+ return new Task!(run, F, Args)(delegateOrFp, args);
+}
+
+/**
+Version of $(D task) usable from $(D @safe) code. Usage mechanics are
+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
+ $(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.
+
+3. $(D Args) must not have unshared aliasing.
+
+4. $(D fun) must not return by reference.
+
+5. The return type must not have unshared aliasing unless $(D fun) is
+ $(D pure) or the $(D Task) is executed via $(D executeInNewThread) instead
+ of using a $(D TaskPool).
+
+*/
+@trusted auto task(F, Args...)(F fun, Args args)
+if (is(typeof(fun(args))) && isSafeTask!F)
+{
+ return new Task!(run, F, Args)(fun, args);
+}
+
+/**
+These functions allow the creation of $(D Task) objects on the stack rather
+than the GC heap. The lifetime of a $(D Task) created by $(D scopedTask)
+cannot exceed the lifetime of the scope it was created in.
+
+$(D scopedTask) might be preferred over $(D task):
+
+1. When a $(D Task) that calls a delegate is being created and a closure
+ cannot be allocated due to objects on the stack that have scoped
+ destruction. The delegate overload of $(D scopedTask) takes a $(D scope)
+ delegate.
+
+2. As a micro-optimization, to avoid the heap allocation associated with
+ $(D task) or with the creation of a closure.
+
+Usage is otherwise identical to $(D task).
+
+Notes: $(D Task) objects created using $(D scopedTask) will automatically
+call $(D Task.yieldForce) in their destructor if necessary to ensure
+the $(D Task) is complete before the stack frame they reside on is destroyed.
+*/
+auto scopedTask(alias fun, Args...)(Args args)
+{
+ auto ret = Task!(fun, Args)(args);
+ ret.isScoped = true;
+ return ret;
+}
+
+/// Ditto
+auto scopedTask(F, Args...)(scope F delegateOrFp, Args args)
+if (is(typeof(delegateOrFp(args))) && !isSafeTask!F)
+{
+ auto ret = Task!(run, F, Args)(delegateOrFp, args);
+ ret.isScoped = true;
+ return ret;
+}
+
+/// Ditto
+@trusted auto scopedTask(F, Args...)(F fun, Args args)
+if (is(typeof(fun(args))) && isSafeTask!F)
+{
+ auto ret = Task!(run, F, Args)(fun, args);
+ ret.isScoped = true;
+ return ret;
+}
+
+/**
+The total number of CPU cores available on the current machine, as reported by
+the operating system.
+*/
+immutable uint totalCPUs;
+
+/*
+This class serves two purposes:
+
+1. It distinguishes std.parallelism threads from other threads so that
+ the std.parallelism daemon threads can be terminated.
+
+2. It adds a reference to the pool that the thread is a member of,
+ which is also necessary to allow the daemon threads to be properly
+ terminated.
+*/
+private final class ParallelismThread : Thread
+{
+ this(void delegate() dg)
+ {
+ super(dg);
+ }
+
+ TaskPool pool;
+}
+
+// Kill daemon threads.
+shared static ~this()
+{
+ foreach (ref thread; Thread)
+ {
+ auto pthread = cast(ParallelismThread) thread;
+ if (pthread is null) continue;
+ auto pool = pthread.pool;
+ if (!pool.isDaemon) continue;
+ pool.stop();
+ pthread.join();
+ }
+}
+
+/**
+This class encapsulates a task queue and a set of worker threads. Its purpose
+is to efficiently map a large number of $(D Task)s onto a smaller number of
+threads. A task queue is a FIFO queue of $(D Task) objects that have been
+submitted to the $(D TaskPool) and are awaiting execution. A worker thread is a
+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 $(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
+ a low priority pool and a high priority pool.
+
+2. When the threads in the global task pool are waiting on a synchronization
+ primitive (for example a mutex), and you want to parallelize the code that
+ needs to run before these threads can be resumed.
+ */
+final class TaskPool
+{
+private:
+
+ // A pool can either be a regular pool or a single-task pool. A
+ // single-task pool is a dummy pool that's fired up for
+ // Task.executeInNewThread().
+ bool isSingleTask;
+
+ ParallelismThread[] pool;
+ Thread singleTaskThread;
+
+ AbstractTask* head;
+ AbstractTask* tail;
+ PoolState status = PoolState.running;
+ Condition workerCondition;
+ Condition waiterCondition;
+ Mutex queueMutex;
+ Mutex waiterMutex; // For waiterCondition
+
+ // The instanceStartIndex of the next instance that will be created.
+ __gshared static size_t nextInstanceIndex = 1;
+
+ // The index of the current thread.
+ static size_t threadIndex;
+
+ // The index of the first thread in this instance.
+ immutable size_t instanceStartIndex;
+
+ // The index that the next thread to be initialized in this pool will have.
+ size_t nextThreadIndex;
+
+ enum PoolState : ubyte
+ {
+ running,
+ finishing,
+ stopNow
+ }
+
+ void doJob(AbstractTask* job)
+ {
+ assert(job.taskStatus == TaskStatus.inProgress);
+ assert(job.next is null);
+ assert(job.prev is null);
+
+ scope(exit)
+ {
+ if (!isSingleTask)
+ {
+ waiterLock();
+ scope(exit) waiterUnlock();
+ notifyWaiters();
+ }
+ }
+
+ try
+ {
+ job.job();
+ }
+ catch (Throwable e)
+ {
+ job.exception = e;
+ }
+
+ atomicSetUbyte(job.taskStatus, TaskStatus.done);
+ }
+
+ // This function is used for dummy pools created by Task.executeInNewThread().
+ void doSingleTask()
+ {
+ // No synchronization. Pool is guaranteed to only have one thread,
+ // and the queue is submitted to before this thread is created.
+ assert(head);
+ auto t = head;
+ t.next = t.prev = head = null;
+ doJob(t);
+ }
+
+ // This function performs initialization for each thread that affects
+ // thread local storage and therefore must be done from within the
+ // worker thread. It then calls executeWorkLoop().
+ void startWorkLoop()
+ {
+ // Initialize thread index.
+ {
+ queueLock();
+ scope(exit) queueUnlock();
+ threadIndex = nextThreadIndex;
+ nextThreadIndex++;
+ }
+
+ executeWorkLoop();
+ }
+
+ // This is the main work loop that worker threads spend their time in
+ // until they terminate. It's also entered by non-worker threads when
+ // finish() is called with the blocking variable set to true.
+ void executeWorkLoop()
+ {
+ while (atomicReadUbyte(status) != PoolState.stopNow)
+ {
+ AbstractTask* task = pop();
+ if (task is null)
+ {
+ if (atomicReadUbyte(status) == PoolState.finishing)
+ {
+ atomicSetUbyte(status, PoolState.stopNow);
+ return;
+ }
+ }
+ else
+ {
+ doJob(task);
+ }
+ }
+ }
+
+ // Pop a task off the queue.
+ AbstractTask* pop()
+ {
+ queueLock();
+ scope(exit) queueUnlock();
+ auto ret = popNoSync();
+ while (ret is null && status == PoolState.running)
+ {
+ wait();
+ ret = popNoSync();
+ }
+ return ret;
+ }
+
+ AbstractTask* popNoSync()
+ out(returned)
+ {
+ /* If task.prev and task.next aren't null, then another thread
+ * can try to delete this task from the pool after it's
+ * alreadly been deleted/popped.
+ */
+ if (returned !is null)
+ {
+ assert(returned.next is null);
+ assert(returned.prev is null);
+ }
+ }
+ body
+ {
+ if (isSingleTask) return null;
+
+ AbstractTask* returned = head;
+ if (head !is null)
+ {
+ head = head.next;
+ returned.prev = null;
+ returned.next = null;
+ returned.taskStatus = TaskStatus.inProgress;
+ }
+ if (head !is null)
+ {
+ head.prev = null;
+ }
+
+ return returned;
+ }
+
+ // Push a task onto the queue.
+ void abstractPut(AbstractTask* task)
+ {
+ queueLock();
+ scope(exit) queueUnlock();
+ abstractPutNoSync(task);
+ }
+
+ void abstractPutNoSync(AbstractTask* task)
+ in
+ {
+ assert(task);
+ }
+ 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)
+ {
+ assert(tail.prev.next is tail, text(tail.prev, '\t', tail.next));
+ }
+ }
+ body
+ {
+ // Not using enforce() to save on function call overhead since this
+ // is a performance critical function.
+ if (status != PoolState.running)
+ {
+ throw new Error(
+ "Cannot submit a new task to a pool after calling " ~
+ "finish() or stop()."
+ );
+ }
+
+ task.next = null;
+ if (head is null) //Queue is empty.
+ {
+ head = task;
+ tail = task;
+ tail.prev = null;
+ }
+ else
+ {
+ assert(tail);
+ task.prev = tail;
+ tail.next = task;
+ tail = task;
+ }
+ notify();
+ }
+
+ void abstractPutGroupNoSync(AbstractTask* h, AbstractTask* t)
+ {
+ if (status != PoolState.running)
+ {
+ throw new Error(
+ "Cannot submit a new task to a pool after calling " ~
+ "finish() or stop()."
+ );
+ }
+
+ if (head is null)
+ {
+ head = h;
+ tail = t;
+ }
+ else
+ {
+ h.prev = tail;
+ tail.next = h;
+ tail = t;
+ }
+
+ notifyAll();
+ }
+
+ void tryDeleteExecute(AbstractTask* toExecute)
+ {
+ if (isSingleTask) return;
+
+ if ( !deleteItem(toExecute) )
+ {
+ return;
+ }
+
+ try
+ {
+ toExecute.job();
+ }
+ catch (Exception e)
+ {
+ toExecute.exception = e;
+ }
+
+ atomicSetUbyte(toExecute.taskStatus, TaskStatus.done);
+ }
+
+ bool deleteItem(AbstractTask* item)
+ {
+ queueLock();
+ scope(exit) queueUnlock();
+ return deleteItemNoSync(item);
+ }
+
+ bool deleteItemNoSync(AbstractTask* item)
+ {
+ if (item.taskStatus != TaskStatus.notStarted)
+ {
+ return false;
+ }
+ item.taskStatus = TaskStatus.inProgress;
+
+ if (item is head)
+ {
+ // Make sure head gets set properly.
+ popNoSync();
+ return true;
+ }
+ if (item is tail)
+ {
+ tail = tail.prev;
+ if (tail !is null)
+ {
+ tail.next = null;
+ }
+ item.next = null;
+ item.prev = null;
+ return true;
+ }
+ if (item.next !is null)
+ {
+ assert(item.next.prev is item); // Check queue consistency.
+ item.next.prev = item.prev;
+ }
+ if (item.prev !is null)
+ {
+ assert(item.prev.next is item); // Check queue consistency.
+ item.prev.next = item.next;
+ }
+ item.next = null;
+ item.prev = null;
+ return true;
+ }
+
+ void queueLock()
+ {
+ assert(queueMutex);
+ if (!isSingleTask) queueMutex.lock();
+ }
+
+ void queueUnlock()
+ {
+ assert(queueMutex);
+ if (!isSingleTask) queueMutex.unlock();
+ }
+
+ void waiterLock()
+ {
+ if (!isSingleTask) waiterMutex.lock();
+ }
+
+ void waiterUnlock()
+ {
+ if (!isSingleTask) waiterMutex.unlock();
+ }
+
+ void wait()
+ {
+ if (!isSingleTask) workerCondition.wait();
+ }
+
+ void notify()
+ {
+ if (!isSingleTask) workerCondition.notify();
+ }
+
+ void notifyAll()
+ {
+ if (!isSingleTask) workerCondition.notifyAll();
+ }
+
+ void waitUntilCompletion()
+ {
+ if (isSingleTask)
+ {
+ singleTaskThread.join();
+ }
+ else
+ {
+ waiterCondition.wait();
+ }
+ }
+
+ void notifyWaiters()
+ {
+ if (!isSingleTask) waiterCondition.notifyAll();
+ }
+
+ // Private constructor for creating dummy pools that only have one thread,
+ // only execute one Task, and then terminate. This is used for
+ // Task.executeInNewThread().
+ this(AbstractTask* task, int priority = int.max)
+ {
+ assert(task);
+
+ // Dummy value, not used.
+ instanceStartIndex = 0;
+
+ this.isSingleTask = true;
+ task.taskStatus = TaskStatus.inProgress;
+ this.head = task;
+ singleTaskThread = new Thread(&doSingleTask);
+ singleTaskThread.start();
+
+ // Disabled until writing code to support
+ // running thread with specified priority
+ // See https://d.puremagic.com/issues/show_bug.cgi?id=8960
+
+ /*if (priority != int.max)
+ {
+ singleTaskThread.priority = priority;
+ }*/
+ }
+
+public:
+ // This is used in parallel_algorithm but is too unstable to document
+ // as public API.
+ size_t defaultWorkUnitSize(size_t rangeLen) const @safe pure nothrow
+ {
+ import std.algorithm.comparison : max;
+
+ if (this.size == 0)
+ {
+ return rangeLen;
+ }
+
+ immutable size_t eightSize = 4 * (this.size + 1);
+ auto ret = (rangeLen / eightSize) + ((rangeLen % eightSize == 0) ? 0 : 1);
+ return max(ret, 1);
+ }
+
+ /**
+ Default constructor that initializes a $(D TaskPool) with
+ $(D totalCPUs) - 1 worker threads. The minus 1 is included because the
+ main thread will also be available to do work.
+
+ Note: On single-core machines, the primitives provided by $(D TaskPool)
+ operate transparently in single-threaded mode.
+ */
+ this() @trusted
+ {
+ this(totalCPUs - 1);
+ }
+
+ /**
+ Allows for custom number of worker threads.
+ */
+ this(size_t nWorkers) @trusted
+ {
+ synchronized(typeid(TaskPool))
+ {
+ instanceStartIndex = nextInstanceIndex;
+
+ // The first worker thread to be initialized will have this index,
+ // and will increment it. The second worker to be initialized will
+ // have this index plus 1.
+ nextThreadIndex = instanceStartIndex;
+ nextInstanceIndex += nWorkers;
+ }
+
+ queueMutex = new Mutex(this);
+ waiterMutex = new Mutex();
+ workerCondition = new Condition(queueMutex);
+ waiterCondition = new Condition(waiterMutex);
+
+ pool = new ParallelismThread[nWorkers];
+ foreach (ref poolThread; pool)
+ {
+ poolThread = new ParallelismThread(&startWorkLoop);
+ poolThread.pool = this;
+ poolThread.start();
+ }
+ }
+
+ /**
+ Implements a parallel foreach loop over a range. This works by implicitly
+ creating and submitting one $(D Task) to the $(D TaskPool) for each worker
+ thread. A work unit is a set of consecutive elements of $(D range) to
+ be processed by a worker thread between communication with any other
+ thread. The number of elements processed per work unit is controlled by the
+ $(D workUnitSize) parameter. Smaller work units provide better load
+ balancing, but larger work units avoid the overhead of communicating
+ with other threads frequently to fetch the next work unit. Large work
+ units also avoid false sharing in cases where the range is being modified.
+ The less time a single iteration of the loop takes, the larger
+ $(D workUnitSize) should be. For very expensive loop bodies,
+ $(D workUnitSize) should be 1. An overload that chooses a default work
+ unit size is also available.
+
+ Example:
+ ---
+ // Find the logarithm of every number from 1 to
+ // 10_000_000 in parallel.
+ auto logs = new double[10_000_000];
+
+ // Parallel foreach works with or without an index
+ // variable. It can be iterate by ref if range.front
+ // returns by ref.
+
+ // Iterate over logs using work units of size 100.
+ foreach (i, ref elem; taskPool.parallel(logs, 100))
+ {
+ elem = log(i + 1.0);
+ }
+
+ // Same thing, but use the default work unit size.
+ //
+ // Timings on an Athlon 64 X2 dual core machine:
+ //
+ // Parallel foreach: 388 milliseconds
+ // Regular foreach: 619 milliseconds
+ foreach (i, ref elem; taskPool.parallel(logs))
+ {
+ elem = log(i + 1.0);
+ }
+ ---
+
+ Notes:
+
+ The memory usage of this implementation is guaranteed to be constant
+ in $(D range.length).
+
+ Breaking from a parallel foreach loop via a break, labeled break,
+ labeled continue, return or goto statement throws a
+ $(D ParallelForeachError).
+
+ In the case of non-random access ranges, parallel foreach buffers lazily
+ to an array of size $(D workUnitSize) before executing the parallel portion
+ of the loop. The exception is that, if a parallel foreach is executed
+ over a range returned by $(D asyncBuf) or $(D map), the copying is elided
+ and the buffers are simply swapped. In this case $(D workUnitSize) is
+ ignored and the work unit size is set to the buffer size of $(D range).
+
+ A memory barrier is guaranteed to be executed on exit from the loop,
+ so that results produced by all threads are visible in the calling thread.
+
+ $(B Exception Handling):
+
+ When at least one exception is thrown from inside a parallel foreach loop,
+ the submission of additional $(D Task) objects is terminated as soon as
+ possible, in a non-deterministic manner. All executing or
+ enqueued work units are allowed to complete. Then, all exceptions that
+ were thrown by any work unit are chained using $(D Throwable.next) and
+ rethrown. The order of the exception chaining is non-deterministic.
+ */
+ 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);
+ }
+
+
+ /// Ditto
+ ParallelForeach!R parallel(R)(R range)
+ {
+ static if (hasLength!R)
+ {
+ // Default work unit size is such that we would use 4x as many
+ // slots as are in this thread pool.
+ size_t workUnitSize = defaultWorkUnitSize(range.length);
+ return parallel(range, workUnitSize);
+ }
+ else
+ {
+ // Just use a really, really dumb guess if the user is too lazy to
+ // specify.
+ return parallel(range, 512);
+ }
+ }
+
+ ///
+ template amap(functions...)
+ {
+ /**
+ 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
+ $(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.
+
+ ---
+ auto numbers = iota(100_000_000.0);
+
+ // Find the square roots of numbers.
+ //
+ // Timings on an Athlon 64 X2 dual core machine:
+ //
+ // Parallel eager map: 0.802 s
+ // Equivalent serial implementation: 1.768 s
+ auto squareRoots = taskPool.amap!sqrt(numbers);
+ ---
+
+ Immediately after the range argument, an optional work unit size argument
+ may be provided. Work units as used by $(D amap) are identical to those
+ defined for parallel foreach. If no work unit size is provided, the
+ default work unit size is used.
+
+ ---
+ // Same thing, but make work unit size 100.
+ auto squareRoots = taskPool.amap!sqrt(numbers, 100);
+ ---
+
+ An output range for returning the results may be provided as the last
+ argument. If one is not provided, an array of the proper type will be
+ allocated on the garbage collected heap. If one is provided, it must be a
+ random access range with assignable elements, must have reference
+ semantics with respect to assignment to its elements, and must have the
+ same length as the input range. Writing to adjacent elements from
+ different threads must be safe.
+
+ ---
+ // Same thing, but explicitly allocate an array
+ // to return the results in. The element type
+ // of the array may be either the exact type
+ // returned by functions or an implicit conversion
+ // target.
+ auto squareRoots = new float[numbers.length];
+ taskPool.amap!sqrt(numbers, squareRoots);
+
+ // Multiple functions, explicit output range, and
+ // explicit work unit size.
+ auto results = new Tuple!(float, real)[numbers.length];
+ taskPool.amap!(sqrt, log)(numbers, 100, results);
+ ---
+
+ Note:
+
+ A memory barrier is guaranteed to be executed after all results are written
+ but before returning so that results produced by all threads are visible
+ in the calling thread.
+
+ Tips:
+
+ To perform the mapping operation in place, provide the same range for the
+ input and output range.
+
+ To parallelize the copying of a range with expensive to evaluate elements
+ to an array, pass an identity function (a function that just returns
+ whatever argument is provided to it) to $(D amap).
+
+ $(B Exception Handling):
+
+ When at least one exception is thrown from inside the map functions,
+ the submission of additional $(D Task) objects is terminated as soon as
+ possible, in a non-deterministic manner. All currently executing or
+ enqueued work units are allowed to complete. Then, all exceptions that
+ were thrown from any work unit are chained using $(D Throwable.next) and
+ rethrown. The order of the exception chaining is non-deterministic.
+ */
+ auto amap(Args...)(Args args)
+ if (isRandomAccessRange!(Args[0]))
+ {
+ import std.conv : emplaceRef;
+
+ alias fun = adjoin!(staticMap!(unaryFun, functions));
+
+ alias range = args[0];
+ immutable len = range.length;
+
+ 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];
+ enforce(buf.length == len,
+ 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)
+ {
+ 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;
+
+ static if (isIntegral!(Args2[$ - 1]))
+ {
+ static assert(args2.length == 2);
+ auto workUnitSize = cast(size_t) args2[1];
+ }
+ else
+ {
+ static assert(args2.length == 1, Args);
+ auto workUnitSize = defaultWorkUnitSize(range.length);
+ }
+
+ alias R = typeof(range);
+
+ if (workUnitSize > len)
+ {
+ workUnitSize = len;
+ }
+
+ // Handle as a special case:
+ if (size == 0)
+ {
+ size_t index = 0;
+ foreach (elem; range)
+ {
+ emplaceRef(buf[index++], fun(elem));
+ }
+ return buf;
+ }
+
+ // Effectively -1: chunkIndex + 1 == 0:
+ shared size_t workUnitIndex = size_t.max;
+ 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))
+ {
+ immutable myUnitIndex = atomicOp!"+="(workUnitIndex, 1);
+ immutable start = workUnitSize * myUnitIndex;
+ if (start >= len)
+ {
+ atomicStore(shouldContinue, false);
+ break;
+ }
+
+ immutable end = min(len, start + workUnitSize);
+
+ static if (hasSlicing!R)
+ {
+ auto subrange = range[start .. end];
+ foreach (i; start .. end)
+ {
+ emplaceRef(buf[i], fun(subrange.front));
+ subrange.popFront();
+ }
+ }
+ else
+ {
+ foreach (i; start .. end)
+ {
+ emplaceRef(buf[i], fun(range[i]));
+ }
+ }
+ }
+ }
+
+ submitAndExecute(this, &doIt);
+ return buf;
+ }
+ }
+
+ ///
+ template map(functions...)
+ {
+ /**
+ A semi-lazy parallel map that can be used for pipelining. The map
+ functions are evaluated for the first $(D bufSize) elements and stored in a
+ buffer and made available to $(D popFront). Meanwhile, in the
+ background a second buffer of the same size is filled. When the first
+ buffer is exhausted, it is swapped with the second buffer and filled while
+ the values from what was originally the second buffer are read. This
+ implementation allows for elements to be written to the buffer without
+ the need for atomic operations or synchronization for each write, and
+ enables the mapping function to be evaluated efficiently in parallel.
+
+ $(D map) has more overhead than the simpler procedure used by $(D amap)
+ but avoids the need to keep all results in memory simultaneously and works
+ with non-random access ranges.
+
+ Params:
+
+ source = The input range to be mapped. If $(D source) is not random
+ access it will be lazily buffered to an array of size $(D bufSize) before
+ the map function is evaluated. (For an exception to this rule, see Notes.)
+
+ bufSize = The size of the buffer to store the evaluated elements.
+
+ workUnitSize = The number of elements to evaluate in a single
+ $(D Task). Must be less than or equal to $(D bufSize), and
+ should be a fraction of $(D bufSize) such that all worker threads can be
+ used. If the default of size_t.max is used, workUnitSize will be set to
+ the pool-wide default.
+
+ Returns: An input range representing the results of the map. This range
+ has a length iff $(D source) has a length.
+
+ Notes:
+
+ If a range returned by $(D map) or $(D asyncBuf) is used as an input to
+ $(D map), then as an optimization the copying from the output buffer
+ of the first range to the input buffer of the second range is elided, even
+ though the ranges returned by $(D map) and $(D asyncBuf) are non-random
+ access ranges. This means that the $(D bufSize) parameter passed to the
+ current call to $(D map) will be ignored and the size of the buffer
+ will be the buffer size of $(D source).
+
+ Example:
+ ---
+ // Pipeline reading a file, converting each line
+ // to a number, taking the logarithms of the numbers,
+ // and performing the additions necessary to find
+ // the sum of the logarithms.
+
+ auto lineRange = File("numberList.txt").byLine();
+ auto dupedLines = std.algorithm.map!"a.idup"(lineRange);
+ auto nums = taskPool.map!(to!double)(dupedLines);
+ auto logs = taskPool.map!log10(nums);
+
+ double sum = 0;
+ foreach (elem; logs)
+ {
+ sum += elem;
+ }
+ ---
+
+ $(B Exception Handling):
+
+ Any exceptions thrown while iterating over $(D source)
+ or computing the map function are re-thrown on a call to $(D popFront) or,
+ if thrown during construction, are simply allowed to propagate to the
+ caller. In the case of exceptions thrown while computing the map function,
+ the exceptions are chained as in $(D TaskPool.amap).
+ */
+ auto
+ map(S)(S source, size_t bufSize = 100, size_t workUnitSize = size_t.max)
+ 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));
+
+ static final class Map
+ {
+ // This is a class because the task needs to be located on the
+ // heap and in the non-random access case source needs to be on
+ // the heap, too.
+
+ private:
+ enum bufferTrick = is(typeof(source.buf1)) &&
+ is(typeof(source.bufPos)) &&
+ is(typeof(source.doBufSwap()));
+
+ alias E = MapType!(S, functions);
+ E[] buf1, buf2;
+ S source;
+ TaskPool pool;
+ Task!(run, E[] delegate(E[]), E[]) nextBufTask;
+ size_t workUnitSize;
+ size_t bufPos;
+ bool lastTaskWaited;
+
+ static if (isRandomAccessRange!S)
+ {
+ alias FromType = S;
+
+ void popSource()
+ {
+ 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..$]))
+ {
+ source = source[min(buf1.length, source.length)..$];
+ }
+ else
+ {
+ static assert(0, "S must have slicing for Map."
+ ~ " " ~ S.stringof ~ " doesn't.");
+ }
+ }
+ }
+ else static if (bufferTrick)
+ {
+ // Make sure we don't have the buffer recycling overload of
+ // asyncBuf.
+ static if (
+ is(typeof(source.source)) &&
+ isRoundRobin!(typeof(source.source))
+ )
+ {
+ static assert(0, "Cannot execute a parallel map on " ~
+ "the buffer recycling overload of asyncBuf."
+ );
+ }
+
+ alias FromType = typeof(source.buf1);
+ FromType from;
+
+ // Just swap our input buffer with source's output buffer.
+ // 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);
+
+ // Just in case this source has been popped before
+ // being sent to map:
+ from = from[source.bufPos..$];
+
+ static if (is(typeof(source._length)))
+ {
+ source._length -= (from.length - source.bufPos);
+ }
+
+ source.doBufSwap();
+
+ return from;
+ }
+ }
+ else
+ {
+ alias FromType = ElementType!S[];
+
+ // The temporary array that data is copied to before being
+ // mapped.
+ FromType from;
+
+ FromType dumpToFrom()
+ {
+ assert(from !is null);
+
+ size_t i;
+ for (; !source.empty && i < from.length; source.popFront())
+ {
+ from[i++] = source.front;
+ }
+
+ from = from[0 .. i];
+ return from;
+ }
+ }
+
+ static if (hasLength!S)
+ {
+ size_t _length;
+
+ public @property size_t length() const @safe pure nothrow
+ {
+ return _length;
+ }
+ }
+
+ this(S source, size_t bufSize, size_t workUnitSize, TaskPool pool)
+ {
+ static if (bufferTrick)
+ {
+ bufSize = source.buf1.length;
+ }
+
+ buf1.length = bufSize;
+ buf2.length = bufSize;
+
+ static if (!isRandomAccessRange!S)
+ {
+ from.length = bufSize;
+ }
+
+ this.workUnitSize = (workUnitSize == size_t.max) ?
+ pool.defaultWorkUnitSize(bufSize) : workUnitSize;
+ this.source = source;
+ this.pool = pool;
+
+ static if (hasLength!S)
+ {
+ _length = source.length;
+ }
+
+ buf1 = fillBuf(buf1);
+ submitBuf2();
+ }
+
+ // The from parameter is a dummy and ignored in the random access
+ // case.
+ E[] fillBuf(E[] buf)
+ {
+ import std.algorithm.comparison : min;
+
+ static if (isRandomAccessRange!S)
+ {
+ import std.range : take;
+ auto toMap = take(source, buf.length);
+ scope(success) popSource();
+ }
+ else
+ {
+ auto toMap = dumpToFrom();
+ }
+
+ buf = buf[0 .. min(buf.length, toMap.length)];
+
+ // Handle as a special case:
+ if (pool.size == 0)
+ {
+ size_t index = 0;
+ foreach (elem; toMap)
+ {
+ buf[index++] = fun(elem);
+ }
+ return buf;
+ }
+
+ pool.amap!functions(toMap, workUnitSize, buf);
+
+ return buf;
+ }
+
+ void submitBuf2()
+ in
+ {
+ assert(nextBufTask.prev is null);
+ assert(nextBufTask.next is null);
+ } body
+ {
+ // Hack to reuse the task object.
+
+ nextBufTask = typeof(nextBufTask).init;
+ nextBufTask._args[0] = &fillBuf;
+ nextBufTask._args[1] = buf2;
+ pool.put(nextBufTask);
+ }
+
+ void doBufSwap()
+ {
+ if (lastTaskWaited)
+ {
+ // Then the source is empty. Signal it here.
+ buf1 = null;
+ buf2 = null;
+
+ static if (!isRandomAccessRange!S)
+ {
+ from = null;
+ }
+
+ return;
+ }
+
+ buf2 = buf1;
+ buf1 = nextBufTask.yieldForce;
+ bufPos = 0;
+
+ if (source.empty)
+ {
+ lastTaskWaited = true;
+ }
+ else
+ {
+ submitBuf2();
+ }
+ }
+
+ public:
+ @property auto front()
+ {
+ return buf1[bufPos];
+ }
+
+ void popFront()
+ {
+ static if (hasLength!S)
+ {
+ _length--;
+ }
+
+ bufPos++;
+ if (bufPos >= buf1.length)
+ {
+ doBufSwap();
+ }
+ }
+
+ static if (isInfinite!S)
+ {
+ enum bool empty = false;
+ }
+ else
+ {
+
+ bool empty() const @property
+ {
+ // popFront() sets this when source is empty
+ return buf1.length == 0;
+ }
+ }
+ }
+ return new Map(source, bufSize, workUnitSize, this);
+ }
+ }
+
+ /**
+ Given a $(D source) range that is expensive to iterate over, returns an
+ input range that asynchronously buffers the contents of
+ $(D source) into a buffer of $(D bufSize) elements in a worker thread,
+ while making previously buffered elements from a second buffer, also of size
+ $(D bufSize), available via the range interface of the returned
+ object. The returned range has a length iff $(D hasLength!S).
+ $(D asyncBuf) is useful, for example, when performing expensive operations
+ on the elements of ranges that represent data on a disk or network.
+
+ Example:
+ ---
+ import std.conv, std.stdio;
+
+ void main()
+ {
+ // Fetch lines of a file in a background thread
+ // while processing previously fetched lines,
+ // dealing with byLine's buffer recycling by
+ // eagerly duplicating every line.
+ auto lines = File("foo.txt").byLine();
+ auto duped = std.algorithm.map!"a.idup"(lines);
+
+ // Fetch more lines in the background while we
+ // process the lines already read into memory
+ // into a matrix of doubles.
+ double[][] matrix;
+ auto asyncReader = taskPool.asyncBuf(duped);
+
+ foreach (line; asyncReader)
+ {
+ auto ls = line.split("\t");
+ matrix ~= to!(double[])(ls);
+ }
+ }
+ ---
+
+ $(B Exception Handling):
+
+ Any exceptions thrown while iterating over $(D source) are re-thrown on a
+ 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)
+ {
+ static final class AsyncBuf
+ {
+ // This is a class because the task and source both need to be on
+ // the heap.
+
+ // The element type of S.
+ alias E = ElementType!S; // Needs to be here b/c of forward ref bugs.
+
+ private:
+ E[] buf1, buf2;
+ S source;
+ TaskPool pool;
+ Task!(run, E[] delegate(E[]), E[]) nextBufTask;
+ size_t bufPos;
+ bool lastTaskWaited;
+
+ static if (hasLength!S)
+ {
+ size_t _length;
+
+ // Available if hasLength!S.
+ public @property size_t length() const @safe pure nothrow
+ {
+ return _length;
+ }
+ }
+
+ this(S source, size_t bufSize, TaskPool pool)
+ {
+ buf1.length = bufSize;
+ buf2.length = bufSize;
+
+ this.source = source;
+ this.pool = pool;
+
+ static if (hasLength!S)
+ {
+ _length = source.length;
+ }
+
+ buf1 = fillBuf(buf1);
+ submitBuf2();
+ }
+
+ E[] fillBuf(E[] buf)
+ {
+ assert(buf !is null);
+
+ size_t i;
+ for (; !source.empty && i < buf.length; source.popFront())
+ {
+ buf[i++] = source.front;
+ }
+
+ buf = buf[0 .. i];
+ return buf;
+ }
+
+ void submitBuf2()
+ in
+ {
+ assert(nextBufTask.prev is null);
+ assert(nextBufTask.next is null);
+ } body
+ {
+ // Hack to reuse the task object.
+
+ nextBufTask = typeof(nextBufTask).init;
+ nextBufTask._args[0] = &fillBuf;
+ nextBufTask._args[1] = buf2;
+ pool.put(nextBufTask);
+ }
+
+ void doBufSwap()
+ {
+ if (lastTaskWaited)
+ {
+ // Then source is empty. Signal it here.
+ buf1 = null;
+ buf2 = null;
+ return;
+ }
+
+ buf2 = buf1;
+ buf1 = nextBufTask.yieldForce;
+ bufPos = 0;
+
+ if (source.empty)
+ {
+ lastTaskWaited = true;
+ }
+ else
+ {
+ submitBuf2();
+ }
+ }
+
+ public:
+ E front() @property
+ {
+ return buf1[bufPos];
+ }
+
+ void popFront()
+ {
+ static if (hasLength!S)
+ {
+ _length--;
+ }
+
+ bufPos++;
+ if (bufPos >= buf1.length)
+ {
+ doBufSwap();
+ }
+ }
+
+ static if (isInfinite!S)
+ {
+ enum bool empty = false;
+ }
+
+ else
+ {
+ ///
+ bool empty() @property
+ {
+ // popFront() sets this when source is empty:
+ return buf1.length == 0;
+ }
+ }
+ }
+ return new AsyncBuf(source, bufSize, this);
+ }
+
+ /**
+ Given a callable object $(D next) that writes to a user-provided buffer and
+ a second callable object $(D empty) that determines whether more data is
+ available to write via $(D next), returns an input range that
+ asynchronously calls $(D next) with a set of size $(D nBuffers) of buffers
+ and makes the results available in the order they were obtained via the
+ input range interface of the returned object. Similarly to the
+ input range overload of $(D asyncBuf), the first half of the buffers
+ are made available via the range interface while the second half are
+ filled and vice-versa.
+
+ Params:
+
+ next = A callable object that takes a single argument that must be an array
+ with mutable elements. When called, $(D next) writes data to
+ the array provided by the caller.
+
+ empty = A callable object that takes no arguments and returns a type
+ implicitly convertible to $(D bool). This is used to signify
+ that no more data is available to be obtained by calling $(D next).
+
+ initialBufSize = The initial size of each buffer. If $(D next) takes its
+ array by reference, it may resize the buffers.
+
+ nBuffers = The number of buffers to cycle through when calling $(D next).
+
+ Example:
+ ---
+ // Fetch lines of a file in a background
+ // thread while processing previously fetched
+ // lines, without duplicating any lines.
+ auto file = File("foo.txt");
+
+ void next(ref char[] buf)
+ {
+ file.readln(buf);
+ }
+
+ // Fetch more lines in the background while we
+ // process the lines already read into memory
+ // into a matrix of doubles.
+ double[][] matrix;
+ auto asyncReader = taskPool.asyncBuf(&next, &file.eof);
+
+ foreach (line; asyncReader)
+ {
+ auto ls = line.split("\t");
+ matrix ~= to!(double[])(ls);
+ }
+ ---
+
+ $(B Exception Handling):
+
+ Any exceptions thrown while iterating over $(D range) are re-thrown on a
+ call to $(D popFront).
+
+ Warning:
+
+ Using the range returned by this function in a parallel foreach loop
+ will not work because buffers may be overwritten while the task that
+ processes them is in queue. This is checked for at compile time
+ 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) &&
+ 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);
+ }
+
+ ///
+ template reduce(functions...)
+ {
+ /**
+ 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
+ point numbers using this function may give different results than summing
+ 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 $(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 $(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.
+ //
+ // Timings on an Athlon 64 X2 dual core machine:
+ //
+ // Parallel reduce: 72 milliseconds
+ // Using std.algorithm.reduce instead: 181 milliseconds
+ auto nums = iota(10_000_000.0f);
+ auto sumSquares = taskPool.reduce!"a + b"(
+ 0.0, std.algorithm.map!"a * a"(nums)
+ );
+ ---
+
+ If no explicit seed is provided, the first element of each work unit
+ is used as a seed. For the final reduction, the result from the first
+ work unit is used as the seed.
+ ---
+ // Find the sum of a range in parallel, using the first
+ // element of each work unit as the seed.
+ auto sum = taskPool.reduce!"a + b"(nums);
+ ---
+
+ An explicit work unit size may be specified as the last argument.
+ Specifying too small a work unit size will effectively serialize the
+ reduction, as the final reduction of the result of each work unit will
+ dominate computation time. If $(D TaskPool.size) for this instance
+ is zero, this parameter is ignored and one work unit is used.
+ ---
+ // Use a work unit size of 100.
+ auto sum2 = taskPool.reduce!"a + b"(nums, 100);
+
+ // Work unit size of 100 and explicit seed.
+ auto sum3 = taskPool.reduce!"a + b"(0.0, nums, 100);
+ ---
+
+ Parallel reduce supports multiple functions, like
+ $(D std.algorithm.reduce).
+ ---
+ // Find both the min and max of nums.
+ auto minMax = taskPool.reduce!(min, max)(nums);
+ assert(minMax[0] == reduce!min(nums));
+ assert(minMax[1] == reduce!max(nums));
+ ---
+
+ $(B Exception Handling):
+
+ After this function is finished executing, any exceptions thrown
+ are chained together via $(D Throwable.next) and rethrown. The chaining
+ order is non-deterministic.
+ */
+ 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]))
+ {
+ size_t workUnitSize = cast(size_t) args[$ - 1];
+ alias args2 = args[0..$ - 1];
+ alias Args2 = Args[0..$ - 1];
+ }
+ else
+ {
+ alias args2 = args;
+ alias Args2 = Args;
+ }
+
+ auto makeStartValue(Type)(Type e)
+ {
+ static if (functions.length == 1)
+ {
+ return e;
+ }
+ else
+ {
+ typeof(adjoin!(staticMap!(binaryFun, functions))(e, e)) seed = void;
+ foreach (i, T; seed.Types)
+ {
+ emplaceRef(seed.expand[i], e);
+ }
+
+ return seed;
+ }
+ }
+
+ 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)))
+ {
+ size_t workUnitSize = defaultWorkUnitSize(range.length);
+ }
+ }
+ else
+ {
+ static assert(args2.length == 1);
+ alias range = args2[0];
+
+ static if (!is(typeof(workUnitSize)))
+ {
+ size_t workUnitSize = defaultWorkUnitSize(range.length);
+ }
+
+ enforce(!range.empty,
+ "Cannot reduce an empty range with first element as start value.");
+
+ auto seed = makeStartValue(range.front);
+ enum explicitSeed = false;
+ range.popFront();
+ }
+
+ alias E = typeof(seed);
+ alias R = typeof(range);
+
+ E reduceOnRange(R range, size_t lowerBound, size_t upperBound)
+ {
+ // This is for exploiting instruction level parallelism by
+ // using multiple accumulator variables within each thread,
+ // since we're assuming functions are associative anyhow.
+
+ // This is so that loops can be unrolled automatically.
+ enum ilpTuple = AliasSeq!(0, 1, 2, 3, 4, 5);
+ enum nILP = ilpTuple.length;
+ immutable subSize = (upperBound - lowerBound) / nILP;
+
+ if (subSize <= 1)
+ {
+ // Handle as a special case.
+ static if (explicitSeed)
+ {
+ E result = seed;
+ }
+ else
+ {
+ E result = makeStartValue(range[lowerBound]);
+ lowerBound++;
+ }
+
+ foreach (i; lowerBound .. upperBound)
+ {
+ result = fun(result, range[i]);
+ }
+
+ return result;
+ }
+
+ assert(subSize > 1);
+ E[nILP] results;
+ size_t[nILP] offsets;
+
+ foreach (i; ilpTuple)
+ {
+ offsets[i] = lowerBound + subSize * i;
+
+ static if (explicitSeed)
+ {
+ results[i] = seed;
+ }
+ else
+ {
+ results[i] = makeStartValue(range[offsets[i]]);
+ offsets[i]++;
+ }
+ }
+
+ immutable nLoop = subSize - (!explicitSeed);
+ foreach (i; 0 .. nLoop)
+ {
+ foreach (j; ilpTuple)
+ {
+ results[j] = fun(results[j], range[offsets[j]]);
+ offsets[j]++;
+ }
+ }
+
+ // Finish the remainder.
+ foreach (i; nILP * subSize + lowerBound .. upperBound)
+ {
+ results[$ - 1] = fun(results[$ - 1], range[i]);
+ }
+
+ foreach (i; ilpTuple[1..$])
+ {
+ results[0] = finishFun(results[0], results[i]);
+ }
+
+ return results[0];
+ }
+
+ immutable len = range.length;
+ if (len == 0)
+ {
+ return seed;
+ }
+
+ if (this.size == 0)
+ {
+ return finishFun(seed, reduceOnRange(range, 0, len));
+ }
+
+ // Unlike the rest of the functions here, I can't use the Task object
+ // recycling trick here because this has to work on non-commutative
+ // operations. After all the tasks are done executing, fun() has to
+ // be applied on the results of these to get a final result, but
+ // it can't be evaluated out of order.
+
+ if (workUnitSize > len)
+ {
+ workUnitSize = len;
+ }
+
+ immutable size_t nWorkUnits = (len / workUnitSize) + ((len % workUnitSize == 0) ? 0 : 1);
+ assert(nWorkUnits * workUnitSize >= len);
+
+ alias RTask = Task!(run, typeof(&reduceOnRange), R, size_t, size_t);
+ RTask[] tasks;
+
+ // Can't use alloca() due to Bug 3753. Use a fixed buffer
+ // backed by malloc().
+ enum maxStack = 2_048;
+ byte[maxStack] buf = void;
+ immutable size_t nBytesNeeded = nWorkUnits * RTask.sizeof;
+
+ import core.stdc.stdlib : malloc, free;
+ if (nBytesNeeded < maxStack)
+ {
+ tasks = (cast(RTask*) buf.ptr)[0 .. nWorkUnits];
+ }
+ else
+ {
+ auto ptr = cast(RTask*) malloc(nBytesNeeded);
+ if (!ptr)
+ {
+ throw new OutOfMemoryError(
+ "Out of memory in std.parallelism."
+ );
+ }
+
+ tasks = ptr[0 .. nWorkUnits];
+ }
+
+ scope(exit)
+ {
+ if (nBytesNeeded > maxStack)
+ {
+ free(tasks.ptr);
+ }
+ }
+
+ 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) @system
+ {
+ 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.
+ task._args[1] = range; // range
+ task._args[2] = curPos; // lower bound.
+
+ curPos += workUnitSize;
+ }
+
+ foreach (ref task; tasks)
+ {
+ useTask(task);
+ }
+
+ foreach (i; 1 .. tasks.length - 1)
+ {
+ tasks[i].next = tasks[i + 1].basePtr;
+ tasks[i + 1].prev = tasks[i].basePtr;
+ }
+
+ if (tasks.length > 1)
+ {
+ queueLock();
+ scope(exit) queueUnlock();
+
+ abstractPutGroupNoSync(
+ tasks[1].basePtr,
+ tasks[$ - 1].basePtr
+ );
+ }
+
+ if (tasks.length > 0)
+ {
+ try
+ {
+ tasks[0].job();
+ }
+ 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..$])
+ {
+ tryDeleteExecute(task.basePtr);
+ }
+ }
+
+ // Now that we've tried to execute every task, they're all either
+ // done or in progress. Force all of them.
+ E result = seed;
+
+ Throwable firstException, lastException;
+
+ foreach (ref task; tasks)
+ {
+ try
+ {
+ task.yieldForce;
+ }
+ catch (Throwable e)
+ {
+ addToChain(e, firstException, lastException);
+ continue;
+ }
+
+ if (!firstException) result = finishFun(result, task.returnVal);
+ }
+
+ if (firstException) throw firstException;
+
+ return result;
+ }
+ }
+
+ /**
+ Gets the index of the current thread relative to this $(D TaskPool). Any
+ thread not in this pool will receive an index of 0. The worker threads in
+ this pool receive unique indices of 1 through $(D this.size).
+
+ This function is useful for maintaining worker-local resources.
+
+ Example:
+ ---
+ // Execute a loop that computes the greatest common
+ // divisor of every number from 0 through 999 with
+ // 42 in parallel. Write the results out to
+ // a set of files, one for each thread. This allows
+ // results to be written out without any synchronization.
+
+ import std.conv, std.range, std.numeric, std.stdio;
+
+ void main()
+ {
+ auto filesHandles = new File[taskPool.size + 1];
+ scope(exit) {
+ foreach (ref handle; fileHandles)
+ {
+ handle.close();
+ }
+ }
+
+ foreach (i, ref handle; fileHandles)
+ {
+ handle = File("workerResults" ~ to!string(i) ~ ".txt");
+ }
+
+ foreach (num; parallel(iota(1_000)))
+ {
+ auto outHandle = fileHandles[taskPool.workerIndex];
+ outHandle.writeln(num, '\t', gcd(num, 42));
+ }
+ }
+ ---
+ */
+ size_t workerIndex() @property @safe const nothrow
+ {
+ immutable rawInd = threadIndex;
+ return (rawInd >= instanceStartIndex && rawInd < instanceStartIndex + size) ?
+ (rawInd - instanceStartIndex + 1) : 0;
+ }
+
+ /**
+ Struct for creating worker-local storage. Worker-local storage is
+ thread-local storage that exists only for worker threads in a given
+ $(D TaskPool) plus a single thread outside the pool. It is allocated on the
+ garbage collected heap in a way that avoids _false sharing, and doesn't
+ necessarily have global scope within any thread. It can be accessed from
+ any worker thread in the $(D TaskPool) that created it, and one thread
+ outside this $(D TaskPool). All threads outside the pool that created a
+ given instance of worker-local storage share a single slot.
+
+ Since the underlying data for this struct is heap-allocated, this struct
+ has reference semantics when passed between functions.
+
+ The main uses cases for $(D WorkerLocalStorageStorage) are:
+
+ 1. Performing parallel reductions with an imperative, as opposed to
+ functional, programming style. In this case, it's useful to treat
+ $(D WorkerLocalStorageStorage) as local to each thread for only the parallel
+ portion of an algorithm.
+
+ 2. Recycling temporary buffers across iterations of a parallel foreach loop.
+
+ Example:
+ ---
+ // Calculate pi as in our synopsis example, but
+ // use an imperative instead of a functional style.
+ immutable n = 1_000_000_000;
+ immutable delta = 1.0L / n;
+
+ auto sums = taskPool.workerLocalStorage(0.0L);
+ foreach (i; parallel(iota(n)))
+ {
+ immutable x = ( i - 0.5L ) * delta;
+ immutable toAdd = delta / ( 1.0 + x * x );
+ sums.get += toAdd;
+ }
+
+ // Add up the results from each worker thread.
+ real pi = 0;
+ foreach (threadResult; sums.toRange)
+ {
+ pi += 4.0L * threadResult;
+ }
+ ---
+ */
+ static struct WorkerLocalStorage(T)
+ {
+ private:
+ TaskPool pool;
+ size_t size;
+
+ size_t elemSize;
+ bool* stillThreadLocal;
+
+ static size_t roundToLine(size_t num) pure nothrow
+ {
+ if (num % cacheLineSize == 0)
+ {
+ return num;
+ }
+ else
+ {
+ return ((num / cacheLineSize) + 1) * cacheLineSize;
+ }
+ }
+
+ void* data;
+
+ void initialize(TaskPool pool)
+ {
+ this.pool = pool;
+ size = pool.size + 1;
+ stillThreadLocal = new bool;
+ *stillThreadLocal = true;
+
+ // Determines whether the GC should scan the array.
+ auto blkInfo = (typeid(T).flags & 1) ?
+ cast(GC.BlkAttr) 0 :
+ GC.BlkAttr.NO_SCAN;
+
+ immutable nElem = pool.size + 1;
+ elemSize = roundToLine(T.sizeof);
+
+ // The + 3 is to pad one full cache line worth of space on either side
+ // of the data structure to make sure false sharing with completely
+ // unrelated heap data is prevented, and to provide enough padding to
+ // make sure that data is cache line-aligned.
+ data = GC.malloc(elemSize * (nElem + 3), blkInfo) + elemSize;
+
+ // Cache line align data ptr.
+ data = cast(void*) roundToLine(cast(size_t) data);
+
+ foreach (i; 0 .. nElem)
+ {
+ this.opIndex(i) = T.init;
+ }
+ }
+
+ ref opIndex(this Qualified)(size_t index)
+ {
+ import std.conv : text;
+ assert(index < size, text(index, '\t', uint.max));
+ return *(cast(CopyTypeQualifiers!(Qualified, T)*) (data + elemSize * index));
+ }
+
+ void opIndexAssign(T val, size_t index)
+ {
+ assert(index < size);
+ *(cast(T*) (data + elemSize * index)) = val;
+ }
+
+ public:
+ /**
+ Get the current thread's instance. Returns by ref.
+ Note that calling $(D get) from any thread
+ outside the $(D TaskPool) that created this instance will return the
+ same reference, so an instance of worker-local storage should only be
+ accessed from one thread outside the pool that created it. If this
+ rule is violated, undefined behavior will result.
+
+ If assertions are enabled and $(D toRange) has been called, then this
+ WorkerLocalStorage instance is no longer worker-local and an assertion
+ failure will result when calling this method. This is not checked
+ when assertions are disabled for performance reasons.
+ */
+ ref get(this Qualified)() @property
+ {
+ assert(*stillThreadLocal,
+ "Cannot call get() on this instance of WorkerLocalStorage " ~
+ "because it is no longer worker-local."
+ );
+ return opIndex(pool.workerIndex);
+ }
+
+ /**
+ Assign a value to the current thread's instance. This function has
+ the same caveats as its overload.
+ */
+ void get(T val) @property
+ {
+ assert(*stillThreadLocal,
+ "Cannot call get() on this instance of WorkerLocalStorage " ~
+ "because it is no longer worker-local."
+ );
+
+ opIndexAssign(val, pool.workerIndex);
+ }
+
+ /**
+ Returns a range view of the values for all threads, which can be used
+ to further process the results of each thread after running the parallel
+ part of your algorithm. Do not use this method in the parallel portion
+ of your algorithm.
+
+ Calling this function sets a flag indicating that this struct is no
+ longer worker-local, and attempting to use the $(D get) method again
+ will result in an assertion failure if assertions are enabled.
+ */
+ WorkerLocalStorageRange!T toRange() @property
+ {
+ if (*stillThreadLocal)
+ {
+ *stillThreadLocal = false;
+
+ // Make absolutely sure results are visible to all threads.
+ // This is probably not necessary since some other
+ // synchronization primitive will be used to signal that the
+ // parallel part of the algorithm is done, but the
+ // performance impact should be negligible, so it's better
+ // to be safe.
+ ubyte barrierDummy;
+ atomicSetUbyte(barrierDummy, 1);
+ }
+
+ return WorkerLocalStorageRange!T(this);
+ }
+ }
+
+ /**
+ Range primitives for worker-local storage. The purpose of this is to
+ access results produced by each worker thread from a single thread once you
+ are no longer using the worker-local storage from multiple threads.
+ Do not use this struct in the parallel portion of your algorithm.
+
+ The proper way to instantiate this object is to call
+ $(D WorkerLocalStorage.toRange). Once instantiated, this object behaves
+ as a finite random-access range with assignable, lvalue elements and
+ a length equal to the number of worker threads in the $(D TaskPool) that
+ created it plus 1.
+ */
+ static struct WorkerLocalStorageRange(T)
+ {
+ private:
+ WorkerLocalStorage!T workerLocalStorage;
+
+ size_t _length;
+ size_t beginOffset;
+
+ this(WorkerLocalStorage!T wl)
+ {
+ this.workerLocalStorage = wl;
+ _length = wl.size;
+ }
+
+ public:
+ ref front(this Qualified)() @property
+ {
+ return this[0];
+ }
+
+ ref back(this Qualified)() @property
+ {
+ return this[_length - 1];
+ }
+
+ void popFront()
+ {
+ if (_length > 0)
+ {
+ beginOffset++;
+ _length--;
+ }
+ }
+
+ void popBack()
+ {
+ if (_length > 0)
+ {
+ _length--;
+ }
+ }
+
+ typeof(this) save() @property
+ {
+ return this;
+ }
+
+ ref opIndex(this Qualified)(size_t index)
+ {
+ assert(index < _length);
+ return workerLocalStorage[index + beginOffset];
+ }
+
+ void opIndexAssign(T val, size_t index)
+ {
+ assert(index < _length);
+ workerLocalStorage[index] = val;
+ }
+
+ typeof(this) opSlice(size_t lower, size_t upper)
+ {
+ assert(upper <= _length);
+ auto newWl = this.workerLocalStorage;
+ newWl.data += lower * newWl.elemSize;
+ newWl.size = upper - lower;
+ return typeof(this)(newWl);
+ }
+
+ bool empty() const @property
+ {
+ return length == 0;
+ }
+
+ size_t length() const @property
+ {
+ return _length;
+ }
+ }
+
+ /**
+ Creates an instance of worker-local storage, initialized with a given
+ value. The value is $(D lazy) so that you can, for example, easily
+ create one instance of a class for each worker. For usage example,
+ see the $(D WorkerLocalStorage) struct.
+ */
+ WorkerLocalStorage!T workerLocalStorage(T)(lazy T initialVal = T.init)
+ {
+ WorkerLocalStorage!T ret;
+ ret.initialize(this);
+ foreach (i; 0 .. size + 1)
+ {
+ ret[i] = initialVal;
+ }
+
+ // Memory barrier to make absolutely sure that what we wrote is
+ // visible to worker threads.
+ ubyte barrierDummy;
+ atomicSetUbyte(barrierDummy, 0);
+
+ return ret;
+ }
+
+ /**
+ Signals to all worker threads to terminate as soon as they are finished
+ with their current $(D Task), or immediately if they are not executing a
+ $(D Task). $(D Task)s that were in queue will not be executed unless
+ a call to $(D Task.workForce), $(D Task.yieldForce) or $(D Task.spinForce)
+ causes them to be executed.
+
+ Use only if you have waited on every $(D Task) and therefore know the
+ queue is empty, or if you speculatively executed some tasks and no longer
+ need the results.
+ */
+ void stop() @trusted
+ {
+ queueLock();
+ scope(exit) queueUnlock();
+ atomicSetUbyte(status, PoolState.stopNow);
+ notifyAll();
+ }
+
+ /**
+ Signals worker threads to terminate when the queue becomes empty.
+
+ If blocking argument is true, wait for all worker threads to terminate
+ before returning. This option might be used in applications where
+ task results are never consumed-- e.g. when $(D TaskPool) is employed as a
+ rudimentary scheduler for tasks which communicate by means other than
+ return values.
+
+ Warning: Calling this function with $(D blocking = true) from a worker
+ thread that is a member of the same $(D TaskPool) that
+ $(D finish) is being called on will result in a deadlock.
+ */
+ void finish(bool blocking = false) @trusted
+ {
+ {
+ queueLock();
+ scope(exit) queueUnlock();
+ atomicCasUbyte(status, PoolState.running, PoolState.finishing);
+ notifyAll();
+ }
+ if (blocking)
+ {
+ // Use this thread as a worker until everything is finished.
+ executeWorkLoop();
+
+ foreach (t; pool)
+ {
+ // Maybe there should be something here to prevent a thread
+ // from calling join() on itself if this function is called
+ // from a worker thread in the same pool, but:
+ //
+ // 1. Using an if statement to skip join() would result in
+ // finish() returning without all tasks being finished.
+ //
+ // 2. If an exception were thrown, it would bubble up to the
+ // Task from which finish() was called and likely be
+ // swallowed.
+ t.join();
+ }
+ }
+ }
+
+ /// Returns the number of worker threads in the pool.
+ @property size_t size() @safe const pure nothrow
+ {
+ return pool.length;
+ }
+
+ /**
+ Put a $(D Task) object on the back of the task queue. The $(D Task)
+ object may be passed by pointer or reference.
+
+ Example:
+ ---
+ import std.file;
+
+ // Create a task.
+ auto t = task!read("foo.txt");
+
+ // Add it to the queue to be executed.
+ taskPool.put(t);
+ ---
+
+ Notes:
+
+ @trusted overloads of this function are called for $(D Task)s if
+ $(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
+ and executed from $(D @safe) code via $(D Task.executeInNewThread) but
+ not via $(D TaskPool).
+
+ While this function takes the address of variables that may
+ be on the stack, some overloads are marked as @trusted.
+ $(D Task) includes a destructor that waits for the task to complete
+ before destroying the stack frame it is allocated on. Therefore,
+ it is impossible for the stack frame to be destroyed before the task is
+ complete and no longer referenced by a $(D TaskPool).
+ */
+ void put(alias fun, Args...)(ref Task!(fun, Args) task)
+ if (!isSafeReturn!(typeof(task)))
+ {
+ task.pool = this;
+ abstractPut(task.basePtr);
+ }
+
+ /// Ditto
+ void put(alias fun, Args...)(Task!(fun, Args)* 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)))
+ {
+ task.pool = this;
+ abstractPut(task.basePtr);
+ }
+
+ @trusted void put(alias fun, Args...)(Task!(fun, Args)* task)
+ if (isSafeReturn!(typeof(*task)))
+ {
+ import std.exception : enforce;
+ enforce(task !is null, "Cannot put a null Task on a TaskPool queue.");
+ put(*task);
+ }
+
+ /**
+ These properties control whether the worker threads are daemon threads.
+ A daemon thread is automatically terminated when all non-daemon threads
+ have terminated. A non-daemon thread will prevent a program from
+ terminating as long as it has not terminated.
+
+ If any $(D TaskPool) with non-daemon threads is active, either $(D stop)
+ or $(D finish) must be called on it before the program can terminate.
+
+ The worker treads in the $(D TaskPool) instance returned by the
+ $(D taskPool) property are daemon by default. The worker threads of
+ manually instantiated task pools are non-daemon by default.
+
+ Note: For a size zero pool, the getter arbitrarily returns true and the
+ setter has no effect.
+ */
+ bool isDaemon() @property @trusted
+ {
+ queueLock();
+ scope(exit) queueUnlock();
+ return (size == 0) ? true : pool[0].isDaemon;
+ }
+
+ /// Ditto
+ void isDaemon(bool newVal) @property @trusted
+ {
+ queueLock();
+ scope(exit) queueUnlock();
+ foreach (thread; pool)
+ {
+ thread.isDaemon = newVal;
+ }
+ }
+
+ /**
+ These functions allow getting and setting the OS scheduling priority of
+ the worker threads in this $(D TaskPool). They forward to
+ $(D core.thread.Thread.priority), so a given priority value here means the
+ same thing as an identical priority value in $(D core.thread).
+
+ Note: For a size zero pool, the getter arbitrarily returns
+ $(D core.thread.Thread.PRIORITY_MIN) and the setter has no effect.
+ */
+ int priority() @property @trusted
+ {
+ return (size == 0) ? core.thread.Thread.PRIORITY_MIN :
+ pool[0].priority;
+ }
+
+ /// Ditto
+ void priority(int newPriority) @property @trusted
+ {
+ if (size > 0)
+ {
+ foreach (t; pool)
+ {
+ t.priority = newPriority;
+ }
+ }
+ }
+}
+
+/**
+Returns a lazily initialized global instantiation of $(D TaskPool).
+This function can safely be called concurrently from multiple non-worker
+threads. The worker threads in this pool are daemon threads, meaning that it
+is not necessary to call $(D TaskPool.stop) or $(D TaskPool.finish) before
+terminating the main thread.
+*/
+@property TaskPool taskPool() @trusted
+{
+ import std.concurrency : initOnce;
+ __gshared TaskPool pool;
+ return initOnce!pool({
+ auto p = new TaskPool(defaultPoolThreads);
+ p.isDaemon = true;
+ return p;
+ }());
+}
+
+private shared uint _defaultPoolThreads;
+shared static this()
+{
+ atomicStore(_defaultPoolThreads, totalCPUs - 1);
+}
+
+/**
+These properties get and set the number of worker threads in the $(D TaskPool)
+instance returned by $(D taskPool). The default value is $(D totalCPUs) - 1.
+Calling the setter after the first call to $(D taskPool) does not changes
+number of worker threads in the instance returned by $(D taskPool).
+*/
+@property uint defaultPoolThreads() @trusted
+{
+ return atomicLoad(_defaultPoolThreads);
+}
+
+/// Ditto
+@property void defaultPoolThreads(uint newVal) @trusted
+{
+ atomicStore(_defaultPoolThreads, newVal);
+}
+
+/**
+Convenience functions that forwards to $(D taskPool.parallel). The
+purpose of these is to make parallel foreach less verbose and more
+readable.
+
+Example:
+---
+// Find the logarithm of every number from
+// 1 to 1_000_000 in parallel, using the
+// default TaskPool instance.
+auto logs = new double[1_000_000];
+
+foreach (i, ref elem; parallel(logs))
+{
+ elem = log(i + 1.0);
+}
+---
+
+*/
+ParallelForeach!R parallel(R)(R range)
+{
+ return taskPool.parallel(range);
+}
+
+/// Ditto
+ParallelForeach!R parallel(R)(R range, size_t workUnitSize)
+{
+ return taskPool.parallel(range, workUnitSize);
+}
+
+// Thrown when a parallel foreach loop is broken from.
+class ParallelForeachError : Error
+{
+ this()
+ {
+ super("Cannot break from a parallel foreach loop using break, return, "
+ ~ "labeled break/continue or goto statements.");
+ }
+}
+
+/*------Structs that implement opApply for parallel foreach.------------------*/
+private template randLen(R)
+{
+ enum randLen = isRandomAccessRange!R && hasLength!R;
+}
+
+private void submitAndExecute(
+ TaskPool pool,
+ scope void delegate() doIt
+)
+{
+ import core.exception : OutOfMemoryError;
+ immutable nThreads = pool.size + 1;
+
+ alias PTask = typeof(scopedTask(doIt));
+ 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
+ // causes problems on Windows for reasons that I don't understand
+ // (tentatively a compiler bug) and definitely doesn't work on Posix due
+ // to Bug 3753. Therefore, allocate a fixed buffer and fall back to
+ // malloc() if someone's using a ridiculous amount of threads. Also,
+ // the using a byte array instead of a PTask array as the fixed buffer
+ // is to prevent d'tors from being called on uninitialized excess PTask
+ // instances.
+ enum nBuf = 64;
+ byte[nBuf * PTask.sizeof] buf = void;
+ PTask[] tasks;
+ if (nThreads <= nBuf)
+ {
+ 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];
+ }
+
+ scope(exit)
+ {
+ if (nThreads > nBuf)
+ {
+ free(tasks.ptr);
+ }
+ }
+
+ 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);
+ 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
+ // task to finish.
+ t.pool = pool;
+ }
+
+ foreach (i; 1 .. tasks.length - 1)
+ {
+ tasks[i].next = tasks[i + 1].basePtr;
+ tasks[i + 1].prev = tasks[i].basePtr;
+ }
+
+ if (tasks.length > 1)
+ {
+ pool.queueLock();
+ scope(exit) pool.queueUnlock();
+
+ pool.abstractPutGroupNoSync(
+ tasks[1].basePtr,
+ tasks[$ - 1].basePtr
+ );
+ }
+
+ if (tasks.length > 0)
+ {
+ try
+ {
+ tasks[0].job();
+ }
+ catch (Throwable e)
+ {
+ tasks[0].exception = e; // nocoverage
+ }
+ tasks[0].taskStatus = TaskStatus.done;
+
+ // Try to execute each of these in the current thread
+ foreach (ref task; tasks[1..$])
+ {
+ pool.tryDeleteExecute(task.basePtr);
+ }
+ }
+
+ Throwable firstException, lastException;
+
+ foreach (i, ref task; tasks)
+ {
+ try
+ {
+ task.yieldForce;
+ }
+ catch (Throwable e)
+ {
+ addToChain(e, firstException, lastException);
+ continue;
+ }
+ }
+
+ if (firstException) throw firstException;
+}
+
+void foreachErr()
+{
+ throw new ParallelForeachError();
+}
+
+int doSizeZeroCase(R, Delegate)(ref ParallelForeach!R p, Delegate dg)
+{
+ with(p)
+ {
+ int res = 0;
+ size_t index = 0;
+
+ // The explicit ElementType!R in the foreach loops is necessary for
+ // correct behavior when iterating over strings.
+ static if (hasLvalueElements!R)
+ {
+ foreach (ref ElementType!R elem; range)
+ {
+ static if (Parameters!dg.length == 2)
+ {
+ res = dg(index, elem);
+ }
+ else
+ {
+ res = dg(elem);
+ }
+ if (res) break;
+ index++;
+ }
+ }
+ else
+ {
+ foreach (ElementType!R elem; range)
+ {
+ static if (Parameters!dg.length == 2)
+ {
+ res = dg(index, elem);
+ }
+ else
+ {
+ res = dg(elem);
+ }
+ if (res) break;
+ index++;
+ }
+ }
+ if (res) foreachErr;
+ return res;
+ }
+}
+
+private enum string parallelApplyMixinRandomAccess = q{
+ // Handle empty thread pool as special case.
+ if (pool.size == 0)
+ {
+ return doSizeZeroCase(this, dg);
+ }
+
+ // Whether iteration is with or without an index variable.
+ 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;
+
+ 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))
+ {
+ immutable myUnitIndex = atomicOp!"+="(workUnitIndex, 1);
+ immutable start = workUnitSize * myUnitIndex;
+ if (start >= len)
+ {
+ atomicStore(shouldContinue, false);
+ break;
+ }
+
+ immutable end = min(len, start + workUnitSize);
+
+ foreach (i; start .. end)
+ {
+ static if (withIndex)
+ {
+ if (dg(i, range[i])) foreachErr();
+ }
+ else
+ {
+ if (dg(range[i])) foreachErr();
+ }
+ }
+ }
+ }
+
+ submitAndExecute(pool, &doIt);
+
+ return 0;
+};
+
+enum string parallelApplyMixinInputRange = q{
+ // Handle empty thread pool as special case.
+ if (pool.size == 0)
+ {
+ return doSizeZeroCase(this, dg);
+ }
+
+ // Whether iteration is with or without an index variable.
+ enum withIndex = Parameters!(typeof(dg)).length == 2;
+
+ // This protects the range while copying it.
+ auto rangeMutex = new Mutex();
+
+ shared bool shouldContinue = true;
+
+ // The total number of elements that have been popped off range.
+ // This is updated only while protected by rangeMutex;
+ size_t nPopped = 0;
+
+ static if (
+ is(typeof(range.buf1)) &&
+ is(typeof(range.bufPos)) &&
+ is(typeof(range.doBufSwap()))
+ )
+ {
+ // Make sure we don't have the buffer recycling overload of
+ // asyncBuf.
+ static if (
+ is(typeof(range.source)) &&
+ isRoundRobin!(typeof(range.source))
+ )
+ {
+ static assert(0, "Cannot execute a parallel foreach loop on " ~
+ "the buffer recycling overload of asyncBuf.");
+ }
+
+ enum bool bufferTrick = true;
+ }
+ else
+ {
+ enum bool bufferTrick = false;
+ }
+
+ void doIt()
+ {
+ scope(failure)
+ {
+ // If an exception is thrown, all threads should bail.
+ atomicStore(shouldContinue, false);
+ }
+
+ static if (hasLvalueElements!R)
+ {
+ alias Temp = ElementType!R*[];
+ Temp temp;
+
+ // Returns: The previous value of nPopped.
+ size_t makeTemp()
+ {
+ import std.algorithm.internal : addressOf;
+ import std.array : uninitializedArray;
+
+ if (temp is null)
+ {
+ temp = uninitializedArray!Temp(workUnitSize);
+ }
+
+ rangeMutex.lock();
+ scope(exit) rangeMutex.unlock();
+
+ size_t i = 0;
+ for (; i < workUnitSize && !range.empty; range.popFront(), i++)
+ {
+ temp[i] = addressOf(range.front);
+ }
+
+ temp = temp[0 .. i];
+ auto ret = nPopped;
+ nPopped += temp.length;
+ return ret;
+ }
+
+ }
+ else
+ {
+
+ alias Temp = ElementType!R[];
+ Temp temp;
+
+ // Returns: The previous value of nPopped.
+ static if (!bufferTrick) size_t makeTemp()
+ {
+ import std.array : uninitializedArray;
+
+ if (temp is null)
+ {
+ temp = uninitializedArray!Temp(workUnitSize);
+ }
+
+ rangeMutex.lock();
+ scope(exit) rangeMutex.unlock();
+
+ size_t i = 0;
+ for (; i < workUnitSize && !range.empty; range.popFront(), i++)
+ {
+ temp[i] = range.front;
+ }
+
+ temp = temp[0 .. i];
+ auto ret = nPopped;
+ nPopped += temp.length;
+ return ret;
+ }
+
+ static if (bufferTrick) size_t makeTemp()
+ {
+ import std.algorithm.mutation : swap;
+ rangeMutex.lock();
+ scope(exit) rangeMutex.unlock();
+
+ // Elide copying by just swapping buffers.
+ temp.length = range.buf1.length;
+ swap(range.buf1, temp);
+
+ // This is necessary in case popFront() has been called on
+ // range before entering the parallel foreach loop.
+ temp = temp[range.bufPos..$];
+
+ static if (is(typeof(range._length)))
+ {
+ range._length -= (temp.length - range.bufPos);
+ }
+
+ range.doBufSwap();
+ auto ret = nPopped;
+ nPopped += temp.length;
+ return ret;
+ }
+ }
+
+ while (atomicLoad(shouldContinue))
+ {
+ auto overallIndex = makeTemp();
+ if (temp.empty)
+ {
+ atomicStore(shouldContinue, false);
+ break;
+ }
+
+ foreach (i; 0 .. temp.length)
+ {
+ scope(success) overallIndex++;
+
+ static if (hasLvalueElements!R)
+ {
+ static if (withIndex)
+ {
+ if (dg(overallIndex, *temp[i])) foreachErr();
+ }
+ else
+ {
+ if (dg(*temp[i])) foreachErr();
+ }
+ }
+ else
+ {
+ static if (withIndex)
+ {
+ if (dg(overallIndex, temp[i])) foreachErr();
+ }
+ else
+ {
+ if (dg(temp[i])) foreachErr();
+ }
+ }
+ }
+ }
+ }
+
+ submitAndExecute(pool, &doIt);
+
+ return 0;
+};
+
+// Calls e.next until the end of the chain is found.
+private Throwable findLastException(Throwable e) pure nothrow
+{
+ if (e is null) return null;
+
+ while (e.next)
+ {
+ e = e.next;
+ }
+
+ return e;
+}
+
+// Adds e to the exception chain.
+private void addToChain(
+ Throwable e,
+ ref Throwable firstException,
+ ref Throwable lastException
+) pure nothrow
+{
+ if (firstException)
+ {
+ assert(lastException); // nocoverage
+ lastException.next = e; // nocoverage
+ lastException = findLastException(e); // nocoverage
+ }
+ else
+ {
+ firstException = e;
+ lastException = findLastException(e);
+ }
+}
+
+private struct ParallelForeach(R)
+{
+ TaskPool pool;
+ R range;
+ size_t workUnitSize;
+ alias E = ElementType!R;
+
+ static if (hasLvalueElements!R)
+ {
+ alias NoIndexDg = int delegate(ref E);
+ alias IndexDg = int delegate(size_t, ref E);
+ }
+ else
+ {
+ alias NoIndexDg = int delegate(E);
+ alias IndexDg = int delegate(size_t, E);
+ }
+
+ int opApply(scope NoIndexDg dg)
+ {
+ static if (randLen!R)
+ {
+ mixin(parallelApplyMixinRandomAccess);
+ }
+ else
+ {
+ mixin(parallelApplyMixinInputRange);
+ }
+ }
+
+ int opApply(scope IndexDg dg)
+ {
+ static if (randLen!R)
+ {
+ mixin(parallelApplyMixinRandomAccess);
+ }
+ else
+ {
+ mixin(parallelApplyMixinInputRange);
+ }
+ }
+}
+
+/*
+This struct buffers the output of a callable that outputs data into a
+user-supplied buffer into a set of buffers of some fixed size. It allows these
+buffers to be accessed with an input range interface. This is used internally
+in the buffer-recycling overload of TaskPool.asyncBuf, which creates an
+instance and forwards it to the input range overload of asyncBuf.
+*/
+private struct RoundRobinBuffer(C1, C2)
+{
+ // No need for constraints because they're already checked for in asyncBuf.
+
+ alias Array = Parameters!(C1.init)[0];
+ alias T = typeof(Array.init[0]);
+
+ T[][] bufs;
+ size_t index;
+ C1 nextDel;
+ C2 emptyDel;
+ bool _empty;
+ bool primed;
+
+ this(
+ C1 nextDel,
+ C2 emptyDel,
+ size_t initialBufSize,
+ size_t nBuffers
+ ) {
+ this.nextDel = nextDel;
+ this.emptyDel = emptyDel;
+ bufs.length = nBuffers;
+
+ foreach (ref buf; bufs)
+ {
+ buf.length = initialBufSize;
+ }
+ }
+
+ void prime()
+ in
+ {
+ assert(!empty);
+ }
+ body
+ {
+ scope(success) primed = true;
+ nextDel(bufs[index]);
+ }
+
+
+ T[] front() @property
+ in
+ {
+ assert(!empty);
+ }
+ body
+ {
+ if (!primed) prime();
+ return bufs[index];
+ }
+
+ void popFront()
+ {
+ if (empty || emptyDel())
+ {
+ _empty = true;
+ return;
+ }
+
+ index = (index + 1) % bufs.length;
+ primed = false;
+ }
+
+ bool empty() @property const @safe pure nothrow
+ {
+ return _empty;
+ }
+}
+
+version (unittest)
+{
+ // This was the only way I could get nested maps to work.
+ __gshared TaskPool poolInstance;
+
+ import std.stdio;
+}
+
+// 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.
+@system unittest
+{
+ import std.algorithm.comparison : equal, min, max;
+ import std.algorithm.iteration : filter, map, reduce;
+ 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();
+
+ // The only way this can be verified is manually.
+ debug(std_parallelism) stderr.writeln("totalCPUs = ", totalCPUs);
+
+ auto oldPriority = poolInstance.priority;
+ poolInstance.priority = Thread.PRIORITY_MAX;
+ assert(poolInstance.priority == Thread.PRIORITY_MAX);
+
+ poolInstance.priority = Thread.PRIORITY_MIN;
+ assert(poolInstance.priority == Thread.PRIORITY_MIN);
+
+ poolInstance.priority = oldPriority;
+ assert(poolInstance.priority == oldPriority);
+
+ static void refFun(ref uint num)
+ {
+ num++;
+ }
+
+ uint x;
+
+ // Test task().
+ auto t = task!refFun(x);
+ poolInstance.put(t);
+ t.yieldForce;
+ assert(t.args[0] == 1);
+
+ auto t2 = task(&refFun, x);
+ poolInstance.put(t2);
+ t2.yieldForce;
+ assert(t2.args[0] == 1);
+
+ // Test scopedTask().
+ auto st = scopedTask!refFun(x);
+ poolInstance.put(st);
+ st.yieldForce;
+ assert(st.args[0] == 1);
+
+ auto st2 = scopedTask(&refFun, x);
+ poolInstance.put(st2);
+ st2.yieldForce;
+ assert(st2.args[0] == 1);
+
+ // Test executeInNewThread().
+ auto ct = scopedTask!refFun(x);
+ ct.executeInNewThread(Thread.PRIORITY_MAX);
+ ct.yieldForce;
+ assert(ct.args[0] == 1);
+
+ // Test ref return.
+ uint toInc = 0;
+ static ref T makeRef(T)(ref T num)
+ {
+ return num;
+ }
+
+ auto t3 = task!makeRef(toInc);
+ taskPool.put(t3);
+ assert(t3.args[0] == 0);
+ t3.spinForce++;
+ assert(t3.args[0] == 1);
+
+ static void testSafe() @safe {
+ static int bump(int num)
+ {
+ return num + 1;
+ }
+
+ auto safePool = new TaskPool(0);
+ auto t = task(&bump, 1);
+ taskPool.put(t);
+ assert(t.yieldForce == 2);
+
+ auto st = scopedTask(&bump, 1);
+ taskPool.put(st);
+ assert(st.yieldForce == 2);
+ safePool.stop();
+ }
+
+ auto arr = [1,2,3,4,5];
+ auto nums = new uint[5];
+ auto nums2 = new uint[5];
+
+ foreach (i, ref elem; poolInstance.parallel(arr))
+ {
+ elem++;
+ nums[i] = cast(uint) i + 2;
+ nums2[i] = elem;
+ }
+
+ assert(nums == [2,3,4,5,6], text(nums));
+ assert(nums2 == nums, text(nums2));
+ assert(arr == nums, text(arr));
+
+ // Test const/immutable arguments.
+ static int add(int lhs, int rhs)
+ {
+ return lhs + rhs;
+ }
+ immutable addLhs = 1;
+ immutable addRhs = 2;
+ auto addTask = task(&add, addLhs, addRhs);
+ auto addScopedTask = scopedTask(&add, addLhs, addRhs);
+ poolInstance.put(addTask);
+ poolInstance.put(addScopedTask);
+ assert(addTask.yieldForce == 3);
+ assert(addScopedTask.yieldForce == 3);
+
+ // Test parallel foreach with non-random access range.
+ auto range = filter!"a != 666"([0, 1, 2, 3, 4]);
+
+ foreach (i, elem; poolInstance.parallel(range))
+ {
+ nums[i] = cast(uint) i;
+ }
+
+ assert(nums == [0,1,2,3,4]);
+
+ auto logs = new double[1_000_000];
+ foreach (i, ref elem; poolInstance.parallel(logs))
+ {
+ elem = log(i + 1.0);
+ }
+
+ foreach (i, elem; logs)
+ {
+ assert(approxEqual(elem, cast(double) log(i + 1)));
+ }
+
+ assert(poolInstance.amap!"a * a"([1,2,3,4,5]) == [1,4,9,16,25]);
+ assert(poolInstance.amap!"a * a"([1,2,3,4,5], new long[5]) == [1,4,9,16,25]);
+ assert(poolInstance.amap!("a * a", "-a")([1,2,3]) ==
+ [tuple(1, -1), tuple(4, -2), tuple(9, -3)]);
+
+ auto tupleBuf = new Tuple!(int, int)[3];
+ poolInstance.amap!("a * a", "-a")([1,2,3], tupleBuf);
+ assert(tupleBuf == [tuple(1, -1), tuple(4, -2), tuple(9, -3)]);
+ poolInstance.amap!("a * a", "-a")([1,2,3], 5, tupleBuf);
+ assert(tupleBuf == [tuple(1, -1), tuple(4, -2), tuple(9, -3)]);
+
+ // Test amap with a non-array buffer.
+ auto toIndex = new int[5];
+ 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"(ind, ind);
+ assert(equal(ind, [1, 2, 3, 4, 5]));
+ assert(equal(toIndex, [4, 2, 5, 1, 3]));
+
+ auto buf = new int[5];
+ poolInstance.amap!"a * a"([1,2,3,4,5], buf);
+ assert(buf == [1,4,9,16,25]);
+ poolInstance.amap!"a * a"([1,2,3,4,5], 4, buf);
+ assert(buf == [1,4,9,16,25]);
+
+ assert(poolInstance.reduce!"a + b"([1]) == 1);
+ assert(poolInstance.reduce!"a + b"([1,2,3,4]) == 10);
+ assert(poolInstance.reduce!"a + b"(0.0, [1,2,3,4]) == 10);
+ assert(poolInstance.reduce!"a + b"(0.0, [1,2,3,4], 1) == 10);
+ assert(poolInstance.reduce!(min, max)([1,2,3,4]) == tuple(1, 4));
+ assert(poolInstance.reduce!("a + b", "a * b")(tuple(0, 1), [1,2,3,4]) ==
+ tuple(10, 24));
+
+ 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))
+ {
+ wl.get = wl.get + i;
+ }
+
+ 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]);
+
+ // Test finish()
+ {
+ static void slowFun() { Thread.sleep(dur!"msecs"(1)); }
+
+ auto pool1 = new TaskPool();
+ auto tSlow = task!slowFun();
+ pool1.put(tSlow);
+ pool1.finish();
+ tSlow.yieldForce;
+ // Can't assert that pool1.status == PoolState.stopNow because status
+ // doesn't change until after the "done" flag is set and the waiting
+ // thread is woken up.
+
+ auto pool2 = new TaskPool();
+ auto tSlow2 = task!slowFun();
+ pool2.put(tSlow2);
+ pool2.finish(true); // blocking
+ assert(tSlow2.done);
+
+ // Test fix for Bug 8582 by making pool size zero.
+ auto pool3 = new TaskPool(0);
+ auto tSlow3 = task!slowFun();
+ pool3.put(tSlow3);
+ pool3.finish(true); // blocking
+ assert(tSlow3.done);
+
+ // This is correct because no thread will terminate unless pool2.status
+ // and pool3.status have already been set to stopNow.
+ assert(pool2.status == TaskPool.PoolState.stopNow);
+ assert(pool3.status == TaskPool.PoolState.stopNow);
+ }
+
+ // Test default pool stuff.
+ assert(taskPool.size == totalCPUs - 1);
+
+ nums = new uint[1000];
+ foreach (i; parallel(iota(1000)))
+ {
+ nums[i] = cast(uint) i;
+ }
+ assert(equal(nums, iota(1000)));
+
+ assert(equal(
+ poolInstance.map!"a * a"(iota(30_000_001), 10_000),
+ map!"a * a"(iota(30_000_001))
+ ));
+
+ // The filter is to kill random access and test the non-random access
+ // branch.
+ assert(equal(
+ poolInstance.map!"a * a"(
+ filter!"a == a"(iota(30_000_001)
+ ), 10_000, 1000),
+ map!"a * a"(iota(30_000_001))
+ ));
+
+ assert(
+ reduce!"a + b"(0UL,
+ poolInstance.map!"a * a"(iota(3_000_001), 10_000)
+ ) ==
+ reduce!"a + b"(0UL,
+ map!"a * a"(iota(3_000_001))
+ )
+ );
+
+ assert(equal(
+ iota(1_000_002),
+ poolInstance.asyncBuf(filter!"a == a"(iota(1_000_002)))
+ ));
+
+ {
+ import std.conv : to;
+ import std.file : deleteme;
+
+ string temp_file = deleteme ~ "-tempDelMe.txt";
+ auto file = File(temp_file, "wb");
+ scope(exit)
+ {
+ file.close();
+ import std.file;
+ remove(temp_file);
+ }
+
+ auto written = [[1.0, 2, 3], [4.0, 5, 6], [7.0, 8, 9]];
+ foreach (row; written)
+ {
+ file.writeln(join(to!(string[])(row), "\t"));
+ }
+
+ file = File(temp_file);
+
+ void next(ref char[] buf)
+ {
+ file.readln(buf);
+ import std.string : chomp;
+ buf = chomp(buf);
+ }
+
+ double[][] read;
+ auto asyncReader = taskPool.asyncBuf(&next, &file.eof);
+
+ foreach (line; asyncReader)
+ {
+ if (line.length == 0) continue;
+ auto ls = line.split("\t");
+ read ~= to!(double[])(ls);
+ }
+
+ assert(read == written);
+ file.close();
+ }
+
+ // Test Map/AsyncBuf chaining.
+
+ auto abuf = poolInstance.asyncBuf(iota(-1.0, 3_000_000), 100);
+ auto temp = poolInstance.map!sqrt(
+ abuf, 100, 5
+ );
+ auto lmchain = poolInstance.map!"a * a"(temp, 100, 5);
+ lmchain.popFront();
+
+ int ii;
+ foreach ( elem; (lmchain))
+ {
+ if (!approxEqual(elem, ii))
+ {
+ stderr.writeln(ii, '\t', elem);
+ }
+ ii++;
+ }
+
+ // Test buffer trick in parallel foreach.
+ abuf = poolInstance.asyncBuf(iota(-1.0, 1_000_000), 100);
+ abuf.popFront();
+ auto bufTrickTest = new size_t[abuf.length];
+ foreach (i, elem; parallel(abuf))
+ {
+ bufTrickTest[i] = i;
+ }
+
+ assert(equal(iota(1_000_000), bufTrickTest));
+
+ auto myTask = task!(std.math.abs)(-1);
+ taskPool.put(myTask);
+ assert(myTask.spinForce == 1);
+
+ // 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))
+ {
+ assert(poolInstance.workerIndex == 0);
+ }
+
+ foreach (i; poolInstance.parallel(iota(1000), 1))
+ {
+ assert(taskPool.workerIndex == 0);
+ }
+
+ // Test exception handling.
+ static void parallelForeachThrow()
+ {
+ foreach (elem; parallel(iota(10)))
+ {
+ throw new Exception("");
+ }
+ }
+
+ assertThrown!Exception(parallelForeachThrow());
+
+ static int reduceException(int a, int b)
+ {
+ throw new Exception("");
+ }
+
+ assertThrown!Exception(
+ poolInstance.reduce!reduceException(iota(3))
+ );
+
+ static int mapException(int a)
+ {
+ throw new Exception("");
+ }
+
+ assertThrown!Exception(
+ poolInstance.amap!mapException(iota(3))
+ );
+
+ static void mapThrow()
+ {
+ auto m = poolInstance.map!mapException(iota(3));
+ m.popFront();
+ }
+
+ assertThrown!Exception(mapThrow());
+
+ struct ThrowingRange
+ {
+ @property int front()
+ {
+ return 1;
+ }
+ void popFront()
+ {
+ throw new Exception("");
+ }
+ enum bool empty = false;
+ }
+
+ assertThrown!Exception(poolInstance.asyncBuf(ThrowingRange.init));
+}
+
+//version = parallelismStressTest;
+
+// These are more like stress tests than real unit tests. They print out
+// tons of stuff and should not be run every time make unittest is run.
+version (parallelismStressTest)
+{
+ @safe unittest
+ {
+ size_t attempt;
+ 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)) )
+ {
+ numbers[i] = cast(uint) i;
+ }
+
+ // Make sure it works.
+ foreach (i; 0 .. numbers.length)
+ {
+ assert(numbers[i] == i);
+ }
+
+ stderr.writeln("Done creating nums.");
+
+
+ auto myNumbers = filter!"a % 7 > 0"( iota(0, 1000));
+ foreach (num; poolInstance.parallel(myNumbers))
+ {
+ assert(num % 7 > 0 && num < 1000);
+ }
+ stderr.writeln("Done modulus test.");
+
+ uint[] squares = poolInstance.amap!"a * a"(numbers, 100);
+ assert(squares.length == numbers.length);
+ foreach (i, number; numbers)
+ {
+ assert(squares[i] == number * number);
+ }
+ stderr.writeln("Done squares.");
+
+ auto sumFuture = task!( reduce!"a + b" )(numbers);
+ poolInstance.put(sumFuture);
+
+ ulong sumSquares = 0;
+ foreach (elem; numbers)
+ {
+ sumSquares += elem * elem;
+ }
+
+ uint mySum = sumFuture.spinForce();
+ assert(mySum == 999 * 1000 / 2);
+
+ auto mySumParallel = poolInstance.reduce!"a + b"(numbers);
+ assert(mySum == mySumParallel);
+ stderr.writeln("Done sums.");
+
+ auto myTask = task(
+ {
+ synchronized writeln("Our lives are parallel...Our lives are parallel.");
+ });
+ poolInstance.put(myTask);
+
+ auto nestedOuter = "abcd";
+ auto nestedInner = iota(0, 10, 2);
+
+ foreach (i, letter; poolInstance.parallel(nestedOuter, 1))
+ {
+ foreach (j, number; poolInstance.parallel(nestedInner, 1))
+ {
+ synchronized writeln(i, ": ", letter, " ", j, ": ", number);
+ }
+ }
+
+ poolInstance.stop();
+ }
+
+ assert(attempt == 10);
+ writeln("Press enter to go to next round of unittests.");
+ readln();
+ }
+
+ // These unittests are intended more for actual testing and not so much
+ // as examples.
+ @safe unittest
+ {
+ foreach (attempt; 0 .. 10)
+ foreach (poolSize; [0, 4])
+ {
+ poolInstance = new TaskPool(poolSize);
+
+ // Test indexing.
+ stderr.writeln("Creator Raw Index: ", poolInstance.threadIndex);
+ assert(poolInstance.workerIndex() == 0);
+
+ // Test worker-local storage.
+ auto workerLocalStorage = poolInstance.workerLocalStorage!uint(1);
+ foreach (i; poolInstance.parallel(iota(0U, 1_000_000)))
+ {
+ workerLocalStorage.get++;
+ }
+ assert(reduce!"a + b"(workerLocalStorage.toRange) ==
+ 1_000_000 + poolInstance.size + 1);
+
+ // Make sure work is reasonably balanced among threads. This test is
+ // 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)
+ {
+ nJobsByThread[cast(void*) thread] = 0;
+ }
+ nJobsByThread[ cast(void*) Thread.getThis()] = 0;
+
+ foreach (i; poolInstance.parallel( iota(0, 1_000_000), 100 ))
+ {
+ atomicOp!"+="( nJobsByThread[ cast(void*) Thread.getThis() ], 1);
+ }
+
+ stderr.writeln("\nCurrent thread is: ",
+ cast(void*) Thread.getThis());
+ stderr.writeln("Workload distribution: ");
+ 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 (j; poolInstance.parallel( iota(0, matrix[0].length) ))
+ {
+ matrix[i][j] = i * j;
+ }
+ }
+
+ // Get around weird bugs having to do w/ sqrt being an intrinsic:
+ static real mySqrt(real num)
+ {
+ return sqrt(num);
+ }
+
+ static real[] parallelSqrt(real[] nums)
+ {
+ return poolInstance.amap!mySqrt(nums);
+ }
+
+ real[][] sqrtMatrix = poolInstance.amap!parallelSqrt(matrix);
+
+ foreach (i, row; sqrtMatrix)
+ {
+ foreach (j, elem; row)
+ {
+ real shouldBe = sqrt( cast(real) i * j);
+ assert(approxEqual(shouldBe, elem));
+ sqrtMatrix[i][j] = shouldBe;
+ }
+ }
+
+ auto saySuccess = task(
+ {
+ stderr.writeln(
+ "Success doing matrix stuff that involves nested pool use.");
+ });
+ poolInstance.put(saySuccess);
+ saySuccess.workForce();
+
+ // A more thorough test of amap, reduce: Find the sum of the square roots of
+ // matrix.
+
+ static real parallelSum(real[] input)
+ {
+ return poolInstance.reduce!"a + b"(input);
+ }
+
+ auto sumSqrt = poolInstance.reduce!"a + b"(
+ poolInstance.amap!parallelSum(
+ sqrtMatrix
+ )
+ );
+
+ assert(approxEqual(sumSqrt, 4.437e8));
+ stderr.writeln("Done sum of square roots.");
+
+ // Test whether tasks work with function pointers.
+ auto nanTask = task(&isNaN, 1.0L);
+ poolInstance.put(nanTask);
+ assert(nanTask.spinForce == false);
+
+ if (poolInstance.size > 0)
+ {
+ // Test work waiting.
+ static void uselessFun()
+ {
+ foreach (i; 0 .. 1_000_000) {}
+ }
+
+ auto uselessTasks = new typeof(task(&uselessFun))[1000];
+ foreach (ref uselessTask; uselessTasks)
+ {
+ uselessTask = task(&uselessFun);
+ }
+ foreach (ref uselessTask; uselessTasks)
+ {
+ poolInstance.put(uselessTask);
+ }
+ foreach (ref uselessTask; uselessTasks)
+ {
+ uselessTask.workForce();
+ }
+ }
+
+ // Test the case of non-random access + ref returns.
+ int[] nums = [1,2,3,4,5];
+ static struct RemoveRandom
+ {
+ int[] arr;
+
+ ref int front()
+ {
+ return arr.front;
+ }
+ void popFront()
+ {
+ arr.popFront();
+ }
+ bool empty()
+ {
+ return arr.empty;
+ }
+ }
+
+ auto refRange = RemoveRandom(nums);
+ foreach (ref elem; poolInstance.parallel(refRange))
+ {
+ elem++;
+ }
+ assert(nums == [2,3,4,5,6], text(nums));
+ stderr.writeln("Nums: ", nums);
+
+ poolInstance.stop();
+ }
+ }
+}
+
+version (unittest)
+{
+ struct __S_12733
+ {
+ 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 = 1_234_567_890;
+ }
+
+ static auto __genPair_12733(ulong n) { return __S_12733(n); }
+}
+
+@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++";
+ })));
+}
+
+// https://issues.dlang.org/show_bug.cgi?id=17539
+@system unittest
+{
+ import std.random : rndGen;
+ // ensure compilation
+ try foreach (rnd; rndGen.parallel) break;
+ catch (ParallelForeachError e) {}
+}
diff --git a/libphobos/src/std/path.d b/libphobos/src/std/path.d
new file mode 100644
index 0000000..32870ea
--- /dev/null
+++ b/libphobos/src/std/path.d
@@ -0,0 +1,4115 @@
+// Written in the D programming language.
+
+/** This module is used to manipulate _path strings.
+
+ All functions, with the exception of $(LREF expandTilde) (and in some
+ cases $(LREF absolutePath) and $(LREF relativePath)), are pure
+ string manipulation functions; they don't depend on any state outside
+ the program, nor do they perform any actual file system actions.
+ This has the consequence that the module does not make any distinction
+ 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 $(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
+ both on equal footing, but in cases where a $(I new) separator is
+ added, a backslash will be used. Furthermore, the $(LREF buildNormalizedPath)
+ function will replace all slashes with backslashes on that platform.
+
+ In general, the functions in this module assume that the input paths
+ are well-formed. (That is, they should not contain invalid characters,
+ they should follow the file system's _path format, etc.) The result
+ of calling a function on an ill-formed _path is undefined. When there
+ is a chance that a _path or a file name is invalid (for instance, when it
+ has been input by the user), it may sometimes be desirable to use the
+ $(LREF isValidFilename) and $(LREF isValidPath) functions to check
+ this.
+
+ Most functions do not perform any memory allocations, and if a string is
+ returned, it is usually a slice of an input string. If a function
+ allocates, this is explicitly mentioned in the documentation.
+
+$(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,
+ $(HTTP digitalmars.com, Walter Bright),
+ Grzegorz Adam Hankiewicz,
+ Thomas K$(UUML)hne,
+ $(HTTP erdani.org, Andrei Alexandrescu)
+ Copyright:
+ Copyright (c) 2000-2014, the authors. All rights reserved.
+ License:
+ $(HTTP boost.org/LICENSE_1_0.txt, Boost License 1.0)
+ Source:
+ $(PHOBOSSRC std/_path.d)
+*/
+module std.path;
+
+
+// FIXME
+import std.file; //: getcwd;
+static import std.meta;
+import std.range.primitives;
+import std.traits;
+
+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");
+
+
+
+
+/** Path separator string. A colon under POSIX, a semicolon
+ under Windows.
+*/
+version (Posix) enum string pathSeparator = ":";
+else version (Windows) enum string pathSeparator = ";";
+else static assert(0, "unsupported platform");
+
+
+
+
+/** Determines whether the given character is a directory separator.
+
+ On Windows, this includes both $(D `\`) and $(D `/`).
+ On POSIX, it's just $(D `/`).
+*/
+bool isDirSeparator(dchar c) @safe pure nothrow @nogc
+{
+ if (c == '/') return true;
+ version (Windows) if (c == '\\') return true;
+ return false;
+}
+
+
+/* Determines whether the given character is a drive separator.
+
+ On Windows, this is true if c is the ':' character that separates
+ the drive letter from the rest of the path. On POSIX, this always
+ returns false.
+*/
+private bool isDriveSeparator(dchar c) @safe pure nothrow @nogc
+{
+ version (Windows) return c == ':';
+ else return false;
+}
+
+
+/* Combines the isDirSeparator and isDriveSeparator tests. */
+version (Windows) private bool isSeparator(dchar c) @safe pure nothrow @nogc
+{
+ return isDirSeparator(c) || isDriveSeparator(c);
+}
+version (Posix) private alias isSeparator = isDirSeparator;
+
+
+/* Helper function that determines the position of the last
+ drive/directory separator in a string. Returns -1 if none
+ is found.
+*/
+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;
+ return i;
+}
+
+
+version (Windows)
+{
+ 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)(R path)
+ if (isRandomAccessRange!R && isSomeChar!(ElementType!R) ||
+ isNarrowString!R)
+ in { assert(isUNC(path)); }
+ body
+ {
+ ptrdiff_t i = 3;
+ while (i < path.length && !isDirSeparator(path[i])) ++i;
+ if (i < path.length)
+ {
+ auto j = i;
+ do { ++j; } while (j < path.length && isDirSeparator(path[j]));
+ if (j < path.length)
+ {
+ do { ++j; } while (j < path.length && !isDirSeparator(path[j]));
+ i = j;
+ }
+ }
+ return i;
+ }
+
+ 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)(R path)
+ if (isRandomAccessRange!R && isSomeChar!(ElementType!R) ||
+ isNarrowString!R)
+ {
+ return path.length >= 3 && isDriveSeparator(path[1])
+ && isDirSeparator(path[2]);
+ }
+}
+
+
+/* Helper functions that strip leading/trailing slashes and backslashes
+ from a path.
+*/
+private auto ltrimDirSeparators(R)(R path)
+if (isInputRange!R && !isInfinite!R && isSomeChar!(ElementType!R) ||
+ isNarrowString!R)
+{
+ 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;
+ }
+}
+
+@system unittest
+{
+ 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 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");
+}
+
+
+
+
+/** This $(D enum) is used as a template argument to functions which
+ compare file names, and determines whether the comparison is
+ case sensitive or not.
+*/
+enum CaseSensitive : bool
+{
+ /// File names are case insensitive
+ no = false,
+
+ /// File names are case sensitive
+ yes = true,
+
+ /** The default (or most common) setting for the current platform.
+ That is, $(D no) on Windows and Mac OS X, and $(D yes) on all
+ POSIX systems except OS X (Linux, *BSD, etc.).
+ */
+ osDefault = osDefaultCaseSensitivity
+}
+version (Windows) private enum osDefaultCaseSensitivity = false;
+else version (OSX) private enum osDefaultCaseSensitivity = false;
+else version (Posix) private enum osDefaultCaseSensitivity = true;
+else static assert(0);
+
+
+
+
+/**
+ 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),
+ where $(D cs) is an optional template parameter determining whether
+ the comparison is case sensitive or not. See the
+ $(LREF filenameCmp) documentation for details.
+
+ 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");
+
+ version (Windows)
+ {
+ assert(baseName(`d:file.ext`) == "file.ext");
+ assert(baseName(`d:\dir\file.ext`) == "file.ext");
+ }
+ ---
+
+ Note:
+ This function $(I only) strips away the specified suffix, which
+ doesn't necessarily have to represent an extension.
+ To remove the extension from a path, regardless of what the extension
+ is, use $(LREF stripExtension).
+ To obtain the filename without leading directories and without
+ an extension, combine the functions like this:
+ ---
+ assert(baseName(stripExtension("dir/file.ext")) == "file");
+ ---
+
+ Standards:
+ This function complies with
+ $(LINK2 http://pubs.opengroup.org/onlinepubs/9699919799/utilities/basename.html,
+ the POSIX requirements for the 'basename' shell utility)
+ (with suitable adaptations for Windows paths).
+*/
+auto baseName(R)(R path)
+if (isRandomAccessRange!R && hasSlicing!R && isSomeChar!(ElementType!R) && !isSomeString!R)
+{
+ return _baseName(path);
+}
+
+/// ditto
+auto baseName(C)(C[] path)
+if (isSomeChar!C)
+{
+ return _baseName(path);
+}
+
+private R _baseName(R)(R path)
+if (isRandomAccessRange!R && hasSlicing!R && isSomeChar!(ElementType!R) || isNarrowString!R)
+{
+ auto p1 = stripDrive(path);
+ if (p1.empty)
+ {
+ version (Windows) if (isUNC(path))
+ return path[0 .. 1];
+ static if (isSomeString!R)
+ return null;
+ else
+ return p1; // which is empty
+ }
+
+ auto p2 = rtrimDirSeparators(p1);
+ if (p2.empty) return p1[0 .. 1];
+
+ return p2[lastSeparator(p2)+1 .. p2.length];
+}
+
+/// ditto
+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)
+{
+ auto p = baseName(path);
+ if (p.length > suffix.length
+ && filenameCmp!cs(cast(const(C)[])p[$-suffix.length .. $], suffix) == 0)
+ {
+ return p[0 .. $-suffix.length];
+ }
+ else return p;
+}
+
+@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!(CaseSensitive.yes)("file.ext", ".EXT") == "file.ext");
+ assert(baseName!(CaseSensitive.no)("file.ext", ".EXT") == "file");
+
+ {
+ auto r = MockRange!(immutable(char))(`dir/file.ext`);
+ auto s = r.baseName();
+ foreach (i, c; `file`)
+ assert(s[i] == c);
+ }
+
+ 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`) == `\`);
+
+ auto r = MockRange!(immutable(char))(`\\server\share`);
+ auto s = r.baseName();
+ foreach (i, c; `\`)
+ assert(s[i] == c);
+ }
+
+ assert(baseName(stripExtension("dir/file.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");
+}
+
+@safe unittest
+{
+ assert(testAliasedString!baseName("file"));
+
+ enum S : string { a = "file/path/to/test" }
+ assert(S.a.baseName == "test");
+
+ char[S.a.length] sa = S.a[];
+ assert(sa.baseName == "test");
+}
+
+/** Returns the directory part of a path. On Windows, this
+ includes the drive letter if present.
+
+ Params:
+ path = A path name.
+
+ Returns:
+ A slice of $(D path) or ".".
+
+ Standards:
+ This function complies with
+ $(LINK2 http://pubs.opengroup.org/onlinepubs/9699919799/utilities/dirname.html,
+ the POSIX requirements for the 'dirname' shell utility)
+ (with suitable adaptations for Windows paths).
+*/
+auto dirName(R)(R path)
+if (isRandomAccessRange!R && hasSlicing!R && hasLength!R && isSomeChar!(ElementType!R) && !isSomeString!R)
+{
+ return _dirName(path);
+}
+
+/// ditto
+auto dirName(C)(C[] path)
+if (isSomeChar!C)
+{
+ return _dirName(path);
+}
+
+private auto _dirName(R)(R path)
+if (isRandomAccessRange!R && hasSlicing!R && hasLength!R && isSomeChar!(ElementType!R) ||
+ isNarrowString!R)
+{
+ 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 result(false, path[0 .. 1]);
+
+ version (Windows)
+ {
+ if (isUNC(p) && uncRootLength(p) == p.length)
+ return result(false, p);
+
+ if (p.length == 2 && isDriveSeparator(p[1]) && path.length > 2)
+ return result(false, path[0 .. 3]);
+ }
+
+ auto i = lastSeparator(p);
+ 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:\
+ // do not chop off the last symbol.
+ if (isDriveSeparator(p[i]) || isDriveSeparator(p[i-1]))
+ return result(false, p[0 .. i+1]);
+ }
+ // Remove any remaining trailing (back)slashes.
+ 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`);
+ }
+}
+
+@safe unittest
+{
+ assert(testAliasedString!dirName("file"));
+
+ enum S : string { a = "file/path/to/test" }
+ assert(S.a.dirName == "file/path/to");
+
+ char[S.a.length] sa = S.a[];
+ assert(sa.dirName == "file/path/to");
+}
+
+@system unittest
+{
+ 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\`.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".byChar).array == "dir");
+}
+
+
+
+
+/** Returns the root directory of the specified path, or $(D null) if the
+ path is not rooted.
+
+ Params:
+ path = A path name.
+
+ Returns:
+ A slice of $(D path).
+*/
+auto rootName(R)(R path)
+if ((isRandomAccessRange!R && hasSlicing!R && hasLength!R && isSomeChar!(ElementType!R) ||
+ isNarrowString!R) &&
+ !isConvertibleToString!R)
+{
+ if (path.empty)
+ goto Lnull;
+
+ version (Posix)
+ {
+ if (isDirSeparator(path[0])) return path[0 .. 1];
+ }
+ else version (Windows)
+ {
+ if (isDirSeparator(path[0]))
+ {
+ if (isUNC(path)) return path[0 .. uncRootLength(path)];
+ else return path[0 .. 1];
+ }
+ else if (path.length >= 3 && isDriveSeparator(path[1]) &&
+ isDirSeparator(path[2]))
+ {
+ return path[0 .. 3];
+ }
+ }
+ else static assert(0, "unsupported platform");
+
+ 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];
+}
+
+///
+@safe unittest
+{
+ 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`);
+ }
+}
+
+@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 == "/");
+
+ version (Windows)
+ {
+ 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.
+*/
+auto driveName(R)(R path)
+if ((isRandomAccessRange!R && hasSlicing!R && hasLength!R && isSomeChar!(ElementType!R) ||
+ isNarrowString!R) &&
+ !isConvertibleToString!R)
+{
+ version (Windows)
+ {
+ if (hasDrive(path))
+ return path[0 .. 2];
+ else if (isUNC(path))
+ return path[0 .. uncRootLength(path)];
+ }
+ static if (isSomeString!R)
+ return cast(ElementEncodingType!R[]) null; // legacy code may rely on null return rather than slice
+ else
+ return path[0 .. 0];
+}
+
+///
+@safe unittest
+{
+ 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`);
+
+ 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;
+
+ version (Posix) assert(driveName("c:/foo".byChar).empty);
+ version (Windows)
+ {
+ 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)(R path)
+if ((isRandomAccessRange!R && hasSlicing!R && isSomeChar!(ElementType!R) ||
+ isNarrowString!R) &&
+ !isConvertibleToString!R)
+{
+ version (Windows)
+ {
+ if (hasDrive!(BaseOf!R)(path)) return path[2 .. path.length];
+ else if (isUNC!(BaseOf!R)(path)) return path[uncRootLength!(BaseOf!R)(path) .. path.length];
+ }
+ return path;
+}
+
+///
+@safe unittest
+{
+ version (Windows)
+ {
+ assert(stripDrive(`d:\dir\file`) == `\dir\file`);
+ assert(stripDrive(`\\server\share\dir\file`) == `\dir\file`);
+ }
+}
+
+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`);
+
+ auto r = MockRange!(immutable(char))(`d:\dir\file`);
+ auto s = r.stripDrive();
+ foreach (i, c; `\dir\file`)
+ assert(s[i] == c);
+ }
+ version (Posix)
+ {
+ assert(stripDrive(`d:\dir\file`) == `d:\dir\file`);
+
+ auto r = MockRange!(immutable(char))(`d:\dir\file`);
+ auto s = r.stripDrive();
+ foreach (i, c; `d:\dir\file`)
+ assert(s[i] == c);
+ }
+}
+
+
+/* Helper function that returns the position of the filename/extension
+ 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 && hasLength!R && isSomeChar!(ElementType!R) ||
+ isNarrowString!R)
+{
+ for (auto i = path.length; i-- > 0 && !isSeparator(path[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);
+}
+
+@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);
+
+ 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);
+
+ 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))
+{
+ auto i = extSeparatorPos!(BaseOf!R)(path);
+ if (i == -1)
+ {
+ static if (is(StringTypeOf!R))
+ return StringTypeOf!R.init[]; // which is null
+ else
+ return path[0 .. 0];
+ }
+ else return path[i .. path.length];
+}
+
+///
+@safe unittest
+{
+ 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");
+
+ 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();
+ foreach (i, c; `.ext2`)
+ assert(s[i] == c);
+ }
+
+ static struct DirEntry { string s; alias s this; }
+ assert(extension(DirEntry("file")).empty);
+}
+
+
+/** Remove extension from path.
+
+ Params:
+ path = string or range to be sliced
+
+ Returns:
+ slice of path with the extension (if any) stripped off
+*/
+auto stripExtension(R)(R path)
+if ((isRandomAccessRange!R && hasSlicing!R && hasLength!R && isSomeChar!(ElementType!R) ||
+ isNarrowString!R) &&
+ !isConvertibleToString!R)
+{
+ auto i = extSeparatorPos(path);
+ 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");
+}
+
+auto stripExtension(R)(auto ref R path)
+if (isConvertibleToString!R)
+{
+ return stripExtension!(StringTypeOf!R)(path);
+}
+
+@safe unittest
+{
+ assert(testAliasedString!stripExtension("file"));
+}
+
+@safe unittest
+{
+ assert(stripExtension("file.ext"w) == "file");
+ assert(stripExtension("file.ext1.ext2"d) == "file.ext1");
+
+ import std.array;
+ import std.utf : byChar, byWchar, byDchar;
+
+ 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.
+
+ 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
+ $(LREF stripExtension).
+
+ This function normally allocates a new string (the possible exception
+ being the case when path is immutable and doesn't already have an
+ extension).
+
+ 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)
+if (isSomeChar!C1 && !is(C1 == immutable) && is(Unqual!C1 == Unqual!C2))
+{
+ 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)
+if (isSomeChar!C1 && is(Unqual!C1 == Unqual!C2))
+{
+ if (ext.length == 0)
+ return stripExtension(path);
+
+ try
+ {
+ import std.conv : to;
+ return withExtension(path, ext).to!(typeof(return));
+ }
+ catch (Exception e)
+ {
+ assert(0);
+ }
+}
+
+///
+@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");
+}
+
+@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"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");
+}
+
+/************
+ * 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.
+
+ 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.
+*/
+immutable(Unqual!C1)[] defaultExtension(C1, C2)(in C1[] path, in C2[] ext)
+if (isSomeChar!C1 && is(Unqual!C1 == Unqual!C2))
+{
+ import std.conv : to;
+ return withDefaultExtension(path, ext).to!(typeof(return));
+}
+
+///
+@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");
+}
+
+@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"w.dup, "ext"w) == "file.ext");
+ static assert(defaultExtension("file.old"d.dup, "new"d) == "file.old");
+}
+
+
+/********************************
+ * 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;
+
+ 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
+ path segments are absolute (as defined by $(LREF isAbsolute)), the
+ preceding segments will be dropped.
+
+ On Windows, if one of the path segments are rooted, but not absolute
+ (e.g. $(D `\foo`)), all preceding path segments down to the previous
+ root will be dropped. (See below for an example.)
+
+ This function always allocates memory to hold the resulting path.
+ 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 && !isInfinite!Range && isSomeString!(ElementType!Range))
+{
+ if (segments.empty) return null;
+
+ // If this is a forward range, we can pre-calculate a maximum length.
+ static if (isForwardRange!Range)
+ {
+ auto segments2 = segments.save;
+ size_t precalc = 0;
+ foreach (segment; segments2) precalc += segment.length + 1;
+ }
+ // Otherwise, just venture a guess and resize later if necessary.
+ else size_t precalc = 255;
+
+ auto buf = new Unqual!(ElementEncodingType!(ElementType!Range))[](precalc);
+ size_t pos = 0;
+ foreach (segment; segments)
+ {
+ if (segment.empty) continue;
+ static if (!isForwardRange!Range)
+ {
+ immutable neededLength = pos + segment.length + 1;
+ if (buf.length < neededLength)
+ buf.length = reserve(buf, neededLength + buf.length/2);
+ }
+ auto r = chainPath(buf[0 .. pos], segment);
+ size_t i;
+ foreach (c; r)
+ {
+ buf[i] = c;
+ ++i;
+ }
+ 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...)
+ @safe pure nothrow
+if (isSomeChar!C)
+{
+ return buildPath!(typeof(paths))(paths);
+}
+
+///
+@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");
+ }
+
+ 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`);
+ }
+}
+
+@system unittest // non-documented
+{
+ import std.range;
+ // ir() wraps an array in a plain (i.e. non-forward) input range, so that
+ // we can test both code paths
+ 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");
+
+ // 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");
+ }
+ 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);
+
+ 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);
+ }
+
+ // 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);
+
+ auto fewLong = 'b'.repeat(500).array().repeat(10).array();
+ auto fewLongCombined = join(fewLong, dirSeparator);
+ assert(buildPath(fewLong) == fewLongCombined);
+ assert(buildPath(ir(fewLong)) == fewLongCombined);
+}
+
+@safe unittest
+{
+ // Test for issue 7397
+ string[] ary = ["a", "b"];
+ version (Posix)
+ {
+ assert(buildPath(ary) == "a/b");
+ }
+ else version (Windows)
+ {
+ 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
+ directory separators.
+ It will return "." if the path leads to the starting directory.
+ On Windows, slashes are replaced with backslashes.
+
+ Using buildNormalizedPath on null paths will always return null.
+
+ 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.
+
+ 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");
+ }
+
+ 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`);
+ }
+}
+
+@safe unittest
+{
+ 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) == "");
+
+ 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");
+ }
+ 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
+
+ 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]);
+
+ static struct Result
+ {
+ @property bool empty()
+ {
+ return c == c.init;
+ }
+
+ @property C front()
+ {
+ return c;
+ }
+
+ void popFront()
+ {
+ C lastc = c;
+ c = c.init;
+ if (!element.empty)
+ {
+ c = getElement0();
+ return;
+ }
+ L1:
+ while (1)
+ {
+ if (elements.empty)
+ {
+ element = element[0 .. 0];
+ return;
+ }
+ element = elements.front;
+ elements.popFront();
+ if (isDot(element) || (rooted && isDotDot(element)))
+ continue;
+
+ if (rooted || !isDotDot(element))
+ {
+ 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;
+ }
+ }
+ break;
+ }
+
+ static assert(dirSeparator.length == 1);
+ if (lastc == dirSeparator[0] || lastc == lastc.init)
+ c = getElement0();
+ else
+ c = dirSeparator[0];
+ }
+
+ static if (isForwardRange!R)
+ {
+ @property auto save()
+ {
+ auto result = this;
+ result.element = element.save;
+ result.elements = elements.save;
+ return result;
+ }
+ }
+
+ private:
+ this(R path)
+ {
+ 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('.');
+ }
+
+ C getElement0()
+ {
+ static if (isNarrowString!S) // avoid autodecode
+ {
+ C c = element[0];
+ element = element[1 .. $];
+ }
+ else
+ {
+ C c = element.front;
+ element.popFront();
+ }
+ version (Windows)
+ {
+ 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 Result(path);
+}
+
+///
+@safe unittest
+{
+ import std.array;
+ assert(asNormalizedPath("foo/..").array == ".");
+
+ version (Posix)
+ {
+ 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");
+ }
+
+ version (Windows)
+ {
+ 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`);
+ }
+}
+
+auto asNormalizedPath(R)(auto ref R path)
+if (isConvertibleToString!R)
+{
+ return asNormalizedPath!(StringTypeOf!R)(path);
+}
+
+@safe unittest
+{
+ assert(testAliasedString!asNormalizedPath(null));
+}
+
+@safe unittest
+{
+ 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(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(asNormalizedPath("./").array == ".");
+ assert(asNormalizedPath("././").array == ".");
+ assert(asNormalizedPath("./foo/..").array == ".");
+ assert(asNormalizedPath("foo/..").array == ".");
+ }
+ else version (Windows)
+ {
+ 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(asNormalizedPath(`.\`).array == ".");
+ assert(asNormalizedPath(`.\.\`).array == ".");
+ assert(asNormalizedPath(`.\foo\..`).array == ".");
+ assert(asNormalizedPath(`foo\..`).array == ".");
+ }
+ else static assert(0);
+}
+
+@safe unittest
+{
+ import std.array;
+
+ version (Posix)
+ {
+ // Trivial
+ assert(asNormalizedPath("").empty);
+ assert(asNormalizedPath("foo/bar").array == "foo/bar");
+
+ // Correct handling of leading slashes
+ 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(asNormalizedPath("/./foo").array == "/foo");
+ assert(asNormalizedPath("/foo/./bar").array == "/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(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(asNormalizedPath("/foo/../bar//./../...///baz//").array == "/.../baz");
+ static assert(asNormalizedPath("/foo/../bar//./../...///baz//").array == "/.../baz");
+ }
+ else version (Windows)
+ {
+ // Trivial
+ assert(asNormalizedPath("").empty);
+ assert(asNormalizedPath(`foo\bar`).array == `foo\bar`);
+ assert(asNormalizedPath("foo/bar").array == `foo\bar`);
+
+ // Correct handling of absolute paths
+ 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(asNormalizedPath(`\./foo`).array == `\foo`);
+ assert(asNormalizedPath(`\foo/.\bar`).array == `\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(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(asNormalizedPath(`c:foo`).array == `c:foo`);
+ assert(asNormalizedPath(`c:..\foo\.\..\bar`).array == `c:..\bar`);
+
+ // The ultimate path
+ 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:
+ bidirectional range of slices of `path`
+*/
+auto pathSplitter(R)(R path)
+if ((isRandomAccessRange!R && hasSlicing!R ||
+ isNarrowString!R) &&
+ !isConvertibleToString!R)
+{
+ static struct PathSplitter
+ {
+ @property bool empty() const { return pe == 0; }
+
+ @property R front()
+ {
+ assert(!empty);
+ return _path[fs .. fe];
+ }
+
+ void popFront()
+ {
+ assert(!empty);
+ if (ps == pe)
+ {
+ if (fs == bs && fe == be)
+ {
+ pe = 0;
+ }
+ else
+ {
+ fs = bs;
+ fe = be;
+ }
+ }
+ else
+ {
+ fs = ps;
+ fe = fs;
+ while (fe < pe && !isDirSeparator(_path[fe]))
+ ++fe;
+ ps = ltrim(fe, pe);
+ }
+ }
+
+ @property R back()
+ {
+ assert(!empty);
+ return _path[bs .. be];
+ }
+
+ void popBack()
+ {
+ assert(!empty);
+ if (ps == pe)
+ {
+ if (fs == bs && fe == be)
+ {
+ pe = 0;
+ }
+ else
+ {
+ bs = fs;
+ be = fe;
+ }
+ }
+ else
+ {
+ bs = pe;
+ be = bs;
+ while (bs > ps && !isDirSeparator(_path[bs - 1]))
+ --bs;
+ pe = rtrim(ps, bs);
+ }
+ }
+ @property auto save() { return this; }
+
+
+ private:
+ R _path;
+ size_t ps, pe;
+ size_t fs, fe;
+ size_t bs, be;
+
+ this(R p)
+ {
+ if (p.empty)
+ {
+ 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);
+ fs = 0;
+ fe = i;
+ ps = ltrim(fe, pe);
+ }
+ else if (isDriveRoot(_path))
+ {
+ fs = 0;
+ fe = 3;
+ ps = ltrim(fe, pe);
+ }
+ else if (_path.length >= 1 && isDirSeparator(_path[0]))
+ {
+ fs = 0;
+ fe = 1;
+ ps = ltrim(fe, pe);
+ }
+ else
+ {
+ assert(!isRooted(_path));
+ popFront();
+ }
+ }
+ else version (Posix)
+ {
+ if (_path.length >= 1 && isDirSeparator(_path[0]))
+ {
+ fs = 0;
+ fe = 1;
+ ps = ltrim(fe, pe);
+ }
+ else
+ {
+ popFront();
+ }
+ }
+ else static assert(0);
+
+ if (ps == pe)
+ {
+ bs = fs;
+ be = fe;
+ }
+ else
+ {
+ 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);
+}
+
+///
+@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.
+ import std.algorithm;
+ import std.range;
+ bool equal2(R1, R2)(R1 r1, R2 r2)
+ {
+ static assert(isBidirectionalRange!R1);
+ return equal(r1, r2) && equal(retro(r1), retro(r2));
+ }
+
+ assert(pathSplitter("").empty);
+
+ // Root directories
+ assert(equal2(pathSplitter("/"), ["/"]));
+ assert(equal2(pathSplitter("//"), ["/"]));
+ assert(equal2(pathSplitter("///"w), ["/"w]));
+
+ // Absolute paths
+ 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]));
+
+ // save()
+ auto ps1 = pathSplitter("foo/bar/baz");
+ auto ps2 = ps1.save;
+ ps1.popFront();
+ 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]));
+ }
+ 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"]));
+ }
+
+ import std.exception;
+ assertCTFEable!(
+ {
+ assert(equal(pathSplitter("/foo/bar".dup), ["/", "foo", "bar"]));
+ });
+
+ 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]));
+}
+
+
+
+
+/** 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"));
+ }
+ ---
+
+ On Windows, this function returns true if the path starts at
+ the root directory of the current drive, of some other drive,
+ or of a network drive.
+ ---
+ version (Windows)
+ {
+ assert(isRooted(`\`));
+ assert(isRooted(`\foo`));
+ assert(isRooted(`d:\foo`));
+ assert(isRooted(`\\foo\bar`));
+ assert(!isRooted("foo"));
+ assert(!isRooted("d:foo"));
+ }
+ ---
+*/
+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;
+ else version (Windows) return isAbsolute!(BaseOf!R)(path);
+}
+
+
+@safe unittest
+{
+ 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"));
+ }
+
+ static assert(isRooted("/foo"));
+ static assert(!isRooted("foo"));
+
+ static struct DirEntry { string s; alias s this; }
+ assert(!isRooted(DirEntry("foo")));
+}
+
+
+
+
+/** Determines whether a path is absolute or not.
+
+ 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"));
+ }
+ ---
+
+ On Windows, an absolute path starts at the root directory of
+ a specific drive. Hence, it must start with $(D `d:\`) or $(D `d:/`),
+ where $(D d) is the drive letter. Alternatively, it may be a
+ network path, i.e. a path starting with a double (back)slash.
+ ---
+ version (Windows)
+ {
+ 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)(R path) pure nothrow @safe
+ if (isRandomAccessRange!R && isSomeChar!(ElementType!R) ||
+ is(StringTypeOf!R));
+}
+else version (Windows)
+{
+ bool isAbsolute(R)(R path)
+ if (isRandomAccessRange!R && isSomeChar!(ElementType!R) ||
+ is(StringTypeOf!R))
+ {
+ return isDriveRoot!(BaseOf!R)(path) || isUNC!(BaseOf!R)(path);
+ }
+}
+else version (Posix)
+{
+ alias isAbsolute = isRooted;
+}
+
+
+@safe unittest
+{
+ assert(!isAbsolute("foo"));
+ assert(!isAbsolute("../foo"w));
+ static assert(!isAbsolute("foo"));
+
+ version (Posix)
+ {
+ 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`));
+ }
+
+ {
+ auto r = MockRange!(immutable(char))(`../foo`);
+ assert(!r.isAbsolute());
+ }
+
+ static struct DirEntry { string s; alias s this; }
+ assert(!isAbsolute(DirEntry("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 $(D base) and return
+ the result. If $(D base) is not specified, the current
+ working directory is used.)
+ )
+ The function allocates memory if and only if it gets to the third stage
+ of this algorithm.
+
+ Params:
+ path = the relative path to transform
+ base = the base directory of the relative path
+
+ 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;
+ auto baseVar = base;
+ if (!isAbsolute(baseVar)) throw new Exception("Base directory must be absolute");
+ 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");
+ }
+
+ 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)
+ {
+ static assert(absolutePath("some/file", "/foo/bar") == "/foo/bar/some/file");
+ }
+
+ version (Windows)
+ {
+ 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.
+
+ The returned _path is relative to $(D base), which is by default
+ taken to be the current working directory. If specified,
+ $(D base) must be an absolute _path, and it is always assumed
+ to refer to a directory. If $(D path) and $(D base) refer to
+ the same directory, the function returns $(D `.`).
+
+ The following algorithm is used:
+ $(OL
+ $(LI If $(D path) is a relative directory, return it unaltered.)
+ $(LI Find a common root between $(D path) and $(D base).
+ If there is no common root, return $(D path) unaltered.)
+ $(LI Prepare a string with as many $(D `../`) or $(D `..\`) as
+ necessary to reach the common root from base path.)
+ $(LI Append the remaining segments of $(D path) to the string
+ and return.)
+ )
+
+ In the second step, path components are compared using $(D filenameCmp!cs),
+ where $(D cs) is an optional template parameter determining whether
+ the comparison is case sensitive or not. See the
+ $(LREF filenameCmp) documentation for details.
+
+ This function allocates memory.
+
+ 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");
+ }
+ 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`);
+ }
+}
+
+@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)
+*/
+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))
+{
+ bool choosePath = !isAbsolute(path);
+
+ // Find common root with current working directory
+
+ auto basePS = pathSplitter(base);
+ auto pathPS = pathSplitter(path);
+ choosePath |= filenameCmp!cs(basePS.front, pathPS.front) != 0;
+
+ basePS.popFront();
+ pathPS.popFront();
+
+ import std.algorithm.comparison : mismatch;
+ import std.algorithm.iteration : joiner;
+ import std.array : array;
+ import std.range.primitives : walkLength;
+ import std.range : repeat, chain, choose;
+ 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
+ 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)
+ {
+ 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");
+ }
+ else version (Windows)
+ {
+ 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);
+}
+
+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);
+}
+
+@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");
+}
+
+@system unittest
+{
+ import std.array, std.utf : bCU=byCodeUnit;
+ version (Posix)
+ {
+ 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(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);
+ }
+}
+
+/** 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).
+
+ On Windows, the backslash and slash characters ($(D `\`) and $(D `/`))
+ are considered equal.
+
+ Params:
+ cs = Case-sensitivity of the comparison.
+ a = A filename character.
+ b = A filename character.
+
+ 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
+{
+ if (isDirSeparator(a) && isDirSeparator(b)) return 0;
+ static if (!cs)
+ {
+ import std.uni : toLower;
+ a = toLower(a);
+ b = toLower(b);
+ }
+ return cast(int)(a - b);
+}
+
+///
+@safe unittest
+{
+ assert(filenameCharCmp('a', 'a') == 0);
+ assert(filenameCharCmp('a', 'b') < 0);
+ assert(filenameCharCmp('b', 'a') > 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
+
+ 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.
+
+ 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);
+ }
+ 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, Range1, Range2)
+ (auto ref Range1 filename1, auto ref Range2 filename2)
+if (isConvertibleToString!Range1 || isConvertibleToString!Range2)
+{
+ import std.meta : staticMap;
+ alias Types = staticMap!(convertToString, Range1, Range2);
+ return filenameCmp!(cs, Types)(filename1, filename2);
+}
+
+@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.
+
+ Some characters of pattern have a special meaning (they are
+ $(I meta-characters)) and can't be escaped. These are:
+
+ $(BOOKTABLE,
+ $(TR $(TD $(D *))
+ $(TD Matches 0 or more instances of any character.))
+ $(TR $(TD $(D ?))
+ $(TD Matches exactly one instance of any character.))
+ $(TR $(TD $(D [)$(I chars)$(D ]))
+ $(TD Matches one instance of any character that appears
+ between the brackets.))
+ $(TR $(TD $(D [!)$(I chars)$(D ]))
+ $(TD Matches one instance of any character that does not
+ appear between the brackets after the exclamation mark.))
+ $(TR $(TD $(D {)$(I string1)$(D ,)$(I string2)$(D ,)&hellip;$(D }))
+ $(TD Matches either of the specified strings.))
+ )
+
+ 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.
+
+ Note that directory
+ 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))
+ */
+bool globMatch(CaseSensitive cs = CaseSensitive.osDefault, C, Range)
+ (Range path, const(C)[] pattern)
+ @safe pure nothrow
+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.searching : balancedParens;
+ assert(balancedParens(pattern, '[', ']', 0));
+ assert(balancedParens(pattern, '{', '}', 0));
+}
+body
+{
+ alias RC = Unqual!(ElementEncodingType!Range);
+
+ 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[] pattmp;
+ foreach (ref pi; 0 .. pattern.length)
+ {
+ const pc = pattern[pi];
+ switch (pc)
+ {
+ case '*':
+ if (pi + 1 == pattern.length)
+ return true;
+ for (; !path.empty; path.popFront())
+ {
+ auto p = path.save;
+ if (globMatch!(cs, C)(p,
+ pattern[pi + 1 .. pattern.length]))
+ return true;
+ }
+ return false;
+
+ case '?':
+ if (path.empty)
+ return false;
+ path.popFront();
+ break;
+
+ case '[':
+ if (path.empty)
+ return false;
+ auto nc = path.front;
+ path.popFront();
+ auto not = false;
+ ++pi;
+ if (pattern[pi] == '!')
+ {
+ not = true;
+ ++pi;
+ }
+ auto anymatch = false;
+ while (1)
+ {
+ const pc2 = pattern[pi];
+ if (pc2 == ']')
+ break;
+ if (!anymatch && (filenameCharCmp!cs(nc, pc2) == 0))
+ anymatch = true;
+ ++pi;
+ }
+ if (anymatch == not)
+ return false;
+ break;
+
+ case '{':
+ // find end of {} section
+ auto piRemain = pi;
+ for (; piRemain < pattern.length
+ && pattern[piRemain] != '}'; ++piRemain)
+ { }
+
+ if (piRemain < pattern.length)
+ ++piRemain;
+ ++pi;
+
+ while (pi < pattern.length)
+ {
+ const pi0 = pi;
+ C pc3 = pattern[pi];
+ // find end of current alternative
+ for (; pi < pattern.length && pc3 != '}' && pc3 != ','; ++pi)
+ {
+ pc3 = pattern[pi];
+ }
+
+ auto p = path.save;
+ if (pi0 == pi)
+ {
+ if (globMatch!(cs, C)(p, pattern[piRemain..$]))
+ {
+ return true;
+ }
+ ++pi;
+ }
+ else
+ {
+ /* Match for:
+ * pattern[pi0 .. pi-1] ~ pattern[piRemain..$]
+ */
+ if (pattmp is null)
+ // Allocate this only once per function invocation.
+ // Should do it with malloc/free, but that would make it impure.
+ pattmp = new C[pattern.length];
+
+ const len1 = pi - 1 - pi0;
+ pattmp[0 .. len1] = pattern[pi0 .. pi - 1];
+
+ const len2 = pattern.length - piRemain;
+ pattmp[len1 .. len1 + len2] = pattern[piRemain .. $];
+
+ if (globMatch!(cs, C)(p, pattmp[0 .. len1 + len2]))
+ {
+ return true;
+ }
+ }
+ if (pc3 == '}')
+ {
+ break;
+ }
+ }
+ return false;
+
+ default:
+ if (path.empty)
+ return false;
+ if (filenameCharCmp!cs(pc, path.front) != 0)
+ return false;
+ path.popFront();
+ break;
+ }
+ }
+ return path.empty;
+ }
+}
+
+///
+@safe unittest
+{
+ 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, Range)
+ (auto ref Range path, const(C)[] pattern)
+ @safe pure nothrow
+if (isConvertibleToString!Range)
+{
+ return globMatch!(cs, C, StringTypeOf!Range)(path, pattern);
+}
+
+@safe unittest
+{
+ assert(testAliasedString!globMatch("foo.bar", "*"));
+}
+
+@safe unittest
+{
+ assert(globMatch!(CaseSensitive.no)("foo", "Foo"));
+ assert(!globMatch!(CaseSensitive.yes)("foo", "Foo"));
+
+ assert(globMatch("foo", "*"));
+ assert(globMatch("foo.bar"w, "*"w));
+ assert(globMatch("foo.bar"d, "*.*"d));
+ assert(globMatch("foo.bar", "foo*"));
+ assert(globMatch("foo.bar"w, "f*bar"w));
+ assert(globMatch("foo.bar"d, "f*b*r"d));
+ assert(globMatch("foo.bar", "f???bar"));
+ assert(globMatch("foo.bar"w, "[fg]???bar"w));
+ assert(globMatch("foo.bar"d, "[!gh]*bar"d));
+
+ assert(!globMatch("foo", "bar"));
+ assert(!globMatch("foo"w, "*.*"w));
+ assert(!globMatch("foo.bar"d, "f*baz"d));
+ assert(!globMatch("foo.bar", "f*b*x"));
+ assert(!globMatch("foo.bar", "[gh]???bar"));
+ assert(!globMatch("foo.bar"w, "[!fg]*bar"w));
+ assert(!globMatch("foo.bar"d, "[fg]???baz"d));
+ assert(!globMatch("foo.di", "*.d")); // test issue 6634: triggered bad assertion
+
+ assert(globMatch("foo.bar", "{foo,bif}.bar"));
+ assert(globMatch("bif.bar"w, "{foo,bif}.bar"w));
+
+ assert(globMatch("bar.foo"d, "bar.{foo,bif}"d));
+ assert(globMatch("bar.bif", "bar.{foo,bif}"));
+
+ assert(globMatch("bar.fooz"w, "bar.{foo,bif}z"w));
+ assert(globMatch("bar.bifz"d, "bar.{foo,bif}z"d));
+
+ assert(globMatch("bar.foo", "bar.{biz,,baz}foo"));
+ assert(globMatch("bar.foo"w, "bar.{biz,}foo"w));
+ assert(globMatch("bar.foo"d, "bar.{,biz}foo"d));
+ assert(globMatch("bar.foo", "bar.{}foo"));
+
+ assert(globMatch("bar.foo"w, "bar.{ar,,fo}o"w));
+ assert(globMatch("bar.foo"d, "bar.{,ar,fo}o"d));
+ assert(globMatch("bar.o", "bar.{,ar,fo}o"));
+
+ assert(!globMatch("foo", "foo?"));
+ assert(!globMatch("foo", "foo[]"));
+ assert(!globMatch("foo", "foob"));
+ assert(!globMatch("foo", "foo{b}"));
+
+
+ static assert(globMatch("foo.bar", "[!gh]*bar"));
+}
+
+
+
+
+/** Checks that the given file or directory name is valid.
+
+ The maximum length of $(D filename) is given by the constant
+ $(D core.stdc.stdio.FILENAME_MAX). (On Windows, this number is
+ defined as the maximum number of UTF-16 code points, and the
+ test will therefore only yield strictly correct results when
+ $(D filename) is a string of $(D wchar)s.)
+
+ On Windows, the following criteria must be satisfied
+ ($(LINK2 http://msdn.microsoft.com/en-us/library/aa365247(v=vs.85).aspx,source)):
+ $(UL
+ $(LI $(D filename) must not contain any characters whose integer
+ representation is in the range 0-31.)
+ $(LI $(D filename) must not contain any of the following $(I reserved
+ characters): <>:"/\|?*)
+ $(LI $(D filename) may not end with a space ($(D ' ')) or a period
+ ($(D '.')).)
+ )
+
+ On POSIX, $(D filename) may not contain a forward slash ($(D '/')) or
+ the null character ($(D '\0')).
+
+ Params:
+ filename = string to check
+
+ Returns:
+ $(D true) if and only if $(D filename) is not
+ empty, not too long, and does not contain invalid characters.
+
+*/
+bool isValidFilename(Range)(Range filename)
+if ((isRandomAccessRange!Range && hasLength!Range && hasSlicing!Range && isSomeChar!(ElementEncodingType!Range) ||
+ isNarrowString!Range) &&
+ !isConvertibleToString!Range)
+{
+ import core.stdc.stdio : FILENAME_MAX;
+ if (filename.length == 0 || filename.length >= FILENAME_MAX) return false;
+ foreach (c; filename)
+ {
+ version (Windows)
+ {
+ switch (c)
+ {
+ case 0:
+ ..
+ case 31:
+ case '<':
+ case '>':
+ case ':':
+ case '"':
+ case '/':
+ case '\\':
+ case '|':
+ case '?':
+ case '*':
+ return false;
+
+ default:
+ break;
+ }
+ }
+ else version (Posix)
+ {
+ if (c == 0 || c == '/') return false;
+ }
+ else static assert(0);
+ }
+ version (Windows)
+ {
+ auto last = filename[filename.length - 1];
+ if (last == '.' || last == ' ') return false;
+ }
+
+ // All criteria passed
+ 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;
+ auto valid = ["foo"];
+ auto invalid = ["", "foo\0bar", "foo/bar"];
+ auto pfdep = [`foo\bar`, "*.txt"];
+ version (Windows) invalid ~= pfdep;
+ else version (Posix) valid ~= pfdep;
+ else static assert(0);
+
+ 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)));
+ foreach (fn; invalid)
+ assert(!isValidFilename(to!T(fn)));
+ }
+
+ {
+ auto r = MockRange!(immutable(char))(`dir/file.d`);
+ assert(!isValidFilename(r));
+ }
+
+ 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[]));
+ }
+ }
+}
+
+
+
+/** Checks whether $(D path) is a valid _path.
+
+ 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 "..").
+
+ $(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
+ $(LI If the second character of $(D path) is a colon ($(D ':')),
+ the first character is interpreted as a drive letter, and
+ must be in the range A-Z (case insensitive).)
+ $(LI If $(D path) is on the form $(D `\\$(I server)\$(I share)\...`)
+ (UNC path), $(LREF isValidFilename) is applied to $(I server)
+ and $(I share) as well.)
+ $(LI If $(D path) starts with $(D `\\?\`) (long UNC path), the
+ only requirement for the rest of the string is that it does
+ not contain the null character.)
+ $(LI If $(D path) starts with $(D `\\.\`) (Win32 device namespace)
+ 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(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(Range component)
+ {
+ assert(component.length > 0);
+ if (component[0] == '.')
+ {
+ if (component.length == 1) return true;
+ else if (component.length == 2 && component[1] == '.') return true;
+ }
+ return isValidFilename(component);
+ }
+
+ if (path.length == 1)
+ return isDirSeparator(path[0]) || isValidComponent(path);
+
+ Range remainder;
+ version (Windows)
+ {
+ if (isDirSeparator(path[0]) && isDirSeparator(path[1]))
+ {
+ // Some kind of UNC path
+ if (path.length < 5)
+ {
+ // All valid UNC paths must have at least 5 characters
+ return false;
+ }
+ else if (path[2] == '?')
+ {
+ // Long UNC path
+ if (!isDirSeparator(path[3])) return false;
+ foreach (c; path[4 .. $])
+ {
+ if (c == '\0') return false;
+ }
+ return true;
+ }
+ else if (path[2] == '.')
+ {
+ // Win32 device namespace not supported
+ return false;
+ }
+ else
+ {
+ // Normal UNC path, i.e. \\server\share\...
+ size_t i = 2;
+ while (i < path.length && !isDirSeparator(path[i])) ++i;
+ if (i == path.length || !isValidFilename(path[2 .. i]))
+ return false;
+ ++i; // Skip a single dir separator
+ size_t j = i;
+ while (j < path.length && !isDirSeparator(path[j])) ++j;
+ if (!isValidFilename(path[i .. j])) return false;
+ remainder = path[j .. $];
+ }
+ }
+ else if (isDriveSeparator(path[1]))
+ {
+ import std.ascii : isAlpha;
+ if (!isAlpha(path[0])) return false;
+ remainder = path[2 .. $];
+ }
+ else
+ {
+ remainder = path;
+ }
+ }
+ else version (Posix)
+ {
+ remainder = path;
+ }
+ else static assert(0);
+ remainder = ltrimDirSeparators(remainder);
+
+ // Check that each component satisfies isValidComponent.
+ while (!remainder.empty)
+ {
+ size_t i = 0;
+ while (i < remainder.length && !isDirSeparator(remainder[i])) ++i;
+ assert(i > 0);
+ if (!isValidComponent(remainder[0 .. i])) return false;
+ remainder = ltrimDirSeparators(remainder[i .. $]);
+ }
+
+ // All criteria passed
+ return true;
+}
+
+///
+@safe pure @nogc nothrow
+unittest
+{
+ 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(`\\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(`\\.\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.
+
+ There are two ways of using tilde expansion in a path. One
+ involves using the tilde alone or followed by a path separator. In
+ this case, the tilde will be expanded with the value of the
+ environment variable $(D HOME). The second way is putting
+ a username after the tilde (i.e. $(D ~john/Mail)). Here,
+ the username will be searched for in the user database
+ (i.e. $(D /etc/passwd) on Unix systems) and will expand to
+ whatever path is stored there. The username is considered the
+ string after the tilde ending at the first instance of a path
+ separator.
+
+ Note that using the $(D ~user) syntax may give different
+ values from just $(D ~) if the environment variable doesn't
+ match the value stored in the user database.
+
+ When the environment variable version is used, the path won't
+ be modified if the environment variable doesn't exist or it
+ is empty. When the database version is used, the path won't be
+ modified if the user doesn't exist in the database or there is
+ not enough memory to perform the query.
+
+ 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).
+
+ Example:
+ -----
+ void processFile(string path)
+ {
+ // Allow calling this function with paths such as ~/foo
+ auto fullPath = expandTilde(path);
+ ...
+ }
+ -----
+*/
+string expandTilde(string inputPath) nothrow
+{
+ version (Posix)
+ {
+ import core.exception : onOutOfMemoryError;
+ import core.stdc.errno : errno, ERANGE;
+ import core.stdc.stdlib : malloc, free, realloc;
+
+ /* Joins a path from a C string to the remainder of path.
+
+ The last path separator from c_path is discarded. The result
+ 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) 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 = strlen(c_path);
+
+ // Remove trailing path separator, if any
+ if (end && isDirSeparator(c_path[end - 1]))
+ end--;
+
+ // (this is the only GC allocation done in expandTilde())
+ string cp;
+ if (char_pos < path.length)
+ // 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) nothrow
+ {
+ import core.stdc.stdlib : getenv;
+
+ assert(path.length >= 1);
+ assert(path[0] == '~');
+
+ // Get HOME and use that to replace the tilde.
+ auto home = getenv("HOME");
+ if (home == null)
+ return path;
+
+ return combineCPathWithDPath(home, path, 1);
+ }
+
+ // Replaces the tilde from path with the path from the user database.
+ static string expandFromDatabase(string path) nothrow
+ {
+ // bionic doesn't really support this, as getpwnam_r
+ // isn't provided and getpwnam is basically just a stub
+ 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.
+ 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[0 .. username_len - 1] = path[1 .. $];
+ last_char = path.length + 1;
+ }
+ else
+ {
+ 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.
+ version (unittest)
+ uint extra_memory_size = 2;
+ else
+ uint extra_memory_size = 5 * 1024;
+ char* extra_memory;
+ scope(exit) free(extra_memory);
+
+ passwd result;
+ while (1)
+ {
+ extra_memory = cast(char*) realloc(extra_memory, extra_memory_size * char.sizeof);
+ if (extra_memory == null)
+ onOutOfMemoryError();
+
+ // Obtain info from database.
+ passwd *verify;
+ errno = 0;
+ if (getpwnam_r(username, &result, extra_memory, extra_memory_size,
+ &verify) == 0)
+ {
+ // Succeeded if verify points at result
+ if (verify == &result)
+ // username is found
+ path = combineCPathWithDPath(result.pw_dir, path, last_char);
+ break;
+ }
+
+ if (errno != ERANGE &&
+ // On FreeBSD and OSX, errno can be left at 0 instead of set to ERANGE
+ errno != 0)
+ onOutOfMemoryError();
+
+ // extra_memory isn't large enough
+ import core.checkedint : mulu;
+ bool overflow;
+ extra_memory_size = mulu(extra_memory_size, 2, overflow);
+ if (overflow) assert(0);
+ }
+ return path;
+ }
+ }
+
+ // Return early if there is no tilde in path.
+ if (inputPath.length < 1 || inputPath[0] != '~')
+ return inputPath;
+
+ if (inputPath.length == 1 || isDirSeparator(inputPath[1]))
+ return expandFromEnvironment(inputPath);
+ else
+ return expandFromDatabase(inputPath);
+ }
+ else version (Windows)
+ {
+ // Put here real windows implementation.
+ return inputPath;
+ }
+ else
+ {
+ static assert(0); // Guard. Implement on other platforms.
+ }
+}
+
+
+version (unittest) import std.process : environment;
+@system unittest
+{
+ version (Posix)
+ {
+ // Retrieve the current home variable.
+ auto oldHome = environment.get("HOME");
+
+ // Testing when there is no environment variable.
+ environment.remove("HOME");
+ assert(expandTilde("~/") == "~/");
+ assert(expandTilde("~") == "~");
+
+ // Testing when an environment variable is set.
+ environment["HOME"] = "dmd/test";
+ assert(expandTilde("~/") == "dmd/test/");
+ assert(expandTilde("~") == "dmd/test");
+
+ // The same, but with a variable ending in a slash.
+ environment["HOME"] = "dmd/test/";
+ assert(expandTilde("~/") == "dmd/test/");
+ assert(expandTilde("~") == "dmd/test");
+
+ // Recover original HOME variable before continuing.
+ if (oldHome !is null) environment["HOME"] = oldHome;
+ else environment.remove("HOME");
+
+ // Test user expansion for root, no /root on Android
+ version (OSX)
+ {
+ assert(expandTilde("~root") == "/var/root", expandTilde("~root"));
+ assert(expandTilde("~root/") == "/var/root/", expandTilde("~root/"));
+ }
+ else version (Android)
+ {
+ }
+ else
+ {
+ assert(expandTilde("~root") == "/root", expandTilde("~root"));
+ assert(expandTilde("~root/") == "/root/", expandTilde("~root/"));
+ }
+ assert(expandTilde("~Idontexist/hey") == "~Idontexist/hey");
+ }
+}
+
+version (unittest)
+{
+ /* Define a mock RandomAccessRange to use for unittesting.
+ */
+
+ struct MockRange(C)
+ {
+ this(C[] array) { this.array = array; }
+ const
+ {
+ @property size_t length() { return array.length; }
+ @property bool empty() { return array.length == 0; }
+ @property C front() { return array[0]; }
+ @property C back() { return array[$ - 1]; }
+ @property size_t opDollar() { return length; }
+ C opIndex(size_t i) { return array[i]; }
+ }
+ void popFront() { array = array[1 .. $]; }
+ void popBack() { array = array[0 .. $-1]; }
+ MockRange!C opSlice( size_t lwr, size_t upr) const
+ {
+ return MockRange!C(array[lwr .. upr]);
+ }
+ @property MockRange save() { return this; }
+ private:
+ C[] array;
+ }
+
+ 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))
+ alias BaseOf = R;
+ else
+ alias BaseOf = StringTypeOf!R;
+}
diff --git a/libphobos/src/std/process.d b/libphobos/src/std/process.d
new file mode 100644
index 0000000..6571d47
--- /dev/null
+++ b/libphobos/src/std/process.d
@@ -0,0 +1,4047 @@
+// Written in the D programming language.
+
+/**
+Functions for starting and interacting with other processes, and for
+working with the current _process' execution environment.
+
+Process_handling:
+$(UL $(LI
+ $(LREF spawnProcess) spawns a new _process, optionally assigning it an
+ arbitrary set of standard input, output, and error streams.
+ The function returns immediately, leaving the child _process to execute
+ in parallel with its parent. All other functions in this module that
+ spawn processes are built around $(D spawnProcess).)
+$(LI
+ $(LREF wait) makes the parent _process wait for a child _process to
+ terminate. In general one should always do this, to avoid
+ child processes becoming "zombies" when the parent _process exits.
+ Scope guards are perfect for this – see the $(LREF spawnProcess)
+ documentation for examples. $(LREF tryWait) is similar to $(D wait),
+ but does not block if the _process has not yet terminated.)
+$(LI
+ $(LREF pipeProcess) also spawns a child _process which runs
+ in parallel with its parent. However, instead of taking
+ arbitrary streams, it automatically creates a set of
+ pipes that allow the parent to communicate with the child
+ through the child's standard input, output, and/or error streams.
+ This function corresponds roughly to C's $(D popen) function.)
+$(LI
+ $(LREF execute) starts a new _process and waits for it
+ to complete before returning. Additionally, it captures
+ the _process' standard output and error streams and returns
+ the output of these as a string.)
+$(LI
+ $(LREF spawnShell), $(LREF pipeShell) and $(LREF executeShell) work like
+ $(D spawnProcess), $(D pipeProcess) and $(D execute), respectively,
+ except that they take a single command string and run it through
+ the current user's default command interpreter.
+ $(D executeShell) corresponds roughly to C's $(D system) function.)
+$(LI
+ $(LREF kill) attempts to terminate a running _process.)
+)
+
+The following table compactly summarises the different _process creation
+functions and how they relate to each other:
+$(BOOKTABLE,
+ $(TR $(TH )
+ $(TH Runs program directly)
+ $(TH Runs shell command))
+ $(TR $(TD Low-level _process creation)
+ $(TD $(LREF spawnProcess))
+ $(TD $(LREF spawnShell)))
+ $(TR $(TD Automatic input/output redirection using pipes)
+ $(TD $(LREF pipeProcess))
+ $(TD $(LREF pipeShell)))
+ $(TR $(TD Execute and wait for completion, collect output)
+ $(TD $(LREF execute))
+ $(TD $(LREF executeShell)))
+)
+
+Other_functionality:
+$(UL
+$(LI
+ $(LREF pipe) is used to create unidirectional pipes.)
+$(LI
+ $(LREF environment) is an interface through which the current _process'
+ environment variables can be read and manipulated.)
+$(LI
+ $(LREF escapeShellCommand) and $(LREF escapeShellFileName) are useful
+ for constructing shell command lines in a portable way.)
+)
+
+Authors:
+ $(LINK2 https://github.com/kyllingstad, Lars Tandle Kyllingstad),
+ $(LINK2 https://github.com/schveiguy, Steven Schveighoffer),
+ $(HTTP thecybershadow.net, Vladimir Panteleev)
+Copyright:
+ Copyright (c) 2013, the authors. All rights reserved.
+License:
+ $(HTTP www.boost.org/LICENSE_1_0.txt, Boost License 1.0).
+Source:
+ $(PHOBOSSRC std/_process.d)
+Macros:
+ OBJECTREF=$(D $(LINK2 object.html#$0,$0))
+ LREF=$(D $(LINK2 #.$0,$0))
+*/
+module std.process;
+
+version (Posix)
+{
+ import core.sys.posix.sys.wait;
+ import core.sys.posix.unistd;
+}
+version (Windows)
+{
+ import core.stdc.stdio;
+ import core.sys.windows.windows;
+ import std.utf;
+ import std.windows.syserror;
+}
+
+import std.internal.cstring;
+import std.range.primitives;
+import std.stdio;
+
+
+// When the DMC runtime is used, we have to use some custom functions
+// to convert between Windows file handles and FILE*s.
+version (Win32) version (CRuntime_DigitalMars) version = DMC_RUNTIME;
+
+
+// Some of the following should be moved to druntime.
+private
+{
+ // Microsoft Visual C Runtime (MSVCRT) declarations.
+ version (Windows)
+ {
+ version (DMC_RUNTIME) { } else
+ {
+ import core.stdc.stdint;
+ enum
+ {
+ STDIN_FILENO = 0,
+ STDOUT_FILENO = 1,
+ STDERR_FILENO = 2,
+ }
+ }
+ }
+
+ // POSIX API declarations.
+ version (Posix)
+ {
+ version (OSX)
+ {
+ extern(C) char*** _NSGetEnviron() nothrow;
+ const(char**) getEnvironPtr() @trusted
+ {
+ return *_NSGetEnviron;
+ }
+ }
+ else
+ {
+ // Made available by the C runtime:
+ extern(C) extern __gshared const char** environ;
+ const(char**) getEnvironPtr() @trusted
+ {
+ return environ;
+ }
+ }
+
+ @system unittest
+ {
+ new Thread({assert(getEnvironPtr !is null);}).start();
+ }
+ }
+} // private
+
+
+// =============================================================================
+// Functions and classes for process management.
+// =============================================================================
+
+
+/**
+Spawns a new _process, optionally assigning it an arbitrary set of standard
+input, output, and error streams.
+
+The function returns immediately, leaving the child _process to execute
+in parallel with its parent. It is recommended to always call $(LREF wait)
+on the returned $(LREF Pid) unless the process was spawned with
+$(D Config.detached) flag, as detailed in the documentation for $(D wait).
+
+Command_line:
+There are four overloads of this function. The first two take an array
+of strings, $(D args), which should contain the program name as the
+zeroth element and any command-line arguments in subsequent elements.
+The third and fourth versions are included for convenience, and may be
+used when there are no command-line arguments. They take a single string,
+$(D program), which specifies the program name.
+
+Unless a directory is specified in $(D args[0]) or $(D program),
+$(D spawnProcess) will search for the program in a platform-dependent
+manner. On POSIX systems, it will look for the executable in the
+directories listed in the PATH environment variable, in the order
+they are listed. On Windows, it will search for the executable in
+the following sequence:
+$(OL
+ $(LI The directory from which the application loaded.)
+ $(LI The current directory for the parent process.)
+ $(LI The 32-bit Windows system directory.)
+ $(LI The 16-bit Windows system directory.)
+ $(LI The Windows directory.)
+ $(LI The directories listed in the PATH environment variable.)
+)
+---
+// Run an executable called "prog" located in the current working
+// directory:
+auto pid = spawnProcess("./prog");
+scope(exit) wait(pid);
+// We can do something else while the program runs. The scope guard
+// ensures that the process is waited for at the end of the scope.
+...
+
+// Run DMD on the file "myprog.d", specifying a few compiler switches:
+auto dmdPid = spawnProcess(["dmd", "-O", "-release", "-inline", "myprog.d" ]);
+if (wait(dmdPid) != 0)
+ writeln("Compilation failed!");
+---
+
+Environment_variables:
+By default, the child process inherits the environment of the parent
+process, along with any additional variables specified in the $(D env)
+parameter. If the same variable exists in both the parent's environment
+and in $(D env), the latter takes precedence.
+
+If the $(LREF Config.newEnv) flag is set in $(D config), the child
+process will $(I not) inherit the parent's environment. Its entire
+environment will then be determined by $(D env).
+---
+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 $(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
+streams of its parent.
+---
+// Run DMD on the file myprog.d, logging any error messages to a
+// file named errors.log.
+auto logFile = File("errors.log", "w");
+auto pid = spawnProcess(["dmd", "myprog.d"],
+ std.stdio.stdin,
+ std.stdio.stdout,
+ logFile);
+if (wait(pid) != 0)
+ writeln("Compilation failed. See errors.log for details.");
+---
+
+Note that if you pass a $(D File) object that is $(I not)
+one of the standard input/output/error streams of the parent process,
+that stream will by default be $(I closed) in the parent process when
+this function returns. See the $(LREF Config) documentation below for
+information about how to disable this behaviour.
+
+Beware of buffering issues when passing $(D File) objects to
+$(D spawnProcess). The child process will inherit the low-level raw
+read/write offset associated with the underlying file descriptor, but
+it will not be aware of any buffered data. In cases where this matters
+(e.g. when a file should be aligned before being passed on to the
+child process), it may be a good idea to use unbuffered streams, or at
+least ensure all relevant buffers are flushed.
+
+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 $(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 $(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 $(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)
+ for an overview of available flags.
+workDir = The working directory for the new process.
+ By default the child process inherits the parent's working
+ directory.
+
+Returns:
+A $(LREF Pid) object that corresponds to the spawned process.
+
+Throws:
+$(LREF ProcessException) on failure to start the process.$(BR)
+$(REF StdioException, std,stdio) on failure to pass one of the streams
+ to the child process (Windows only).$(BR)
+$(REF RangeError, core,exception) if $(D args) is empty.
+*/
+Pid spawnProcess(in char[][] args,
+ File stdin = std.stdio.stdin,
+ File stdout = std.stdio.stdout,
+ File stderr = std.stdio.stderr,
+ const string[string] env = null,
+ Config config = Config.none,
+ in char[] workDir = null)
+ @trusted // TODO: Should be @safe
+{
+ version (Windows) auto args2 = escapeShellArguments(args);
+ else version (Posix) alias args2 = args;
+ return spawnProcessImpl(args2, stdin, stdout, stderr, env, config, workDir);
+}
+
+/// ditto
+Pid spawnProcess(in char[][] args,
+ const string[string] env,
+ Config config = Config.none,
+ in char[] workDir = null)
+ @trusted // TODO: Should be @safe
+{
+ return spawnProcess(args,
+ std.stdio.stdin,
+ std.stdio.stdout,
+ std.stdio.stderr,
+ env,
+ config,
+ workDir);
+}
+
+/// ditto
+Pid spawnProcess(in char[] program,
+ File stdin = std.stdio.stdin,
+ File stdout = std.stdio.stdout,
+ File stderr = std.stdio.stderr,
+ const string[string] env = null,
+ Config config = Config.none,
+ in char[] workDir = null)
+ @trusted
+{
+ return spawnProcess((&program)[0 .. 1],
+ stdin, stdout, stderr, env, config, workDir);
+}
+
+/// ditto
+Pid spawnProcess(in char[] program,
+ const string[string] env,
+ Config config = Config.none,
+ in char[] workDir = null)
+ @trusted
+{
+ return spawnProcess((&program)[0 .. 1], env, config, workDir);
+}
+
+version (Posix) private enum InternalError : ubyte
+{
+ noerror,
+ exec,
+ chdir,
+ getrlimit,
+ doubleFork,
+}
+
+/*
+Implementation of spawnProcess() for POSIX.
+
+envz should be a zero-terminated array of zero-terminated strings
+on the form "var=value".
+*/
+version (Posix)
+private Pid spawnProcessImpl(in char[][] args,
+ File stdin,
+ File stdout,
+ File stderr,
+ const string[string] env,
+ Config config,
+ in char[] workDir)
+ @trusted // TODO: Should be @safe
+{
+ import core.exception : RangeError;
+ import std.algorithm.searching : any;
+ import std.conv : text;
+ import std.path : isDirSeparator;
+ import std.string : toStringz;
+
+ if (args.empty) throw new RangeError();
+ const(char)[] name = args[0];
+ if (any!isDirSeparator(name))
+ {
+ if (!isExecutable(name))
+ throw new ProcessException(text("Not an executable file: ", name));
+ }
+ else
+ {
+ name = searchPathFor(name);
+ if (name is null)
+ throw new ProcessException(text("Executable file not found: ", args[0]));
+ }
+
+ // Convert program name and arguments to C-style strings.
+ auto argz = new const(char)*[args.length+1];
+ argz[0] = toStringz(name);
+ foreach (i; 1 .. args.length) argz[i] = toStringz(args[i]);
+ argz[$-1] = null;
+
+ // Prepare environment.
+ auto envz = createEnv(env, !(config & Config.newEnv));
+
+ // Open the working directory.
+ // We use open in the parent and fchdir in the child
+ // so that most errors (directory doesn't exist, not a directory)
+ // can be propagated as exceptions before forking.
+ int workDirFD = -1;
+ scope(exit) if (workDirFD >= 0) close(workDirFD);
+ if (workDir.length)
+ {
+ 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");
+ stat_t s;
+ 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);
+ }
+
+ static int getFD(ref File f) { return core.stdc.stdio.fileno(f.getFP()); }
+
+ // Get the file descriptors of the streams.
+ // These could potentially be invalid, but that is OK. If so, later calls
+ // to dup2() and close() will just silently fail without causing any harm.
+ auto stdinFD = getFD(stdin);
+ auto stdoutFD = getFD(stdout);
+ auto stderrFD = getFD(stderr);
+
+ // We don't have direct access to the errors that may happen in a child process.
+ // So we use this pipe to deliver them.
+ int[2] forkPipe;
+ if (core.sys.posix.unistd.pipe(forkPipe) == 0)
+ setCLOEXEC(forkPipe[1], true);
+ else
+ throw ProcessException.newFromErrno("Could not create pipe to check startup of child");
+ scope(exit) close(forkPipe[0]);
+
+ /*
+ To create detached process, we use double fork technique
+ but we don't have a direct access to the second fork pid from the caller side thus use a pipe.
+ We also can't reuse forkPipe for that purpose
+ because we can't predict the order in which pid and possible error will be written
+ since the first and the second forks will run in parallel.
+ */
+ int[2] pidPipe;
+ if (config & Config.detached)
+ {
+ if (core.sys.posix.unistd.pipe(pidPipe) != 0)
+ throw ProcessException.newFromErrno("Could not create pipe to get process pid");
+ setCLOEXEC(pidPipe[1], true);
+ }
+ scope(exit) if (config & Config.detached) close(pidPipe[0]);
+
+ static void abortOnError(int forkPipeOut, InternalError errorType, int error) nothrow
+ {
+ core.sys.posix.unistd.write(forkPipeOut, &errorType, errorType.sizeof);
+ core.sys.posix.unistd.write(forkPipeOut, &error, error.sizeof);
+ close(forkPipeOut);
+ core.sys.posix.unistd._exit(1);
+ assert(0);
+ }
+
+ void closePipeWriteEnds()
+ {
+ close(forkPipe[1]);
+ if (config & Config.detached)
+ close(pidPipe[1]);
+ }
+
+ auto id = core.sys.posix.unistd.fork();
+ if (id < 0)
+ {
+ closePipeWriteEnds();
+ throw ProcessException.newFromErrno("Failed to spawn new process");
+ }
+
+ void forkChild() nothrow @nogc
+ {
+ static import core.sys.posix.stdio;
+ pragma(inline, true);
+
+ // Child process
+
+ // no need for the read end of pipe on child side
+ if (config & Config.detached)
+ close(pidPipe[0]);
+ close(forkPipe[0]);
+ immutable forkPipeOut = forkPipe[1];
+ immutable pidPipeOut = pidPipe[1];
+
+ // Set the working directory.
+ if (workDirFD >= 0)
+ {
+ if (fchdir(workDirFD) < 0)
+ {
+ // Fail. It is dangerous to run a program
+ // in an unexpected working directory.
+ abortOnError(forkPipeOut, InternalError.chdir, .errno);
+ }
+ close(workDirFD);
+ }
+
+ void execProcess()
+ {
+ // Redirect streams and close the old file descriptors.
+ // In the case that stderr is redirected to stdout, we need
+ // to backup the file descriptor since stdout may be redirected
+ // as well.
+ if (stderrFD == STDOUT_FILENO) stderrFD = dup(stderrFD);
+ dup2(stdinFD, STDIN_FILENO);
+ dup2(stdoutFD, STDOUT_FILENO);
+ dup2(stderrFD, STDERR_FILENO);
+
+ // Ensure that the standard streams aren't closed on execute, and
+ // optionally close all other file descriptors.
+ setCLOEXEC(STDIN_FILENO, false);
+ setCLOEXEC(STDOUT_FILENO, false);
+ setCLOEXEC(STDERR_FILENO, false);
+
+ if (!(config & Config.inheritFDs))
+ {
+ import core.stdc.stdlib : malloc;
+ import core.sys.posix.poll : pollfd, poll, POLLNVAL;
+ import core.sys.posix.sys.resource : rlimit, getrlimit, RLIMIT_NOFILE;
+
+ // Get the maximum number of file descriptors that could be open.
+ rlimit r;
+ if (getrlimit(RLIMIT_NOFILE, &r) != 0)
+ {
+ abortOnError(forkPipeOut, InternalError.getrlimit, .errno);
+ }
+ 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:
+ auto 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)
+ {
+ // don't close pipe write end
+ if (pfds[i].fd == forkPipeOut) continue;
+ // 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)
+ {
+ if (i == forkPipeOut) continue;
+ 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);
+ }
+
+ // Execute program.
+ core.sys.posix.unistd.execve(argz[0], argz.ptr, envz);
+
+ // If execution fails, exit as quickly as possible.
+ abortOnError(forkPipeOut, InternalError.exec, .errno);
+ }
+
+ if (config & Config.detached)
+ {
+ auto secondFork = core.sys.posix.unistd.fork();
+ if (secondFork == 0)
+ {
+ close(pidPipeOut);
+ execProcess();
+ }
+ else if (secondFork == -1)
+ {
+ auto secondForkErrno = .errno;
+ close(pidPipeOut);
+ abortOnError(forkPipeOut, InternalError.doubleFork, secondForkErrno);
+ }
+ else
+ {
+ core.sys.posix.unistd.write(pidPipeOut, &secondFork, pid_t.sizeof);
+ close(pidPipeOut);
+ close(forkPipeOut);
+ _exit(0);
+ }
+ }
+ else
+ {
+ execProcess();
+ }
+ }
+
+ if (id == 0)
+ {
+ forkChild();
+ assert(0);
+ }
+ else
+ {
+ closePipeWriteEnds();
+ auto status = InternalError.noerror;
+ auto readExecResult = core.sys.posix.unistd.read(forkPipe[0], &status, status.sizeof);
+ // Save error number just in case if subsequent "waitpid" fails and overrides errno
+ immutable lastError = .errno;
+
+ if (config & Config.detached)
+ {
+ // Forked child exits right after creating second fork. So it should be safe to wait here.
+ import core.sys.posix.sys.wait : waitpid;
+ int waitResult;
+ waitpid(id, &waitResult, 0);
+ }
+
+ if (readExecResult == -1)
+ throw ProcessException.newFromErrno(lastError, "Could not read from pipe to get child status");
+
+ bool owned = true;
+ if (status != InternalError.noerror)
+ {
+ int error;
+ readExecResult = read(forkPipe[0], &error, error.sizeof);
+ string errorMsg;
+ final switch (status)
+ {
+ case InternalError.chdir:
+ errorMsg = "Failed to set working directory";
+ break;
+ case InternalError.getrlimit:
+ errorMsg = "getrlimit failed";
+ break;
+ case InternalError.exec:
+ errorMsg = "Failed to execute program";
+ break;
+ case InternalError.doubleFork:
+ // Can happen only when starting detached process
+ assert(config & Config.detached);
+ errorMsg = "Failed to fork twice";
+ break;
+ case InternalError.noerror:
+ assert(false);
+ }
+ if (readExecResult == error.sizeof)
+ throw ProcessException.newFromErrno(error, errorMsg);
+ throw new ProcessException(errorMsg);
+ }
+ else if (config & Config.detached)
+ {
+ owned = false;
+ if (read(pidPipe[0], &id, id.sizeof) != id.sizeof)
+ throw ProcessException.newFromErrno("Could not read from pipe to get detached process id");
+ }
+
+ // Parent process: Close streams and return.
+ if (!(config & Config.retainStdin ) && stdinFD > STDERR_FILENO
+ && stdinFD != getFD(std.stdio.stdin ))
+ stdin.close();
+ if (!(config & Config.retainStdout) && stdoutFD > STDERR_FILENO
+ && stdoutFD != getFD(std.stdio.stdout))
+ stdout.close();
+ if (!(config & Config.retainStderr) && stderrFD > STDERR_FILENO
+ && stderrFD != getFD(std.stdio.stderr))
+ stderr.close();
+ return new Pid(id, owned);
+ }
+}
+
+/*
+Implementation of spawnProcess() for Windows.
+
+commandLine must contain the entire command line, properly
+quoted/escaped as required by CreateProcessW().
+
+envz must be a pointer to a block of UTF-16 characters on the form
+"var1=value1\0var2=value2\0...varN=valueN\0\0".
+*/
+version (Windows)
+private Pid spawnProcessImpl(in char[] commandLine,
+ File stdin,
+ File stdout,
+ File stderr,
+ const string[string] env,
+ Config config,
+ in char[] workDir)
+ @trusted
+{
+ import core.exception : RangeError;
+
+ if (commandLine.empty) throw new RangeError("Command line is empty");
+
+ // Prepare environment.
+ auto envz = createEnv(env, !(config & Config.newEnv));
+
+ // Startup info for CreateProcessW().
+ STARTUPINFO_W startinfo;
+ startinfo.cb = startinfo.sizeof;
+ static int getFD(ref File f) { return f.isOpen ? f.fileno : -1; }
+
+ // Extract file descriptors and HANDLEs from the streams and make the
+ // handles inheritable.
+ static void prepareStream(ref File file, DWORD stdHandle, string which,
+ out int fileDescriptor, out HANDLE handle)
+ {
+ fileDescriptor = getFD(file);
+ 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))
+ {
+ if (!(dwFlags & HANDLE_FLAG_INHERIT))
+ {
+ if (!SetHandleInformation(handle,
+ HANDLE_FLAG_INHERIT,
+ HANDLE_FLAG_INHERIT))
+ {
+ throw new StdioException(
+ "Failed to make "~which~" stream inheritable by child process ("
+ ~sysErrorString(GetLastError()) ~ ')',
+ 0);
+ }
+ }
+ }
+ }
+ int stdinFD = -1, stdoutFD = -1, stderrFD = -1;
+ prepareStream(stdin, STD_INPUT_HANDLE, "stdin" , stdinFD, startinfo.hStdInput );
+ 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 ? pworkDir : null, &startinfo, &pi))
+ throw ProcessException.newFromLastError("Failed to spawn new process");
+
+ // figure out if we should close any of the streams
+ if (!(config & Config.retainStdin ) && stdinFD > STDERR_FILENO
+ && stdinFD != getFD(std.stdio.stdin ))
+ stdin.close();
+ if (!(config & Config.retainStdout) && stdoutFD > STDERR_FILENO
+ && stdoutFD != getFD(std.stdio.stdout))
+ stdout.close();
+ if (!(config & Config.retainStderr) && stderrFD > STDERR_FILENO
+ && stderrFD != getFD(std.stdio.stderr))
+ stderr.close();
+
+ // close the thread handle in the process info structure
+ CloseHandle(pi.hThread);
+ if (config & Config.detached)
+ {
+ CloseHandle(pi.hProcess);
+ return new Pid(pi.dwProcessId);
+ }
+ return new Pid(pi.dwProcessId, pi.hProcess);
+}
+
+// Converts childEnv to a zero-terminated array of zero-terminated strings
+// on the form "name=value", optionally adding those of the current process'
+// environment strings that are not present in childEnv. If the parent's
+// environment should be inherited without modification, this function
+// returns environ directly.
+version (Posix)
+private const(char*)* createEnv(const string[string] childEnv,
+ bool mergeWithParentEnv)
+{
+ // Determine the number of strings in the parent's environment.
+ int parentEnvLength = 0;
+ auto environ = getEnvironPtr;
+ if (mergeWithParentEnv)
+ {
+ if (childEnv.length == 0) return environ;
+ while (environ[parentEnvLength] != null) ++parentEnvLength;
+ }
+
+ // Convert the "new" variables to C-style strings.
+ auto envz = new const(char)*[parentEnvLength + childEnv.length + 1];
+ int pos = 0;
+ foreach (var, val; childEnv)
+ envz[pos++] = (var~'='~val~'\0').ptr;
+
+ // Add the parent's environment.
+ foreach (environStr; environ[0 .. parentEnvLength])
+ {
+ int eqPos = 0;
+ while (environStr[eqPos] != '=' && environStr[eqPos] != '\0') ++eqPos;
+ if (environStr[eqPos] != '=') continue;
+ auto var = environStr[0 .. eqPos];
+ if (var in childEnv) continue;
+ envz[pos++] = environStr;
+ }
+ envz[pos] = null;
+ return envz.ptr;
+}
+
+version (Posix) @system unittest
+{
+ auto e1 = createEnv(null, false);
+ assert(e1 != null && *e1 == null);
+
+ auto e2 = createEnv(null, true);
+ assert(e2 != null);
+ int i = 0;
+ auto environ = getEnvironPtr;
+ for (; environ[i] != null; ++i)
+ {
+ assert(e2[i] != null);
+ import core.stdc.string;
+ assert(strcmp(e2[i], environ[i]) == 0);
+ }
+ 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")
+ || (e3[0][0 .. 12] == "hello=world\0" && e3[1][0 .. 8] == "foo=bar\0"));
+}
+
+
+// Converts childEnv to a Windows environment block, which is on the form
+// "name1=value1\0name2=value2\0...nameN=valueN\0\0", optionally adding
+// those of the current process' environment strings that are not present
+// in childEnv. Returns null if the parent's environment should be
+// inherited without modification, as this is what is expected by
+// CreateProcess().
+version (Windows)
+private LPVOID createEnv(const string[string] childEnv,
+ bool mergeWithParentEnv)
+{
+ if (mergeWithParentEnv && childEnv.length == 0) return null;
+ import std.array : appender;
+ import std.uni : toUpper;
+ auto envz = appender!(wchar[])();
+ void put(string var, string val)
+ {
+ envz.put(var);
+ envz.put('=');
+ envz.put(val);
+ envz.put(cast(wchar) '\0');
+ }
+
+ // Add the variables in childEnv, removing them from parentEnv
+ // if they exist there too.
+ auto parentEnv = mergeWithParentEnv ? environment.toAA() : null;
+ foreach (k, v; childEnv)
+ {
+ auto uk = toUpper(k);
+ put(uk, v);
+ if (uk in parentEnv) parentEnv.remove(uk);
+ }
+
+ // Add remaining parent environment variables.
+ foreach (k, v; parentEnv) put(k, v);
+
+ // Two final zeros are needed in case there aren't any environment vars,
+ // and the last one does no harm when there are.
+ envz.put("\0\0"w);
+ return envz.data.ptr;
+}
+
+version (Windows) @system unittest
+{
+ 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);
+}
+
+// Searches the PATH variable for the given executable file,
+// (checking that it is in fact executable).
+version (Posix)
+private string searchPathFor(in char[] executable)
+ @trusted //TODO: @safe nothrow
+{
+ import std.algorithm.iteration : splitter;
+ import std.conv : to;
+ import std.path : buildPath;
+
+ auto pathz = core.stdc.stdlib.getenv("PATH");
+ if (pathz == null) return null;
+
+ foreach (dir; splitter(to!string(pathz), ':'))
+ {
+ auto execPath = buildPath(dir, executable);
+ if (isExecutable(execPath)) return execPath;
+ }
+
+ return null;
+}
+
+// Checks whether the file exists and can be executed by the
+// current user.
+version (Posix)
+private bool isExecutable(in char[] path) @trusted nothrow @nogc //TODO: @safe
+{
+ return (access(path.tempCString(), X_OK) == 0);
+}
+
+version (Posix) @safe unittest
+{
+ import std.algorithm;
+ 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?");
+}
+
+// Sets or unsets the FD_CLOEXEC flag on the given file descriptor.
+version (Posix)
+private void setCLOEXEC(int fd, bool on) nothrow @nogc
+{
+ import core.sys.posix.fcntl : fcntl, F_GETFD, FD_CLOEXEC, F_SETFD;
+ auto flags = fcntl(fd, F_GETFD);
+ if (flags >= 0)
+ {
+ if (on) flags |= FD_CLOEXEC;
+ else flags &= ~(cast(typeof(flags)) FD_CLOEXEC);
+ flags = fcntl(fd, F_SETFD, flags);
+ }
+ assert(flags != -1 || .errno == EBADF);
+}
+
+@system unittest // Command line arguments in spawnProcess().
+{
+ version (Windows) TestScript prog =
+ "if not [%~1]==[foo] ( exit 1 )
+ if not [%~2]==[bar] ( exit 2 )
+ exit 0";
+ else version (Posix) TestScript prog =
+ `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);
+}
+
+// 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.algorithm.searching : canFind, findSplitBefore;
+ import std.array : split;
+ import std.conv : to;
+ static import std.file;
+ import std.functional : reverseArgs;
+ import std.path : buildPath;
+
+ 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/<pid>/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
+}
+
+@system unittest // Environment variables in spawnProcess().
+{
+ // We really should use set /a on Windows, but Wine doesn't support it.
+ version (Windows) TestScript envProg =
+ `if [%STD_PROCESS_UNITTEST1%] == [1] (
+ if [%STD_PROCESS_UNITTEST2%] == [2] (exit 3)
+ exit 1
+ )
+ if [%STD_PROCESS_UNITTEST1%] == [4] (
+ if [%STD_PROCESS_UNITTEST2%] == [2] (exit 6)
+ exit 4
+ )
+ if [%STD_PROCESS_UNITTEST2%] == [2] (exit 2)
+ exit 0`;
+ version (Posix) TestScript envProg =
+ `if test "$std_process_unittest1" = ""; then
+ std_process_unittest1=0
+ fi
+ if test "$std_process_unittest2" = ""; then
+ std_process_unittest2=0
+ fi
+ exit $(($std_process_unittest1+$std_process_unittest2))`;
+
+ 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);
+
+ environment["std_process_unittest1"] = "1";
+ 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);
+
+ env["std_process_unittest1"] = "4";
+ 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);
+}
+
+@system unittest // Stream redirection in spawnProcess().
+{
+ import std.path : buildPath;
+ import std.string;
+ version (Windows) TestScript prog =
+ "set /p INPUT=
+ echo %INPUT% output %~1
+ echo %INPUT% error %~2 1>&2";
+ else version (Posix) TestScript prog =
+ "read INPUT
+ echo $INPUT output $1
+ echo $INPUT error $2 >&2";
+
+ // Pipes
+ void testPipes(Config config)
+ {
+ auto pipei = pipe();
+ auto pipeo = pipe();
+ auto pipee = pipe();
+ auto pid = spawnProcess([prog.path, "foo", "bar"],
+ pipei.readEnd, pipeo.writeEnd, pipee.writeEnd, null, config);
+ pipei.writeEnd.writeln("input");
+ pipei.writeEnd.flush();
+ assert(pipeo.readEnd.readln().chomp() == "input output foo");
+ assert(pipee.readEnd.readln().chomp().stripRight() == "input error bar");
+ if (!(config & Config.detached))
+ wait(pid);
+ }
+
+ // Files
+ void testFiles(Config config)
+ {
+ import std.ascii, std.file, std.uuid, core.thread;
+ auto pathi = buildPath(tempDir(), randomUUID().toString());
+ auto patho = buildPath(tempDir(), randomUUID().toString());
+ auto pathe = buildPath(tempDir(), randomUUID().toString());
+ std.file.write(pathi, "INPUT"~std.ascii.newline);
+ auto filei = File(pathi, "r");
+ auto fileo = File(patho, "w");
+ auto filee = File(pathe, "w");
+ auto pid = spawnProcess([prog.path, "bar", "baz" ], filei, fileo, filee, null, config);
+ if (!(config & Config.detached))
+ wait(pid);
+ else
+ // We need to wait a little to ensure that the process has finished and data was written to files
+ Thread.sleep(2.seconds);
+ assert(readText(patho).chomp() == "INPUT output bar");
+ assert(readText(pathe).chomp().stripRight() == "INPUT error baz");
+ remove(pathi);
+ remove(patho);
+ remove(pathe);
+ }
+
+ testPipes(Config.none);
+ testFiles(Config.none);
+ testPipes(Config.detached);
+ testFiles(Config.detached);
+}
+
+@system unittest // Error handling in spawnProcess()
+{
+ import std.exception : assertThrown;
+ assertThrown!ProcessException(spawnProcess("ewrgiuhrifuheiohnmnvqweoijwf"));
+ assertThrown!ProcessException(spawnProcess("./rgiuhrifuheiohnmnvqweoijwf"));
+ assertThrown!ProcessException(spawnProcess("ewrgiuhrifuheiohnmnvqweoijwf", null, Config.detached));
+ assertThrown!ProcessException(spawnProcess("./rgiuhrifuheiohnmnvqweoijwf", null, Config.detached));
+
+ // can't execute malformed file with executable permissions
+ version (Posix)
+ {
+ import std.path : buildPath;
+ import std.file : remove, write, setAttributes;
+ import core.sys.posix.sys.stat : S_IRUSR, S_IWUSR, S_IXUSR, S_IRGRP, S_IXGRP, S_IROTH, S_IXOTH;
+ string deleteme = buildPath(tempDir(), "deleteme.std.process.unittest.pid") ~ to!string(thisProcessID);
+ write(deleteme, "");
+ scope(exit) remove(deleteme);
+ setAttributes(deleteme, S_IRUSR|S_IWUSR|S_IXUSR|S_IRGRP|S_IXGRP|S_IROTH|S_IXOTH);
+ assertThrown!ProcessException(spawnProcess(deleteme));
+ assertThrown!ProcessException(spawnProcess(deleteme, null, Config.detached));
+ }
+}
+
+@system unittest // Specifying a working directory.
+{
+ import std.path;
+ TestScript prog = "echo foo>bar";
+
+ auto directory = uniqueTempPath();
+ mkdir(directory);
+ scope(exit) rmdirRecurse(directory);
+
+ auto pid = spawnProcess([prog.path], null, Config.none, directory);
+ wait(pid);
+ assert(exists(buildPath(directory, "bar")));
+}
+
+@system unittest // Specifying a bad working directory.
+{
+ import std.exception : assertThrown;
+ TestScript prog = "echo";
+
+ auto directory = uniqueTempPath();
+ assertThrown!ProcessException(spawnProcess([prog.path], null, Config.none, directory));
+ assertThrown!ProcessException(spawnProcess([prog.path], null, Config.detached, directory));
+
+ std.file.write(directory, "foo");
+ scope(exit) remove(directory);
+ assertThrown!ProcessException(spawnProcess([prog.path], null, Config.none, directory));
+ assertThrown!ProcessException(spawnProcess([prog.path], null, Config.detached, directory));
+
+ // can't run in directory if user does not have search permission on this directory
+ version (Posix)
+ {
+ import core.sys.posix.sys.stat : S_IRUSR;
+ auto directoryNoSearch = uniqueTempPath();
+ mkdir(directoryNoSearch);
+ scope(exit) rmdirRecurse(directoryNoSearch);
+ setAttributes(directoryNoSearch, S_IRUSR);
+ assertThrown!ProcessException(spawnProcess(prog.path, null, Config.none, directoryNoSearch));
+ assertThrown!ProcessException(spawnProcess(prog.path, null, Config.detached, directoryNoSearch));
+ }
+}
+
+@system unittest // Specifying empty working directory.
+{
+ TestScript prog = "";
+
+ string directory = "";
+ assert(directory.ptr && !directory.length);
+ spawnProcess([prog.path], null, Config.none, directory).wait();
+}
+
+@system unittest // Reopening the standard streams (issue 13258)
+{
+ import std.string;
+ void fun()
+ {
+ spawnShell("echo foo").wait();
+ spawnShell("echo bar").wait();
+ }
+
+ auto tmpFile = uniqueTempPath();
+ scope(exit) if (exists(tmpFile)) remove(tmpFile);
+
+ {
+ auto oldOut = std.stdio.stdout;
+ scope(exit) std.stdio.stdout = oldOut;
+
+ std.stdio.stdout = File(tmpFile, "w");
+ fun();
+ std.stdio.stdout.close();
+ }
+
+ auto lines = readText(tmpFile).splitLines();
+ 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).
+
+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 defaults to $(LREF nativeShell).
+
+In all other respects this function works just like $(D spawnProcess).
+Please refer to the $(LREF spawnProcess) documentation for descriptions
+of the other function parameters, the return value and any exceptions
+that may be thrown.
+---
+// Run the command/program "foo" on the file named "my file.txt", and
+// redirect its output into foo.log.
+auto pid = spawnShell(`foo "my file.txt" > foo.log`);
+wait(pid);
+---
+
+See_also:
+$(LREF escapeShellCommand), which may be helpful in constructing a
+properly quoted and escaped shell _command line for the current platform.
+*/
+Pid spawnShell(in char[] command,
+ File stdin = std.stdio.stdin,
+ File stdout = std.stdio.stdout,
+ File stderr = std.stdio.stderr,
+ const string[string] env = null,
+ Config config = Config.none,
+ in char[] workDir = null,
+ string shellPath = nativeShell)
+ @trusted // TODO: Should be @safe
+{
+ version (Windows)
+ {
+ // CMD does not parse its arguments like other programs.
+ // It does not use CommandLineToArgvW.
+ // Instead, it treats the first and last quote specially.
+ // See CMD.EXE /? for details.
+ auto args = escapeShellFileName(shellPath)
+ ~ ` ` ~ shellSwitch ~ ` "` ~ command ~ `"`;
+ }
+ else version (Posix)
+ {
+ const(char)[][3] args;
+ args[0] = shellPath;
+ args[1] = shellSwitch;
+ args[2] = command;
+ }
+ return spawnProcessImpl(args, stdin, stdout, stderr, env, config, workDir);
+}
+
+/// ditto
+Pid spawnShell(in char[] command,
+ const string[string] env,
+ Config config = Config.none,
+ in char[] workDir = null,
+ string shellPath = nativeShell)
+ @trusted // TODO: Should be @safe
+{
+ return spawnShell(command,
+ std.stdio.stdin,
+ std.stdio.stdout,
+ std.stdio.stderr,
+ env,
+ config,
+ workDir,
+ shellPath);
+}
+
+@system unittest
+{
+ version (Windows)
+ auto cmd = "echo %FOO%";
+ else version (Posix)
+ auto cmd = "echo $foo";
+ import std.file;
+ auto tmpFile = uniqueTempPath();
+ scope(exit) if (exists(tmpFile)) remove(tmpFile);
+ auto redir = "> \""~tmpFile~'"';
+ auto env = ["foo" : "bar"];
+ 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);
+ f.close();
+ auto output = std.file.readText(tmpFile);
+ assert(output == "bar\nbar\n" || output == "bar\r\nbar\r\n");
+}
+
+version (Windows)
+@system unittest
+{
+ import std.string;
+ TestScript prog = "echo %0 %*";
+ auto outputFn = uniqueTempPath();
+ scope(exit) if (exists(outputFn)) remove(outputFn);
+ auto args = [`a b c`, `a\b\c\`, `a"b"c"`];
+ auto result = executeShell(
+ escapeShellCommand([prog.path] ~ args)
+ ~ " > " ~
+ escapeShellFileName(outputFn));
+ assert(result.status == 0);
+ auto args2 = outputFn.readText().strip().parseCommandLine()[1..$];
+ assert(args == args2, text(args2));
+}
+
+
+/**
+Flags that control the behaviour of $(LREF spawnProcess) and
+$(LREF spawnShell).
+
+Use bitwise OR to combine flags.
+
+Example:
+---
+auto logFile = File("myapp_error.log", "w");
+
+// Start program, suppressing the console window (Windows only),
+// redirect its error stream to logFile, and leave logFile open
+// in the parent process as well.
+auto pid = spawnProcess("myapp", stdin, stdout, logFile,
+ Config.retainStderr | Config.suppressConsole);
+scope(exit)
+{
+ auto exitCode = wait(pid);
+ logFile.writeln("myapp exited with code ", exitCode);
+ logFile.close();
+}
+---
+*/
+enum Config
+{
+ none = 0,
+
+ /**
+ By default, the child process inherits the parent's environment,
+ and any environment variables passed to $(LREF spawnProcess) will
+ be added to it. If this flag is set, the only variables in the
+ child process' environment will be those given to spawnProcess.
+ */
+ newEnv = 1,
+
+ /**
+ Unless the child process inherits the standard input/output/error
+ streams of its parent, one almost always wants the streams closed
+ in the parent when $(LREF spawnProcess) returns. Therefore, by
+ default, this is done. If this is not desirable, pass any of these
+ options to spawnProcess.
+ */
+ retainStdin = 2,
+ retainStdout = 4, /// ditto
+ retainStderr = 8, /// ditto
+
+ /**
+ On Windows, if the child process is a console application, this
+ flag will prevent the creation of a console window. Otherwise,
+ it will be ignored. On POSIX, $(D suppressConsole) has no effect.
+ */
+ suppressConsole = 16,
+
+ /**
+ On POSIX, open $(LINK2 http://en.wikipedia.org/wiki/File_descriptor,file descriptors)
+ are by default inherited by the child process. As this may lead
+ to subtle bugs when pipes or multiple threads are involved,
+ $(LREF spawnProcess) ensures that all file descriptors except the
+ ones that correspond to standard input/output/error are closed
+ in the child process when it starts. Use $(D inheritFDs) to prevent
+ this.
+
+ On Windows, this option has no effect, and any handles which have been
+ explicitly marked as inheritable will always be inherited by the child
+ process.
+ */
+ inheritFDs = 32,
+
+ /**
+ Spawn process in detached state. This removes the need in calling
+ $(LREF wait) to clean up the process resources.
+
+ Note:
+ Calling $(LREF wait) or $(LREF kill) with the resulting $(D Pid) is invalid.
+ */
+ detached = 64,
+}
+
+
+/// A handle that corresponds to a spawned process.
+final class Pid
+{
+ /**
+ The process ID number.
+
+ This is a number that uniquely identifies the process on the operating
+ system, for at least as long as the process is running. Once $(LREF wait)
+ has been called on the $(LREF Pid), this method will return an
+ invalid (negative) process ID.
+ */
+ @property int processID() const @safe pure nothrow
+ {
+ return _processID;
+ }
+
+ /**
+ An operating system handle to the process.
+
+ This handle is used to specify the process in OS-specific APIs.
+ On POSIX, this function returns a $(D core.sys.posix.sys.types.pid_t)
+ with the same value as $(LREF Pid.processID), while on Windows it returns
+ a $(D core.sys.windows.windows.HANDLE).
+
+ Once $(LREF wait) has been called on the $(LREF Pid), this method
+ will return an invalid handle.
+ */
+ // Note: Since HANDLE is a reference, this function cannot be const.
+ version (Windows)
+ @property HANDLE osHandle() @safe pure nothrow
+ {
+ return _handle;
+ }
+ else version (Posix)
+ @property pid_t osHandle() @safe pure nothrow
+ {
+ return _processID;
+ }
+
+private:
+ /*
+ Pid.performWait() does the dirty work for wait() and nonBlockingWait().
+
+ If block == true, this function blocks until the process terminates,
+ sets _processID to terminated, and returns the exit code or terminating
+ signal as described in the wait() documentation.
+
+ If block == false, this function returns immediately, regardless
+ of the status of the process. If the process has terminated, the
+ function has the exact same effect as the blocking version. If not,
+ it returns 0 and does not modify _processID.
+ */
+ version (Posix)
+ int performWait(bool block) @trusted
+ {
+ import std.exception : enforceEx;
+ enforceEx!ProcessException(owned, "Can't wait on a detached process");
+ if (_processID == terminated) return _exitCode;
+ int exitCode;
+ while (true)
+ {
+ int status;
+ auto check = waitpid(_processID, &status, block ? 0 : WNOHANG);
+ if (check == -1)
+ {
+ if (errno == ECHILD)
+ {
+ throw new ProcessException(
+ "Process does not exist or is not a child process.");
+ }
+ else
+ {
+ // waitpid() was interrupted by a signal. We simply
+ // restart it.
+ assert(errno == EINTR);
+ continue;
+ }
+ }
+ if (!block && check == 0) return 0;
+ if (WIFEXITED(status))
+ {
+ exitCode = WEXITSTATUS(status);
+ break;
+ }
+ else if (WIFSIGNALED(status))
+ {
+ exitCode = -WTERMSIG(status);
+ break;
+ }
+ // We check again whether the call should be blocking,
+ // since we don't care about other status changes besides
+ // "exited" and "terminated by signal".
+ if (!block) return 0;
+
+ // Process has stopped, but not terminated, so we continue waiting.
+ }
+ // Mark Pid as terminated, and cache and return exit code.
+ _processID = terminated;
+ _exitCode = exitCode;
+ return exitCode;
+ }
+ else version (Windows)
+ {
+ int performWait(bool block) @trusted
+ {
+ import std.exception : enforceEx;
+ enforceEx!ProcessException(owned, "Can't wait on a detached process");
+ if (_processID == terminated) return _exitCode;
+ assert(_handle != INVALID_HANDLE_VALUE);
+ if (block)
+ {
+ auto result = WaitForSingleObject(_handle, INFINITE);
+ if (result != WAIT_OBJECT_0)
+ throw ProcessException.newFromLastError("Wait failed.");
+ }
+ if (!GetExitCodeProcess(_handle, cast(LPDWORD)&_exitCode))
+ throw ProcessException.newFromLastError();
+ if (!block && _exitCode == STILL_ACTIVE) return 0;
+ CloseHandle(_handle);
+ _handle = INVALID_HANDLE_VALUE;
+ _processID = terminated;
+ return _exitCode;
+ }
+
+ ~this()
+ {
+ if (_handle != INVALID_HANDLE_VALUE)
+ {
+ CloseHandle(_handle);
+ _handle = INVALID_HANDLE_VALUE;
+ }
+ }
+ }
+
+ // Special values for _processID.
+ enum invalid = -1, terminated = -2;
+
+ // OS process ID number. Only nonnegative IDs correspond to
+ // running processes.
+ int _processID = invalid;
+
+ // Exit code cached by wait(). This is only expected to hold a
+ // sensible value if _processID == terminated.
+ int _exitCode;
+
+ // Whether the process can be waited for by wait() for or killed by kill().
+ // False if process was started as detached. True otherwise.
+ bool owned;
+
+ // Pids are only meant to be constructed inside this module, so
+ // we make the constructor private.
+ version (Windows)
+ {
+ HANDLE _handle = INVALID_HANDLE_VALUE;
+ this(int pid, HANDLE handle) @safe pure nothrow
+ {
+ _processID = pid;
+ _handle = handle;
+ this.owned = true;
+ }
+ this(int pid) @safe pure nothrow
+ {
+ _processID = pid;
+ this.owned = false;
+ }
+ }
+ else
+ {
+ this(int id, bool owned) @safe pure nothrow
+ {
+ _processID = id;
+ this.owned = owned;
+ }
+ }
+}
+
+
+/**
+Waits for the process associated with $(D pid) to terminate, and returns
+its exit status.
+
+In general one should always _wait for child processes to terminate
+before exiting the parent process unless the process was spawned as detached
+(that was spawned with $(D Config.detached) flag).
+Otherwise, they may become "$(HTTP en.wikipedia.org/wiki/Zombie_process,zombies)"
+– processes that are defunct, yet still occupy a slot in the OS process table.
+You should not and must not wait for detached processes, since you don't own them.
+
+If the process has already terminated, this function returns directly.
+The exit code is cached, so that if wait() is called multiple times on
+the same $(LREF Pid) it will always return the same value.
+
+POSIX_specific:
+If the process is terminated by a signal, this function returns a
+negative number whose absolute value is the signal number.
+Since POSIX restricts normal exit codes to the range 0-255, a
+negative return value will always indicate termination by signal.
+Signal codes are defined in the $(D core.sys.posix.signal) module
+(which corresponds to the $(D signal.h) POSIX header).
+
+Throws:
+$(LREF ProcessException) on failure or on attempt to wait for detached process.
+
+Example:
+See the $(LREF spawnProcess) documentation.
+
+See_also:
+$(LREF tryWait), for a non-blocking function.
+*/
+int wait(Pid pid) @safe
+{
+ assert(pid !is null, "Called wait on a null Pid.");
+ return pid.performWait(true);
+}
+
+
+@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);
+ 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);
+}
+
+
+/**
+A non-blocking version of $(LREF wait).
+
+If the process associated with $(D pid) has already terminated,
+$(D tryWait) has the exact same effect as $(D wait).
+In this case, it returns a tuple where the $(D terminated) field
+is set to $(D true) and the $(D status) field has the same
+interpretation as the return value of $(D wait).
+
+If the process has $(I not) yet terminated, this function differs
+from $(D wait) in that does not wait for this to happen, but instead
+returns immediately. The $(D terminated) field of the returned
+tuple will then be set to $(D false), while the $(D status) field
+will always be 0 (zero). $(D wait) or $(D tryWait) should then be
+called again on the same $(D Pid) at some later time; not only to
+get the exit code, but also to avoid the process becoming a "zombie"
+when it finally terminates. (See $(LREF wait) for details).
+
+Returns:
+An $(D std.typecons.Tuple!(bool, "terminated", int, "status")).
+
+Throws:
+$(LREF ProcessException) on failure or on attempt to wait for detached process.
+
+Example:
+---
+auto pid = spawnProcess("dmd myapp.d");
+scope(exit) wait(pid);
+...
+auto dmd = tryWait(pid);
+if (dmd.terminated)
+{
+ if (dmd.status == 0) writeln("Compilation succeeded!");
+ else writeln("Compilation failed");
+}
+else writeln("Still compiling...");
+...
+---
+Note that in this example, the first $(D wait) call will have no
+effect if the process has already terminated by the time $(D tryWait)
+is called. In the opposite case, however, the $(D scope) statement
+ensures that we always wait for the process if it hasn't terminated
+by the time we reach the end of the scope.
+*/
+auto tryWait(Pid pid) @safe
+{
+ import std.typecons : Tuple;
+ assert(pid !is null, "Called tryWait on a null Pid.");
+ auto code = pid.performWait(false);
+ return Tuple!(bool, "terminated", int, "status")(pid._processID == Pid.terminated, code);
+}
+// unittest: This function is tested together with kill() below.
+
+
+/**
+Attempts to terminate the process associated with $(D pid).
+
+The effect of this function, as well as the meaning of $(D codeOrSignal),
+is highly platform dependent. Details are given below. Common to all
+platforms is that this function only $(I initiates) termination of the process,
+and returns immediately. It does not wait for the process to end,
+nor does it guarantee that the process does in fact get terminated.
+
+Always call $(LREF wait) to wait for a process to complete, even if $(D kill)
+has been called on it.
+
+Windows_specific:
+The process will be
+$(LINK2 http://msdn.microsoft.com/en-us/library/windows/desktop/ms686714%28v=vs.100%29.aspx,
+forcefully and abruptly terminated). If $(D codeOrSignal) is specified, it
+must be a nonnegative number which will be used as the exit code of the process.
+If not, the process wil exit with code 1. Do not use $(D codeOrSignal = 259),
+as this is a special value (aka. $(LINK2 http://msdn.microsoft.com/en-us/library/windows/desktop/ms683189.aspx,STILL_ACTIVE))
+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);
+---
+
+POSIX_specific:
+A $(LINK2 http://en.wikipedia.org/wiki/Unix_signal,signal) will be sent to
+the process, whose value is given by $(D codeOrSignal). Depending on the
+signal sent, this may or may not terminate the process. Symbolic constants
+for various $(LINK2 http://en.wikipedia.org/wiki/Unix_signal#POSIX_signals,
+POSIX signals) are defined in $(D core.sys.posix.signal), which corresponds to the
+$(LINK2 http://pubs.opengroup.org/onlinepubs/9699919799/basedefs/signal.h.html,
+$(D signal.h) POSIX header). If $(D codeOrSignal) is omitted, the
+$(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;
+auto pid = spawnProcess("some_app");
+kill(pid, SIGKILL);
+assert(wait(pid) == -SIGKILL); // Negative return value on POSIX!
+---
+
+Throws:
+$(LREF ProcessException) on error (e.g. if codeOrSignal is invalid).
+ or on attempt to kill detached process.
+ Note that failure to terminate the process is considered a "normal"
+ outcome, not an error.$(BR)
+*/
+void kill(Pid pid)
+{
+ version (Windows) kill(pid, 1);
+ else version (Posix)
+ {
+ import core.sys.posix.signal : SIGTERM;
+ kill(pid, SIGTERM);
+ }
+}
+
+/// ditto
+void kill(Pid pid, int codeOrSignal)
+{
+ import std.exception : enforceEx;
+ enforceEx!ProcessException(pid.owned, "Can't kill detached process");
+ version (Windows)
+ {
+ if (codeOrSignal < 0) throw new ProcessException("Invalid exit code");
+ // On Windows, TerminateProcess() appears to terminate the
+ // *current* process if it is passed an invalid handle...
+ if (pid.osHandle == INVALID_HANDLE_VALUE)
+ throw new ProcessException("Invalid process handle");
+ if (!TerminateProcess(pid.osHandle, codeOrSignal))
+ throw ProcessException.newFromLastError();
+ }
+ else version (Posix)
+ {
+ import core.sys.posix.signal : kill;
+ if (kill(pid.osHandle, codeOrSignal) == -1)
+ throw ProcessException.newFromErrno();
+ }
+}
+
+@system unittest // tryWait() and kill()
+{
+ import core.thread;
+ import std.exception : assertThrown;
+ // The test script goes into an infinite loop.
+ version (Windows)
+ {
+ TestScript prog = ":loop
+ goto loop";
+ }
+ else version (Posix)
+ {
+ import core.sys.posix.signal : SIGTERM, SIGKILL;
+ TestScript prog = "while true; do sleep 1; done";
+ }
+ auto pid = spawnProcess(prog.path);
+ // 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);
+
+ pid = spawnProcess(prog.path);
+ Thread.sleep(dur!"seconds"(1));
+ auto s = tryWait(pid);
+ 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);
+ assertThrown!ProcessException(kill(pid));
+}
+
+@system unittest // wait() and kill() detached process
+{
+ import core.thread;
+ import std.exception : assertThrown;
+ TestScript prog = "exit 0";
+ auto pid = spawnProcess([prog.path], null, Config.detached);
+ /*
+ This sleep is needed because we can't wait() for detached process to end
+ and therefore TestScript destructor may run at the same time as /bin/sh tries to start the script.
+ This leads to the annoying message like "/bin/sh: 0: Can't open /tmp/std.process temporary file" to appear when running tests.
+ It does not happen in unittests with non-detached processes because we always wait() for them to finish.
+ */
+ Thread.sleep(1.seconds);
+ assert(!pid.owned);
+ version (Windows) assert(pid.osHandle == INVALID_HANDLE_VALUE);
+ assertThrown!ProcessException(wait(pid));
+ assertThrown!ProcessException(kill(pid));
+}
+
+
+/**
+Creates a unidirectional _pipe.
+
+Data is written to one end of the _pipe and read from the other.
+---
+auto p = pipe();
+p.writeEnd.writeln("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
+the child, while the parent uses the other end.
+(See also $(LREF pipeProcess) and $(LREF pipeShell) for an easier
+way of doing this.)
+---
+// Use cURL to download the dlang.org front page, pipe its
+// output to grep to extract a list of links to ZIP files,
+// and write the list to the file "D downloads.txt":
+auto p = pipe();
+auto outFile = File("D downloads.txt", "w");
+auto cpid = spawnProcess(["curl", "http://dlang.org/download.html"],
+ std.stdio.stdin, p.writeEnd);
+scope(exit) wait(cpid);
+auto gpid = spawnProcess(["grep", "-o", `http://\S*\.zip`],
+ p.readEnd, outFile);
+scope(exit) wait(gpid);
+---
+
+Returns:
+A $(LREF Pipe) object that corresponds to the created _pipe.
+
+Throws:
+$(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");
+ Pipe p;
+ auto readFP = fdopen(fds[0], "r");
+ if (readFP == null)
+ throw new StdioException("Cannot open read end of pipe");
+ p._read = File(readFP, null);
+ auto writeFP = fdopen(fds[1], "w");
+ if (writeFP == null)
+ throw new StdioException("Cannot open write end of pipe");
+ p._write = File(writeFP, null);
+ return p;
+}
+else version (Windows)
+Pipe pipe() @trusted //TODO: @safe
+{
+ // use CreatePipe to create an anonymous pipe
+ HANDLE readHandle;
+ HANDLE writeHandle;
+ if (!CreatePipe(&readHandle, &writeHandle, null, 0))
+ {
+ throw new StdioException(
+ "Error creating pipe (" ~ sysErrorString(GetLastError()) ~ ')',
+ 0);
+ }
+
+ scope(failure)
+ {
+ CloseHandle(readHandle);
+ CloseHandle(writeHandle);
+ }
+
+ try
+ {
+ Pipe p;
+ p._read .windowsHandleOpen(readHandle , "r");
+ p._write.windowsHandleOpen(writeHandle, "a");
+ return p;
+ }
+ catch (Exception e)
+ {
+ throw new StdioException("Error attaching pipe (" ~ e.msg ~ ")",
+ 0);
+ }
+}
+
+
+/// An interface to a pipe created by the $(LREF pipe) function.
+struct Pipe
+{
+ /// The read end of the pipe.
+ @property File readEnd() @safe nothrow { return _read; }
+
+
+ /// The write end of the pipe.
+ @property File writeEnd() @safe nothrow { return _write; }
+
+
+ /**
+ Closes both ends of the pipe.
+
+ 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.
+
+ Note that if either end of the pipe has been passed to a child process,
+ it will only be closed in the parent process. (What happens in the
+ child process is platform dependent.)
+
+ Throws:
+ $(REF ErrnoException, std,exception) if an error occurs.
+ */
+ void close() @safe
+ {
+ _read.close();
+ _write.close();
+ }
+
+private:
+ File _read, _write;
+}
+
+@system unittest
+{
+ import std.string;
+ auto p = pipe();
+ p.writeEnd.writeln("Hello World");
+ p.writeEnd.flush();
+ assert(p.readEnd.readln().chomp() == "Hello World");
+ p.close();
+ assert(!p.readEnd.isOpen);
+ assert(!p.writeEnd.isOpen);
+}
+
+
+/**
+Starts a new process, creating pipes to redirect its standard
+input, output and/or error streams.
+
+$(D pipeProcess) and $(D pipeShell) are convenient wrappers around
+$(LREF spawnProcess) and $(LREF spawnShell), respectively, and
+automate the task of redirecting one or more of the child process'
+standard streams through pipes. Like the functions they wrap,
+these functions return immediately, leaving the child process to
+execute in parallel with the invoking process. It is recommended
+to always call $(LREF wait) on the returned $(LREF ProcessPipes.pid),
+as detailed in the documentation for $(D wait).
+
+The $(D args)/$(D program)/$(D command), $(D env) and $(D config)
+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.
+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 $(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)
+$(REF StdioException, std,stdio) on failure to redirect any of the streams.$(BR)
+
+Example:
+---
+// my_application writes to stdout and might write to stderr
+auto pipes = pipeProcess("my_application", Redirect.stdout | Redirect.stderr);
+scope(exit) wait(pipes.pid);
+
+// Store lines of output.
+string[] output;
+foreach (line; pipes.stdout.byLine) output ~= line.idup;
+
+// Store lines of errors.
+string[] errors;
+foreach (line; pipes.stderr.byLine) errors ~= line.idup;
+
+
+// sendmail expects to read from stdin
+pipes = pipeProcess(["/usr/bin/sendmail", "-t"], Redirect.stdin);
+pipes.stdin.writeln("To: you");
+pipes.stdin.writeln("From: me");
+pipes.stdin.writeln("Subject: dlang");
+pipes.stdin.writeln("");
+pipes.stdin.writeln(message);
+
+// a single period tells sendmail we are finished
+pipes.stdin.writeln(".");
+
+// but at this point sendmail might not see it, we need to flush
+pipes.stdin.flush();
+
+// sendmail happens to exit on ".", but some you have to close the file:
+pipes.stdin.close();
+
+// otherwise this wait will wait forever
+wait(pipes.pid);
+
+---
+*/
+ProcessPipes pipeProcess(in char[][] args,
+ Redirect redirect = Redirect.all,
+ const string[string] env = null,
+ Config config = Config.none,
+ in char[] workDir = null)
+ @safe
+{
+ return pipeProcessImpl!spawnProcess(args, redirect, env, config, workDir);
+}
+
+/// ditto
+ProcessPipes pipeProcess(in char[] program,
+ Redirect redirect = Redirect.all,
+ const string[string] env = null,
+ Config config = Config.none,
+ in char[] workDir = null)
+ @safe
+{
+ return pipeProcessImpl!spawnProcess(program, redirect, env, config, workDir);
+}
+
+/// ditto
+ProcessPipes pipeShell(in char[] command,
+ Redirect redirect = Redirect.all,
+ const string[string] env = null,
+ Config config = Config.none,
+ in char[] workDir = null,
+ string shellPath = nativeShell)
+ @safe
+{
+ return pipeProcessImpl!spawnShell(command,
+ redirect,
+ env,
+ config,
+ workDir,
+ shellPath);
+}
+
+// Implementation of the pipeProcess() family of functions.
+private ProcessPipes pipeProcessImpl(alias spawnFunc, Cmd, ExtraSpawnFuncArgs...)
+ (Cmd command,
+ Redirect redirectFlags,
+ const string[string] env = null,
+ Config config = Config.none,
+ in char[] workDir = null,
+ ExtraSpawnFuncArgs extraArgs = ExtraSpawnFuncArgs.init)
+ @trusted //TODO: @safe
+{
+ File childStdin, childStdout, childStderr;
+ ProcessPipes pipes;
+ pipes._redirectFlags = redirectFlags;
+
+ if (redirectFlags & Redirect.stdin)
+ {
+ auto p = pipe();
+ childStdin = p.readEnd;
+ pipes._stdin = p.writeEnd;
+ }
+ else
+ {
+ childStdin = std.stdio.stdin;
+ }
+
+ if (redirectFlags & Redirect.stdout)
+ {
+ if ((redirectFlags & Redirect.stdoutToStderr) != 0)
+ throw new StdioException("Cannot create pipe for stdout AND "
+ ~"redirect it to stderr", 0);
+ auto p = pipe();
+ childStdout = p.writeEnd;
+ pipes._stdout = p.readEnd;
+ }
+ else
+ {
+ childStdout = std.stdio.stdout;
+ }
+
+ if (redirectFlags & Redirect.stderr)
+ {
+ if ((redirectFlags & Redirect.stderrToStdout) != 0)
+ throw new StdioException("Cannot create pipe for stderr AND "
+ ~"redirect it to stdout", 0);
+ auto p = pipe();
+ childStderr = p.writeEnd;
+ pipes._stderr = p.readEnd;
+ }
+ else
+ {
+ childStderr = std.stdio.stderr;
+ }
+
+ if (redirectFlags & Redirect.stdoutToStderr)
+ {
+ if (redirectFlags & Redirect.stderrToStdout)
+ {
+ // We know that neither of the other options have been
+ // set, so we assign the std.stdio.std* streams directly.
+ childStdout = std.stdio.stderr;
+ childStderr = std.stdio.stdout;
+ }
+ else
+ {
+ childStdout = childStderr;
+ }
+ }
+ else if (redirectFlags & Redirect.stderrToStdout)
+ {
+ childStderr = childStdout;
+ }
+
+ config &= ~(Config.retainStdin | Config.retainStdout | Config.retainStderr);
+ pipes._pid = spawnFunc(command, childStdin, childStdout, childStderr,
+ env, config, workDir, extraArgs);
+ return pipes;
+}
+
+
+/**
+Flags that can be passed to $(LREF pipeProcess) and $(LREF pipeShell)
+to specify which of the child process' standard streams are redirected.
+Use bitwise OR to combine flags.
+*/
+enum Redirect
+{
+ /// Redirect the standard input, output or error streams, respectively.
+ stdin = 1,
+ stdout = 2, /// ditto
+ stderr = 4, /// ditto
+
+ /**
+ Redirect _all three streams. This is equivalent to
+ $(D Redirect.stdin | Redirect.stdout | Redirect.stderr).
+ */
+ all = stdin | stdout | stderr,
+
+ /**
+ Redirect the standard error stream into the standard output stream.
+ This can not be combined with $(D Redirect.stderr).
+ */
+ stderrToStdout = 8,
+
+ /**
+ Redirect the standard output stream into the standard error stream.
+ This can not be combined with $(D Redirect.stdout).
+ */
+ stdoutToStderr = 16,
+}
+
+@system unittest
+{
+ import std.string;
+ version (Windows) TestScript prog =
+ "call :sub %~1 %~2 0
+ call :sub %~1 %~2 1
+ call :sub %~1 %~2 2
+ call :sub %~1 %~2 3
+ exit 3
+
+ :sub
+ set /p INPUT=
+ if -%INPUT%-==-stop- ( exit %~3 )
+ echo %INPUT% %~1
+ echo %INPUT% %~2 1>&2";
+ else version (Posix) TestScript prog =
+ `for EXITCODE in 0 1 2 3; do
+ read INPUT
+ if test "$INPUT" = stop; then break; fi
+ echo "$INPUT $1"
+ echo "$INPUT $2" >&2
+ done
+ exit $EXITCODE`;
+ 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");
+ pp.stdin.writeln("1234567890");
+ pp.stdin.flush();
+ 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);
+
+ 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");
+ pp.stdin.writeln("stop");
+ pp.stdin.flush();
+ 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");
+ pp.stdin.writeln("stop");
+ pp.stdin.flush();
+ assert(wait(pp.pid) == 1);
+}
+
+@system unittest
+{
+ import std.exception : assertThrown;
+ TestScript prog = "exit 0";
+ assertThrown!StdioException(pipeProcess(
+ prog.path,
+ Redirect.stdout | Redirect.stdoutToStderr));
+ assertThrown!StdioException(pipeProcess(
+ prog.path,
+ Redirect.stderr | Redirect.stderrToStdout));
+ auto p = pipeProcess(prog.path, Redirect.stdin);
+ assertThrown!Error(p.stdout);
+ assertThrown!Error(p.stderr);
+ wait(p.pid);
+ p = pipeProcess(prog.path, Redirect.stderr);
+ assertThrown!Error(p.stdin);
+ assertThrown!Error(p.stdout);
+ wait(p.pid);
+}
+
+/**
+Object which contains $(REF File, std,stdio) handles that allow communication
+with a child process through its standard streams.
+*/
+struct ProcessPipes
+{
+ /// The $(LREF Pid) of the child process.
+ @property Pid pid() @safe nothrow
+ {
+ return _pid;
+ }
+
+ /**
+ An $(REF File, std,stdio) that allows writing to the child process'
+ standard input stream.
+
+ Throws:
+ $(OBJECTREF Error) if the child process' standard input stream hasn't
+ been redirected.
+ */
+ @property File stdin() @safe nothrow
+ {
+ if ((_redirectFlags & Redirect.stdin) == 0)
+ throw new Error("Child process' standard input stream hasn't "
+ ~"been redirected.");
+ return _stdin;
+ }
+
+ /**
+ An $(REF File, std,stdio) that allows reading from the child process'
+ standard output stream.
+
+ Throws:
+ $(OBJECTREF Error) if the child process' standard output stream hasn't
+ been redirected.
+ */
+ @property File stdout() @safe nothrow
+ {
+ if ((_redirectFlags & Redirect.stdout) == 0)
+ throw new Error("Child process' standard output stream hasn't "
+ ~"been redirected.");
+ return _stdout;
+ }
+
+ /**
+ An $(REF File, std,stdio) that allows reading from the child process'
+ standard error stream.
+
+ Throws:
+ $(OBJECTREF Error) if the child process' standard error stream hasn't
+ been redirected.
+ */
+ @property File stderr() @safe nothrow
+ {
+ if ((_redirectFlags & Redirect.stderr) == 0)
+ throw new Error("Child process' standard error stream hasn't "
+ ~"been redirected.");
+ return _stderr;
+ }
+
+private:
+ Redirect _redirectFlags;
+ Pid _pid;
+ File _stdin, _stdout, _stderr;
+}
+
+
+
+/**
+Executes the given program or shell command and returns its exit
+code and output.
+
+$(D execute) and $(D executeShell) start a new process using
+$(LREF spawnProcess) and $(LREF spawnShell), respectively, and wait
+for the process to complete before returning. The functions capture
+what the child process prints to both its standard output and
+standard error streams, and return this together with its exit code.
+---
+auto dmd = execute(["dmd", "myapp.d"]);
+if (dmd.status != 0) writeln("Compilation failed:\n", dmd.output);
+
+auto ls = executeShell("ls -l");
+if (ls.status != 0) writeln("Failed to retrieve file listing");
+else writeln(ls.output);
+---
+
+The $(D args)/$(D program)/$(D command), $(D env) and $(D config)
+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.)
+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.
+maxOutput = The maximum number of bytes of output that should be
+ captured.
+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")).
+
+POSIX_specific:
+If the process is terminated by a signal, the $(D status) field of
+the return value will contain a negative number whose absolute
+value is the signal number. (See $(LREF wait) for details.)
+
+Throws:
+$(LREF ProcessException) on failure to start the process.$(BR)
+$(REF StdioException, std,stdio) on failure to capture output.
+*/
+auto execute(in char[][] args,
+ const string[string] env = null,
+ Config config = Config.none,
+ size_t maxOutput = size_t.max,
+ in char[] workDir = null)
+ @trusted //TODO: @safe
+{
+ return executeImpl!pipeProcess(args, env, config, maxOutput, workDir);
+}
+
+/// ditto
+auto execute(in char[] program,
+ const string[string] env = null,
+ Config config = Config.none,
+ size_t maxOutput = size_t.max,
+ in char[] workDir = null)
+ @trusted //TODO: @safe
+{
+ return executeImpl!pipeProcess(program, env, config, maxOutput, workDir);
+}
+
+/// ditto
+auto executeShell(in char[] command,
+ const string[string] env = null,
+ Config config = Config.none,
+ size_t maxOutput = size_t.max,
+ in char[] workDir = null,
+ string shellPath = nativeShell)
+ @trusted //TODO: @safe
+{
+ return executeImpl!pipeShell(command,
+ env,
+ config,
+ maxOutput,
+ workDir,
+ shellPath);
+}
+
+// Does the actual work for execute() and executeShell().
+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,
+ ExtraPipeFuncArgs extraArgs = ExtraPipeFuncArgs.init)
+{
+ import std.algorithm.comparison : min;
+ import std.array : appender;
+ import std.typecons : Tuple;
+
+ auto p = pipeFunc(commandLine, Redirect.stdout | Redirect.stderrToStdout,
+ env, config, workDir, extraArgs);
+
+ auto a = appender!(ubyte[])();
+ enum size_t defaultChunkSize = 4096;
+ immutable chunkSize = min(maxOutput, defaultChunkSize);
+
+ // Store up to maxOutput bytes in a.
+ foreach (ubyte[] chunk; p.stdout.byChunk(chunkSize))
+ {
+ immutable size_t remain = maxOutput - a.data.length;
+
+ if (chunk.length < remain) a.put(chunk);
+ else
+ {
+ a.put(chunk[0 .. remain]);
+ break;
+ }
+ }
+ // Exhaust the stream, if necessary.
+ foreach (ubyte[] chunk; p.stdout.byChunk(defaultChunkSize)) { }
+
+ return Tuple!(int, "status", string, "output")(wait(p.pid), cast(string) a.data);
+}
+
+@system unittest
+{
+ import std.string;
+ // To avoid printing the newline characters, we use the echo|set trick on
+ // Windows, and printf on POSIX (neither echo -n nor echo \c are portable).
+ version (Windows) TestScript prog =
+ "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");
+ auto s = execute([prog.path, "Hello", "World"]);
+ assert(s.status == 123);
+ assert(s.output.stripRight() == "HelloWorld");
+}
+
+@safe unittest
+{
+ import std.string;
+ auto r1 = executeShell("echo 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");
+ auto r3 = executeShell("exit 123");
+ assert(r3.status == 123);
+ assert(r3.output.empty);
+}
+
+@safe unittest
+{
+ import std.typecons : Tuple;
+ void foo() //Just test the compilation
+ {
+ auto ret1 = execute(["dummy", "arg"]);
+ auto ret2 = executeShell("dummy arg");
+ static assert(is(typeof(ret1) == typeof(ret2)));
+
+ Tuple!(int, string) ret3 = execute(["dummy", "arg"]);
+ }
+}
+
+/// An exception that signals a problem with starting or waiting for a process.
+class ProcessException : Exception
+{
+ 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 : errno;
+ return newFromErrno(errno, customMsg, file, line);
+ }
+
+ // ditto, but error number is provided by caller
+ static ProcessException newFromErrno(int error,
+ string customMsg = null,
+ string file = __FILE__,
+ size_t line = __LINE__)
+ {
+ import std.exception : errnoString;
+ auto errnoMsg = errnoString(error);
+ auto msg = customMsg.empty ? errnoMsg
+ : customMsg ~ " (" ~ errnoMsg ~ ')';
+ return new ProcessException(msg, file, line);
+ }
+
+ // Creates a new ProcessException based on GetLastError() (Windows only).
+ version (Windows)
+ static ProcessException newFromLastError(string customMsg = null,
+ string file = __FILE__,
+ size_t line = __LINE__)
+ {
+ auto lastMsg = sysErrorString(GetLastError());
+ auto msg = customMsg.empty ? lastMsg
+ : customMsg ~ " (" ~ lastMsg ~ ')';
+ return new ProcessException(msg, file, line);
+ }
+}
+
+
+/**
+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 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 the result of
+$(LREF nativeShell).
+*/
+@property string userShell() @safe
+{
+ 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 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();
+ else version (Posix) return core.sys.posix.unistd.getpid();
+}
+
+
+/**
+ * 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
+// POSIX its executable permission bit is set. The file is
+// automatically deleted when the object goes out of scope.
+version (unittest)
+private struct TestScript
+{
+ this(string code) @system
+ {
+ // @system due to chmod
+ import std.ascii : newline;
+ import std.file : write;
+ version (Windows)
+ {
+ auto ext = ".cmd";
+ auto firstLine = "@echo off";
+ }
+ else version (Posix)
+ {
+ auto ext = "";
+ auto firstLine = "#!" ~ nativeShell;
+ }
+ path = uniqueTempPath()~ext;
+ write(path, firstLine ~ newline ~ code ~ newline);
+ version (Posix)
+ {
+ import core.sys.posix.sys.stat : chmod;
+ chmod(path.tempCString(), octal!777);
+ }
+ }
+
+ ~this()
+ {
+ import std.file : remove, exists;
+ if (!path.empty && exists(path))
+ {
+ try { remove(path); }
+ catch (Exception e)
+ {
+ debug std.stdio.stderr.writeln(e.msg);
+ }
+ }
+ }
+
+ string path;
+}
+
+version (unittest)
+private string uniqueTempPath() @safe
+{
+ 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());
+}
+
+
+// =============================================================================
+// Functions for shell command quoting/escaping.
+// =============================================================================
+
+
+/*
+ Command line arguments exist in three forms:
+ 1) string or char* array, as received by main.
+ Also used internally on POSIX systems.
+ 2) Command line string, as used in Windows'
+ CreateProcess and CommandLineToArgvW functions.
+ A specific quoting and escaping algorithm is used
+ to distinguish individual arguments.
+ 3) Shell command string, as written at a shell prompt
+ or passed to cmd /C - this one may contain shell
+ control characters, e.g. > or | for redirection /
+ piping - thus, yet another layer of escaping is
+ used to distinguish them from program arguments.
+
+ Except for escapeWindowsArgument, the intermediary
+ format (2) is hidden away from the user in this module.
+*/
+
+/**
+Escapes an argv-style argument array to be used with $(LREF spawnShell),
+$(LREF pipeShell) or $(LREF executeShell).
+---
+string url = "http://dlang.org/";
+executeShell(escapeShellCommand("wget", url, "-O", "dlang-index.html"));
+---
+
+Concatenate multiple $(D escapeShellCommand) and
+$(LREF escapeShellFileName) results to use shell redirection or
+piping operators.
+---
+executeShell(
+ escapeShellCommand("curl", "http://dlang.org/download.html") ~
+ "|" ~
+ escapeShellCommand("grep", "-o", `http://\S*\.zip`) ~
+ ">" ~
+ escapeShellFileName("D download links.txt"));
+---
+
+Throws:
+$(OBJECTREF Exception) if any part of the command line contains unescapable
+characters (NUL on all platforms, as well as CR and LF on Windows).
+*/
+string escapeShellCommand(in char[][] args...) @safe pure
+{
+ if (args.empty)
+ return null;
+ version (Windows)
+ {
+ // Do not ^-escape the first argument (the program path),
+ // as the shell parses it differently from parameters.
+ // ^-escaping a program path that contains spaces will fail.
+ string result = escapeShellFileName(args[0]);
+ if (args.length > 1)
+ {
+ result ~= " " ~ escapeShellCommandString(
+ escapeShellArguments(args[1..$]));
+ }
+ return result;
+ }
+ version (Posix)
+ {
+ return escapeShellCommandString(escapeShellArguments(args));
+ }
+}
+
+@safe unittest
+{
+ // This is a simple unit test without any special requirements,
+ // in addition to the unittest_burnin one below which requires
+ // special preparation.
+
+ struct TestVector { string[] args; string windows, posix; }
+ TestVector[] tests =
+ [
+ {
+ args : ["foo bar"],
+ windows : `"foo bar"`,
+ posix : `'foo bar'`
+ },
+ {
+ args : ["foo bar", "hello"],
+ windows : `"foo bar" hello`,
+ posix : `'foo bar' 'hello'`
+ },
+ {
+ args : ["foo bar", "hello world"],
+ windows : `"foo bar" ^"hello world^"`,
+ posix : `'foo bar' 'hello world'`
+ },
+ {
+ args : ["foo bar", "hello", "world"],
+ windows : `"foo bar" hello world`,
+ posix : `'foo bar' 'hello' 'world'`
+ },
+ {
+ args : ["foo bar", `'"^\`],
+ windows : `"foo bar" ^"'\^"^^\\^"`,
+ posix : `'foo bar' ''\''"^\'`
+ },
+ ];
+
+ foreach (test; tests)
+ version (Windows)
+ assert(escapeShellCommand(test.args) == test.windows);
+ else
+ assert(escapeShellCommand(test.args) == test.posix );
+}
+
+private string escapeShellCommandString(string command) @safe pure
+{
+ version (Windows)
+ return escapeWindowsShellCommand(command);
+ else
+ return command;
+}
+
+private string escapeWindowsShellCommand(in char[] command) @safe pure
+{
+ import std.array : appender;
+ auto result = appender!string();
+ result.reserve(command.length);
+
+ foreach (c; command)
+ switch (c)
+ {
+ case '\0':
+ throw new Exception("Cannot put NUL in command line");
+ case '\r':
+ case '\n':
+ throw new Exception("CR/LF are not escapable");
+ case '\x01': .. case '\x09':
+ case '\x0B': .. case '\x0C':
+ case '\x0E': .. case '\x1F':
+ case '"':
+ case '^':
+ case '&':
+ case '<':
+ case '>':
+ case '|':
+ result.put('^');
+ goto default;
+ default:
+ result.put(c);
+ }
+ return result.data;
+}
+
+private string escapeShellArguments(in char[][] args...)
+ @trusted pure nothrow
+{
+ import std.exception : assumeUnique;
+ char[] buf;
+
+ @safe nothrow
+ char[] allocator(size_t size)
+ {
+ if (buf.length == 0)
+ return buf = new char[size];
+ else
+ {
+ auto p = buf.length;
+ buf.length = buf.length + 1 + size;
+ buf[p++] = ' ';
+ return buf[p .. p+size];
+ }
+ }
+
+ foreach (arg; args)
+ escapeShellArgument!allocator(arg);
+ return assumeUnique(buf);
+}
+
+private auto escapeShellArgument(alias allocator)(in char[] arg) @safe nothrow
+{
+ // The unittest for this function requires special
+ // preparation - see below.
+
+ version (Windows)
+ return escapeWindowsArgumentImpl!allocator(arg);
+ else
+ return escapePosixArgumentImpl!allocator(arg);
+}
+
+/**
+Quotes a command-line argument in a manner conforming to the behavior of
+$(LINK2 http://msdn.microsoft.com/en-us/library/windows/desktop/bb776391(v=vs.85).aspx,
+CommandLineToArgvW).
+*/
+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);
+}
+
+
+private char[] charAllocator(size_t size) @safe pure nothrow
+{
+ return new char[size];
+}
+
+
+private char[] escapeWindowsArgumentImpl(alias allocator)(in char[] arg)
+ @safe nothrow
+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
+ // * http://blogs.msdn.com/b/oldnewthing/archive/2010/09/17/10063629.aspx
+
+ // Check if the string needs to be escaped,
+ // and calculate the total string size.
+
+ // Trailing backslashes must be escaped
+ bool escaping = true;
+ bool needEscape = false;
+ // Result size = input size + 2 for surrounding quotes + 1 for the
+ // backslash for each escaped character.
+ size_t size = 1 + arg.length + 1;
+
+ foreach_reverse (char c; arg)
+ {
+ if (c == '"')
+ {
+ needEscape = true;
+ escaping = true;
+ size++;
+ }
+ else
+ if (c == '\\')
+ {
+ if (escaping)
+ size++;
+ }
+ else
+ {
+ if (c == ' ' || c == '\t')
+ needEscape = true;
+ escaping = false;
+ }
+ }
+
+ 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 (isDigit(arg[$-1]))
+ needEscape = true;
+
+ if (!needEscape)
+ return allocator(arg.length)[] = arg;
+
+ // Construct result string.
+
+ auto buf = allocator(size);
+ size_t p = size;
+ buf[--p] = '"';
+ escaping = true;
+ foreach_reverse (char c; arg)
+ {
+ if (c == '"')
+ escaping = true;
+ else
+ if (c != '\\')
+ escaping = false;
+
+ buf[--p] = c;
+ if (escaping)
+ buf[--p] = '\\';
+ }
+ buf[--p] = '"';
+ assert(p == 0);
+
+ return buf;
+}
+
+version (Windows) version (unittest)
+{
+ import core.stdc.stddef;
+ import core.stdc.wchar_ : wcslen;
+ import core.sys.windows.shellapi : CommandLineToArgvW;
+ import core.sys.windows.windows;
+ import std.array;
+
+ string[] parseCommandLine(string line)
+ {
+ 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)]))
+ .array();
+ }
+
+ @system unittest
+ {
+ string[] testStrings = [
+ `Hello`,
+ `Hello, world`,
+ `Hello, "world"`,
+ `C:\`,
+ `C:\dmd`,
+ `C:\Program Files\`,
+ ];
+
+ enum CHARS = `_x\" *&^` ~ "\t"; // _ is placeholder for nothing
+ foreach (c1; CHARS)
+ foreach (c2; CHARS)
+ foreach (c3; CHARS)
+ foreach (c4; CHARS)
+ testStrings ~= [c1, c2, c3, c4].replace("_", "");
+
+ foreach (s; testStrings)
+ {
+ auto q = escapeWindowsArgument(s);
+ auto args = parseCommandLine("Dummy.exe " ~ q);
+ assert(args.length == 2, s ~ " => " ~ q ~ " #" ~ text(args.length-1));
+ assert(args[1] == s, s ~ " => " ~ q ~ " => " ~ args[1]);
+ }
+ }
+}
+
+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)))
+{
+ // '\'' means: close quoted part of argument, append an escaped
+ // single quote, and reopen quotes
+
+ // Below code is equivalent to:
+ // return `'` ~ std.array.replace(arg, `'`, `'\''`) ~ `'`;
+
+ size_t size = 1 + arg.length + 1;
+ foreach (char c; arg)
+ if (c == '\'')
+ size += 3;
+
+ auto buf = allocator(size);
+ size_t p = 0;
+ buf[p++] = '\'';
+ foreach (char c; arg)
+ if (c == '\'')
+ {
+ buf[p .. p+4] = `'\''`;
+ p += 4;
+ }
+ else
+ buf[p++] = c;
+ buf[p++] = '\'';
+ assert(p == size);
+
+ return buf;
+}
+
+/**
+Escapes a filename to be used for shell redirection with $(LREF spawnShell),
+$(LREF pipeShell) or $(LREF executeShell).
+*/
+string escapeShellFileName(in char[] fileName) @trusted pure nothrow
+{
+ // The unittest for this function requires special
+ // preparation - see below.
+
+ version (Windows)
+ {
+ // If a file starts with &, it can cause cmd.exe to misinterpret
+ // the file name as the stream redirection syntax:
+ // command > "&foo.txt"
+ // gets interpreted as
+ // command >&foo.txt
+ // Prepend .\ to disambiguate.
+
+ if (fileName.length && fileName[0] == '&')
+ return cast(string)(`".\` ~ fileName ~ '"');
+
+ return cast(string)('"' ~ fileName ~ '"');
+ }
+ else
+ return escapePosixArgument(fileName);
+}
+
+// Loop generating strings with random characters
+//version = unittest_burnin;
+
+version (unittest_burnin)
+@system unittest
+{
+ // There are no readily-available commands on all platforms suitable
+ // for properly testing command escaping. The behavior of CMD's "echo"
+ // built-in differs from the POSIX program, and Windows ports of POSIX
+ // environments (Cygwin, msys, gnuwin32) may interfere with their own
+ // "echo" ports.
+
+ // To run this unit test, create std_process_unittest_helper.d with the
+ // following content and compile it:
+ // import std.stdio, std.array; void main(string[] args) { write(args.join("\0")); }
+ // Then, test this module with:
+ // rdmd --main -unittest -version=unittest_burnin process.d
+
+ auto helper = absolutePath("std_process_unittest_helper");
+ assert(executeShell(helper ~ " hello").output.split("\0")[1..$] == ["hello"], "Helper malfunction");
+
+ void test(string[] s, string fn)
+ {
+ string e;
+ string[] g;
+
+ e = escapeShellCommand(helper ~ s);
+ {
+ scope(failure) writefln("executeShell() failed.\nExpected:\t%s\nEncoded:\t%s", s, [e]);
+ auto result = executeShell(e);
+ assert(result.status == 0, "std_process_unittest_helper failed");
+ g = result.output.split("\0")[1..$];
+ }
+ assert(s == g, format("executeShell() test failed.\nExpected:\t%s\nGot:\t\t%s\nEncoded:\t%s", s, g, [e]));
+
+ 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]);
+ 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]));
+ }
+
+ while (true)
+ {
+ string[] args;
+ foreach (n; 0 .. uniform(1, 4))
+ {
+ string arg;
+ foreach (l; 0 .. uniform(0, 10))
+ {
+ dchar c;
+ while (true)
+ {
+ version (Windows)
+ {
+ // As long as DMD's system() uses CreateProcessA,
+ // we can't reliably pass Unicode
+ c = uniform(0, 128);
+ }
+ else
+ c = uniform!ubyte();
+
+ if (c == 0)
+ continue; // argv-strings are zero-terminated
+ version (Windows)
+ if (c == '\r' || c == '\n')
+ continue; // newlines are unescapable on Windows
+ break;
+ }
+ arg ~= c;
+ }
+ args ~= arg;
+ }
+
+ // generate filename
+ string fn;
+ foreach (l; 0 .. uniform(1, 10))
+ {
+ dchar c;
+ while (true)
+ {
+ version (Windows)
+ c = uniform(0, 128); // as above
+ else
+ c = uniform!ubyte();
+
+ if (c == 0 || c == '/')
+ continue; // NUL and / are the only characters
+ // forbidden in POSIX filenames
+ version (Windows)
+ if (c < '\x20' || c == '<' || c == '>' || c == ':' ||
+ c == '"' || c == '\\' || c == '|' || c == '?' || c == '*')
+ continue; // http://msdn.microsoft.com/en-us/library/aa365247(VS.85).aspx
+ break;
+ }
+
+ fn ~= c;
+ }
+ fn = fn[0..$/2] ~ "_testfile_" ~ fn[$/2..$];
+
+ test(args, fn);
+ }
+}
+
+
+// =============================================================================
+// Environment variable manipulation.
+// =============================================================================
+
+
+/**
+Manipulates _environment variables using an associative-array-like
+interface.
+
+This class contains only static methods, and cannot be instantiated.
+See below for examples of use.
+*/
+abstract final class environment
+{
+static:
+ /**
+ Retrieves the value of the environment variable with the given $(D name).
+ ---
+ auto path = environment["PATH"];
+ ---
+
+ Throws:
+ $(OBJECTREF Exception) if the environment variable does not exist,
+ or $(REF UTFException, std,utf) if the variable contains invalid UTF-16
+ characters (Windows only).
+
+ See_also:
+ $(LREF environment.get), which doesn't throw on failure.
+ */
+ string opIndex(in char[] name) @safe
+ {
+ import std.exception : enforce;
+ string value;
+ enforce(getImpl(name, value), "Environment variable not found: "~name);
+ return value;
+ }
+
+ /**
+ Retrieves the value of the environment variable with the given $(D name),
+ or a default value if the variable doesn't exist.
+
+ Unlike $(LREF environment.opIndex), this function never throws.
+ ---
+ auto sh = environment.get("SHELL", "/bin/sh");
+ ---
+ This function is also useful in checking for the existence of an
+ environment variable.
+ ---
+ auto myVar = environment.get("MYVAR");
+ if (myVar is null)
+ {
+ // Environment variable doesn't exist.
+ // Note that we have to use 'is' for the comparison, since
+ // myVar == null is also true if the variable exists but is
+ // empty.
+ }
+ ---
+
+ Throws:
+ $(REF UTFException, std,utf) if the variable contains invalid UTF-16
+ characters (Windows only).
+ */
+ string get(in char[] name, string defaultValue = null) @safe
+ {
+ string value;
+ auto found = getImpl(name, value);
+ return found ? value : defaultValue;
+ }
+
+ /**
+ 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.
+ ---
+ environment["foo"] = "bar";
+ ---
+
+ 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;
+ }
+ // The default errno error message is very uninformative
+ // in the most common case, so we handle it manually.
+ enforce(errno != EINVAL,
+ "Invalid environment variable name: '"~name~"'");
+ errnoEnforce(false,
+ "Failed to add environment variable");
+ assert(0);
+ }
+ else version (Windows)
+ {
+ import std.exception : enforce;
+ enforce(
+ SetEnvironmentVariableW(name.tempCStringW(), value.tempCStringW()),
+ sysErrorString(GetLastError())
+ );
+ return value;
+ }
+ else static assert(0);
+ }
+
+ /**
+ Removes the environment variable with the given $(D name).
+
+ 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
+ {
+ version (Windows) SetEnvironmentVariableW(name.tempCStringW(), null);
+ else version (Posix) core.sys.posix.stdlib.unsetenv(name.tempCString());
+ 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.
+
+ Windows_specific:
+ While Windows environment variable names are case insensitive, D's
+ built-in associative arrays are not. This function will store all
+ variable names in uppercase (e.g. $(D PATH)).
+
+ Throws:
+ $(OBJECTREF Exception) if the environment variables could not
+ be retrieved (Windows only).
+ */
+ string[string] toAA() @trusted
+ {
+ import std.conv : to;
+ string[string] aa;
+ version (Posix)
+ {
+ auto environ = getEnvironPtr;
+ for (int i=0; environ[i] != null; ++i)
+ {
+ import std.string : indexOf;
+
+ immutable varDef = to!string(environ[i]);
+ immutable eq = indexOf(varDef, '=');
+ assert(eq >= 0);
+
+ immutable name = varDef[0 .. eq];
+ immutable value = varDef[eq+1 .. $];
+
+ // In POSIX, environment variables may be defined more
+ // than once. This is a security issue, which we avoid
+ // by checking whether the key already exists in the array.
+ // For more info:
+ // http://www.dwheeler.com/secure-programs/Secure-Programs-HOWTO/environment-variables.html
+ if (name !in aa) aa[name] = value;
+ }
+ }
+ else version (Windows)
+ {
+ import std.exception : enforce;
+ import std.uni : toUpper;
+ auto envBlock = GetEnvironmentStringsW();
+ enforce(envBlock, "Failed to retrieve environment variables.");
+ scope(exit) FreeEnvironmentStringsW(envBlock);
+
+ for (int i=0; envBlock[i] != '\0'; ++i)
+ {
+ auto start = i;
+ while (envBlock[i] != '=') ++i;
+ immutable name = toUTF8(toUpper(envBlock[start .. i]));
+
+ start = i+1;
+ while (envBlock[i] != '\0') ++i;
+
+ // Ignore variables with empty names. These are used internally
+ // by Windows to keep track of each drive's individual current
+ // directory.
+ if (!name.length)
+ continue;
+
+ // Just like in POSIX systems, environment variables may be
+ // defined more than once in an environment block on Windows,
+ // 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.
+ auto val = toUTF8(envBlock[start .. i]);
+ if (name !in aa) aa[name] = val is null ? "" : val;
+ }
+ }
+ else static assert(0);
+ return aa;
+ }
+
+private:
+ // 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();
+ 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;
+
+ 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)
+ {
+ const vz = core.sys.posix.stdlib.getenv(name.tempCString());
+ if (vz == null) return false;
+ auto v = vz[0 .. strlen(vz)];
+
+ // Cache the last call's result.
+ static string lastResult;
+ 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;
+ }
+ else static assert(0);
+ }
+}
+
+@safe unittest
+{
+ import std.exception : assertThrown;
+ // New variable
+ environment["std_process"] = "foo";
+ assert(environment["std_process"] == "foo");
+ assert("std_process" in environment);
+
+ // 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") is null);
+
+ // get() with default value
+ 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);
+ 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).
+
+ assert(v == environment[n]);
+ }
+
+ // ... and back again.
+ foreach (n, v; aa)
+ environment[n] = v;
+
+ // Complete the roundtrip
+ auto aa2 = environment.toAA();
+ 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);
+}
+
+
+
+
+// =============================================================================
+// Everything below this line was part of the old std.process, and most of
+// it will be deprecated and removed.
+// =============================================================================
+
+
+/*
+Copyright: Copyright Digital Mars 2007 - 2009.
+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)
+*/
+/*
+ Copyright Digital Mars 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)
+*/
+
+
+import core.stdc.errno;
+import core.stdc.stdlib;
+import core.stdc.string;
+import core.thread;
+
+version (Windows)
+{
+ import std.file, std.format, std.random;
+}
+version (Posix)
+{
+ import core.sys.posix.stdlib;
+}
+version (unittest)
+{
+ import std.conv, std.file, std.random;
+}
+
+
+private void toAStringz(in string[] a, const(char)**az)
+{
+ import std.string : toStringz;
+ foreach (string s; a)
+ {
+ *az++ = toStringz(s);
+ }
+ *az = null;
+}
+
+
+/* ========================================================== */
+
+//version (Windows)
+//{
+// int spawnvp(int mode, string pathname, string[] argv)
+// {
+// char** argv_ = cast(char**) core.stdc.stdlib.malloc((char*).sizeof * (1 + argv.length));
+// scope(exit) core.stdc.stdlib.free(argv_);
+//
+// toAStringz(argv, argv_);
+//
+// return spawnvp(mode, pathname.tempCString(), argv_);
+// }
+//}
+
+// Incorporating idea (for spawnvp() on Posix) from Dave Fladebo
+
+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;
+
+/* ========================================================== */
+
+version (StdDdoc)
+{
+ /**
+ Replaces the current process by executing a command, $(D pathname), with
+ the arguments in $(D argv).
+
+ $(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'
+ versions of $(D exec) search the PATH environment variable for $(D
+ pathname). The 'e' versions additionally take the new process'
+ environment variables as an array of strings of the form key=value.
+
+ Does not return on success (the current process will have been
+ replaced). Returns -1 on failure with no indication of the
+ underlying error.
+
+ Windows_specific:
+ These functions are only supported on POSIX platforms, as the Windows
+ operating systems do not provide the ability to overwrite the current
+ process image with another. In single-threaded programs it is possible
+ to approximate the effect of $(D execv*) by using $(LREF spawnProcess)
+ and terminating the current process once the child process has returned.
+ For example:
+ ---
+ auto commandLine = [ "program", "arg1", "arg2" ];
+ version (Posix)
+ {
+ execv(commandLine[0], commandLine);
+ throw new Exception("Failed to execute program");
+ }
+ else version (Windows)
+ {
+ import core.stdc.stdlib : _exit;
+ _exit(wait(spawnProcess(commandLine)));
+ }
+ ---
+ This is, however, NOT equivalent to POSIX' $(D execv*). For one thing, the
+ executed program is started as a separate process, with all this entails.
+ Secondly, in a multithreaded program, other threads will continue to do
+ work while the current thread is waiting for the child process to complete.
+
+ A better option may sometimes be to terminate the current program immediately
+ after spawning the child process. This is the behaviour exhibited by the
+ $(LINK2 http://msdn.microsoft.com/en-us/library/431x4c1w.aspx,$(D __exec))
+ functions in Microsoft's C runtime library, and it is how D's now-deprecated
+ Windows $(D execv*) functions work. Example:
+ ---
+ auto commandLine = [ "program", "arg1", "arg2" ];
+ version (Posix)
+ {
+ execv(commandLine[0], commandLine);
+ throw new Exception("Failed to execute program");
+ }
+ else version (Windows)
+ {
+ spawnProcess(commandLine);
+ import core.stdc.stdlib : _exit;
+ _exit(0);
+ }
+ ---
+ */
+ int execv(in string pathname, in string[] argv);
+ ///ditto
+ int execve(in string pathname, in string[] argv, in string[] envp);
+ /// ditto
+ int execvp(in string pathname, in string[] argv);
+ /// ditto
+ int execvpe(in string pathname, in string[] argv, in string[] envp);
+}
+else version (Posix)
+{
+ 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);
+ }
+}
+
+// Move these C declarations to druntime if we decide to keep the D wrappers
+extern(C)
+{
+ int execv(in char *, in char **);
+ int execve(in char *, in char **, in char **);
+ int execvp(in char *, in char **);
+ version (Windows) int execvpe(in char *, in char **, in char **);
+}
+
+private int execv_(in string pathname, in string[] argv)
+{
+ auto argv_ = cast(const(char)**)core.stdc.stdlib.malloc((char*).sizeof * (1 + argv.length));
+ scope(exit) core.stdc.stdlib.free(argv_);
+
+ toAStringz(argv, argv_);
+
+ return execv(pathname.tempCString(), argv_);
+}
+
+private int execve_(in string pathname, in string[] argv, in string[] envp)
+{
+ 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_);
+
+ return execve(pathname.tempCString(), argv_, envp_);
+}
+
+private int execvp_(in string pathname, in string[] argv)
+{
+ auto argv_ = cast(const(char)**)core.stdc.stdlib.malloc((char*).sizeof * (1 + argv.length));
+ scope(exit) core.stdc.stdlib.free(argv_);
+
+ toAStringz(argv, argv_);
+
+ return execvp(pathname.tempCString(), argv_);
+}
+
+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] == '/')
+ {
+ // Yes, so just call execve()
+ return execve(pathname, argv, envp);
+ }
+ else
+ {
+ // No, so must traverse PATHs, looking for first match
+ string[] envPaths = split(
+ to!string(core.stdc.stdlib.getenv("PATH")), ":");
+ int iRet = 0;
+
+ // Note: if any call to execve() succeeds, this process will cease
+ // execution, so there's no need to check the execve() result through
+ // the loop.
+
+ foreach (string pathDir; envPaths)
+ {
+ string composite = cast(string) (pathDir ~ "/" ~ pathname);
+
+ iRet = execve(composite, argv, envp);
+ }
+ if (0 != iRet)
+ {
+ iRet = execve(pathname, argv, envp);
+ }
+
+ return iRet;
+ }
+}
+else version (Windows)
+{
+ 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_);
+
+ return execvpe(pathname.tempCString(), argv_, envp_);
+}
+else
+{
+ static assert(0);
+} // version
+}
+
+version (StdDdoc)
+{
+ /****************************************
+ * Start up the browser and set it to viewing the page at url.
+ */
+ void browse(const(char)[] url);
+}
+else
+version (Windows)
+{
+ import core.sys.windows.windows;
+
+ version (GNU) {}
+ else pragma(lib,"shell32.lib");
+
+ void browse(const(char)[] url)
+ {
+ ShellExecuteW(null, "open", url.tempCStringW(), null, null, SW_SHOWNORMAL);
+ }
+}
+else version (OSX)
+{
+ import core.stdc.stdio;
+ import core.stdc.string;
+ import core.sys.posix.unistd;
+
+ 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] = curl;
+ args[2] = null;
+ }
+ else
+ {
+ args[0] = "open".ptr;
+ args[1] = curl;
+ args[2] = null;
+ }
+
+ auto childpid = core.sys.posix.unistd.fork();
+ if (childpid == 0)
+ {
+ core.sys.posix.unistd.execvp(args[0], cast(char**) args.ptr);
+ perror(args[0]); // failed to execute
+ return;
+ }
+ if (browser)
+ free(cast(void*) browser);
+ }
+}
+else version (Posix)
+{
+ import core.stdc.stdio;
+ import core.stdc.string;
+ import core.sys.posix.unistd;
+
+ void browse(const(char)[] url) nothrow @nogc
+ {
+ const(char)*[3] args;
+
+ const(char)* browser = core.stdc.stdlib.getenv("BROWSER");
+ if (browser)
+ { browser = strdup(browser);
+ args[0] = browser;
+ }
+ else
+ //args[0] = "x-www-browser".ptr; // doesn't work on some systems
+ args[0] = "xdg-open".ptr;
+
+ args[1] = url.tempCString();
+ args[2] = null;
+
+ auto childpid = core.sys.posix.unistd.fork();
+ if (childpid == 0)
+ {
+ core.sys.posix.unistd.execvp(args[0], cast(char**) args.ptr);
+ perror(args[0]); // failed to execute
+ return;
+ }
+ if (browser)
+ free(cast(void*) browser);
+ }
+}
+else
+ static assert(0, "os not supported");
diff --git a/libphobos/src/std/random.d b/libphobos/src/std/random.d
new file mode 100644
index 0000000..b3116e18
--- /dev/null
+++ b/libphobos/src/std/random.d
@@ -0,0 +1,3344 @@
+// Written in the D programming language.
+
+/**
+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
+"$(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.
+
+In addition to random number generators, this module features
+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.
+
+Source: $(PHOBOSSRC std/_random.d)
+
+Macros:
+
+Copyright: Copyright Andrei Alexandrescu 2008 - 2009, Joseph Rushton Wakeling 2012.
+License: $(HTTP www.boost.org/LICENSE_1_0.txt, Boost License 1.0).
+Authors: $(HTTP erdani.org, Andrei Alexandrescu)
+ Masahiro Nakagawa (Xorshift random generator)
+ $(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 $(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).
+*/
+/*
+ Copyright Andrei Alexandrescu 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.random;
+
+
+import std.range.primitives;
+import std.traits;
+
+///
+@safe unittest
+{
+ // seed a random generator with a constant
+ auto rnd = Random(42);
+
+ // Generate a uniformly-distributed integer in the range [0, 14]
+ // If no random generator is passed, the global `rndGen` would be used
+ auto i = uniform(0, 15, rnd);
+ assert(i == 12);
+
+ // Generate a uniformly-distributed real in the range [0, 100)
+ auto r = uniform(0.0L, 100.0L, rnd);
+ assert(r == 79.65429843861011285);
+
+ // Generate a 32-bit random number
+ auto u = uniform!uint(rnd);
+ assert(u == 4083286876);
+}
+
+version (unittest)
+{
+ 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
+// From "Inner Loops" by Rick Booth, Addison-Wesley
+
+// Work derived from:
+
+/*
+ A C-program for MT19937, with initialization improved 2002/1/26.
+ Coded by Takuji Nishimura and Makoto Matsumoto.
+
+ Before using, initialize the state by using init_genrand(seed)
+ or init_by_array(init_key, key_length).
+
+ Copyright (C) 1997 - 2002, Makoto Matsumoto and Takuji Nishimura,
+ All rights reserved.
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions
+ are met:
+
+ 1. Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+
+ 2. Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+
+ 3. The names of its contributors may not be used to endorse or promote
+ products derived from this software without specific prior written
+ permission.
+
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+
+ Any feedback is very welcome.
+ http://www.math.sci.hiroshima-u.ac.jp/~m-mat/MT/emt.html
+ email: m-mat @ math.sci.hiroshima-u.ac.jp (remove space)
+*/
+
+/**
+ * Test if Rng is a random-number generator. The overload
+ * taking a ElementType also makes sure that the Rng generates
+ * values of that type.
+ *
+ * A random-number generator has at least the following features:
+ * $(UL
+ * $(LI it's an InputRange)
+ * $(LI it has a 'bool isUniformRandom' field readable in CTFE)
+ * )
+ */
+template isUniformRNG(Rng, ElementType)
+{
+ enum bool isUniformRNG = isInputRange!Rng &&
+ is(typeof(Rng.front) == ElementType) &&
+ is(typeof(
+ {
+ static assert(Rng.isUniformRandom); //tag
+ }));
+}
+
+/**
+ * ditto
+ */
+template isUniformRNG(Rng)
+{
+ enum bool isUniformRNG = isInputRange!Rng &&
+ is(typeof(
+ {
+ static assert(Rng.isUniformRandom); //tag
+ }));
+}
+
+/**
+ * Test if Rng is seedable. The overload
+ * taking a SeedType also makes sure that the Rng can be seeded with SeedType.
+ *
+ * A seedable random-number generator has the following additional features:
+ * $(UL
+ * $(LI it has a 'seed(ElementType)' function)
+ * )
+ */
+template isSeedable(Rng, SeedType)
+{
+ enum bool isSeedable = isUniformRNG!(Rng) &&
+ is(typeof(
+ {
+ Rng r = void; // can define a Rng object
+ r.seed(SeedType.init); // can seed a Rng
+ }));
+}
+
+///ditto
+template isSeedable(Rng)
+{
+ enum bool isSeedable = isUniformRNG!Rng &&
+ is(typeof(
+ {
+ Rng r = void; // can define a Rng object
+ r.seed(typeof(r.front).init); // can seed a Rng
+ }));
+}
+
+@safe pure nothrow unittest
+{
+ struct NoRng
+ {
+ @property uint front() {return 0;}
+ @property bool empty() {return false;}
+ void popFront() {}
+ }
+ static assert(!isUniformRNG!(NoRng, uint));
+ static assert(!isUniformRNG!(NoRng));
+ static assert(!isSeedable!(NoRng, uint));
+ static assert(!isSeedable!(NoRng));
+
+ struct NoRng2
+ {
+ @property uint front() {return 0;}
+ @property bool empty() {return false;}
+ void popFront() {}
+
+ enum isUniformRandom = false;
+ }
+ static assert(!isUniformRNG!(NoRng2, uint));
+ static assert(!isUniformRNG!(NoRng2));
+ static assert(!isSeedable!(NoRng2, uint));
+ static assert(!isSeedable!(NoRng2));
+
+ struct NoRng3
+ {
+ @property bool empty() {return false;}
+ void popFront() {}
+
+ enum isUniformRandom = true;
+ }
+ static assert(!isUniformRNG!(NoRng3, uint));
+ static assert(!isUniformRNG!(NoRng3));
+ static assert(!isSeedable!(NoRng3, uint));
+ static assert(!isSeedable!(NoRng3));
+
+ struct validRng
+ {
+ @property uint front() {return 0;}
+ @property bool empty() {return false;}
+ void popFront() {}
+
+ enum isUniformRandom = true;
+ }
+ static assert(isUniformRNG!(validRng, uint));
+ static assert(isUniformRNG!(validRng));
+ static assert(!isSeedable!(validRng, uint));
+ static assert(!isSeedable!(validRng));
+
+ struct seedRng
+ {
+ @property uint front() {return 0;}
+ @property bool empty() {return false;}
+ void popFront() {}
+ void seed(uint val){}
+ enum isUniformRandom = true;
+ }
+ static assert(isUniformRNG!(seedRng, uint));
+ static assert(isUniformRNG!(seedRng));
+ static assert(isSeedable!(seedRng, uint));
+ static assert(isSeedable!(seedRng));
+}
+
+/**
+Linear Congruential generator.
+ */
+struct LinearCongruentialEngine(UIntType, UIntType a, UIntType c, UIntType m)
+if (isUnsigned!UIntType)
+{
+ ///Mark this as a Rng
+ enum bool isUniformRandom = true;
+ /// Does this generator have a fixed range? ($(D_PARAM true)).
+ enum bool hasFixedRange = true;
+ /// Lowest generated value ($(D 1) if $(D c == 0), $(D 0) otherwise).
+ enum UIntType min = ( c == 0 ? 1 : 0 );
+ /// Highest generated value ($(D modulus - 1)).
+ enum UIntType max = m - 1;
+/**
+The parameters of this distribution. The random number is $(D_PARAM x
+= (x * multipler + increment) % modulus).
+ */
+ enum UIntType multiplier = a;
+ ///ditto
+ enum UIntType increment = c;
+ ///ditto
+ enum UIntType modulus = m;
+
+ static assert(isIntegral!(UIntType));
+ 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));
+
+ // Check for maximum range
+ private static ulong gcd(ulong a, ulong b) @safe pure nothrow @nogc
+ {
+ while (b)
+ {
+ auto t = b;
+ b = a % b;
+ a = t;
+ }
+ return a;
+ }
+
+ private static ulong primeFactorsOnly(ulong n) @safe pure nothrow @nogc
+ {
+ ulong result = 1;
+ ulong iter = 2;
+ for (; n >= iter * iter; iter += 2 - (iter == 2))
+ {
+ if (n % iter) continue;
+ result *= iter;
+ do
+ {
+ n /= iter;
+ } while (n % iter == 0);
+ }
+ return result * n;
+ }
+
+ @safe pure nothrow unittest
+ {
+ static assert(primeFactorsOnly(100) == 10);
+ //writeln(primeFactorsOnly(11));
+ static assert(primeFactorsOnly(11) == 11);
+ static assert(primeFactorsOnly(7 * 7 * 7 * 11 * 15 * 11) == 7 * 11 * 15);
+ static assert(primeFactorsOnly(129 * 2) == 129 * 2);
+ // enum x = primeFactorsOnly(7 * 7 * 7 * 11 * 15);
+ // static assert(x == 7 * 11 * 15);
+ }
+
+ private static bool properLinearCongruentialParameters(ulong m,
+ ulong a, ulong c) @safe pure nothrow @nogc
+ {
+ if (m == 0)
+ {
+ static if (is(UIntType == uint))
+ {
+ // Assume m is uint.max + 1
+ m = (1uL << 32);
+ }
+ else
+ {
+ return false;
+ }
+ }
+ // Bounds checking
+ if (a == 0 || a >= m || c >= m) return false;
+ // c and m are relatively prime
+ if (c > 0 && gcd(c, m) != 1) return false;
+ // a - 1 is divisible by all prime factors of m
+ if ((a - 1) % primeFactorsOnly(m)) return false;
+ // if a - 1 is multiple of 4, then m is a multiple of 4 too.
+ if ((a - 1) % 4 == 0 && m % 4) return false;
+ // Passed all tests
+ return true;
+ }
+
+ // check here
+ static assert(c == 0 || properLinearCongruentialParameters(m, a, c),
+ "Incorrect instantiation of LinearCongruentialEngine");
+
+/**
+Constructs a $(D_PARAM LinearCongruentialEngine) generator seeded with
+$(D x0).
+ */
+ this(UIntType x0) @safe pure
+ {
+ seed(x0);
+ }
+
+/**
+ (Re)seeds the generator.
+*/
+ void seed(UIntType x0 = 1) @safe pure
+ {
+ static if (c == 0)
+ {
+ import std.exception : enforce;
+ enforce(x0, "Invalid (zero) seed for "
+ ~ LinearCongruentialEngine.stringof);
+ }
+ _x = modulus ? (x0 % modulus) : x0;
+ popFront();
+ }
+
+/**
+ Advances the random sequence.
+*/
+ void popFront() @safe pure nothrow @nogc
+ {
+ static if (m)
+ {
+ static if (is(UIntType == uint) && m == uint.max)
+ {
+ immutable ulong
+ x = (cast(ulong) a * _x + c),
+ v = x >> 32,
+ w = x & uint.max;
+ immutable y = cast(uint)(v + w);
+ _x = (y < v || y == uint.max) ? (y + 1) : y;
+ }
+ else static if (is(UIntType == uint) && m == int.max)
+ {
+ immutable ulong
+ x = (cast(ulong) a * _x + c),
+ v = x >> 31,
+ w = x & int.max;
+ immutable uint y = cast(uint)(v + w);
+ _x = (y >= int.max) ? (y - int.max) : y;
+ }
+ else
+ {
+ _x = cast(UIntType) ((cast(ulong) a * _x + c) % m);
+ }
+ }
+ else
+ {
+ _x = a * _x + c;
+ }
+ }
+
+/**
+ Returns the current number in the random sequence.
+*/
+ @property UIntType front() const @safe pure nothrow @nogc
+ {
+ return _x;
+ }
+
+///
+ @property typeof(this) save() @safe pure nothrow @nogc
+ {
+ return this;
+ }
+
+/**
+Always $(D false) (random generators are infinite ranges).
+ */
+ enum bool empty = false;
+
+/**
+ Compares against $(D_PARAM rhs) for equality.
+ */
+ bool opEquals(ref const LinearCongruentialEngine rhs) const @safe pure nothrow @nogc
+ {
+ return _x == rhs._x;
+ }
+
+ private UIntType _x = m ? (a + c) % m : (a + c);
+}
+
+/**
+Define $(D_PARAM LinearCongruentialEngine) generators with well-chosen
+parameters. $(D MinstdRand0) implements Park and Miller's "minimal
+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.
+ */
+alias MinstdRand0 = LinearCongruentialEngine!(uint, 16_807, 0, 2_147_483_647);
+/// ditto
+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
+}
+
+@safe unittest
+{
+ import std.range;
+ static assert(isForwardRange!MinstdRand);
+ static assert(isUniformRNG!MinstdRand);
+ static assert(isUniformRNG!MinstdRand0);
+ static assert(isUniformRNG!(MinstdRand, uint));
+ static assert(isUniformRNG!(MinstdRand0, uint));
+ static assert(isSeedable!MinstdRand);
+ static assert(isSeedable!MinstdRand0);
+ static assert(isSeedable!(MinstdRand, uint));
+ static assert(isSeedable!(MinstdRand0, uint));
+
+ // The correct numbers are taken from The Database of Integer Sequences
+ // http://www.research.att.com/~njas/sequences/eisBTfry00128.txt
+ auto checking0 = [
+ 16807UL,282475249,1622650073,984943658,1144108930,470211272,
+ 101027544,1457850878,1458777923,2007237709,823564440,1115438165,
+ 1784484492,74243042,114807987,1137522503,1441282327,16531729,
+ 823378840,143542612 ];
+ //auto rnd0 = MinstdRand0(1);
+ MinstdRand0 rnd0;
+
+ foreach (e; checking0)
+ {
+ assert(rnd0.front == e);
+ rnd0.popFront();
+ }
+ // Test the 10000th invocation
+ // Correct value taken from:
+ // http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2007/n2461.pdf
+ rnd0.seed();
+ popFrontN(rnd0, 9999);
+ assert(rnd0.front == 1043618065);
+
+ // Test MinstdRand
+ auto checking = [48271UL,182605794,1291394886,1914720637,2078669041,
+ 407355683];
+ //auto rnd = MinstdRand(1);
+ MinstdRand rnd;
+ foreach (e; checking)
+ {
+ assert(rnd.front == e);
+ rnd.popFront();
+ }
+
+ // Test the 10000th invocation
+ // Correct value taken from:
+ // http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2007/n2461.pdf
+ rnd.seed();
+ popFrontN(rnd, 9999);
+ assert(rnd.front == 399268537);
+
+ // Check .save works
+ foreach (Type; std.meta.AliasSeq!(MinstdRand0, MinstdRand))
+ {
+ auto rnd1 = Type(unpredictableSeed);
+ auto rnd2 = rnd1.save;
+ assert(rnd1 == rnd2);
+ // Enable next test when RNGs are reference types
+ version (none) { assert(rnd1 !is rnd2); }
+ assert(rnd1.take(100).array() == rnd2.take(100).array());
+ }
+}
+
+/**
+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, UIntType d, size_t s,
+ UIntType b, size_t t,
+ 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;
+
+/**
+Parameters for the generator.
+*/
+ enum size_t wordSize = w;
+ enum size_t stateSize = n; /// ditto
+ enum size_t shiftSize = m; /// ditto
+ enum size_t maskBits = r; /// ditto
+ enum UIntType xorMask = a; /// 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);
+ // 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 @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^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 @nogc
+ {
+ 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)
+ {
+ mtState.data[$ - 1] &= this.max;
+ }
+
+ foreach_reverse (size_t i, ref e; mtState.data[0 .. $ - 1])
+ {
+ 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;
+ }
+ }
+
+ 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);
+ }
+
+/**
+ Seeds a MersenneTwisterEngine object using an InputRange.
+
+ 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.
+ */
+ 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())
+ {
+ sizediff_t idx = n - j - 1;
+ mtState.data[idx] = range.front;
+ }
+
+ mtState.index = n - 1;
+
+ if (range.empty && j < 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);
+ }
+
+ /* 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 @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
+ {
+ z ^= (z >> u) & d;
+ }
+
+ 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;
+
+ /* 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 const pure nothrow @nogc
+ {
+ return this.state.front;
+ }
+
+///
+ @property typeof(this) save() @safe pure nothrow @nogc
+ {
+ return this;
+ }
+
+/**
+Always $(D false).
+ */
+ enum bool empty = false;
+}
+
+/**
+A $(D MersenneTwisterEngine) instantiated with the parameters of the
+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.
+ */
+alias Mt19937 = MersenneTwisterEngine!(uint, 32, 624, 397, 31,
+ 0x9908b0df, 11, 0xffffffff, 7,
+ 0x9d2c5680, 15,
+ 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
+}
+
+@safe nothrow unittest
+{
+ import std.algorithm;
+ import std.range;
+ static assert(isUniformRNG!Mt19937);
+ static assert(isUniformRNG!(Mt19937, uint));
+ static assert(isSeedable!Mt19937);
+ 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
+}
+
+@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.algorithm;
+ import std.exception;
+ import std.range;
+
+ Mt19937 gen;
+
+ assertThrown(gen.seed(map!((a) => unpredictableSeed)(repeat(0, 623))));
+
+ gen.seed(map!((a) => unpredictableSeed)(repeat(0, 624)));
+ //infinite Range
+ gen.seed(map!((a) => unpredictableSeed)(repeat(0)));
+}
+
+@safe pure nothrow unittest
+{
+ uint a, b;
+ {
+ Mt19937 gen;
+ a = gen.front;
+ }
+ {
+ Mt19937 gen;
+ gen.popFront();
+ //popFrontN(gen, 1); // skip 1 element
+ b = gen.front;
+ }
+ assert(a != b);
+}
+
+@safe unittest
+{
+ import std.range;
+ // Check .save works
+ foreach (Type; std.meta.AliasSeq!(Mt19937, Mt19937_64))
+ {
+ auto gen1 = Type(unpredictableSeed);
+ auto gen2 = gen1.save;
+ assert(gen1 == gen2); // Danger, Will Robinson -- no opEquals for MT
+ // Enable next test when RNGs are reference types
+ version (none) { assert(gen1 !is gen2); }
+ assert(gen1.take(100).array() == gen2.take(100).array());
+ }
+}
+
+@safe pure nothrow unittest //11690
+{
+ alias MT(UIntType, uint w) = MersenneTwisterEngine!(UIntType, w, 624, 397, 31,
+ 0x9908b0df, 11, 0xffffffff, 7,
+ 0x9d2c5680, 15,
+ 0xefc60000, 18, 1812433253);
+
+ ulong[] expectedFirstValue = [3499211612uL, 3499211612uL,
+ 171143175841277uL, 1145028863177033374uL];
+
+ 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 $(HTTP www.jstatsoft.org/v08/i14/paper, Xorshift RNGs).
+ * Supporting bits are below, $(D bits) means second parameter of XorshiftEngine.
+ *
+ * $(BOOKTABLE ,
+ * $(TR $(TH bits) $(TH period))
+ * $(TR $(TD 32) $(TD 2^32 - 1))
+ * $(TR $(TD 64) $(TD 2^64 - 1))
+ * $(TR $(TD 96) $(TD 2^96 - 1))
+ * $(TR $(TD 128) $(TD 2^128 - 1))
+ * $(TR $(TD 160) $(TD 2^160 - 1))
+ * $(TR $(TD 192) $(TD 2^192 - 2^32))
+ * )
+ */
+struct XorshiftEngine(UIntType, UIntType bits, UIntType a, UIntType b, UIntType c)
+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. "
+ ~ to!string(bits) ~ " is not supported.");
+
+ public:
+ ///Mark this as a Rng
+ enum bool isUniformRandom = true;
+ /// Always $(D false) (random generators are infinite ranges).
+ enum empty = false;
+ /// Smallest generated value.
+ enum UIntType min = 0;
+ /// Largest generated value.
+ enum UIntType max = UIntType.max;
+
+
+ private:
+ enum size = bits / 32;
+
+ static if (bits == 32)
+ UIntType[size] seeds_ = [2_463_534_242];
+ else static if (bits == 64)
+ UIntType[size] seeds_ = [123_456_789, 362_436_069];
+ else static if (bits == 96)
+ UIntType[size] seeds_ = [123_456_789, 362_436_069, 521_288_629];
+ else static if (bits == 128)
+ UIntType[size] seeds_ = [123_456_789, 362_436_069, 521_288_629, 88_675_123];
+ else static if (bits == 160)
+ 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_ = [123_456_789, 362_436_069, 521_288_629, 88_675_123, 5_783_321, 6_615_241];
+ UIntType value_;
+ }
+ else
+ {
+ static assert(false, "Phobos Error: Xorshift has no instantiation rule for "
+ ~ to!string(bits) ~ " bits.");
+ }
+
+
+ public:
+ /**
+ * Constructs a $(D XorshiftEngine) generator seeded with $(D_PARAM x0).
+ */
+ this(UIntType x0) @safe pure nothrow @nogc
+ {
+ seed(x0);
+ }
+
+
+ /**
+ * (Re)seeds the generator.
+ */
+ void seed(UIntType x0) @safe pure nothrow @nogc
+ {
+ // Initialization routine from MersenneTwisterEngine.
+ foreach (i, e; seeds_)
+ seeds_[i] = x0 = cast(UIntType)(1_812_433_253U * (x0 ^ (x0 >> 30)) + i + 1);
+
+ // All seeds must not be 0.
+ sanitizeSeeds(seeds_);
+
+ popFront();
+ }
+
+
+ /**
+ * Returns the current number in the random sequence.
+ */
+ @property
+ UIntType front() const @safe pure nothrow @nogc
+ {
+ static if (bits == 192)
+ return value_;
+ else
+ return seeds_[size - 1];
+ }
+
+
+ /**
+ * Advances the random sequence.
+ */
+ void popFront() @safe pure nothrow @nogc
+ {
+ UIntType temp;
+
+ static if (bits == 32)
+ {
+ temp = seeds_[0] ^ (seeds_[0] << a);
+ temp = temp ^ (temp >> b);
+ seeds_[0] = temp ^ (temp << c);
+ }
+ else static if (bits == 64)
+ {
+ temp = seeds_[0] ^ (seeds_[0] << a);
+ seeds_[0] = seeds_[1];
+ seeds_[1] = seeds_[1] ^ (seeds_[1] >> c) ^ temp ^ (temp >> b);
+ }
+ else static if (bits == 96)
+ {
+ temp = seeds_[0] ^ (seeds_[0] << a);
+ seeds_[0] = seeds_[1];
+ seeds_[1] = seeds_[2];
+ seeds_[2] = seeds_[2] ^ (seeds_[2] >> c) ^ temp ^ (temp >> b);
+ }
+ else static if (bits == 128)
+ {
+ temp = seeds_[0] ^ (seeds_[0] << a);
+ seeds_[0] = seeds_[1];
+ seeds_[1] = seeds_[2];
+ seeds_[2] = seeds_[3];
+ seeds_[3] = seeds_[3] ^ (seeds_[3] >> c) ^ temp ^ (temp >> b);
+ }
+ else static if (bits == 160)
+ {
+ temp = seeds_[0] ^ (seeds_[0] << a);
+ seeds_[0] = seeds_[1];
+ seeds_[1] = seeds_[2];
+ seeds_[2] = seeds_[3];
+ seeds_[3] = seeds_[4];
+ seeds_[4] = seeds_[4] ^ (seeds_[4] >> c) ^ temp ^ (temp >> b);
+ }
+ else static if (bits == 192)
+ {
+ temp = seeds_[0] ^ (seeds_[0] >> a);
+ seeds_[0] = seeds_[1];
+ seeds_[1] = seeds_[2];
+ seeds_[2] = seeds_[3];
+ seeds_[3] = seeds_[4];
+ seeds_[4] = seeds_[4] ^ (seeds_[4] << c) ^ temp ^ (temp << b);
+ value_ = seeds_[4] + (seeds_[5] += 362_437);
+ }
+ else
+ {
+ static assert(false, "Phobos Error: Xorshift has no popFront() update for "
+ ~ to!string(bits) ~ " bits.");
+ }
+ }
+
+
+ /**
+ * Captures a range state.
+ */
+ @property
+ typeof(this) save() @safe pure nothrow @nogc
+ {
+ return this;
+ }
+
+
+ /**
+ * Compares against $(D_PARAM rhs) for equality.
+ */
+ bool opEquals(ref const XorshiftEngine rhs) const @safe pure nothrow @nogc
+ {
+ return seeds_ == rhs.seeds_;
+ }
+
+
+ private:
+ static void sanitizeSeeds(ref UIntType[size] seeds) @safe pure nothrow @nogc
+ {
+ for (uint i; i < seeds.length; i++)
+ {
+ if (seeds[i] == 0)
+ seeds[i] = i + 1;
+ }
+ }
+
+
+ @safe pure nothrow unittest
+ {
+ static if (size == 4) // Other bits too
+ {
+ UIntType[size] seeds = [1, 0, 0, 4];
+
+ sanitizeSeeds(seeds);
+
+ assert(seeds == [1, 2, 3, 4]);
+ }
+ }
+}
+
+
+/**
+ * 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.
+ */
+alias Xorshift32 = XorshiftEngine!(uint, 32, 13, 17, 15) ;
+alias Xorshift64 = XorshiftEngine!(uint, 64, 10, 13, 10); /// ditto
+alias Xorshift96 = XorshiftEngine!(uint, 96, 10, 5, 26); /// ditto
+alias Xorshift128 = XorshiftEngine!(uint, 128, 11, 8, 19); /// ditto
+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
+}
+
+@safe unittest
+{
+ import std.range;
+ static assert(isForwardRange!Xorshift);
+ static assert(isUniformRNG!Xorshift);
+ static assert(isUniformRNG!(Xorshift, uint));
+ static assert(isSeedable!Xorshift);
+ static assert(isSeedable!(Xorshift, uint));
+
+ // 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]
+ ];
+
+ alias XorshiftTypes = std.meta.AliasSeq!(Xorshift32, Xorshift64, Xorshift96, Xorshift128, Xorshift160, Xorshift192);
+
+ foreach (I, Type; XorshiftTypes)
+ {
+ Type rnd;
+
+ foreach (e; checking[I])
+ {
+ assert(rnd.front == e);
+ rnd.popFront();
+ }
+ }
+
+ // Check .save works
+ foreach (Type; XorshiftTypes)
+ {
+ auto rnd1 = Type(unpredictableSeed);
+ auto rnd2 = rnd1.save;
+ assert(rnd1 == rnd2);
+ // Enable next test when RNGs are reference types
+ version (none) { assert(rnd1 !is rnd2); }
+ assert(rnd1.take(100).array() == rnd2.take(100).array());
+ }
+}
+
+
+/* A complete list of all pseudo-random number generators implemented in
+ * 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.
+ */
+@safe unittest
+{
+ foreach (Rng; PseudoRngTypes)
+ {
+ static assert(isUniformRNG!Rng);
+ auto rng = Rng(unpredictableSeed);
+ }
+}
+
+
+/**
+A "good" seed for initializing random number engines. Initializing
+with $(D_PARAM unpredictableSeed) makes engines generate different
+random number sequences every run.
+
+Returns:
+A single unsigned integer seed value, different on each successive call
+*/
+@property uint unpredictableSeed() @trusted
+{
+ 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) MonoTime.currTime.ticks);
+ seeded = true;
+ }
+ rand.popFront();
+ return cast(uint) (MonoTime.currTime.ticks ^ rand.front);
+}
+
+///
+@safe unittest
+{
+ auto rnd = Random(unpredictableSeed);
+ auto n = rnd.front;
+ static assert(is(typeof(n) == uint));
+}
+
+/**
+The "default", "favorite", "suggested" random number generator type on
+the current platform. It is an alias for one of the previously-defined
+generators. You may want to use it if (1) you need to generate some
+nice random numbers, and (2) you don't care for the minutiae of the
+method being used.
+ */
+
+alias Random = Mt19937;
+
+@safe unittest
+{
+ static assert(isUniformRNG!Random);
+ static assert(isUniformRNG!(Random, uint));
+ static assert(isSeedable!Random);
+ static assert(isSeedable!(Random, uint));
+}
+
+/**
+Global random number generator used by various functions in this
+module whenever no generator is specified. It is allocated per-thread
+and initialized to an unpredictable value for each thread.
+
+Returns:
+A singleton instance of the default random number generator
+ */
+@property ref Random rndGen() @safe
+{
+ 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)))))
+ result.seed(map!((a) => unpredictableSeed)(repeat(0)));
+ else
+ result = Random(unpredictableSeed);
+ initialized = true;
+ }
+ return result;
+}
+
+/**
+Generates a number between $(D a) and $(D b). The $(D boundaries)
+parameter controls the shape of the interval (open vs. closed on
+either side). Valid values for $(D boundaries) are $(D "[]"), $(D
+"$(LPAREN)]"), $(D "[$(RPAREN)"), and $(D "()"). The default interval
+is closed to the left and open to the right. The version that does not
+take $(D urng) uses the default generator $(D rndGen).
+
+Params:
+ a = lower bound of the _uniform distribution
+ b = upper bound of the _uniform distribution
+ urng = (optional) random number generator to use;
+ if not specified, defaults to $(D rndGen)
+
+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
+ */
+auto uniform(string boundaries = "[)", T1, T2)
+(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;
+ foreach (i; 0 .. 20)
+ {
+ auto x = uniform(0.0, 15.0, gen);
+ assert(0 <= x && x < 15);
+ }
+ foreach (i; 0 .. 20)
+ {
+ auto x = uniform!"[]"('a', 'z', gen);
+ assert('a' <= x && x <= 'z');
+ }
+
+ foreach (i; 0 .. 20)
+ {
+ auto x = uniform('a', 'z', gen);
+ assert('a' <= x && x < 'z');
+ }
+
+ foreach (i; 0 .. 20)
+ {
+ immutable ubyte a = 0;
+ immutable ubyte b = 15;
+ auto x = uniform(a, b, gen);
+ assert(a <= x && x < b);
+ }
+}
+
+// Implementation of uniform for floating-point types
+/// ditto
+auto uniform(string boundaries = "[)",
+ T1, T2, UniformRandomNumberGenerator)
+(T1 a, T2 b, ref UniformRandomNumberGenerator urng)
+if (isFloatingPoint!(CommonType!(T1, T2)) && isUniformRNG!UniformRandomNumberGenerator)
+{
+ import std.conv : text;
+ import std.exception : enforce;
+ alias NumberType = Unqual!(CommonType!(T1, T2));
+ static if (boundaries[0] == '(')
+ {
+ import std.math : nextafter;
+ NumberType _a = nextafter(cast(NumberType) a, NumberType.infinity);
+ }
+ else
+ {
+ NumberType _a = a;
+ }
+ static if (boundaries[1] == ')')
+ {
+ import std.math : nextafter;
+ NumberType _b = nextafter(cast(NumberType) b, -NumberType.infinity);
+ }
+ else
+ {
+ NumberType _b = b;
+ }
+ enforce(_a <= _b,
+ text("std.random.uniform(): invalid bounding interval ",
+ boundaries[0], a, ", ", b, boundaries[1]));
+ NumberType result =
+ _a + (_b - _a) * cast(NumberType) (urng.front - urng.min)
+ / (urng.max - urng.min);
+ urng.popFront();
+ return result;
+}
+
+// Implementation of uniform for integral types
+/+ Description of algorithm and suggestion of correctness:
+
+The modulus operator maps an integer to a small, finite space. For instance, `x
+% 3` will map whatever x is into the range [0 .. 3). 0 maps to 0, 1 maps to 1, 2
+maps to 2, 3 maps to 0, and so on infinitely. As long as the integer is
+uniformly chosen from the infinite space of all non-negative integers then `x %
+3` will uniformly fall into that range.
+
+(Non-negative is important in this case because some definitions of modulus,
+namely the one used in computers generally, map negative numbers differently to
+(-3 .. 0]. `uniform` does not use negative number modulus, thus we can safely
+ignore that fact.)
+
+The issue with computers is that integers have a finite space they must fit in,
+and our uniformly chosen random number is picked in that finite space. So, that
+method is not sufficient. You can look at it as the integer space being divided
+into "buckets" and every bucket after the first bucket maps directly into that
+first bucket. `[0, 1, 2]`, `[3, 4, 5]`, ... When integers are finite, then the
+last bucket has the chance to be "incomplete": `[uint.max - 3, uint.max - 2,
+uint.max - 1]`, `[uint.max]` ... (the last bucket only has 1!). The issue here
+is that _every_ bucket maps _completely_ to the first bucket except for that
+last one. The last one doesn't have corresponding mappings to 1 or 2, in this
+case, which makes it unfair.
+
+So, the answer is to simply "reroll" if you're in that last bucket, since it's
+the only unfair one. Eventually you'll roll into a fair bucket. Simply, instead
+of the meaning of the last bucket being "maps to `[0]`", it changes to "maps to
+`[0, 1, 2]`", which is precisely what we want.
+
+To generalize, `upperDist` represents the size of our buckets (and, thus, the
+exclusive upper bound for our desired uniform number). `rnum` is a uniformly
+random number picked from the space of integers that a computer can hold (we'll
+say `UpperType` represents that type).
+
+We'll first try to do the mapping into the first bucket by doing `offset = rnum
+% upperDist`. We can figure out the position of the front of the bucket we're in
+by `bucketFront = rnum - offset`.
+
+If we start at `UpperType.max` and walk backwards `upperDist - 1` spaces, then
+the space we land on is the last acceptable position where a full bucket can
+fit:
+
+```
+ bucketFront UpperType.max
+ v v
+[..., 0, 1, 2, ..., upperDist - 1]
+ ^~~ upperDist - 1 ~~^
+```
+
+If the bucket starts any later, then it must have lost at least one number and
+at least that number won't be represented fairly.
+
+```
+ bucketFront UpperType.max
+ v v
+[..., upperDist - 1, 0, 1, 2, ..., upperDist - 2]
+ ^~~~~~~~ upperDist - 1 ~~~~~~~^
+```
+
+Hence, our condition to reroll is
+`bucketFront > (UpperType.max - (upperDist - 1))`
++/
+auto uniform(string boundaries = "[)", T1, T2, RandomGen)
+(T1 a, T2 b, ref RandomGen rng)
+if ((isIntegral!(CommonType!(T1, T2)) || isSomeChar!(CommonType!(T1, T2))) &&
+ isUniformRNG!RandomGen)
+{
+ import std.conv : text, unsigned;
+ import std.exception : enforce;
+ alias ResultType = Unqual!(CommonType!(T1, T2));
+ static if (boundaries[0] == '(')
+ {
+ enforce(a < ResultType.max,
+ text("std.random.uniform(): invalid left bound ", a));
+ ResultType lower = cast(ResultType) (a + 1);
+ }
+ else
+ {
+ ResultType lower = a;
+ }
+
+ static if (boundaries[1] == ']')
+ {
+ enforce(lower <= b,
+ text("std.random.uniform(): invalid bounding interval ",
+ boundaries[0], a, ", ", b, boundaries[1]));
+ /* Cannot use this next optimization with dchar, as dchar
+ * only partially uses its full bit range
+ */
+ static if (!is(ResultType == dchar))
+ {
+ if (b == ResultType.max && lower == ResultType.min)
+ {
+ // Special case - all bits are occupied
+ return std.random.uniform!ResultType(rng);
+ }
+ }
+ auto upperDist = unsigned(b - lower) + 1u;
+ }
+ else
+ {
+ enforce(lower < b,
+ text("std.random.uniform(): invalid bounding interval ",
+ boundaries[0], a, ", ", b, boundaries[1]));
+ auto upperDist = unsigned(b - lower);
+ }
+
+ assert(upperDist != 0);
+
+ alias UpperType = typeof(upperDist);
+ static assert(UpperType.min == 0);
+
+ UpperType offset, rnum, bucketFront;
+ do
+ {
+ rnum = uniform!UpperType(rng);
+ offset = rnum % upperDist;
+ bucketFront = rnum - offset;
+ } // while we're in an unfair bucket...
+ while (bucketFront > (UpperType.max - (upperDist - 1)));
+
+ return cast(ResultType)(lower + offset);
+}
+
+@safe unittest
+{
+ import std.conv : to;
+ auto gen = Mt19937(unpredictableSeed);
+ static assert(isForwardRange!(typeof(gen)));
+
+ auto a = uniform(0, 1024, gen);
+ assert(0 <= a && a <= 1024);
+ auto b = uniform(0.0f, 1.0f, gen);
+ assert(0 <= b && b < 1, to!string(b));
+ auto c = uniform(0.0, 1.0);
+ assert(0 <= c && c < 1);
+
+ foreach (T; std.meta.AliasSeq!(char, wchar, dchar, byte, ubyte, short, ushort,
+ int, uint, long, ulong, float, double, real))
+ {
+ T lo = 0, hi = 100;
+
+ // Try tests with each of the possible bounds
+ {
+ T init = uniform(lo, hi);
+ size_t i = 50;
+ while (--i && uniform(lo, hi) == init) {}
+ assert(i > 0);
+ }
+ {
+ T init = uniform!"[)"(lo, hi);
+ size_t i = 50;
+ while (--i && uniform(lo, hi) == init) {}
+ assert(i > 0);
+ }
+ {
+ T init = uniform!"(]"(lo, hi);
+ size_t i = 50;
+ while (--i && uniform(lo, hi) == init) {}
+ assert(i > 0);
+ }
+ {
+ T init = uniform!"()"(lo, hi);
+ size_t i = 50;
+ while (--i && uniform(lo, hi) == init) {}
+ assert(i > 0);
+ }
+ {
+ T init = uniform!"[]"(lo, hi);
+ size_t i = 50;
+ while (--i && uniform(lo, hi) == init) {}
+ assert(i > 0);
+ }
+
+ /* Test case with closed boundaries covering whole range
+ * of integral type
+ */
+ static if (isIntegral!T || isSomeChar!T)
+ {
+ foreach (immutable _; 0 .. 100)
+ {
+ auto u = uniform!"[]"(T.min, T.max);
+ static assert(is(typeof(u) == T));
+ assert(T.min <= u, "Lower bound violation for uniform!\"[]\" with " ~ T.stringof);
+ assert(u <= T.max, "Upper bound violation for uniform!\"[]\" with " ~ T.stringof);
+ }
+ }
+ }
+
+ auto reproRng = Xorshift(239842);
+
+ 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;
+ T init = uniform(lo, hi, reproRng);
+ size_t i = 50;
+ while (--i && uniform(lo, hi, reproRng) == init) {}
+ assert(i > 0);
+ }
+
+ {
+ bool sawLB = false, sawUB = false;
+ foreach (i; 0 .. 50)
+ {
+ auto x = uniform!"[]"('a', 'd', reproRng);
+ if (x == 'a') sawLB = true;
+ if (x == 'd') sawUB = true;
+ assert('a' <= x && x <= 'd');
+ }
+ assert(sawLB && sawUB);
+ }
+
+ {
+ bool sawLB = false, sawUB = false;
+ foreach (i; 0 .. 50)
+ {
+ auto x = uniform('a', 'd', reproRng);
+ if (x == 'a') sawLB = true;
+ if (x == 'c') sawUB = true;
+ assert('a' <= x && x < 'd');
+ }
+ assert(sawLB && sawUB);
+ }
+
+ {
+ bool sawLB = false, sawUB = false;
+ foreach (i; 0 .. 50)
+ {
+ immutable int lo = -2, hi = 2;
+ auto x = uniform!"()"(lo, hi, reproRng);
+ if (x == (lo+1)) sawLB = true;
+ if (x == (hi-1)) sawUB = true;
+ assert(lo < x && x < hi);
+ }
+ assert(sawLB && sawUB);
+ }
+
+ {
+ bool sawLB = false, sawUB = false;
+ foreach (i; 0 .. 50)
+ {
+ immutable ubyte lo = 0, hi = 5;
+ auto x = uniform(lo, hi, reproRng);
+ if (x == lo) sawLB = true;
+ if (x == (hi-1)) sawUB = true;
+ assert(lo <= x && x < hi);
+ }
+ assert(sawLB && sawUB);
+ }
+
+ {
+ foreach (i; 0 .. 30)
+ {
+ assert(i == uniform(i, i+1, reproRng));
+ }
+ }
+}
+
+/**
+Generates a uniformly-distributed number in the range $(D [T.min,
+T.max]) for any integral or character type $(D T). If no random
+number generator is passed, uses the default $(D rndGen).
+
+Params:
+ urng = (optional) random number generator to use;
+ if not specified, defaults to $(D rndGen)
+
+Returns:
+ Random variate drawn from the _uniform distribution across all
+ possible values of the integral or character type $(D T).
+ */
+auto uniform(T, UniformRandomNumberGenerator)
+(ref UniformRandomNumberGenerator urng)
+if (!is(T == enum) && (isIntegral!T || isSomeChar!T) && isUniformRNG!UniformRandomNumberGenerator)
+{
+ /* dchar does not use its full bit range, so we must
+ * revert to the uniform with specified bounds
+ */
+ static if (is(T == dchar))
+ {
+ return uniform!"[]"(T.min, T.max);
+ }
+ else
+ {
+ auto r = urng.front;
+ urng.popFront();
+ static if (T.sizeof <= r.sizeof)
+ {
+ return cast(T) r;
+ }
+ else
+ {
+ static assert(T.sizeof == 8 && r.sizeof == 4);
+ T r1 = urng.front | (cast(T) r << 32);
+ urng.popFront();
+ return r1;
+ }
+ }
+}
+
+/// Ditto
+auto uniform(T)()
+if (!is(T == enum) && (isIntegral!T || isSomeChar!T))
+{
+ return uniform!T(rndGen);
+}
+
+@safe unittest
+{
+ foreach (T; std.meta.AliasSeq!(char, wchar, dchar, byte, ubyte, short, ushort,
+ int, uint, long, ulong))
+ {
+ T init = uniform!T();
+ size_t i = 50;
+ while (--i && uniform!T() == init) {}
+ assert(i > 0);
+
+ foreach (immutable _; 0 .. 100)
+ {
+ auto u = uniform!T();
+ static assert(is(typeof(u) == T));
+ assert(T.min <= u, "Lower bound violation for uniform!" ~ T.stringof);
+ assert(u <= T.max, "Upper bound violation for uniform!" ~ T.stringof);
+ }
+ }
+}
+
+/**
+Returns a uniformly selected member of enum $(D E). If no random number
+generator is passed, uses the default $(D rndGen).
+
+Params:
+ urng = (optional) random number generator to use;
+ if not specified, defaults to $(D rndGen)
+
+Returns:
+ Random variate drawn with equal probability from any
+ of the possible values of the enum $(D E).
+ */
+auto uniform(E, UniformRandomNumberGenerator)
+(ref UniformRandomNumberGenerator urng)
+if (is(E == enum) && isUniformRNG!UniformRandomNumberGenerator)
+{
+ static immutable E[EnumMembers!E.length] members = [EnumMembers!E];
+ return members[std.random.uniform(0, members.length, urng)];
+}
+
+/// Ditto
+auto uniform(E)()
+if (is(E == enum))
+{
+ return uniform!E(rndGen);
+}
+
+///
+@safe unittest
+{
+ enum Fruit { apple, mango, pear }
+ auto randFruit = uniform!Fruit();
+}
+
+@safe unittest
+{
+ enum Fruit { Apple = 12, Mango = 29, Pear = 72 }
+ foreach (_; 0 .. 100)
+ {
+ foreach (f; [uniform!Fruit(), rndGen.uniform!Fruit()])
+ {
+ assert(f == Fruit.Apple || f == Fruit.Mango || f == Fruit.Pear);
+ }
+ }
+}
+
+/**
+ * Generates a uniformly-distributed floating point number of type
+ * $(D T) in the range [0, 1$(RPAREN). If no random number generator is
+ * specified, the default RNG $(D rndGen) will be used as the source
+ * of randomness.
+ *
+ * $(D uniform01) offers a faster generation of random variates than
+ * the equivalent $(D uniform!"[$(RPAREN)"(0.0, 1.0)) and so may be preferred
+ * for some applications.
+ *
+ * Params:
+ * 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
+ * distribution across the half-open interval [0, 1$(RPAREN).
+ *
+ */
+T uniform01(T = double)()
+if (isFloatingPoint!T)
+{
+ return uniform01!T(rndGen);
+}
+
+/// ditto
+T uniform01(T = double, UniformRNG)(ref UniformRNG rng)
+if (isFloatingPoint!T && isUniformRNG!UniformRNG)
+out (result)
+{
+ assert(0 <= result);
+ assert(result < 1);
+}
+body
+{
+ alias R = typeof(rng.front);
+ static if (isIntegral!R)
+ {
+ enum T factor = 1 / (T(1) + rng.max - rng.min);
+ }
+ else static if (isFloatingPoint!R)
+ {
+ enum T factor = 1 / (rng.max - rng.min);
+ }
+ else
+ {
+ static assert(false);
+ }
+
+ while (true)
+ {
+ immutable T u = (rng.front - rng.min) * factor;
+ rng.popFront();
+
+ import core.stdc.limits : CHAR_BIT; // CHAR_BIT is always 8
+ static if (isIntegral!R && T.mant_dig >= (CHAR_BIT * R.sizeof))
+ {
+ /* If RNG variates are integral and T has enough precision to hold
+ * R without loss, we're guaranteed by the definition of factor
+ * that precisely u < 1.
+ */
+ return u;
+ }
+ else
+ {
+ /* Otherwise we have to check whether u is beyond the assumed range
+ * because of the loss of precision, or for another reason, a
+ * floating-point RNG can return a variate that is exactly equal to
+ * its maximum.
+ */
+ if (u < 1)
+ {
+ return u;
+ }
+ }
+ }
+
+ // Shouldn't ever get here.
+ assert(false);
+}
+
+@safe unittest
+{
+ import std.meta;
+ foreach (UniformRNG; PseudoRngTypes)
+ {
+
+ foreach (T; std.meta.AliasSeq!(float, double, real))
+ (){ // avoid slow optimizations for large functions @@@BUG@@@ 2396
+ UniformRNG rng = UniformRNG(unpredictableSeed);
+
+ auto a = uniform01();
+ assert(is(typeof(a) == double));
+ assert(0 <= a && a < 1);
+
+ auto b = uniform01(rng);
+ assert(is(typeof(a) == double));
+ assert(0 <= b && b < 1);
+
+ auto c = uniform01!T();
+ assert(is(typeof(c) == T));
+ assert(0 <= c && c < 1);
+
+ auto d = uniform01!T(rng);
+ assert(is(typeof(d) == T));
+ assert(0 <= d && d < 1);
+
+ T init = uniform01!T(rng);
+ size_t i = 50;
+ while (--i && uniform01!T(rng) == init) {}
+ assert(i > 0);
+ assert(i < 50);
+ }();
+ }
+}
+
+/**
+Generates a uniform probability distribution of size $(D n), i.e., an
+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)
+{
+ import std.numeric : normalize;
+ useThis.length = n;
+ foreach (ref e; useThis)
+ {
+ e = uniform(0.0, 1);
+ }
+ normalize(useThis);
+ return useThis;
+}
+
+@safe unittest
+{
+ import std.algorithm;
+ import std.math;
+ static assert(is(CommonType!(double, int) == double));
+ auto a = uniformDistribution(5);
+ assert(a.length == 5);
+ assert(approxEqual(reduce!"a + b"(a), 1));
+ a = uniformDistribution(10, a);
+ assert(a.length == 10);
+ 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.iteration : map;
+ import std.algorithm.searching : canFind;
+
+ 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)
+will be used.
+
+Params:
+ r = random-access range whose elements are to be shuffled
+ gen = (optional) random number generator to use; if not
+ specified, defaults to $(D rndGen)
+ */
+
+void randomShuffle(Range, RandomGen)(Range r, ref RandomGen gen)
+if (isRandomAccessRange!Range && isUniformRNG!RandomGen)
+{
+ return partialShuffle!(Range, RandomGen)(r, r.length, gen);
+}
+
+/// ditto
+void randomShuffle(Range)(Range r)
+if (isRandomAccessRange!Range)
+{
+ return randomShuffle(r, rndGen);
+}
+
+@safe unittest
+{
+ import std.algorithm.sorting : sort;
+ foreach (RandomGen; PseudoRngTypes)
+ {
+ // Also tests partialShuffle indirectly.
+ auto a = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9];
+ auto b = a.dup;
+ auto gen = RandomGen(unpredictableSeed);
+ randomShuffle(a, gen);
+ sort(a);
+ assert(a == b);
+ randomShuffle(a);
+ sort(a);
+ assert(a == b);
+ }
+}
+
+/**
+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.
+
+$(D r) must be a random-access range with length. $(D n) must be less than
+or equal to $(D r.length). If no RNG is specified, $(D rndGen) will be used.
+
+Params:
+ r = random-access range whose elements are to be shuffled
+ n = number of elements of $(D r) to shuffle (counting from the beginning);
+ must be less than $(D r.length)
+ gen = (optional) random number generator to use; if not
+ specified, defaults to $(D rndGen)
+*/
+void partialShuffle(Range, RandomGen)(Range r, in size_t n, ref RandomGen gen)
+if (isRandomAccessRange!Range && isUniformRNG!RandomGen)
+{
+ import std.algorithm.mutation : swapAt;
+ import std.exception : enforce;
+ enforce(n <= r.length, "n must be <= r.length for partialShuffle.");
+ foreach (i; 0 .. n)
+ {
+ r.swapAt(i, uniform(i, r.length, gen));
+ }
+}
+
+/// ditto
+void partialShuffle(Range)(Range r, in size_t n)
+if (isRandomAccessRange!Range)
+{
+ return partialShuffle(r, n, rndGen);
+}
+
+@safe unittest
+{
+ import std.algorithm;
+ foreach (RandomGen; PseudoRngTypes)
+ {
+ auto a = [0, 1, 1, 2, 3];
+ auto b = a.dup;
+
+ // 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));
+ }
+}
+
+/**
+Rolls a dice with relative probabilities stored in $(D
+proportions). Returns the index in $(D proportions) that was chosen.
+
+Params:
+ rnd = (optional) random number generator to use; if not
+ specified, defaults to $(D rndGen)
+ proportions = forward range or list of individual values
+ whose elements correspond to the probabilities
+ with which to choose the corresponding index
+ value
+
+Returns:
+ Random variate drawn from the index values
+ [0, ... $(D proportions.length) - 1], with the probability
+ of getting an individual index value $(D i) being proportional to
+ $(D proportions[i]).
+*/
+size_t dice(Rng, Num)(ref Rng rnd, Num[] proportions...)
+if (isNumeric!Num && isForwardRange!Rng)
+{
+ return diceImpl(rnd, proportions);
+}
+
+/// Ditto
+size_t dice(R, Range)(ref R rnd, Range proportions)
+if (isForwardRange!Range && isNumeric!(ElementType!Range) && !isArray!Range)
+{
+ return diceImpl(rnd, proportions);
+}
+
+/// Ditto
+size_t dice(Range)(Range proportions)
+if (isForwardRange!Range && isNumeric!(ElementType!Range) && !isArray!Range)
+{
+ return diceImpl(rndGen, proportions);
+}
+
+/// Ditto
+size_t dice(Num)(Num[] proportions...)
+if (isNumeric!Num)
+{
+ return diceImpl(rndGen, proportions);
+}
+
+///
+@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.searching : all;
+ assert(proportions.save.all!"a >= 0");
+}
+body
+{
+ import std.algorithm.iteration : reduce;
+ import std.exception : enforce;
+ 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);
+ assert(point < sum);
+ auto mass = 0.0;
+
+ size_t i = 0;
+ foreach (e; proportions)
+ {
+ mass += e;
+ if (point < mass) return i;
+ i++;
+ }
+ // this point should not be reached
+ assert(false);
+}
+
+@safe unittest
+{
+ auto rnd = Random(unpredictableSeed);
+ auto i = dice(rnd, 0.0, 100.0);
+ assert(i == 1);
+ i = dice(rnd, 100.0, 0.0);
+ assert(i == 0);
+
+ i = dice(100U, 0U);
+ assert(i == 0);
+}
+
+/**
+Covers a given range $(D r) in a random manner, i.e. goes through each
+element of $(D r) once and only once, just in a random order. $(D r)
+must be a random-access range with length.
+
+If no random number generator is passed to $(D randomCover), the
+thread-global RNG rndGen will be used internally.
+
+Params:
+ r = random-access range to cover
+ rng = (optional) random number generator to use;
+ if not specified, defaults to $(D rndGen)
+
+Returns:
+ Range whose elements consist of the elements of $(D r),
+ in random order. Will be a forward range if both $(D r) and
+ $(D rng) are forward ranges, an input range otherwise.
+
+Example:
+----
+int[] a = [ 0, 1, 2, 3, 4, 5, 6, 7, 8 ];
+foreach (e; randomCover(a))
+{
+ writeln(e);
+}
+----
+
+$(B WARNING:) If an alternative RNG is desired, it is essential for this
+to be a $(I new) RNG seeded in an unpredictable manner. Passing it a RNG
+used elsewhere in the program will result in unintended correlations,
+due to the current implementation of RNGs as value types.
+
+Example:
+----
+int[] a = [ 0, 1, 2, 3, 4, 5, 6, 7, 8 ];
+foreach (e; randomCover(a, Random(unpredictableSeed))) // correct!
+{
+ writeln(e);
+}
+
+foreach (e; randomCover(a, rndGen)) // DANGEROUS!! rndGen gets copied by value
+{
+ writeln(e);
+}
+
+foreach (e; randomCover(a, rndGen)) // ... so this second random cover
+{ // will output the same sequence as
+ writeln(e); // the previous one.
+}
+----
+ */
+struct RandomCover(Range, 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))
+ {
+ this(Range input)
+ {
+ _input = input;
+ _chosen.length = _input.length;
+ if (_input.empty)
+ {
+ _isEmpty = true;
+ }
+ else
+ {
+ _current = uniform(0, _chosen.length);
+ }
+ }
+ }
+ else
+ {
+ private UniformRNG _rng;
+
+ this(Range input, ref UniformRNG rng)
+ {
+ _input = input;
+ _rng = rng;
+ _chosen.length = _input.length;
+ if (_input.empty)
+ {
+ _isEmpty = true;
+ }
+ else
+ {
+ _current = uniform(0, _chosen.length, rng);
+ }
+ }
+
+ this(Range input, UniformRNG rng)
+ {
+ this(input, rng);
+ }
+ }
+
+ static if (hasLength!Range)
+ {
+ @property size_t length()
+ {
+ return _input.length - _alreadyChosen;
+ }
+ }
+
+ @property auto ref front()
+ {
+ assert(!_isEmpty);
+ return _input[_current];
+ }
+
+ void popFront()
+ {
+ assert(!_isEmpty);
+
+ size_t k = _input.length - _alreadyChosen - 1;
+ if (k == 0)
+ {
+ _isEmpty = true;
+ ++_alreadyChosen;
+ return;
+ }
+
+ size_t i;
+ foreach (e; _input)
+ {
+ if (_chosen[i] || i == _current) { ++i; continue; }
+ // Roll a dice with k faces
+ static if (is(UniformRNG == void))
+ {
+ auto chooseMe = uniform(0, k) == 0;
+ }
+ else
+ {
+ auto chooseMe = uniform(0, k, _rng) == 0;
+ }
+ assert(k > 1 || chooseMe);
+ if (chooseMe)
+ {
+ _chosen[_current] = true;
+ _current = i;
+ ++_alreadyChosen;
+ return;
+ }
+ --k;
+ ++i;
+ }
+ }
+
+ static if (isForwardRange!UniformRNG)
+ {
+ @property typeof(this) save()
+ {
+ auto ret = this;
+ ret._input = _input.save;
+ ret._rng = _rng.save;
+ return ret;
+ }
+ }
+
+ @property bool empty() { return _isEmpty; }
+}
+
+/// Ditto
+auto randomCover(Range, UniformRNG)(Range r, auto ref UniformRNG rng)
+if (isRandomAccessRange!Range && isUniformRNG!UniformRNG)
+{
+ return RandomCover!(Range, UniformRNG)(r, rng);
+}
+
+/// Ditto
+auto randomCover(Range)(Range r)
+if (isRandomAccessRange!Range)
+{
+ return RandomCover!(Range, void)(r);
+}
+
+@safe unittest
+{
+ import std.algorithm;
+ import std.conv;
+ int[] a = [ 0, 1, 2, 3, 4, 5, 6, 7, 8 ];
+ int[] c;
+ foreach (UniformRNG; std.meta.AliasSeq!(void, PseudoRngTypes))
+ {
+ static if (is(UniformRNG == void))
+ {
+ auto rc = randomCover(a);
+ static assert(isInputRange!(typeof(rc)));
+ static assert(!isForwardRange!(typeof(rc)));
+ }
+ else
+ {
+ auto rng = UniformRNG(unpredictableSeed);
+ auto rc = randomCover(a, rng);
+ static assert(isForwardRange!(typeof(rc)));
+ // 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];
+ uint i;
+ foreach (e; rc)
+ {
+ //writeln(e);
+ b[i++] = e;
+ }
+ sort(b);
+ assert(a == b, text(b));
+ }
+}
+
+@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
+/**
+Selects a random subsample out of $(D r), containing exactly $(D n)
+elements. The order of elements is the same as in the original
+range. The total length of $(D r) must be known. If $(D total) is
+passed in, the total number of sample is considered to be $(D
+total). Otherwise, $(D RandomSample) uses $(D r.length).
+
+Params:
+ r = range to sample from
+ n = number of elements to include in the sample;
+ must be less than or equal to the total number
+ of elements in $(D r) and/or the parameter
+ $(D total) (if provided)
+ total = (semi-optional) number of elements of $(D r)
+ from which to select the sample (counting from
+ the beginning); must be less than or equal to
+ the total number of elements in $(D r) itself.
+ May be omitted if $(D r) has the $(D .length)
+ property and the sample is to be drawn from
+ all elements of $(D r).
+ rng = (optional) random number generator to use;
+ if not specified, defaults to $(D rndGen)
+
+Returns:
+ Range whose elements consist of a randomly selected subset of
+ the elements of $(D r), in the same order as these elements
+ appear in $(D r) itself. Will be a forward range if both $(D r)
+ and $(D rng) are forward ranges, an input range otherwise.
+
+$(D RandomSample) implements Jeffrey Scott Vitter's Algorithm D
+(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
+to this is if traversing k elements on the input range is itself
+an O(k) operation (e.g. when sampling lines from an input file),
+in which case the sampling calculation will inevitably be of
+O(total).
+
+RandomSample will throw an exception if $(D total) is verifiably
+less than the total number of elements available in the input,
+or if $(D n > total).
+
+If no random number generator is passed to $(D randomSample), the
+thread-global RNG rndGen will be used internally.
+
+Example:
+----
+int[] a = [ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 ];
+// Print 5 random elements picked off from a
+foreach (e; randomSample(a, 5))
+{
+ writeln(e);
+}
+----
+
+$(B WARNING:) If an alternative RNG is desired, it is essential for this
+to be a $(I new) RNG seeded in an unpredictable manner. Passing it a RNG
+used elsewhere in the program will result in unintended correlations,
+due to the current implementation of RNGs as value types.
+
+Example:
+----
+int[] a = [ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 ];
+foreach (e; randomSample(a, 5, Random(unpredictableSeed))) // correct!
+{
+ writeln(e);
+}
+
+foreach (e; randomSample(a, 5, rndGen)) // DANGEROUS!! rndGen gets
+{ // copied by value
+ writeln(e);
+}
+
+foreach (e; randomSample(a, 5, rndGen)) // ... so this second random
+{ // sample will select the same
+ writeln(e); // values as the previous one.
+}
+----
+*/
+struct RandomSample(Range, UniformRNG = void)
+if (isInputRange!Range && (isUniformRNG!UniformRNG || is(UniformRNG == void)))
+{
+ private size_t _available, _toSelect;
+ private enum ushort _alphaInverse = 13; // Vitter's recommended value.
+ private double _Vprime;
+ private Range _input;
+ private size_t _index;
+ private enum Skip { None, A, D }
+ private Skip _skip = Skip.None;
+
+ // If we're using the default thread-local random number generator then
+ // we shouldn't store a copy of it here. UniformRNG == void is a sentinel
+ // for this. If we're using a user-specified generator then we have no
+ // choice but to store a copy.
+ static if (is(UniformRNG == void))
+ {
+ static if (hasLength!Range)
+ {
+ this(Range input, size_t howMany)
+ {
+ _input = input;
+ initialize(howMany, input.length);
+ }
+ }
+
+ this(Range input, size_t howMany, size_t total)
+ {
+ _input = input;
+ initialize(howMany, total);
+ }
+ }
+ else
+ {
+ UniformRNG _rng;
+
+ static if (hasLength!Range)
+ {
+ this(Range input, size_t howMany, ref UniformRNG rng)
+ {
+ _rng = rng;
+ _input = input;
+ initialize(howMany, input.length);
+ }
+
+ this(Range input, size_t howMany, UniformRNG rng)
+ {
+ this(input, howMany, rng);
+ }
+ }
+
+ this(Range input, size_t howMany, size_t total, ref UniformRNG rng)
+ {
+ _rng = rng;
+ _input = input;
+ initialize(howMany, total);
+ }
+
+ this(Range input, size_t howMany, size_t total, UniformRNG rng)
+ {
+ this(input, howMany, total, rng);
+ }
+ }
+
+ private void initialize(size_t howMany, size_t total)
+ {
+ import std.conv : text;
+ import std.exception : enforce;
+ _available = total;
+ _toSelect = howMany;
+ enforce(_toSelect <= _available,
+ text("RandomSample: cannot sample ", _toSelect,
+ " items when only ", _available, " are available"));
+ static if (hasLength!Range)
+ {
+ enforce(_available <= _input.length,
+ text("RandomSample: specified ", _available,
+ " items as available when input contains only ",
+ _input.length));
+ }
+ }
+
+ private void initializeFront()
+ {
+ assert(_skip == Skip.None);
+ // We can save ourselves a random variate by checking right
+ // at the beginning if we should use Algorithm A.
+ if ((_alphaInverse * _toSelect) > _available)
+ {
+ _skip = Skip.A;
+ }
+ else
+ {
+ _skip = Skip.D;
+ _Vprime = newVprime(_toSelect);
+ }
+ prime();
+ }
+
+/**
+ Range primitives.
+*/
+ @property bool empty() const
+ {
+ return _toSelect == 0;
+ }
+
+/// Ditto
+ @property auto ref front()
+ {
+ assert(!empty);
+ // The first sample point must be determined here to avoid
+ // having it always correspond to the first element of the
+ // input. The rest of the sample points are determined each
+ // time we call popFront().
+ if (_skip == Skip.None)
+ {
+ initializeFront();
+ }
+ return _input.front;
+ }
+
+/// Ditto
+ void popFront()
+ {
+ // First we need to check if the sample has
+ // been initialized in the first place.
+ if (_skip == Skip.None)
+ {
+ initializeFront();
+ }
+
+ _input.popFront();
+ --_available;
+ --_toSelect;
+ ++_index;
+ prime();
+ }
+
+/// Ditto
+ static if (isForwardRange!Range && isForwardRange!UniformRNG)
+ {
+ @property typeof(this) save()
+ {
+ auto ret = this;
+ ret._input = _input.save;
+ ret._rng = _rng.save;
+ return ret;
+ }
+ }
+
+/// Ditto
+ @property size_t length()
+ {
+ return _toSelect;
+ }
+
+/**
+Returns the index of the visited record.
+ */
+ @property size_t index()
+ {
+ if (_skip == Skip.None)
+ {
+ initializeFront();
+ }
+ return _index;
+ }
+
+ private size_t skip()
+ {
+ assert(_skip != Skip.None);
+
+ // Step D1: if the number of points still to select is greater
+ // than a certain proportion of the remaining data points, i.e.
+ // if n >= alpha * N where alpha = 1/13, we carry out the
+ // sampling with Algorithm A.
+ if (_skip == Skip.A)
+ {
+ return skipA();
+ }
+ else if ((_alphaInverse * _toSelect) > _available)
+ {
+ // We shouldn't get here unless the current selected
+ // algorithm is D.
+ assert(_skip == Skip.D);
+ _skip = Skip.A;
+ return skipA();
+ }
+ else
+ {
+ assert(_skip == Skip.D);
+ return skipD();
+ }
+ }
+
+/*
+Vitter's Algorithm A, used when the ratio of needed sample values
+to remaining data values is sufficiently large.
+*/
+ private size_t skipA()
+ {
+ size_t s;
+ double v, quot, top;
+
+ if (_toSelect == 1)
+ {
+ static if (is(UniformRNG == void))
+ {
+ s = uniform(0, _available);
+ }
+ else
+ {
+ s = uniform(0, _available, _rng);
+ }
+ }
+ else
+ {
+ v = 0;
+ top = _available - _toSelect;
+ quot = top / _available;
+
+ static if (is(UniformRNG == void))
+ {
+ v = uniform!"()"(0.0, 1.0);
+ }
+ else
+ {
+ v = uniform!"()"(0.0, 1.0, _rng);
+ }
+
+ while (quot > v)
+ {
+ ++s;
+ quot *= (top - s) / (_available - s);
+ }
+ }
+
+ return s;
+ }
+
+/*
+Randomly reset the value of _Vprime.
+*/
+ private double newVprime(size_t remaining)
+ {
+ static if (is(UniformRNG == void))
+ {
+ double r = uniform!"()"(0.0, 1.0);
+ }
+ else
+ {
+ double r = uniform!"()"(0.0, 1.0, _rng);
+ }
+
+ return r ^^ (1.0 / remaining);
+ }
+
+/*
+Vitter's Algorithm D. For an extensive description of the algorithm
+and its rationale, see:
+
+ * Vitter, J.S. (1984), "Faster methods for random sampling",
+ Commun. ACM 27(7): 703--718
+
+ * Vitter, J.S. (1987) "An efficient algorithm for sequential random
+ sampling", ACM Trans. Math. Softw. 13(1): 58-67.
+
+Variable names are chosen to match those in Vitter's paper.
+*/
+ private size_t skipD()
+ {
+ import std.math : isNaN, trunc;
+ // Confirm that the check in Step D1 is valid and we
+ // haven't been sent here by mistake
+ assert((_alphaInverse * _toSelect) <= _available);
+
+ // Now it's safe to use the standard Algorithm D mechanism.
+ if (_toSelect > 1)
+ {
+ size_t s;
+ size_t qu1 = 1 + _available - _toSelect;
+ double x, y1;
+
+ assert(!_Vprime.isNaN());
+
+ while (true)
+ {
+ // Step D2: set values of x and u.
+ while (1)
+ {
+ x = _available * (1-_Vprime);
+ s = cast(size_t) trunc(x);
+ if (s < qu1)
+ break;
+ _Vprime = newVprime(_toSelect);
+ }
+
+ static if (is(UniformRNG == void))
+ {
+ double u = uniform!"()"(0.0, 1.0);
+ }
+ else
+ {
+ double u = uniform!"()"(0.0, 1.0, _rng);
+ }
+
+ y1 = (u * (cast(double) _available) / qu1) ^^ (1.0/(_toSelect - 1));
+
+ _Vprime = y1 * ((-x/_available)+1.0) * ( qu1/( (cast(double) qu1) - s ) );
+
+ // Step D3: if _Vprime <= 1.0 our work is done and we return S.
+ // Otherwise ...
+ if (_Vprime > 1.0)
+ {
+ size_t top = _available - 1, limit;
+ double y2 = 1.0, bottom;
+
+ if (_toSelect > (s+1))
+ {
+ bottom = _available - _toSelect;
+ limit = _available - s;
+ }
+ else
+ {
+ bottom = _available - (s+1);
+ limit = qu1;
+ }
+
+ foreach (size_t t; limit .. _available)
+ {
+ y2 *= top/bottom;
+ top--;
+ bottom--;
+ }
+
+ // Step D4: decide whether or not to accept the current value of S.
+ if (_available/(_available-x) < y1 * (y2 ^^ (1.0/(_toSelect-1))))
+ {
+ // If it's not acceptable, we generate a new value of _Vprime
+ // and go back to the start of the for (;;) loop.
+ _Vprime = newVprime(_toSelect);
+ }
+ else
+ {
+ // If it's acceptable we generate a new value of _Vprime
+ // based on the remaining number of sample points needed,
+ // and return S.
+ _Vprime = newVprime(_toSelect-1);
+ return s;
+ }
+ }
+ else
+ {
+ // Return if condition D3 satisfied.
+ return s;
+ }
+ }
+ }
+ else
+ {
+ // If only one sample point remains to be taken ...
+ return cast(size_t) trunc(_available * _Vprime);
+ }
+ }
+
+ private void prime()
+ {
+ if (empty)
+ {
+ return;
+ }
+ assert(_available && _available >= _toSelect);
+ immutable size_t s = skip();
+ assert(s + _toSelect <= _available);
+ static if (hasLength!Range)
+ {
+ assert(s + _toSelect <= _input.length);
+ }
+ assert(!_input.empty);
+ _input.popFrontExactly(s);
+ _index += s;
+ _available -= s;
+ assert(_available > 0);
+ }
+}
+
+/// Ditto
+auto randomSample(Range)(Range r, size_t n, size_t total)
+if (isInputRange!Range)
+{
+ return RandomSample!(Range, void)(r, n, total);
+}
+
+/// Ditto
+auto randomSample(Range)(Range r, size_t n)
+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)
+{
+ 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)
+{
+ return RandomSample!(Range, UniformRNG)(r, n, r.length, rng);
+}
+
+@system unittest
+{
+ // @system because it takes the address of a local
+ import std.conv : text;
+ import std.exception;
+ import std.range;
+ // For test purposes, an infinite input range
+ struct TestInputRange
+ {
+ private auto r = recurrence!"a[n-1] + 1"(0);
+ bool empty() @property const pure nothrow { return r.empty; }
+ auto front() @property pure nothrow { return r.front; }
+ void popFront() pure nothrow { r.popFront(); }
+ }
+ static assert(isInputRange!TestInputRange);
+ static assert(!isForwardRange!TestInputRange);
+
+ int[] a = [ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 ];
+
+ foreach (UniformRNG; PseudoRngTypes)
+ {
+ auto rng = UniformRNG(1234);
+ /* First test the most general case: randomSample of input range, with and
+ * without a specified random number generator.
+ */
+ static assert(isInputRange!(typeof(randomSample(TestInputRange(), 5, 10))));
+ static assert(isInputRange!(typeof(randomSample(TestInputRange(), 5, 10, rng))));
+ static assert(!isForwardRange!(typeof(randomSample(TestInputRange(), 5, 10))));
+ static assert(!isForwardRange!(typeof(randomSample(TestInputRange(), 5, 10, rng))));
+ // test case with range initialized by direct call to struct
+ {
+ auto sample =
+ RandomSample!(TestInputRange, UniformRNG)
+ (TestInputRange(), 5, 10, UniformRNG(unpredictableSeed));
+ static assert(isInputRange!(typeof(sample)));
+ static assert(!isForwardRange!(typeof(sample)));
+ }
+
+ /* Now test the case of an input range with length. We ignore the cases
+ * already covered by the previous tests.
+ */
+ static assert(isInputRange!(typeof(randomSample(TestInputRange().takeExactly(10), 5))));
+ static assert(isInputRange!(typeof(randomSample(TestInputRange().takeExactly(10), 5, rng))));
+ static assert(!isForwardRange!(typeof(randomSample(TestInputRange().takeExactly(10), 5))));
+ static assert(!isForwardRange!(typeof(randomSample(TestInputRange().takeExactly(10), 5, rng))));
+ // test case with range initialized by direct call to struct
+ {
+ auto sample =
+ RandomSample!(typeof(TestInputRange().takeExactly(10)), UniformRNG)
+ (TestInputRange().takeExactly(10), 5, 10, UniformRNG(unpredictableSeed));
+ static assert(isInputRange!(typeof(sample)));
+ static assert(!isForwardRange!(typeof(sample)));
+ }
+
+ // Now test the case of providing a forward range as input.
+ static assert(!isForwardRange!(typeof(randomSample(a, 5))));
+ static if (isForwardRange!UniformRNG)
+ {
+ static assert(isForwardRange!(typeof(randomSample(a, 5, rng))));
+ // ... and test with range initialized directly
+ {
+ auto sample =
+ RandomSample!(int[], UniformRNG)
+ (a, 5, UniformRNG(unpredictableSeed));
+ static assert(isForwardRange!(typeof(sample)));
+ }
+ }
+ else
+ {
+ static assert(isInputRange!(typeof(randomSample(a, 5, rng))));
+ static assert(!isForwardRange!(typeof(randomSample(a, 5, rng))));
+ // ... and test with range initialized directly
+ {
+ auto sample =
+ RandomSample!(int[], UniformRNG)
+ (a, 5, UniformRNG(unpredictableSeed));
+ static assert(isInputRange!(typeof(sample)));
+ static assert(!isForwardRange!(typeof(sample)));
+ }
+ }
+
+ /* 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");
+
+ /* Check that sampling algorithm never accidentally overruns the end of
+ * the input range. If input is an InputRange without .length, this
+ * relies on the user specifying the total number of available items
+ * correctly.
+ */
+ {
+ uint i = 0;
+ foreach (e; randomSample(a, a.length))
+ {
+ assert(e == i);
+ ++i;
+ }
+ assert(i == a.length);
+
+ i = 0;
+ foreach (e; randomSample(TestInputRange(), 17, 17))
+ {
+ assert(e == i);
+ ++i;
+ }
+ assert(i == 17);
+ }
+
+
+ // Check length properties of random samples.
+ assert(randomSample(a, 5).length == 5);
+ assert(randomSample(a, 5, 10).length == 5);
+ assert(randomSample(a, 5, rng).length == 5);
+ assert(randomSample(a, 5, 10, rng).length == 5);
+ assert(randomSample(TestInputRange(), 5, 10).length == 5);
+ assert(randomSample(TestInputRange(), 5, 10, rng).length == 5);
+
+ // ... and emptiness!
+ assert(randomSample(a, 0).empty);
+ assert(randomSample(a, 0, 5).empty);
+ assert(randomSample(a, 0, rng).empty);
+ assert(randomSample(a, 0, 5, rng).empty);
+ assert(randomSample(TestInputRange(), 0, 10).empty);
+ assert(randomSample(TestInputRange(), 0, 10, rng).empty);
+
+ /* Test that the (lazy) evaluation of random samples works correctly.
+ *
+ * We cover 2 different cases: a sample where the ratio of sample points
+ * to total points is greater than the threshold for using Algorithm, and
+ * one where the ratio is small enough (< 1/13) for Algorithm D to be used.
+ *
+ * For each, we also cover the case with and without a specified RNG.
+ */
+ {
+ // Small sample/source ratio, no specified RNG.
+ uint i = 0;
+ foreach (e; randomSample(randomCover(a), 5))
+ {
+ ++i;
+ }
+ assert(i == 5);
+
+ // Small sample/source ratio, specified RNG.
+ i = 0;
+ foreach (e; randomSample(randomCover(a), 5, rng))
+ {
+ ++i;
+ }
+ assert(i == 5);
+
+ // Large sample/source ratio, no specified RNG.
+ i = 0;
+ foreach (e; randomSample(TestInputRange(), 123, 123_456))
+ {
+ ++i;
+ }
+ assert(i == 123);
+
+ // Large sample/source ratio, specified RNG.
+ i = 0;
+ foreach (e; randomSample(TestInputRange(), 123, 123_456, rng))
+ {
+ ++i;
+ }
+ assert(i == 123);
+
+ /* Sample/source ratio large enough to start with Algorithm D,
+ * small enough to switch to Algorithm A.
+ */
+ i = 0;
+ foreach (e; randomSample(TestInputRange(), 10, 131))
+ {
+ ++i;
+ }
+ assert(i == 10);
+ }
+
+ // Test that the .index property works correctly
+ {
+ auto sample1 = randomSample(TestInputRange(), 654, 654_321);
+ for (; !sample1.empty; sample1.popFront())
+ {
+ assert(sample1.front == sample1.index);
+ }
+
+ auto sample2 = randomSample(TestInputRange(), 654, 654_321, rng);
+ for (; !sample2.empty; sample2.popFront())
+ {
+ assert(sample2.front == sample2.index);
+ }
+
+ /* Check that it also works if .index is called before .front.
+ * See: http://d.puremagic.com/issues/show_bug.cgi?id=10322
+ */
+ auto sample3 = randomSample(TestInputRange(), 654, 654_321);
+ for (; !sample3.empty; sample3.popFront())
+ {
+ assert(sample3.index == sample3.front);
+ }
+
+ auto sample4 = randomSample(TestInputRange(), 654, 654_321, rng);
+ for (; !sample4.empty; sample4.popFront())
+ {
+ assert(sample4.index == sample4.front);
+ }
+ }
+
+ /* Test behaviour if .popFront() is called before sample is read.
+ * 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
+ * copy of the popFronted (and hence initialized) sample.
+ */
+ {
+ size_t count0, count1, count99;
+ foreach (_; 0 .. 100_000)
+ {
+ auto sample = randomSample(iota(100), 5, &rng);
+ sample.popFront();
+ foreach (s; sample)
+ {
+ if (s == 0)
+ {
+ ++count0;
+ }
+ else if (s == 1)
+ {
+ ++count1;
+ }
+ else if (s == 99)
+ {
+ ++count99;
+ }
+ }
+ }
+ /* Statistical assumptions here: this is a sequential sampling process
+ * so (i) 0 can only be the first sample point, so _can't_ be in the
+ * remainder of the sample after .popFront() is called. (ii) By similar
+ * token, 1 can only be in the remainder if it's the 2nd point of the
+ * whole sample, and hence if 0 was the first; probability of 0 being
+ * first and 1 second is 5/100 * 4/99 (thank you, Algorithm S:-) and
+ * so the mean count of 1 should be about 202. Finally, 99 can only
+ * be the _last_ sample point to be picked, so its probability of
+ * inclusion should be independent of the .popFront() and it should
+ * occur with frequency 5/100, hence its count should be about 5000.
+ * Unfortunately we have to set quite a high tolerance because with
+ * sample size small enough for unittests to run in reasonable time,
+ * the variance can be quite high.
+ */
+ assert(count0 == 0);
+ assert(count1 < 300, text("1: ", count1, " > 300."));
+ assert(4_700 < count99, text("99: ", count99, " < 4700."));
+ assert(count99 < 5_300, text("99: ", count99, " > 5300."));
+ }
+
+ /* Odd corner-cases: RandomSample has 2 constructors that are not called
+ * by the randomSample() helper functions, but that can be used if the
+ * constructor is called directly. These cover the case of the user
+ * specifying input but not input length.
+ */
+ {
+ auto input1 = TestInputRange().takeExactly(456_789);
+ static assert(hasLength!(typeof(input1)));
+ auto sample1 = RandomSample!(typeof(input1), void)(input1, 789);
+ static assert(isInputRange!(typeof(sample1)));
+ static assert(!isForwardRange!(typeof(sample1)));
+ assert(sample1.length == 789);
+ assert(sample1._available == 456_789);
+ uint i = 0;
+ for (; !sample1.empty; sample1.popFront())
+ {
+ assert(sample1.front == sample1.index);
+ ++i;
+ }
+ assert(i == 789);
+
+ auto input2 = TestInputRange().takeExactly(456_789);
+ static assert(hasLength!(typeof(input2)));
+ auto sample2 = RandomSample!(typeof(input2), typeof(rng))(input2, 789, rng);
+ static assert(isInputRange!(typeof(sample2)));
+ static assert(!isForwardRange!(typeof(sample2)));
+ assert(sample2.length == 789);
+ assert(sample2._available == 456_789);
+ i = 0;
+ for (; !sample2.empty; sample2.popFront())
+ {
+ assert(sample2.front == sample2.index);
+ ++i;
+ }
+ assert(i == 789);
+ }
+
+ /* Test that the save property works where input is a forward range,
+ * and RandomSample is using a (forward range) random number generator
+ * that is not rndGen.
+ */
+ static if (isForwardRange!UniformRNG)
+ {
+ auto sample1 = randomSample(a, 5, rng);
+ auto sample2 = sample1.save;
+ assert(sample1.array() == sample2.array());
+ }
+
+ // Bugzilla 8314
+ {
+ auto sample(RandomGen)(uint seed) { return randomSample(a, 1, RandomGen(seed)).front; }
+
+ // Start from 1 because not all RNGs accept 0 as seed.
+ immutable fst = sample!UniformRNG(1);
+ uint n = 1;
+ while (sample!UniformRNG(++n) == fst && n < n.max) {}
+ assert(n < n.max);
+ }
+ }
+}
diff --git a/libphobos/src/std/range/interfaces.d b/libphobos/src/std/range/interfaces.d
new file mode 100644
index 0000000..7207776
--- /dev/null
+++ b/libphobos/src/std/range/interfaces.d
@@ -0,0 +1,567 @@
+/**
+This module is a submodule of $(MREF std, range).
+
+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 $(MREF std, range) templates.
+
+$(SCRIPT inhibitQuickIndex = 1;)
+$(BOOKTABLE ,
+ $(TR $(TD $(LREF InputRange))
+ $(TD Wrapper for input ranges.
+ ))
+ $(TR $(TD $(LREF InputAssignable))
+ $(TD Wrapper for input ranges with assignable elements.
+ ))
+ $(TR $(TD $(LREF ForwardRange))
+ $(TD Wrapper for forward ranges.
+ ))
+ $(TR $(TD $(LREF ForwardAssignable))
+ $(TD Wrapper for forward ranges with assignable elements.
+ ))
+ $(TR $(TD $(LREF BidirectionalRange))
+ $(TD Wrapper for bidirectional ranges.
+ ))
+ $(TR $(TD $(LREF BidirectionalAssignable))
+ $(TD Wrapper for bidirectional ranges with assignable elements.
+ ))
+ $(TR $(TD $(LREF RandomAccessFinite))
+ $(TD Wrapper for finite random-access ranges.
+ ))
+ $(TR $(TD $(LREF RandomAccessAssignable))
+ $(TD Wrapper for finite random-access ranges with assignable elements.
+ ))
+ $(TR $(TD $(LREF RandomAccessInfinite))
+ $(TD Wrapper for infinite random-access ranges.
+ ))
+ $(TR $(TD $(LREF OutputRange))
+ $(TD Wrapper for output ranges.
+ ))
+ $(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 $(LREF InputRangeObject))
+ $(TD Class that implements the $(D InputRange) interface and wraps the
+ input _range methods in virtual functions.
+ ))
+ $(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)
+
+License: $(HTTP boost.org/LICENSE_1_0.txt, Boost License 1.0).
+
+Authors: $(HTTP erdani.com, Andrei Alexandrescu), David Simcha,
+and Jonathan M Davis. Credit for some of the ideas in building this module goes
+to $(HTTP fantascienza.net/leonardo/so/, Leonardo Maffi).
+*/
+module std.range.interfaces;
+
+import std.meta;
+import std.range.primitives;
+import std.traits;
+
+/**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
+ * $(REF_ALTTEXT isInputRange, isInputRange, std, range, primitives)
+ * and friends check for conformance to structural interfaces
+ * not for implementation of these $(D interface) types.
+ *
+ * Limitations:
+ *
+ * These interfaces are not capable of forwarding $(D ref) access to elements.
+ *
+ * Infiniteness of the wrapped range is not propagated.
+ *
+ * Length is not propagated in the case of non-random access ranges.
+ *
+ * See_Also:
+ * $(LREF inputRangeObject)
+ */
+interface InputRange(E) {
+ ///
+ @property E front();
+
+ ///
+ E moveFront();
+
+ ///
+ void popFront();
+
+ ///
+ @property bool empty();
+
+ /* Measurements of the benefits of using opApply instead of range primitives
+ * for foreach, using timings for iterating over an iota(100_000_000) range
+ * with an empty loop body, using the same hardware in each case:
+ *
+ * Bare Iota struct, range primitives: 278 milliseconds
+ * InputRangeObject, opApply: 436 milliseconds (1.57x penalty)
+ * InputRangeObject, range primitives: 877 milliseconds (3.15x penalty)
+ */
+
+ /**$(D foreach) iteration uses opApply, since one delegate call per loop
+ * iteration is faster than three virtual function calls.
+ */
+ int opApply(scope int delegate(E));
+
+ /// Ditto
+ int opApply(scope int delegate(size_t, E));
+
+}
+
+///
+@safe unittest
+{
+ import std.algorithm.iteration : map;
+ import std.range : iota;
+
+ void useRange(InputRange!int range) {
+ // Function body.
+ }
+
+ // Create a range type.
+ auto squares = map!"a * a"(iota(10));
+
+ // Wrap it in an interface.
+ auto squaresWrapped = inputRangeObject(squares);
+
+ // Use it.
+ useRange(squaresWrapped);
+}
+
+/**Interface for a forward range of type $(D E).*/
+interface ForwardRange(E) : InputRange!E {
+ ///
+ @property ForwardRange!E save();
+}
+
+/**Interface for a bidirectional range of type $(D E).*/
+interface BidirectionalRange(E) : ForwardRange!(E) {
+ ///
+ @property BidirectionalRange!E save();
+
+ ///
+ @property E back();
+
+ ///
+ E moveBack();
+
+ ///
+ void popBack();
+}
+
+/**Interface for a finite random access range of type $(D E).*/
+interface RandomAccessFinite(E) : BidirectionalRange!(E) {
+ ///
+ @property RandomAccessFinite!E save();
+
+ ///
+ E opIndex(size_t);
+
+ ///
+ E moveAt(size_t);
+
+ ///
+ @property size_t length();
+
+ ///
+ alias opDollar = length;
+
+ // Can't support slicing until issues with requiring slicing for all
+ // finite random access ranges are fully resolved.
+ version (none)
+ {
+ ///
+ RandomAccessFinite!E opSlice(size_t, size_t);
+ }
+}
+
+/**Interface for an infinite random access range of type $(D E).*/
+interface RandomAccessInfinite(E) : ForwardRange!E {
+ ///
+ E moveAt(size_t);
+
+ ///
+ @property RandomAccessInfinite!E save();
+
+ ///
+ E opIndex(size_t);
+}
+
+/**Adds assignable elements to InputRange.*/
+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.*/
+interface ForwardAssignable(E) : InputAssignable!E, ForwardRange!E {
+ ///
+ @property ForwardAssignable!E save();
+}
+
+/**Adds assignable elements to BidirectionalRange.*/
+interface BidirectionalAssignable(E) : ForwardAssignable!E, BidirectionalRange!E {
+ ///
+ @property BidirectionalAssignable!E save();
+
+ ///
+ @property void back(E newVal);
+}
+
+/**Adds assignable elements to RandomAccessFinite.*/
+interface RandomFiniteAssignable(E) : RandomAccessFinite!E, BidirectionalAssignable!E {
+ ///
+ @property RandomFiniteAssignable!E save();
+
+ ///
+ void opIndexAssign(E val, size_t index);
+}
+
+/**Interface for an output range of type $(D E). Usage is similar to the
+ * $(D InputRange) interface and descendants.*/
+interface OutputRange(E) {
+ ///
+ void put(E);
+}
+
+@safe unittest
+{
+ // 6973
+ static assert(isOutputRange!(OutputRange!int, int));
+}
+
+
+// CTFE function that generates mixin code for one put() method for each
+// type E.
+private string putMethods(E...)()
+{
+ import std.conv : to;
+
+ string ret;
+
+ foreach (ti, Unused; E)
+ {
+ ret ~= "void put(E[" ~ to!string(ti) ~ "] e) { .put(_range, e); }";
+ }
+
+ return ret;
+}
+
+/**Implements the $(D OutputRange) interface for all types E and wraps the
+ * $(D put) method for each type $(D E) in a virtual function.
+ */
+class OutputRangeObject(R, E...) : staticMap!(OutputRange, E) {
+ // @BUG 4689: There should be constraints on this template class, but
+ // DMD won't let me put them in.
+ private R _range;
+
+ ///
+ this(R range) {
+ this._range = range;
+ }
+
+ mixin(putMethods!E());
+}
+
+
+/**Returns the interface type that best matches $(D R).*/
+template MostDerivedInputRange(R)
+if (isInputRange!(Unqual!R))
+{
+ private alias E = ElementType!R;
+
+ static if (isRandomAccessRange!R)
+ {
+ static if (isInfinite!R)
+ {
+ alias MostDerivedInputRange = RandomAccessInfinite!E;
+ }
+ else static if (hasAssignableElements!R)
+ {
+ alias MostDerivedInputRange = RandomFiniteAssignable!E;
+ }
+ else
+ {
+ alias MostDerivedInputRange = RandomAccessFinite!E;
+ }
+ }
+ else static if (isBidirectionalRange!R)
+ {
+ static if (hasAssignableElements!R)
+ {
+ alias MostDerivedInputRange = BidirectionalAssignable!E;
+ }
+ else
+ {
+ alias MostDerivedInputRange = BidirectionalRange!E;
+ }
+ }
+ else static if (isForwardRange!R)
+ {
+ static if (hasAssignableElements!R)
+ {
+ alias MostDerivedInputRange = ForwardAssignable!E;
+ }
+ else
+ {
+ alias MostDerivedInputRange = ForwardRange!E;
+ }
+ }
+ else
+ {
+ static if (hasAssignableElements!R)
+ {
+ alias MostDerivedInputRange = InputAssignable!E;
+ }
+ else
+ {
+ alias MostDerivedInputRange = InputRange!E;
+ }
+ }
+}
+
+/**Implements the most derived interface that $(D R) works with and wraps
+ * 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)))
+ {
+ alias InputRangeObject = R;
+ }
+ else static if (!is(Unqual!R == R))
+ {
+ alias InputRangeObject = InputRangeObject!(Unqual!R);
+ }
+ else
+ {
+
+ ///
+ class InputRangeObject : MostDerivedInputRange!(R) {
+ private R _range;
+ private alias E = ElementType!R;
+
+ this(R range) {
+ this._range = range;
+ }
+
+ @property E front() { return _range.front; }
+
+ E moveFront() {
+ return _range.moveFront();
+ }
+
+ void popFront() { _range.popFront(); }
+ @property bool empty() { return _range.empty; }
+
+ static if (isForwardRange!R)
+ {
+ @property typeof(this) save() {
+ return new typeof(this)(_range.save);
+ }
+ }
+
+ static if (hasAssignableElements!R)
+ {
+ @property void front(E newVal) {
+ _range.front = newVal;
+ }
+ }
+
+ static if (isBidirectionalRange!R)
+ {
+ @property E back() { return _range.back; }
+
+ E moveBack() {
+ return _range.moveBack();
+ }
+
+ void popBack() { return _range.popBack(); }
+
+ static if (hasAssignableElements!R)
+ {
+ @property void back(E newVal) {
+ _range.back = newVal;
+ }
+ }
+ }
+
+ static if (isRandomAccessRange!R)
+ {
+ E opIndex(size_t index) {
+ return _range[index];
+ }
+
+ E moveAt(size_t index) {
+ return _range.moveAt(index);
+ }
+
+ static if (hasAssignableElements!R)
+ {
+ void opIndexAssign(E val, size_t index) {
+ _range[index] = val;
+ }
+ }
+
+ static if (!isInfinite!R)
+ {
+ @property size_t length() {
+ return _range.length;
+ }
+
+ alias opDollar = length;
+
+ // Can't support slicing until all the issues with
+ // requiring slicing support for finite random access
+ // ranges are resolved.
+ version (none)
+ {
+ typeof(this) opSlice(size_t lower, size_t upper) {
+ return new typeof(this)(_range[lower .. upper]);
+ }
+ }
+ }
+ }
+
+ // Optimization: One delegate call is faster than three virtual
+ // function calls. Use opApply for foreach syntax.
+ int opApply(scope int delegate(E) dg) {
+ int res;
+
+ for (auto r = _range; !r.empty; r.popFront())
+ {
+ res = dg(r.front);
+ if (res) break;
+ }
+
+ return res;
+ }
+
+ int opApply(scope int delegate(size_t, E) dg) {
+ int res;
+
+ size_t i = 0;
+ for (auto r = _range; !r.empty; r.popFront())
+ {
+ res = dg(i, r.front);
+ if (res) break;
+ i++;
+ }
+
+ return res;
+ }
+ }
+ }
+}
+
+/**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)))
+ {
+ return range;
+ }
+ else
+ {
+ return new InputRangeObject!R(range);
+ }
+}
+
+/**Convenience function for creating an $(D OutputRangeObject) with a base range
+ * of type $(D R) that accepts types $(D E).
+*/
+template outputRangeObject(E...) {
+
+ ///
+ OutputRangeObject!(R, E) outputRangeObject(R)(R range) {
+ return new OutputRangeObject!(R, E)(range);
+ }
+}
+
+///
+@safe unittest
+{
+ import std.array;
+ auto app = appender!(uint[])();
+ auto appWrapped = outputRangeObject!(uint, uint[])(app);
+ static assert(is(typeof(appWrapped) : OutputRange!(uint[])));
+ static assert(is(typeof(appWrapped) : OutputRange!(uint)));
+}
+
+@system unittest
+{
+ import std.algorithm.comparison : equal;
+ import std.array;
+ import std.internal.test.dummyrange;
+
+ static void testEquality(R)(iInputRange r1, R r2) {
+ assert(equal(r1, r2));
+ }
+
+ auto arr = [1,2,3,4];
+ RandomFiniteAssignable!int arrWrapped = inputRangeObject(arr);
+ static assert(isRandomAccessRange!(typeof(arrWrapped)));
+ // static assert(hasSlicing!(typeof(arrWrapped)));
+ static assert(hasLength!(typeof(arrWrapped)));
+ arrWrapped[0] = 0;
+ assert(arr[0] == 0);
+ assert(arr.moveFront() == 0);
+ assert(arr.moveBack() == 4);
+ assert(arr.moveAt(1) == 2);
+
+ foreach (elem; arrWrapped) {}
+ foreach (i, elem; arrWrapped) {}
+
+ assert(inputRangeObject(arrWrapped) is arrWrapped);
+
+ foreach (DummyType; AllDummyRanges)
+ {
+ auto d = DummyType.init;
+ static assert(propagatesRangeType!(DummyType,
+ typeof(inputRangeObject(d))));
+ static assert(propagatesRangeType!(DummyType,
+ MostDerivedInputRange!DummyType));
+ InputRange!uint wrapped = inputRangeObject(d);
+ assert(equal(wrapped, d));
+ }
+
+ // Test output range stuff.
+ auto app = appender!(uint[])();
+ auto appWrapped = outputRangeObject!(uint, uint[])(app);
+ static assert(is(typeof(appWrapped) : OutputRange!(uint[])));
+ static assert(is(typeof(appWrapped) : OutputRange!(uint)));
+
+ appWrapped.put(1);
+ appWrapped.put([2, 3]);
+ assert(app.data.length == 3);
+ assert(equal(app.data, [1,2,3]));
+}
diff --git a/libphobos/src/std/range/package.d b/libphobos/src/std/range/package.d
new file mode 100644
index 0000000..fe581f3
--- /dev/null
+++ b/libphobos/src/std/range/package.d
@@ -0,0 +1,12019 @@
+// Written in the D programming language.
+
+/**
+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 $(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:
+
+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.
+
+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 $(LREF chain))
+ $(TD Concatenates several ranges into a single _range.
+ ))
+ $(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 $(LREF cycle))
+ $(TD Creates an infinite _range that repeats the given forward _range
+ indefinitely. Good for implementing circular buffers.
+ ))
+ $(TR $(TD $(LREF drop))
+ $(TD Creates the _range that results from discarding the first $(I n)
+ elements from the given _range.
+ ))
+ $(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 $(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 last element from the given _range.
+ ))
+ $(TR $(TD $(LREF enumerate))
+ $(TD Iterates a _range with an attached index variable.
+ ))
+ $(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 $(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 $(LREF iota))
+ $(TD Creates a _range consisting of numbers between a starting point
+ and ending point, spaced apart by a given interval.
+ ))
+ $(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 $(LREF NullSink))
+ $(TD An output _range that discards the data it receives.
+ ))
+ $(TR $(TD $(LREF only))
+ $(TD Creates a _range that iterates over the given arguments.
+ ))
+ $(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 $(LREF recurrence))
+ $(TD Creates a forward _range whose values are defined by a
+ mathematical recurrence relation.
+ ))
+ $(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 $(LREF retro))
+ $(TD Iterates a bidirectional _range backwards.
+ ))
+ $(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 $(LREF sequence))
+ $(TD Similar to $(D recurrence), except that a random-access _range is
+ created.
+ ))
+ $(COMMENT Explicitly undocumented to delay the release until 2.076
+ $(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 $(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 $(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 $(LREF takeNone))
+ $(TD Creates a random-access _range consisting of zero elements of the
+ given _range.
+ ))
+ $(TR $(TD $(LREF takeOne))
+ $(TD Creates a random-access _range consisting of exactly the first
+ element of the given _range.
+ ))
+ $(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 $(LREF transposed))
+ $(TD Transposes a _range of ranges.
+ ))
+ $(TR $(TD $(LREF transversal))
+ $(TD Creates a _range that iterates over the $(I n)'th elements of the
+ given random-access ranges.
+ ))
+ $(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 $(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)
+
+License: $(HTTP boost.org/LICENSE_1_0.txt, Boost License 1.0).
+
+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;
+
+public import std.array;
+public import std.range.interfaces;
+public import std.range.primitives;
+public import std.typecons : Flag, Yes, No;
+
+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))
+{
+ // Check for retro(retro(r)) and just return r in that case
+ static if (is(typeof(retro(r.source)) == Range))
+ {
+ return r.source;
+ }
+ else
+ {
+ static struct Result()
+ {
+ private alias R = Unqual!Range;
+
+ // User code can get and set source, too
+ R source;
+
+ static if (hasLength!R)
+ {
+ size_t retroIndex(size_t n)
+ {
+ return source.length - n - 1;
+ }
+ }
+
+ public:
+ alias Source = R;
+
+ @property bool empty() { return source.empty; }
+ @property auto save()
+ {
+ return Result(source.save);
+ }
+ @property auto ref front() { return source.back; }
+ void popFront() { source.popBack(); }
+ @property auto ref back() { return source.front; }
+ void popBack() { source.popFront(); }
+
+ static if (is(typeof(source.moveBack())))
+ {
+ ElementType!R moveFront()
+ {
+ return source.moveBack();
+ }
+ }
+
+ static if (is(typeof(source.moveFront())))
+ {
+ ElementType!R moveBack()
+ {
+ return source.moveFront();
+ }
+ }
+
+ static if (hasAssignableElements!R)
+ {
+ @property void front(ElementType!R val)
+ {
+ source.back = val;
+ }
+
+ @property void back(ElementType!R val)
+ {
+ source.front = val;
+ }
+ }
+
+ static if (isRandomAccessRange!(R) && hasLength!(R))
+ {
+ auto ref opIndex(size_t n) { return source[retroIndex(n)]; }
+
+ static if (hasAssignableElements!R)
+ {
+ void opIndexAssign(ElementType!R val, size_t n)
+ {
+ source[retroIndex(n)] = val;
+ }
+ }
+
+ static if (is(typeof(source.moveAt(0))))
+ {
+ ElementType!R moveAt(size_t index)
+ {
+ return source.moveAt(retroIndex(index));
+ }
+ }
+
+ static if (hasSlicing!R)
+ typeof(this) opSlice(size_t a, size_t b)
+ {
+ return typeof(this)(source[source.length - b .. source.length - a]);
+ }
+ }
+
+ static if (hasLength!R)
+ {
+ @property auto length()
+ {
+ return source.length;
+ }
+
+ alias opDollar = length;
+ }
+ }
+
+ return Result!()(r);
+ }
+}
+
+
+///
+pure @safe nothrow @nogc unittest
+{
+ 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[]);
+}
+
+pure @safe nothrow unittest
+{
+ import std.algorithm.comparison : equal;
+ static assert(isBidirectionalRange!(typeof(retro("hello"))));
+ int[] a;
+ static assert(is(typeof(a) == typeof(retro(retro(a)))));
+ assert(retro(retro(a)) is a);
+ static assert(isRandomAccessRange!(typeof(retro([1, 2, 3]))));
+ void test(int[] input, int[] witness)
+ {
+ auto r = retro(input);
+ assert(r.front == witness.front);
+ assert(r.back == witness.back);
+ assert(equal(r, witness));
+ }
+ test([ 1 ], [ 1 ]);
+ test([ 1, 2 ], [ 2, 1 ]);
+ test([ 1, 2, 3 ], [ 3, 2, 1 ]);
+ test([ 1, 2, 3, 4 ], [ 4, 3, 2, 1 ]);
+ test([ 1, 2, 3, 4, 5 ], [ 5, 4, 3, 2, 1 ]);
+ test([ 1, 2, 3, 4, 5, 6 ], [ 6, 5, 4, 3, 2, 1 ]);
+
+ immutable foo = [1,2,3].idup;
+ auto r = retro(foo);
+ assert(equal(r, [3, 2, 1]));
+}
+
+pure @safe nothrow unittest
+{
+ import std.internal.test.dummyrange : AllDummyRanges, propagatesRangeType,
+ ReturnBy;
+
+ foreach (DummyType; AllDummyRanges)
+ {
+ static if (!isBidirectionalRange!DummyType)
+ {
+ static assert(!__traits(compiles, Retro!DummyType));
+ }
+ else
+ {
+ DummyType dummyRange;
+ dummyRange.reinit();
+
+ auto myRetro = retro(dummyRange);
+ static assert(propagatesRangeType!(typeof(myRetro), DummyType));
+ assert(myRetro.front == 10);
+ assert(myRetro.back == 1);
+ assert(myRetro.moveFront() == 10);
+ assert(myRetro.moveBack() == 1);
+
+ static if (isRandomAccessRange!DummyType && hasLength!DummyType)
+ {
+ assert(myRetro[0] == myRetro.front);
+ assert(myRetro.moveAt(2) == 8);
+
+ static if (DummyType.r == ReturnBy.Reference)
+ {
+ {
+ myRetro[9]++;
+ scope(exit) myRetro[9]--;
+ assert(dummyRange[0] == 2);
+ myRetro.front++;
+ scope(exit) myRetro.front--;
+ assert(myRetro.front == 11);
+ myRetro.back++;
+ scope(exit) myRetro.back--;
+ assert(myRetro.back == 3);
+ }
+
+ {
+ myRetro.front = 0xFF;
+ scope(exit) myRetro.front = 10;
+ assert(dummyRange.back == 0xFF);
+
+ myRetro.back = 0xBB;
+ scope(exit) myRetro.back = 1;
+ assert(dummyRange.front == 0xBB);
+
+ myRetro[1] = 11;
+ scope(exit) myRetro[1] = 8;
+ assert(dummyRange[8] == 11);
+ }
+ }
+ }
+ }
+ }
+}
+
+pure @safe nothrow @nogc unittest
+{
+ import std.algorithm.comparison : equal;
+ auto LL = iota(1L, 4L);
+ auto r = retro(LL);
+ long[3] excepted = [3, 2, 1];
+ assert(equal(r, excepted[]));
+}
+
+// Issue 12662
+pure @safe nothrow @nogc unittest
+{
+ int[3] src = [1,2,3];
+ int[] data = src[];
+ foreach_reverse (x; data) {}
+ foreach (x; data.retro) {}
+}
+
+
+/**
+Iterates range $(D r) with stride $(D n). If the range is a
+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))
+in
+{
+ assert(n != 0, "stride cannot have step zero.");
+}
+body
+{
+ import std.algorithm.comparison : min;
+
+ static if (is(typeof(stride(r.source, n)) == Range))
+ {
+ // stride(stride(r, n1), n2) is stride(r, n1 * n2)
+ return stride(r.source, r._n * n);
+ }
+ else
+ {
+ static struct Result
+ {
+ private alias R = Unqual!Range;
+ public R source;
+ private size_t _n;
+
+ // Chop off the slack elements at the end
+ static if (hasLength!R &&
+ (isRandomAccessRange!R && hasSlicing!R
+ || isBidirectionalRange!R))
+ private void eliminateSlackElements()
+ {
+ auto slack = source.length % _n;
+
+ if (slack)
+ {
+ slack--;
+ }
+ else if (!source.empty)
+ {
+ slack = min(_n, source.length) - 1;
+ }
+ else
+ {
+ slack = 0;
+ }
+ if (!slack) return;
+ static if (isRandomAccessRange!R && hasLength!R && hasSlicing!R)
+ {
+ source = source[0 .. source.length - slack];
+ }
+ else static if (isBidirectionalRange!R)
+ {
+ foreach (i; 0 .. slack)
+ {
+ source.popBack();
+ }
+ }
+ }
+
+ static if (isForwardRange!R)
+ {
+ @property auto save()
+ {
+ return Result(source.save, _n);
+ }
+ }
+
+ static if (isInfinite!R)
+ {
+ enum bool empty = false;
+ }
+ else
+ {
+ @property bool empty()
+ {
+ return source.empty;
+ }
+ }
+
+ @property auto ref front()
+ {
+ return source.front;
+ }
+
+ static if (is(typeof(.moveFront(source))))
+ {
+ ElementType!R moveFront()
+ {
+ return source.moveFront();
+ }
+ }
+
+ static if (hasAssignableElements!R)
+ {
+ @property void front(ElementType!R val)
+ {
+ source.front = val;
+ }
+ }
+
+ void popFront()
+ {
+ source.popFrontN(_n);
+ }
+
+ static if (isBidirectionalRange!R && hasLength!R)
+ {
+ void popBack()
+ {
+ popBackN(source, _n);
+ }
+
+ @property auto ref back()
+ {
+ eliminateSlackElements();
+ return source.back;
+ }
+
+ static if (is(typeof(.moveBack(source))))
+ {
+ ElementType!R moveBack()
+ {
+ eliminateSlackElements();
+ return source.moveBack();
+ }
+ }
+
+ static if (hasAssignableElements!R)
+ {
+ @property void back(ElementType!R val)
+ {
+ eliminateSlackElements();
+ source.back = val;
+ }
+ }
+ }
+
+ static if (isRandomAccessRange!R && hasLength!R)
+ {
+ auto ref opIndex(size_t n)
+ {
+ return source[_n * n];
+ }
+
+ /**
+ Forwards to $(D moveAt(source, n)).
+ */
+ static if (is(typeof(source.moveAt(0))))
+ {
+ ElementType!R moveAt(size_t n)
+ {
+ return source.moveAt(_n * n);
+ }
+ }
+
+ static if (hasAssignableElements!R)
+ {
+ void opIndexAssign(ElementType!R val, size_t n)
+ {
+ source[_n * n] = val;
+ }
+ }
+ }
+
+ static if (hasSlicing!R && hasLength!R)
+ typeof(this) opSlice(size_t lower, size_t upper)
+ {
+ assert(upper >= lower && upper <= length);
+ immutable translatedUpper = (upper == 0) ? 0 :
+ (upper * _n - (_n - 1));
+ immutable translatedLower = min(lower * _n, translatedUpper);
+
+ assert(translatedLower <= translatedUpper);
+
+ return typeof(this)(source[translatedLower .. translatedUpper], _n);
+ }
+
+ static if (hasLength!R)
+ {
+ @property auto length()
+ {
+ return (source.length + _n - 1) / _n;
+ }
+
+ alias opDollar = length;
+ }
+ }
+ return Result(r, n);
+ }
+}
+
+///
+pure @safe nothrow unittest
+{
+ 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));
+}
+
+pure @safe nothrow @nogc unittest
+{
+ import std.algorithm.comparison : equal;
+
+ int[4] testArr = [1,2,3,4];
+ static immutable result = [1, 3];
+ assert(equal(testArr[].stride(2), result));
+}
+
+debug pure nothrow @system unittest
+{//check the contract
+ int[4] testArr = [1,2,3,4];
+ bool passed = false;
+ scope (success) assert(passed);
+ import core.exception : AssertError;
+ //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;
+ }
+}
+
+pure @safe nothrow unittest
+{
+ 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)
+ {
+ assert(equal(stride(input, n), witness));
+ }
+ test(1, [], []);
+ int[] arr = [ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 ];
+ assert(stride(stride(arr, 2), 3) is stride(arr, 6));
+ test(1, arr, arr);
+ test(2, arr, [1, 3, 5, 7, 9]);
+ test(3, arr, [1, 4, 7, 10]);
+ test(4, arr, [1, 5, 9]);
+
+ // 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(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(s2[$ .. $].empty);
+ assert(s2[s2.opDollar .. s2.opDollar].empty);
+
+ // Test fix for Bug 5035
+ auto m = [1, 2, 3, 4, 1, 2, 3, 4, 1, 2, 3, 4]; // 3 rows, 4 columns
+ auto col = stride(m, 4);
+ assert(equal(col, [1, 1, 1]));
+ assert(equal(retro(col), [1, 1, 1]));
+
+ immutable int[] immi = [ 1, 2, 3 ];
+ static assert(isRandomAccessRange!(typeof(stride(immi, 1))));
+
+ // Check for infiniteness propagation.
+ static assert(isInfinite!(typeof(stride(repeat(1), 3))));
+
+ foreach (DummyType; AllDummyRanges)
+ {
+ DummyType dummyRange;
+ dummyRange.reinit();
+
+ auto myStride = stride(dummyRange, 4);
+
+ // 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 assert(propagatesRangeType!(typeof(myStride), DummyType));
+ }
+ assert(myStride.front == 1);
+ assert(myStride.moveFront() == 1);
+ assert(equal(myStride, [1, 5, 9]));
+
+ static if (hasLength!DummyType)
+ {
+ assert(myStride.length == 3);
+ }
+
+ static if (isBidirectionalRange!DummyType && hasLength!DummyType)
+ {
+ assert(myStride.back == 9);
+ assert(myStride.moveBack() == 9);
+ }
+
+ static if (isRandomAccessRange!DummyType && hasLength!DummyType)
+ {
+ assert(myStride[0] == 1);
+ assert(myStride[1] == 5);
+ assert(myStride.moveAt(1) == 5);
+ assert(myStride[2] == 9);
+
+ static assert(hasSlicing!(typeof(myStride)));
+ }
+
+ static if (DummyType.r == ReturnBy.Reference)
+ {
+ // Make sure reference is propagated.
+
+ {
+ myStride.front++;
+ scope(exit) myStride.front--;
+ assert(dummyRange.front == 2);
+ }
+ {
+ myStride.front = 4;
+ scope(exit) myStride.front = 1;
+ assert(dummyRange.front == 4);
+ }
+
+ static if (isBidirectionalRange!DummyType && hasLength!DummyType)
+ {
+ {
+ myStride.back++;
+ scope(exit) myStride.back--;
+ assert(myStride.back == 10);
+ }
+ {
+ myStride.back = 111;
+ scope(exit) myStride.back = 9;
+ assert(myStride.back == 111);
+ }
+
+ static if (isRandomAccessRange!DummyType)
+ {
+ {
+ myStride[1]++;
+ scope(exit) myStride[1]--;
+ assert(dummyRange[4] == 6);
+ }
+ {
+ myStride[1] = 55;
+ scope(exit) myStride[1] = 5;
+ assert(dummyRange[4] == 55);
+ }
+ }
+ }
+ }
+ }
+}
+
+pure @safe nothrow unittest
+{
+ import std.algorithm.comparison : equal;
+
+ auto LL = iota(1L, 10L);
+ auto s = stride(LL, 3);
+ assert(equal(s, [1L, 4L, 7L]));
+}
+
+/**
+Spans multiple ranges in sequence. The function $(D chain) takes any
+number of ranges and returns a $(D Chain!(R1, R2,...)) object. The
+ranges may be different, but they must have the same element type. The
+result is a range that offers the $(D front), $(D popFront), and $(D
+empty) primitives. If all input ranges offer random access and $(D
+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 &&
+ allSatisfy!(isInputRange, staticMap!(Unqual, Ranges)) &&
+ !is(CommonType!(staticMap!(ElementType, staticMap!(Unqual, Ranges))) == void))
+{
+ static if (Ranges.length == 1)
+ {
+ return rs[0];
+ }
+ else
+ {
+ static struct Result
+ {
+ private:
+ alias R = staticMap!(Unqual, Ranges);
+ alias RvalueElementType = CommonType!(staticMap!(.ElementType, R));
+ private template sameET(A)
+ {
+ enum sameET = is(.ElementType!A == RvalueElementType);
+ }
+
+ enum bool allSameType = allSatisfy!(sameET, R);
+
+ // This doesn't work yet
+ static if (allSameType)
+ {
+ alias ElementType = ref RvalueElementType;
+ }
+ else
+ {
+ alias ElementType = RvalueElementType;
+ }
+ static if (allSameType && allSatisfy!(hasLvalueElements, R))
+ {
+ static ref RvalueElementType fixRef(ref RvalueElementType val)
+ {
+ return val;
+ }
+ }
+ else
+ {
+ static RvalueElementType fixRef(RvalueElementType val)
+ {
+ return val;
+ }
+ }
+
+ // This is the entire state
+ R source;
+ // TODO: use a vtable (or more) instead of linear iteration
+
+ public:
+ this(R input)
+ {
+ foreach (i, v; input)
+ {
+ source[i] = v;
+ }
+ }
+
+ import std.meta : anySatisfy;
+
+ static if (anySatisfy!(isInfinite, R))
+ {
+ // Propagate infiniteness.
+ enum bool empty = false;
+ }
+ else
+ {
+ @property bool empty()
+ {
+ foreach (i, Unused; R)
+ {
+ if (!source[i].empty) return false;
+ }
+ return true;
+ }
+ }
+
+ static if (allSatisfy!(isForwardRange, R))
+ @property auto save()
+ {
+ typeof(this) result = this;
+ foreach (i, Unused; R)
+ {
+ result.source[i] = result.source[i].save;
+ }
+ return result;
+ }
+
+ void popFront()
+ {
+ foreach (i, Unused; R)
+ {
+ if (source[i].empty) continue;
+ source[i].popFront();
+ return;
+ }
+ }
+
+ @property auto ref front()
+ {
+ foreach (i, Unused; R)
+ {
+ if (source[i].empty) continue;
+ return fixRef(source[i].front);
+ }
+ assert(false);
+ }
+
+ static if (allSameType && allSatisfy!(hasAssignableElements, R))
+ {
+ // @@@BUG@@@
+ //@property void front(T)(T v) if (is(T : RvalueElementType))
+
+ @property void front(RvalueElementType v)
+ {
+ foreach (i, Unused; R)
+ {
+ if (source[i].empty) continue;
+ source[i].front = v;
+ return;
+ }
+ assert(false);
+ }
+ }
+
+ static if (allSatisfy!(hasMobileElements, R))
+ {
+ RvalueElementType moveFront()
+ {
+ foreach (i, Unused; R)
+ {
+ if (source[i].empty) continue;
+ return source[i].moveFront();
+ }
+ assert(false);
+ }
+ }
+
+ static if (allSatisfy!(isBidirectionalRange, R))
+ {
+ @property auto ref back()
+ {
+ foreach_reverse (i, Unused; R)
+ {
+ if (source[i].empty) continue;
+ return fixRef(source[i].back);
+ }
+ assert(false);
+ }
+
+ void popBack()
+ {
+ foreach_reverse (i, Unused; R)
+ {
+ if (source[i].empty) continue;
+ source[i].popBack();
+ return;
+ }
+ }
+
+ static if (allSatisfy!(hasMobileElements, R))
+ {
+ RvalueElementType moveBack()
+ {
+ foreach_reverse (i, Unused; R)
+ {
+ if (source[i].empty) continue;
+ return source[i].moveBack();
+ }
+ assert(false);
+ }
+ }
+
+ static if (allSameType && allSatisfy!(hasAssignableElements, R))
+ {
+ @property void back(RvalueElementType v)
+ {
+ foreach_reverse (i, Unused; R)
+ {
+ if (source[i].empty) continue;
+ source[i].back = v;
+ return;
+ }
+ assert(false);
+ }
+ }
+ }
+
+ static if (allSatisfy!(hasLength, R))
+ {
+ @property size_t length()
+ {
+ size_t result;
+ foreach (i, Unused; R)
+ {
+ result += source[i].length;
+ }
+ return result;
+ }
+
+ alias opDollar = length;
+ }
+
+ static if (allSatisfy!(isRandomAccessRange, R))
+ {
+ auto ref opIndex(size_t index)
+ {
+ foreach (i, Range; R)
+ {
+ static if (isInfinite!(Range))
+ {
+ return source[i][index];
+ }
+ else
+ {
+ immutable length = source[i].length;
+ if (index < length) return fixRef(source[i][index]);
+ index -= length;
+ }
+ }
+ assert(false);
+ }
+
+ static if (allSatisfy!(hasMobileElements, R))
+ {
+ RvalueElementType moveAt(size_t index)
+ {
+ foreach (i, Range; R)
+ {
+ static if (isInfinite!(Range))
+ {
+ return source[i].moveAt(index);
+ }
+ else
+ {
+ immutable length = source[i].length;
+ if (index < length) return source[i].moveAt(index);
+ index -= length;
+ }
+ }
+ assert(false);
+ }
+ }
+
+ static if (allSameType && allSatisfy!(hasAssignableElements, R))
+ void opIndexAssign(ElementType v, size_t index)
+ {
+ foreach (i, Range; R)
+ {
+ static if (isInfinite!(Range))
+ {
+ source[i][index] = v;
+ }
+ else
+ {
+ immutable length = source[i].length;
+ if (index < length)
+ {
+ source[i][index] = v;
+ return;
+ }
+ index -= length;
+ }
+ }
+ assert(false);
+ }
+ }
+
+ static if (allSatisfy!(hasLength, R) && allSatisfy!(hasSlicing, R))
+ auto opSlice(size_t begin, size_t end)
+ {
+ auto result = this;
+ foreach (i, Unused; R)
+ {
+ immutable len = result.source[i].length;
+ if (len < begin)
+ {
+ result.source[i] = result.source[i]
+ [len .. len];
+ begin -= len;
+ }
+ else
+ {
+ result.source[i] = result.source[i]
+ [begin .. len];
+ break;
+ }
+ }
+ auto cut = length;
+ cut = cut <= end ? 0 : cut - end;
+ foreach_reverse (i, Unused; R)
+ {
+ immutable len = result.source[i].length;
+ if (cut > len)
+ {
+ result.source[i] = result.source[i]
+ [0 .. 0];
+ cut -= len;
+ }
+ else
+ {
+ result.source[i] = result.source[i]
+ [0 .. len - cut];
+ break;
+ }
+ }
+ return result;
+ }
+ }
+ return Result(rs);
+ }
+}
+
+///
+pure @safe nothrow unittest
+{
+ import std.algorithm.comparison : equal;
+
+ int[] arr1 = [ 1, 2, 3, 4 ];
+ int[] arr2 = [ 5, 6 ];
+ int[] arr3 = [ 7 ];
+ auto s = chain(arr1, arr2, arr3);
+ assert(s.length == 7);
+ assert(s[5] == 6);
+ assert(equal(s, [1, 2, 3, 4, 5, 6, 7][]));
+}
+
+/**
+ * Range primitives are carried over to the returned range if
+ * all of the ranges provide them
+ */
+pure @safe nothrow unittest
+{
+ import std.algorithm.comparison : equal;
+ import std.algorithm.sorting : sort;
+
+ 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 ];
+ int[] arr2 = [ 5, 6 ];
+ int[] arr3 = [ 7 ];
+ int[] witness = [ 1, 2, 3, 4, 5, 6, 7 ];
+ auto s1 = chain(arr1);
+ static assert(isRandomAccessRange!(typeof(s1)));
+ auto s2 = chain(arr1, arr2);
+ static assert(isBidirectionalRange!(typeof(s2)));
+ static assert(isRandomAccessRange!(typeof(s2)));
+ s2.front = 1;
+ auto s = chain(arr1, arr2, arr3);
+ assert(s[5] == 6);
+ assert(equal(s, witness));
+ assert(s[5] == 6);
+ }
+ {
+ int[] arr1 = [ 1, 2, 3, 4 ];
+ int[] witness = [ 1, 2, 3, 4 ];
+ assert(equal(chain(arr1), witness));
+ }
+ {
+ uint[] foo = [1,2,3,4,5];
+ uint[] bar = [1,2,3,4,5];
+ auto c = chain(foo, bar);
+ c[3] = 42;
+ assert(c[3] == 42);
+ assert(c.moveFront() == 1);
+ assert(c.moveBack() == 5);
+ assert(c.moveAt(4) == 5);
+ assert(c.moveAt(5) == 1);
+ }
+
+ // Make sure bug 3311 is fixed. ChainImpl should compile even if not all
+ // elements are mutable.
+ assert(equal(chain(iota(0, 3), iota(0, 3)), [0, 1, 2, 0, 1, 2]));
+
+ // Test the case where infinite ranges are present.
+ auto inf = chain([0,1,2][], cycle([4,5,6][]), [7,8,9][]); // infinite range
+ assert(inf[0] == 0);
+ assert(inf[3] == 4);
+ assert(inf[6] == 4);
+ assert(inf[7] == 5);
+ static assert(isInfinite!(typeof(inf)));
+
+ immutable int[] immi = [ 1, 2, 3 ];
+ immutable float[] immf = [ 1, 2, 3 ];
+ static assert(is(typeof(chain(immi, immf))));
+
+ // Check that chain at least instantiates and compiles with every possible
+ // pair of DummyRange types, in either order.
+
+ foreach (DummyType1; AllDummyRanges)
+ {
+ DummyType1 dummy1;
+ foreach (DummyType2; AllDummyRanges)
+ {
+ DummyType2 dummy2;
+ auto myChain = chain(dummy1, dummy2);
+
+ static assert(
+ propagatesRangeType!(typeof(myChain), DummyType1, DummyType2)
+ );
+
+ assert(myChain.front == 1);
+ foreach (i; 0 .. dummyLength)
+ {
+ myChain.popFront();
+ }
+ assert(myChain.front == 1);
+
+ static if (isBidirectionalRange!DummyType1 &&
+ isBidirectionalRange!DummyType2) {
+ assert(myChain.back == 10);
+ }
+
+ static if (isRandomAccessRange!DummyType1 &&
+ isRandomAccessRange!DummyType2) {
+ assert(myChain[0] == 1);
+ }
+
+ static if (hasLvalueElements!DummyType1 && hasLvalueElements!DummyType2)
+ {
+ static assert(hasLvalueElements!(typeof(myChain)));
+ }
+ else
+ {
+ static assert(!hasLvalueElements!(typeof(myChain)));
+ }
+ }
+ }
+}
+
+pure @safe nothrow @nogc unittest
+{
+ class Foo{}
+ immutable(Foo)[] a;
+ immutable(Foo)[] b;
+ assert(chain(a, b).empty);
+}
+
+/**
+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 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))
+{
+ static struct Result
+ {
+ import std.algorithm.comparison : max;
+ import std.algorithm.internal : addressOf;
+ import std.traits : hasElaborateCopyConstructor, hasElaborateDestructor;
+
+ 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;
+ }
+
+ this(bool condition, R1 r1, R2 r2)
+ {
+ 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)
+ {
+ static if (hasElaborateCopyConstructor!R1) r1.__postblit();
+ }
+ else
+ {
+ static if (hasElaborateCopyConstructor!R2) r2.__postblit();
+ }
+ }
+
+ static if (hasElaborateDestructor!R1 || hasElaborateDestructor!R2)
+ ~this()
+ {
+ if (condition) destroy(r1);
+ else destroy(r2);
+ }
+
+ static if (isInfinite!R1 && isInfinite!R2)
+ // Propagate infiniteness.
+ enum bool empty = false;
+ else
+ @property bool empty()
+ {
+ return condition ? r1.empty : r2.empty;
+ }
+
+ @property auto ref front()
+ {
+ return condition ? r1.front : r2.front;
+ }
+
+ void popFront()
+ {
+ return condition ? r1.popFront : r2.popFront;
+ }
+
+ static if (isForwardRange!R1 && isForwardRange!R2)
+ @property auto save()
+ {
+ auto result = this;
+ if (condition) r1 = r1.save;
+ else r2 = r2.save;
+ return result;
+ }
+
+ @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()
+ {
+ return condition ? r1.moveFront : r2.moveFront;
+ }
+
+ static if (isBidirectionalRange!R1 && isBidirectionalRange!R2)
+ {
+ @property auto ref back()
+ {
+ return condition ? r1.back : r2.back;
+ }
+
+ void popBack()
+ {
+ return condition ? r1.popBack : r2.popBack;
+ }
+
+ static if (hasMobileElements!R1 && hasMobileElements!R2)
+ auto moveBack()
+ {
+ return condition ? r1.moveBack : r2.moveBack;
+ }
+
+ @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 (hasLength!R1 && hasLength!R2)
+ {
+ @property size_t length()
+ {
+ 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)
+ {
+ return condition ? r1.moveAt(index) : r2.moveAt(index);
+ }
+
+ 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;
+ }
+ }
+
+ // 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);
+}
+
+///
+@system unittest
+{
+ import std.algorithm.comparison : equal;
+ import std.algorithm.iteration : filter, map;
+
+ 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 ]));
+}
+
+/**
+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
+ && allSatisfy!(isInputRange, staticMap!(Unqual, Ranges))
+ && !is(CommonType!(staticMap!(ElementType, Ranges)) == void))
+{
+ static if (Ranges.length == 2)
+ return choose(index == 0, rs[0], rs[1]);
+ else
+ return choose(index == 0, rs[0], chooseAmong(index - 1, 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) && hasSlicing!(Unqual!R))
+{
+ return .radial(r, (r.length - !r.empty) / 2);
+}
+
+///
+@safe unittest
+{
+ 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.internal.test.dummyrange : DummyRange, Length, RangeType, ReturnBy;
+
+ void test(int[] input, int[] witness)
+ {
+ enforce(equal(radial(input), witness),
+ text(radial(input), " vs. ", witness));
+ }
+ test([], []);
+ test([ 1 ], [ 1 ]);
+ test([ 1, 2 ], [ 1, 2 ]);
+ test([ 1, 2, 3 ], [ 2, 3, 1 ]);
+ test([ 1, 2, 3, 4 ], [ 2, 3, 1, 4 ]);
+ test([ 1, 2, 3, 4, 5 ], [ 3, 4, 2, 5, 1 ]);
+ 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, 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())
+ {
+ assert(rr.front == moveFront(rr));
+ }
+ r.front = 5;
+ assert(r.front == 5);
+
+ // Test instantiation without lvalue elements.
+ DummyRange!(ReturnBy.Value, Length.Yes, RangeType.Random) dummy;
+ assert(equal(radial(dummy, 4), [5, 6, 4, 7, 3, 8, 2, 9, 1, 10]));
+
+ // immutable int[] immi = [ 1, 2 ];
+ // static assert(is(typeof(radial(immi))));
+}
+
+@safe unittest
+{
+ import std.algorithm.comparison : equal;
+
+ auto LL = iota(1L, 6L);
+ auto r = radial(LL);
+ assert(equal(r, [3L, 4L, 2L, 5L, 1L]));
+}
+
+/**
+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))
+{
+ alias U = Unqual!R;
+ static if (is(R T == Take!T))
+ {
+ import std.algorithm.comparison : min;
+ return R(input.source, min(n, input._maxAvailable));
+ }
+ else static if (!isInfinite!U && hasSlicing!U)
+ {
+ import std.algorithm.comparison : min;
+ return input[0 .. min(n, input.length)];
+ }
+ else
+ {
+ return Take!R(input, n);
+ }
+}
+
+/// ditto
+struct Take(Range)
+if (isInputRange!(Unqual!Range) &&
+ //take _cannot_ test hasSlicing on infinite ranges, because hasSlicing uses
+ //take for slicing infinite ranges.
+ !((!isInfinite!(Unqual!Range) && hasSlicing!(Unqual!Range)) || is(Range T == Take!T)))
+{
+ private alias R = Unqual!Range;
+
+ /// 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,
+ "Attempting to fetch the front of an empty "
+ ~ Take.stringof);
+ return source.front;
+ }
+
+ /// ditto
+ void popFront()
+ {
+ assert(!empty,
+ "Attempting to popFront() past the end of a "
+ ~ Take.stringof);
+ source.popFront();
+ --_maxAvailable;
+ }
+
+ static if (isForwardRange!R)
+ /// ditto
+ @property Take save()
+ {
+ return Take(source.save, _maxAvailable);
+ }
+
+ static if (hasAssignableElements!R)
+ /// ditto
+ @property void front(ElementType!R v)
+ {
+ assert(!empty,
+ "Attempting to assign to the front of an empty "
+ ~ Take.stringof);
+ // This has to return auto instead of void because of Bug 4706.
+ source.front = v;
+ }
+
+ static if (hasMobileElements!R)
+ {
+ /// ditto
+ auto moveFront()
+ {
+ assert(!empty,
+ "Attempting to move the front of an empty "
+ ~ Take.stringof);
+ 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 <= length, "Attempting to slice past the end of a "
+ ~ Take.stringof);
+ return source[i .. j];
+ }
+ }
+ else static if (hasLength!R)
+ {
+ /// ditto
+ @property size_t length()
+ {
+ import std.algorithm.comparison : min;
+ return min(_maxAvailable, source.length);
+ }
+
+ alias opDollar = length;
+ }
+
+ static if (isRandomAccessRange!R)
+ {
+ /// ditto
+ void popBack()
+ {
+ assert(!empty,
+ "Attempting to popBack() past the beginning of a "
+ ~ Take.stringof);
+ --_maxAvailable;
+ }
+
+ /// ditto
+ @property auto ref back()
+ {
+ assert(!empty,
+ "Attempting to fetch the back of an empty "
+ ~ Take.stringof);
+ return source[this.length - 1];
+ }
+
+ /// ditto
+ auto ref opIndex(size_t index)
+ {
+ assert(index < length,
+ "Attempting to index out of the bounds of a "
+ ~ Take.stringof);
+ return source[index];
+ }
+
+ static if (hasAssignableElements!R)
+ {
+ /// ditto
+ @property void back(ElementType!R v)
+ {
+ // This has to return auto instead of void because of Bug 4706.
+ assert(!empty,
+ "Attempting to assign to the back of an empty "
+ ~ Take.stringof);
+ source[this.length - 1] = v;
+ }
+
+ /// ditto
+ void opIndexAssign(ElementType!R v, size_t index)
+ {
+ assert(index < length,
+ "Attempting to index out of the bounds of a "
+ ~ Take.stringof);
+ source[index] = v;
+ }
+ }
+
+ static if (hasMobileElements!R)
+ {
+ /// ditto
+ auto moveBack()
+ {
+ assert(!empty,
+ "Attempting to move the back of an empty "
+ ~ Take.stringof);
+ 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 source.moveAt(index);
+ }
+ }
+ }
+
+ /**
+ 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.
+*/
+template Take(R)
+if (isInputRange!(Unqual!R) &&
+ ((!isInfinite!(Unqual!R) && hasSlicing!(Unqual!R)) || is(R T == Take!T)))
+{
+ alias Take = R;
+}
+
+///
+pure @safe nothrow unittest
+{
+ import std.algorithm.comparison : equal;
+
+ int[] arr1 = [ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 ];
+ auto s = take(arr1, 5);
+ assert(s.length == 5);
+ assert(s[4] == 5);
+ assert(equal(s, [ 1, 2, 3, 4, 5 ][]));
+}
+
+/**
+ * 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 ]));
+}
+
+pure @safe nothrow unittest
+{
+ 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);
+ assert(s.length == 5);
+ assert(s[4] == 5);
+ assert(equal(s, [ 1, 2, 3, 4, 5 ][]));
+ assert(equal(retro(s), [ 5, 4, 3, 2, 1 ][]));
+
+ // Test fix for bug 4464.
+ static assert(is(typeof(s) == Take!(int[])));
+ 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(assumeWontThrow(equal(takeMyStr, "This is")));
+ // Test fix for bug 5052.
+ auto takeMyStrAgain = take(takeMyStr, 4);
+ assert(assumeWontThrow(equal(takeMyStrAgain, "This")));
+ static assert(is (typeof(takeMyStrAgain) == typeof(takeMyStr)));
+ takeMyStrAgain = take(takeMyStr, 10);
+ assert(assumeWontThrow(equal(takeMyStrAgain, "This is")));
+
+ foreach (DummyType; AllDummyRanges)
+ {
+ DummyType dummy;
+ auto t = take(dummy, 5);
+ alias T = typeof(t);
+
+ 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)
+ {
+ static assert(isForwardRange!T);
+ }
+
+ for (auto tt = t; !tt.empty; tt.popFront())
+ {
+ assert(tt.front == moveFront(tt));
+ }
+
+ // Bidirectional ranges can't be propagated properly if they don't
+ // also have random access.
+
+ assert(equal(t, [1,2,3,4,5]));
+
+ //Test that take doesn't wrap the result of take.
+ assert(take(t, 4) == take(dummy, 4));
+ }
+
+ immutable myRepeat = repeat(1);
+ static assert(is(Take!(typeof(myRepeat))));
+}
+
+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
+ // take(). (See issue 4464.)
+ int[] r1;
+ Take!(int[]) t1;
+ t1 = take(r1, 1);
+ assert(t1.empty);
+
+ string r2;
+ Take!string t2;
+ t2 = take(r2, 1);
+ assert(t2.empty);
+
+ Take!(Take!string) t3;
+ t3 = take(t2, 1);
+ assert(t3.empty);
+}
+
+pure @safe nothrow @nogc unittest
+{
+ alias R1 = typeof(repeat(1));
+ alias R2 = typeof(cycle([1]));
+ alias TR1 = Take!R1;
+ alias TR2 = Take!R2;
+ static assert(isBidirectionalRange!TR1);
+ static assert(isBidirectionalRange!TR2);
+}
+
+pure @safe nothrow @nogc unittest //12731
+{
+ auto a = repeat(1);
+ auto s = a[1 .. 5];
+ s = s[1 .. 3];
+ assert(s.length == 2);
+ assert(s[0] == 1);
+ assert(s[1] == 1);
+}
+
+pure @safe nothrow @nogc unittest //13151
+{
+ import std.algorithm.comparison : equal;
+
+ auto r = take(repeat(1, 4), 3);
+ assert(r.take(2).equal(repeat(1, 2)));
+}
+
+
+/**
+Similar to $(LREF take), but assumes that $(D range) has at least $(D
+n) elements. Consequently, the result of $(D takeExactly(range, n))
+always defines the $(D length) property (and initializes it to $(D n))
+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)
+{
+ static if (is(typeof(takeExactly(range._input, n)) == R))
+ {
+ assert(n <= range._n,
+ "Attempted to take more than the length of the range with takeExactly.");
+ // takeExactly(takeExactly(r, n1), n2) has the same type as
+ // takeExactly(r, n1) and simply returns takeExactly(r, n2)
+ range._n = n;
+ return range;
+ }
+ //Also covers hasSlicing!R for finite ranges.
+ else static if (hasLength!R)
+ {
+ assert(n <= range.length,
+ "Attempted to take more than the length of the range with takeExactly.");
+ return take(range, n);
+ }
+ else static if (isInfinite!R)
+ return Take!R(range, n);
+ else
+ {
+ static struct Result
+ {
+ R _input;
+ private size_t _n;
+
+ @property bool empty() const { return !_n; }
+ @property auto ref front()
+ {
+ assert(_n > 0, "front() on an empty " ~ Result.stringof);
+ return _input.front;
+ }
+ void popFront() { _input.popFront(); --_n; }
+ @property size_t length() const { return _n; }
+ alias opDollar = length;
+
+ @property Take!R _takeExactly_Result_asTake()
+ {
+ return typeof(return)(_input, _n);
+ }
+
+ alias _takeExactly_Result_asTake this;
+
+ static if (isForwardRange!R)
+ @property auto save()
+ {
+ return Result(_input.save, _n);
+ }
+
+ static if (hasMobileElements!R)
+ {
+ auto moveFront()
+ {
+ assert(!empty,
+ "Attempting to move the front of an empty "
+ ~ typeof(this).stringof);
+ return _input.moveFront();
+ }
+ }
+
+ static if (hasAssignableElements!R)
+ {
+ @property auto ref front(ElementType!R v)
+ {
+ assert(!empty,
+ "Attempting to assign to the front of an empty "
+ ~ typeof(this).stringof);
+ return _input.front = v;
+ }
+ }
+ }
+
+ return Result(range, n);
+ }
+}
+
+///
+pure @safe nothrow unittest
+{
+ import std.algorithm.comparison : equal;
+
+ auto a = [ 1, 2, 3, 4, 5 ];
+
+ auto b = takeExactly(a, 3);
+ assert(equal(b, [1, 2, 3]));
+ static assert(is(typeof(b.length) == size_t));
+ assert(b.length == 3);
+ assert(b.front == 1);
+ assert(b.back == 3);
+}
+
+pure @safe nothrow unittest
+{
+ import std.algorithm.comparison : equal;
+ import std.algorithm.iteration : filter;
+
+ auto a = [ 1, 2, 3, 4, 5 ];
+ auto b = takeExactly(a, 3);
+ assert(equal(b, [1, 2, 3]));
+ auto c = takeExactly(b, 2);
+ assert(equal(c, [1, 2]));
+
+
+
+ auto d = filter!"a > 2"(a);
+ auto e = takeExactly(d, 3);
+ assert(equal(e, [3, 4, 5]));
+ static assert(is(typeof(e.length) == size_t));
+ assert(e.length == 3);
+ assert(e.front == 3);
+
+ assert(equal(takeExactly(e, 3), [3, 4, 5]));
+}
+
+pure @safe nothrow unittest
+{
+ 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
+ //but aren't sliceable.
+ struct L
+ {
+ @property auto front() { return _arr[0]; }
+ @property bool empty() { return _arr.empty; }
+ void popFront() { _arr.popFront(); }
+ @property size_t length() { return _arr.length; }
+ int[] _arr;
+ }
+ static assert(is(typeof(take(L(a), 3)) == typeof(takeExactly(L(a), 3))));
+ assert(take(L(a), 3) == takeExactly(L(a), 3));
+
+ //Test that take and takeExactly are the same for ranges which are sliceable.
+ static assert(is(typeof(take(a, 3)) == typeof(takeExactly(a, 3))));
+ assert(take(a, 3) == takeExactly(a, 3));
+
+ //Test that take and takeExactly are the same for infinite ranges.
+ auto inf = repeat(1);
+ static assert(is(typeof(take(inf, 5)) == Take!(typeof(inf))));
+ assert(take(inf, 5) == takeExactly(inf, 5));
+
+ //Test that take and takeExactly are _not_ the same for ranges which don't
+ //define length.
+ static assert(!is(typeof(take(filter!"true"(a), 3)) == typeof(takeExactly(filter!"true"(a), 3))));
+
+ foreach (DummyType; AllDummyRanges)
+ {
+ {
+ DummyType dummy;
+ auto t = takeExactly(dummy, 5);
+
+ //Test that takeExactly doesn't wrap the result of takeExactly.
+ assert(takeExactly(t, 4) == takeExactly(dummy, 4));
+ }
+
+ static if (hasMobileElements!DummyType)
+ {
+ {
+ auto t = takeExactly(DummyType.init, 4);
+ assert(t.moveFront() == 1);
+ assert(equal(t, [1, 2, 3, 4]));
+ }
+ }
+
+ static if (hasAssignableElements!DummyType)
+ {
+ {
+ auto t = takeExactly(DummyType.init, 4);
+ t.front = 9;
+ assert(equal(t, [9, 2, 3, 4]));
+ }
+ }
+ }
+}
+
+pure @safe nothrow unittest
+{
+ 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);
+ Take!DummyType t = te;
+ assert(equal(t, [1, 2, 3, 4, 5]));
+ assert(equal(t, te));
+}
+
+/**
+Returns a range with at most one element; for example, $(D
+takeOne([42, 43, 44])) returns a range consisting of the integer $(D
+42). Calling $(D popFront()) off that range renders it empty.
+
+In effect $(D takeOne(r)) is somewhat equivalent to $(D take(r, 1)) but in
+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, 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)
+{
+ static if (hasSlicing!R)
+ {
+ return source[0 .. !source.empty];
+ }
+ else
+ {
+ static struct Result
+ {
+ private R _source;
+ private bool _empty = true;
+ @property bool empty() const { return _empty; }
+ @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, "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, "Attempting to index a takeOne out of bounds");
+ return _source.front;
+ }
+ auto opSlice(size_t m, size_t n)
+ {
+ assert(m <= n && n < length, "Attempting to index a takeOne out of bounds");
+ return n > m ? this : Result(_source, false);
+ }
+ // Non-standard property
+ @property R source() { return _source; }
+ }
+
+ return Result(source, source.empty);
+ }
+}
+
+///
+pure @safe nothrow unittest
+{
+ auto s = takeOne([42, 43, 44]);
+ static assert(isRandomAccessRange!(typeof(s)));
+ assert(s.length == 1);
+ assert(!s.empty);
+ assert(s.front == 42);
+ s.front = 43;
+ assert(s.front == 43);
+ assert(s.back == 43);
+ assert(s[0] == 43);
+ s.popFront();
+ assert(s.length == 0);
+ assert(s.empty);
+}
+
+pure @safe nothrow @nogc unittest
+{
+ struct NonForwardRange
+ {
+ enum empty = false;
+ int front() { return 42; }
+ void popFront() {}
+ }
+
+ static assert(!isForwardRange!NonForwardRange);
+
+ auto s = takeOne(NonForwardRange());
+ 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)
+{
+ return typeof(takeOne(R.init)).init;
+}
+
+///
+pure @safe nothrow @nogc unittest
+{
+ auto range = takeNone!(int[])();
+ assert(range.length == 0);
+ assert(range.empty);
+}
+
+pure @safe nothrow @nogc unittest
+{
+ enum ctfe = takeNone!(int[])();
+ static assert(ctfe.length == 0);
+ static assert(ctfe.empty);
+}
+
+
+/++
+ Creates an empty range from the given range in $(BIGOH 1). If it can, it
+ will return the same range type. If not, it will return
+ $(D takeExactly(range, 0)).
+ +/
+auto takeNone(R)(R range)
+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)))
+ auto retval = range.takeNone();
+ //@@@BUG@@@ 8339
+ 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)
+ auto retval = range[0 .. 0];
+ else
+ auto retval = takeExactly(range, 0);
+
+ //@@@BUG@@@ 7892 prevents this from being done in an out block.
+ assert(retval.empty);
+ return retval;
+}
+
+///
+pure @safe nothrow unittest
+{
+ import std.algorithm.iteration : filter;
+ assert(takeNone([42, 27, 19]).empty);
+ assert(takeNone("dlang.org").empty);
+ assert(takeNone(filter!"true"([42, 27, 19])).empty);
+}
+
+@safe unittest
+{
+ 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(); }
+ static assert(isInputRange!(typeof(this)));
+ }
+ }
+ alias genInput = Dummy.genInput;
+
+ static struct NormalStruct
+ {
+ //Disabled to make sure that the takeExactly version is used.
+ @disable this();
+ this(int[] arr) { _arr = arr; }
+ mixin genInput;
+ int[] _arr;
+ }
+
+ static struct SliceStruct
+ {
+ @disable this();
+ this(int[] arr) { _arr = arr; }
+ mixin genInput;
+ @property auto save() { return this; }
+ auto opSlice(size_t i, size_t j) { return typeof(this)(_arr[i .. j]); }
+ @property size_t length() { return _arr.length; }
+ int[] _arr;
+ }
+
+ static struct InitStruct
+ {
+ mixin genInput;
+ int[] _arr;
+ }
+
+ static struct TakeNoneStruct
+ {
+ this(int[] arr) { _arr = arr; }
+ @disable this();
+ mixin genInput;
+ auto takeNone() { return typeof(this)(null); }
+ int[] _arr;
+ }
+
+ static class NormalClass
+ {
+ this(int[] arr) {_arr = arr;}
+ mixin genInput;
+ int[] _arr;
+ }
+
+ static class SliceClass
+ {
+ @safe:
+ this(int[] arr) { _arr = arr; }
+ mixin genInput;
+ @property auto save() { return new typeof(this)(_arr); }
+ auto opSlice(size_t i, size_t j) { return new typeof(this)(_arr[i .. j]); }
+ @property size_t length() { return _arr.length; }
+ int[] _arr;
+ }
+
+ static class TakeNoneClass
+ {
+ @safe:
+ this(int[] arr) { _arr = arr; }
+ mixin genInput;
+ auto takeNone() { return new typeof(this)(null); }
+ int[] _arr;
+ }
+
+ import std.format : format;
+
+ 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; AliasSeq!(NormalStruct([1, 2, 3]),
+ InitStruct([1, 2, 3])))
+ {
+ static assert(takeNone(range).empty, typeof(range).stringof);
+ assert(takeNone(range).empty);
+ static assert(is(typeof(takeExactly(range, 0)) == typeof(takeNone(range))), typeof(range).stringof);
+ }
+
+ //Don't work in CTFE.
+ auto normal = new NormalClass([1, 2, 3]);
+ assert(takeNone(normal).empty);
+ static assert(is(typeof(takeExactly(normal, 0)) == typeof(takeNone(normal))), typeof(normal).stringof);
+
+ auto slice = new SliceClass([1, 2, 3]);
+ assert(takeNone(slice).empty);
+ static assert(is(SliceClass == typeof(takeNone(slice))), typeof(slice).stringof);
+
+ auto taken = new TakeNoneClass([1, 2, 3]);
+ assert(takeNone(taken).empty);
+ static assert(is(TakeNoneClass == typeof(takeNone(taken))), typeof(taken).stringof);
+
+ auto filtered = filter!"true"([1, 2, 3, 4, 5]);
+ assert(takeNone(filtered).empty);
+ //@@@BUG@@@ 8339 and 5941 force this to be takeExactly
+ //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.exception : assumeWontThrow;
+ import std.string : lineSplitter;
+ 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
+ $(REF popFrontN, std, _range, primitives)`(range, 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 `popFrontN` would require multiple statements.
+
+ `dropBack` provides the same functionality but instead calls
+ $(REF popBackN, std, _range, primitives)`(range, 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`.
+
+ Params:
+ range = the input range to drop from
+ n = the number of elements to drop
+
+ 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)
+{
+ range.popFrontN(n);
+ return range;
+}
+/// ditto
+R dropBack(R)(R range, size_t n)
+if (isBidirectionalRange!R)
+{
+ range.popBackN(n);
+ return range;
+}
+
+///
+@safe unittest
+{
+ import std.algorithm.comparison : equal;
+
+ assert([0, 2, 1, 5, 0, 3].drop(3) == [5, 0, 3]);
+ assert("hello world".drop(6) == "world");
+ assert("hello world".drop(50).empty);
+ assert("hello world".take(6).drop(3).equal("lo "));
+}
+
+@safe unittest
+{
+ import std.algorithm.comparison : equal;
+
+ assert([0, 2, 1, 5, 0, 3].dropBack(3) == [0, 2, 1]);
+ assert("hello world".dropBack(6) == "hello");
+ assert("hello world".dropBack(50).empty);
+ assert("hello world".drop(4).dropBack(4).equal("o w"));
+}
+
+@safe unittest
+{
+ 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);
+ a.remove(a[].drop(2));
+ assert(a[].equal(a[].take(2)));
+}
+
+@safe unittest
+{
+ 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]));
+}
+
+@safe unittest
+{
+ 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);
+ a.insertAfter(a[].dropBack(2), [3, 4]);
+ assert(a[].equal(iota(0, 7)));
+}
+
+/++
+ Similar to $(LREF drop) and $(D dropBack) but they call
+ $(D range.$(LREF popFrontExactly)(n)) and $(D range.popBackExactly(n))
+ instead.
+
+ Note: Unlike $(D drop), $(D dropExactly) will assume that the
+ range holds at least $(D n) elements. This makes $(D dropExactly)
+ faster than $(D drop), but it also means that if $(D range) does
+ not contain at least $(D n) elements, it will attempt to call $(D popFront)
+ 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)
+{
+ popFrontExactly(range, n);
+ return range;
+}
+/// ditto
+R dropBackExactly(R)(R range, size_t n)
+if (isBidirectionalRange!R)
+{
+ popBackExactly(range, n);
+ return range;
+}
+
+///
+@safe unittest
+{
+ import std.algorithm.comparison : equal;
+ import std.algorithm.iteration : filterBidirectional;
+
+ auto a = [1, 2, 3];
+ assert(a.dropExactly(2) == [3]);
+ assert(a.dropBackExactly(2) == [1]);
+
+ string s = "日本語";
+ assert(s.dropExactly(2) == "語");
+ assert(s.dropBackExactly(2) == "日");
+
+ auto bd = filterBidirectional!"true"([1, 2, 3]);
+ assert(bd.dropExactly(2).equal([3]));
+ assert(bd.dropBackExactly(2).equal([1]));
+}
+
+/++
+ Convenience function which calls
+ $(D range.popFront()) and returns $(D range). $(D dropOne)
+ makes it easier to pop an element from a range
+ and then pass it to another function within a single expression,
+ whereas $(D popFront) would require multiple statements.
+
+ $(D dropBackOne) provides the same functionality but instead calls
+ $(D range.popBack()).
++/
+R dropOne(R)(R range)
+if (isInputRange!R)
+{
+ range.popFront();
+ return range;
+}
+/// ditto
+R dropBackOne(R)(R range)
+if (isBidirectionalRange!R)
+{
+ range.popBack();
+ return range;
+}
+
+///
+pure @safe nothrow unittest
+{
+ 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]));
+
+ auto a = [1, 2, 3];
+ assert(a.dropOne() == [2, 3]);
+ assert(a.dropBackOne() == [1, 2]);
+
+ string s = "日本語";
+ 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]));
+ assert(bd.dropBackOne().equal([1, 2]));
+}
+
+/**
+Create a range which repeats one value forever.
+
+Params:
+ value = the value to repeat
+
+Returns:
+ An infinite random access range with slicing.
+*/
+struct Repeat(T)
+{
+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 : Rebindable;
+ alias UT = Rebindable!T;
+ }
+ else static if (is(T : Unqual!T) && is(Unqual!T : T))
+ alias UT = Unqual!T;
+ else
+ alias UT = T;
+ 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
+ {
+ 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; }
+}
+
+/// Ditto
+Repeat!T repeat(T)(T value) { return Repeat!T(value); }
+
+///
+pure @safe nothrow unittest
+{
+ import std.algorithm.comparison : equal;
+
+ assert(equal(5.repeat().take(4), [ 5, 5, 5, 5 ]));
+}
+
+pure @safe nothrow unittest
+{
+ import std.algorithm.comparison : equal;
+
+ auto r = repeat(5);
+ alias R = typeof(r);
+ static assert(isBidirectionalRange!R);
+ static assert(isForwardRange!R);
+ static assert(isInfinite!R);
+ static assert(hasSlicing!R);
+
+ assert(r.back == 5);
+ assert(r.front == 5);
+ assert(r.take(4).equal([ 5, 5, 5, 5 ]));
+ assert(r[0 .. 4].equal([ 5, 5, 5, 5 ]));
+
+ R r2 = r[5 .. $];
+ assert(r2.back == 5);
+ assert(r2.front == 5);
+}
+
+/**
+ Repeats $(D value) exactly $(D n) times. Equivalent to $(D
+ take(repeat(value), n)).
+*/
+Take!(Repeat!T) repeat(T)(T value, size_t n)
+{
+ return take(repeat(value), n);
+}
+
+///
+pure @safe nothrow @nogc unittest
+{
+ import std.algorithm.comparison : equal;
+
+ assert(equal(5.repeat(4), 5.repeat().take(4)));
+}
+
+pure @safe nothrow unittest //12007
+{
+ static class C{}
+ Repeat!(immutable int) ri;
+ ri = ri.save;
+ Repeat!(immutable C) rc;
+ rc = rc.save;
+
+ import std.algorithm.setops : cartesianProduct;
+ import std.algorithm.comparison : equal;
+ import std.typecons : tuple;
+ immutable int[] A = [1,2,3];
+ immutable int[] B = [4,5,6];
+
+ assert(equal(cartesianProduct(A,B),
+ [
+ tuple(1, 4), tuple(1, 5), tuple(1, 6),
+ tuple(2, 4), tuple(2, 5), tuple(2, 6),
+ tuple(3, 4), tuple(3, 5), tuple(3, 6),
+ ]));
+}
+
+/**
+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 `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)
+{
+ auto gen = Generator!(Fun)(fun);
+ gen.popFront(); // prime the first element
+ return gen;
+}
+
+/// ditto
+auto generate(alias fun)()
+if (isCallable!fun)
+{
+ auto gen = Generator!(fun)();
+ gen.popFront(); // prime the first element
+ return gen;
+}
+
+///
+@safe pure unittest
+{
+ import std.algorithm.comparison : equal;
+ import std.algorithm.iteration : map;
+
+ int i = 1;
+ auto powersOfTwo = generate!(() => i *= 2)().take(10);
+ assert(equal(powersOfTwo, iota(1, 11).map!"2^^a"()));
+}
+
+///
+@safe pure unittest
+{
+ import std.algorithm.comparison : equal;
+
+ //Returns a run-time delegate
+ auto infiniteIota(T)(T low, T high)
+ {
+ T i = high;
+ return (){if (i == high) i = low; return i++;};
+ }
+ //adapted as a range.
+ 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.comparison : equal;
+
+ struct StaticOpCall
+ {
+ static ubyte opCall() { return 5 ; }
+ }
+
+ assert(equal(generate!StaticOpCall().take(10), repeat(5).take(10)));
+}
+
+@safe pure unittest
+{
+ import std.algorithm.comparison : equal;
+
+ struct OpCall
+ {
+ ubyte opCall() @safe pure { return 5 ; }
+ }
+
+ OpCall op;
+ 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. 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.
+
+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)
+{
+ 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];
+ }
+
+ static if (is(typeof((cast(const R)_original)[_index])))
+ {
+ /// ditto
+ @property auto ref front() const
+ {
+ return _original[_index];
+ }
+ }
+
+ static if (hasAssignableElements!R)
+ {
+ /// ditto
+ @property void front(ElementType!R val)
+ {
+ _original[_index] = val;
+ }
+ }
+
+ /// ditto
+ enum bool empty = false;
+
+ /// ditto
+ void popFront()
+ {
+ ++_index;
+ if (_index >= _original.length)
+ _index = 0;
+ }
+
+ /// ditto
+ auto ref opIndex(size_t n)
+ {
+ return _original[(n + _index) % _original.length];
+ }
+
+ 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];
+ }
+ }
+
+ static if (hasAssignableElements!R)
+ {
+ /// 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
+ return Cycle(_original, _index);
+ }
+
+ private static struct DollarToken {}
+
+ /// ditto
+ enum opDollar = DollarToken.init;
+
+ static if (hasSlicing!R)
+ {
+ /// ditto
+ auto opSlice(size_t i, size_t j)
+ in
+ {
+ assert(i <= j);
+ }
+ body
+ {
+ return this[i .. $].takeExactly(j - i);
+ }
+
+ /// ditto
+ auto opSlice(size_t i, DollarToken)
+ {
+ return typeof(this)(_original, _index + i);
+ }
+ }
+ }
+ else
+ {
+ private R _original;
+ private R _current;
+
+ /// ditto
+ this(R input)
+ {
+ _original = input;
+ _current = input.save;
+ }
+
+ /// ditto
+ @property auto ref front()
+ {
+ return _current.front;
+ }
+
+ static if (is(typeof((cast(const R)_current).front)))
+ {
+ /// ditto
+ @property auto ref front() const
+ {
+ return _current.front;
+ }
+ }
+
+ 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();
+ if (_current.empty)
+ _current = _original.save;
+ }
+
+ /// ditto
+ @property Cycle save()
+ {
+ //No need to call _original.save, because Cycle never actually modifies _original
+ Cycle ret = this;
+ ret._original = _original;
+ ret._current = _current.save;
+ return ret;
+ }
+ }
+}
+
+/// ditto
+template Cycle(R)
+if (isInfinite!R)
+{
+ alias Cycle = R;
+}
+
+///
+struct Cycle(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
+ {
+ return p[idx];
+ }
+ return trustedPtrIdx(_ptr, _index);
+ }
+
+ /// ditto
+ enum bool empty = false;
+
+ /// ditto
+ void popFront() @safe
+ {
+ ++_index;
+ if (_index >= R.length)
+ _index = 0;
+ }
+
+ /// ditto
+ ref inout(ElementType) opIndex(size_t n) inout @safe
+ {
+ static ref auto trustedPtrIdx(typeof(_ptr) p, size_t idx) @trusted
+ {
+ return p[idx % R.length];
+ }
+ 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
+ {
+ 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 trustedCtor(_ptr, _index + i);
+ }
+}
+
+/// Ditto
+auto cycle(R)(R input)
+if (isInputRange!R)
+{
+ 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.comparison : equal;
+ import std.range : cycle, take;
+
+ // 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)
+{
+ assert(!input.empty, "Attempting to pass an empty input to cycle");
+ return Cycle!R(input, index);
+}
+
+/// Ditto
+Cycle!R cycle(R)(ref R input, size_t index = 0) @system
+if (isStaticArray!R)
+{
+ return Cycle!R(input, index);
+}
+
+@safe unittest
+{
+ import std.algorithm.comparison : equal;
+ import std.internal.test.dummyrange : AllDummyRanges;
+
+ static assert(isForwardRange!(Cycle!(uint[])));
+
+ // Make sure ref is getting propagated properly.
+ int[] nums = [1,2,3];
+ auto c2 = cycle(nums);
+ c2[3]++;
+ assert(nums[0] == 2);
+
+ immutable int[] immarr = [1, 2, 3];
+
+ foreach (DummyType; AllDummyRanges)
+ {
+ static if (isForwardRange!DummyType)
+ {
+ DummyType dummy;
+ auto cy = cycle(dummy);
+ static assert(isForwardRange!(typeof(cy)));
+ auto t = take(cy, 20);
+ assert(equal(t, [1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10]));
+
+ const cRange = cy;
+ assert(cRange.front == 1);
+
+ static if (hasAssignableElements!DummyType)
+ {
+ {
+ cy.front = 66;
+ scope(exit) cy.front = 1;
+ assert(dummy.front == 66);
+ }
+
+ static if (isRandomAccessRange!DummyType)
+ {
+ {
+ cy[10] = 66;
+ scope(exit) cy[10] = 1;
+ assert(dummy.front == 66);
+ }
+
+ assert(cRange[10] == 1);
+ }
+ }
+
+ static if (hasSlicing!DummyType)
+ {
+ auto slice = cy[5 .. 15];
+ assert(equal(slice, [6, 7, 8, 9, 10, 1, 2, 3, 4, 5]));
+ static assert(is(typeof(slice) == typeof(takeExactly(cy, 5))));
+
+ auto infSlice = cy[7 .. $];
+ assert(equal(take(infSlice, 5), [8, 9, 10, 1, 2]));
+ static assert(isInfinite!(typeof(infSlice)));
+ }
+ }
+ }
+}
+
+@system unittest // For static arrays.
+{
+ import std.algorithm.comparison : equal;
+
+ int[3] a = [ 1, 2, 3 ];
+ static assert(isStaticArray!(typeof(a)));
+ auto c = cycle(a);
+ assert(a.ptr == c._ptr);
+ assert(equal(take(cycle(a), 5), [ 1, 2, 3, 1, 2 ][]));
+ static assert(isForwardRange!(typeof(c)));
+
+ // Test qualifiers on slicing.
+ alias C = typeof(c);
+ static assert(is(typeof(c[1 .. $]) == C));
+ const cConst = c;
+ static assert(is(typeof(cConst[1 .. $]) == const(C)));
+}
+
+@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; }
+ enum empty = false;
+ }
+
+ InfRange i;
+ NonForwardInfRange j;
+ auto c = cycle(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.comparison : equal;
+
+ int[5] arr = [0, 1, 2, 3, 4];
+ auto cleD = cycle(arr[]); //Dynamic
+ assert(equal(cleD[5 .. 10], arr[]));
+
+ //n is a multiple of 5 worth about 3/4 of size_t.max
+ auto n = size_t.max/4 + size_t.max/2;
+ n -= n % 5;
+
+ //Test index overflow
+ foreach (_ ; 0 .. 10)
+ {
+ cleD = cleD[n .. $];
+ assert(equal(cleD[5 .. 10], arr[]));
+ }
+}
+
+@system unittest
+{
+ import std.algorithm.comparison : equal;
+
+ int[5] arr = [0, 1, 2, 3, 4];
+ auto cleS = cycle(arr); //Static
+ assert(equal(cleS[5 .. 10], arr[]));
+
+ //n is a multiple of 5 worth about 3/4 of size_t.max
+ auto n = size_t.max/4 + size_t.max/2;
+ n -= n % 5;
+
+ //Test index overflow
+ foreach (_ ; 0 .. 10)
+ {
+ cleS = cleS[n .. $];
+ assert(equal(cleS[5 .. 10], arr[]));
+ }
+}
+
+@system unittest
+{
+ import std.algorithm.comparison : equal;
+
+ int[1] arr = [0];
+ auto cleS = cycle(arr);
+ cleS = cleS[10 .. $];
+ assert(equal(cleS[5 .. 10], 0.repeat(5)));
+ assert(cleS.front == 0);
+}
+
+@system unittest //10845
+{
+ 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]));
+}
+
+@safe unittest // 12177
+{
+ static assert(__traits(compiles, recurrence!q{a[n - 1] ~ a[n - 2]}("1", "0")));
+}
+
+// Issue 13390
+@system unittest
+{
+ import core.exception : AssertError;
+ import std.exception : assertThrown;
+ assertThrown!AssertError(cycle([0, 1, 2][0 .. 0]));
+}
+
+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]).
+
+ `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))
+{
+ import std.format : format; //for generic mixins
+ import std.typecons : Tuple;
+
+ alias R = Ranges;
+ private R ranges;
+ alias ElementType = Tuple!(staticMap!(.ElementType, R));
+ private StoppingPolicy stoppingPolicy = StoppingPolicy.shortest;
+
+/**
+ Builds an object. Usually this is invoked indirectly by using the
+ $(LREF zip) function.
+ */
+ this(R rs, StoppingPolicy s = StoppingPolicy.shortest)
+ {
+ ranges[] = rs[];
+ stoppingPolicy = s;
+ }
+
+/**
+ Returns $(D true) if the range is at end. The test depends on the
+ stopping policy.
+*/
+ static if (allSatisfy!(isInfinite, R))
+ {
+ // BUG: Doesn't propagate infiniteness if only some ranges are infinite
+ // and s == StoppingPolicy.longest. This isn't fixable in the
+ // current design since StoppingPolicy is known only at runtime.
+ enum bool empty = false;
+ }
+ else
+ {
+ ///
+ @property bool empty()
+ {
+ import std.exception : enforce;
+ import std.meta : anySatisfy;
+
+ final switch (stoppingPolicy)
+ {
+ case StoppingPolicy.shortest:
+ foreach (i, Unused; R)
+ {
+ if (ranges[i].empty) return true;
+ }
+ return false;
+ case StoppingPolicy.longest:
+ static if (anySatisfy!(isInfinite, R))
+ {
+ return false;
+ }
+ else
+ {
+ foreach (i, Unused; R)
+ {
+ if (!ranges[i].empty) return false;
+ }
+ return true;
+ }
+ case StoppingPolicy.requireSameLength:
+ foreach (i, Unused; R[1 .. $])
+ {
+ enforce(ranges[0].empty ==
+ ranges[i + 1].empty,
+ "Inequal-length ranges passed to Zip");
+ }
+ return ranges[0].empty;
+ }
+ assert(false);
+ }
+ }
+
+ static if (allSatisfy!(isForwardRange, R))
+ {
+ ///
+ @property Zip save()
+ {
+ //Zip(ranges[0].save, ranges[1].save, ..., stoppingPolicy)
+ return mixin (q{Zip(%(ranges[%s].save%|, %), stoppingPolicy)}.format(iota(0, R.length)));
+ }
+ }
+
+ private .ElementType!(R[i]) tryGetInit(size_t i)()
+ {
+ alias E = .ElementType!(R[i]);
+ static if (!is(typeof({static E i;})))
+ throw new Exception("Range with non-default constructable elements exhausted.");
+ else
+ return E.init;
+ }
+
+/**
+ Returns the current iterated element.
+*/
+ @property ElementType front()
+ {
+ @property tryGetFront(size_t i)(){return ranges[i].empty ? tryGetInit!i() : ranges[i].front;}
+ //ElementType(tryGetFront!0, tryGetFront!1, ...)
+ return mixin(q{ElementType(%(tryGetFront!%s, %))}.format(iota(0, R.length)));
+ }
+
+/**
+ Sets the front of all iterated ranges.
+*/
+ static if (allSatisfy!(hasAssignableElements, R))
+ {
+ @property void front(ElementType v)
+ {
+ foreach (i, Unused; R)
+ {
+ if (!ranges[i].empty)
+ {
+ ranges[i].front = v[i];
+ }
+ }
+ }
+ }
+
+/**
+ Moves out the front.
+*/
+ static if (allSatisfy!(hasMobileElements, R))
+ {
+ ElementType moveFront()
+ {
+ @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)));
+ }
+ }
+
+/**
+ Returns the rightmost element.
+*/
+ static if (allSatisfy!(isBidirectionalRange, R))
+ {
+ @property ElementType back()
+ {
+ //TODO: Fixme! BackElement != back of all ranges in case of jagged-ness
+
+ @property tryGetBack(size_t i)(){return ranges[i].empty ? tryGetInit!i() : ranges[i].back;}
+ //ElementType(tryGetBack!0, tryGetBack!1, ...)
+ return mixin(q{ElementType(%(tryGetBack!%s, %))}.format(iota(0, R.length)));
+ }
+
+/**
+ Moves out the back.
+*/
+ static if (allSatisfy!(hasMobileElements, R))
+ {
+ ElementType moveBack()
+ {
+ //TODO: Fixme! BackElement != back of all ranges in case of jagged-ness
+
+ @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)));
+ }
+ }
+
+/**
+ Returns the current iterated element.
+*/
+ static if (allSatisfy!(hasAssignableElements, R))
+ {
+ @property void back(ElementType v)
+ {
+ //TODO: Fixme! BackElement != back of all ranges in case of jagged-ness.
+ //Not sure the call is even legal for StoppingPolicy.longest
+
+ foreach (i, Unused; R)
+ {
+ if (!ranges[i].empty)
+ {
+ ranges[i].back = v[i];
+ }
+ }
+ }
+ }
+ }
+
+/**
+ Advances to the next element in all controlled ranges.
+*/
+ void popFront()
+ {
+ import std.exception : enforce;
+
+ final switch (stoppingPolicy)
+ {
+ case StoppingPolicy.shortest:
+ foreach (i, Unused; R)
+ {
+ assert(!ranges[i].empty);
+ ranges[i].popFront();
+ }
+ break;
+ case StoppingPolicy.longest:
+ foreach (i, Unused; R)
+ {
+ if (!ranges[i].empty) ranges[i].popFront();
+ }
+ break;
+ case StoppingPolicy.requireSameLength:
+ foreach (i, Unused; R)
+ {
+ enforce(!ranges[i].empty, "Invalid Zip object");
+ ranges[i].popFront();
+ }
+ break;
+ }
+ }
+
+/**
+ Calls $(D popBack) for all controlled ranges.
+*/
+ static if (allSatisfy!(isBidirectionalRange, R))
+ {
+ void popBack()
+ {
+ //TODO: Fixme! In case of jaggedness, this is wrong.
+ import std.exception : enforce;
+
+ final switch (stoppingPolicy)
+ {
+ case StoppingPolicy.shortest:
+ foreach (i, Unused; R)
+ {
+ assert(!ranges[i].empty);
+ ranges[i].popBack();
+ }
+ break;
+ case StoppingPolicy.longest:
+ foreach (i, Unused; R)
+ {
+ if (!ranges[i].empty) ranges[i].popBack();
+ }
+ break;
+ case StoppingPolicy.requireSameLength:
+ foreach (i, Unused; R)
+ {
+ enforce(!ranges[i].empty, "Invalid Zip object");
+ ranges[i].popBack();
+ }
+ break;
+ }
+ }
+ }
+
+/**
+ Returns the length of this range. Defined only if all ranges define
+ $(D length).
+*/
+ static if (allSatisfy!(hasLength, R))
+ {
+ @property auto length()
+ {
+ static if (Ranges.length == 1)
+ return ranges[0].length;
+ else
+ {
+ if (stoppingPolicy == StoppingPolicy.requireSameLength)
+ return ranges[0].length;
+
+ //[min|max](ranges[0].length, ranges[1].length, ...)
+ import std.algorithm.comparison : min, max;
+ if (stoppingPolicy == StoppingPolicy.shortest)
+ return mixin(q{min(%(ranges[%s].length%|, %))}.format(iota(0, R.length)));
+ else
+ return mixin(q{max(%(ranges[%s].length%|, %))}.format(iota(0, R.length)));
+ }
+ }
+
+ alias opDollar = length;
+ }
+
+/**
+ Returns a slice of the range. Defined only if all range define
+ slicing.
+*/
+ static if (allSatisfy!(hasSlicing, R))
+ {
+ auto opSlice(size_t from, size_t to)
+ {
+ //Slicing an infinite range yields the type Take!R
+ //For finite ranges, the type Take!R aliases to R
+ alias ZipResult = Zip!(staticMap!(Take, R));
+
+ //ZipResult(ranges[0][from .. to], ranges[1][from .. to], ..., stoppingPolicy)
+ return mixin (q{ZipResult(%(ranges[%s][from .. to]%|, %), stoppingPolicy)}.format(iota(0, R.length)));
+ }
+ }
+
+/**
+ Returns the $(D n)th element in the composite range. Defined if all
+ ranges offer random access.
+*/
+ static if (allSatisfy!(isRandomAccessRange, R))
+ {
+ ElementType opIndex(size_t n)
+ {
+ //TODO: Fixme! This may create an out of bounds access
+ //for StoppingPolicy.longest
+
+ //ElementType(ranges[0][n], ranges[1][n], ...)
+ return mixin (q{ElementType(%(ranges[%s][n]%|, %))}.format(iota(0, R.length)));
+ }
+
+/**
+ Assigns to the $(D n)th element in the composite range. Defined if
+ all ranges offer random access.
+*/
+ static if (allSatisfy!(hasAssignableElements, R))
+ {
+ void opIndexAssign(ElementType v, size_t n)
+ {
+ //TODO: Fixme! Not sure the call is even legal for StoppingPolicy.longest
+ foreach (i, Range; R)
+ {
+ ranges[i][n] = v[i];
+ }
+ }
+ }
+
+/**
+ Destructively reads the $(D n)th element in the composite
+ range. Defined if all ranges offer random access.
+*/
+ static if (allSatisfy!(hasMobileElements, R))
+ {
+ ElementType moveAt(size_t n)
+ {
+ //TODO: Fixme! This may create an out of bounds access
+ //for StoppingPolicy.longest
+
+ //ElementType(ranges[0].moveAt(n), ranges[1].moveAt(n), ..., )
+ return mixin (q{ElementType(%(ranges[%s].moveAt(n)%|, %))}.format(iota(0, R.length)));
+ }
+ }
+ }
+}
+
+/// Ditto
+auto zip(Ranges...)(Ranges ranges)
+if (Ranges.length && allSatisfy!(isInputRange, Ranges))
+{
+ return Zip!Ranges(ranges);
+}
+
+///
+pure @safe unittest
+{
+ 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]));
+}
+
+///
+pure @safe unittest
+{
+ import std.conv : to;
+
+ 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))
+{
+ return Zip!Ranges(ranges, sp);
+}
+
+/**
+ Dictates how iteration in a $(D Zip) should stop. By default stop at
+ the end of the shortest of all ranges.
+*/
+enum StoppingPolicy
+{
+ /// Stop when the shortest range is exhausted
+ shortest,
+ /// Stop when the longest range is exhausted
+ longest,
+ /// Require that all ranges are equal
+ requireSameLength,
+}
+
+@system unittest
+{
+ 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;
+
+ int[] a = [ 1, 2, 3 ];
+ float[] b = [ 1.0, 2.0, 3.0 ];
+ foreach (e; zip(a, b))
+ {
+ assert(e[0] == e[1]);
+ }
+
+ swap(a[0], a[1]);
+ auto z = zip(a, b);
+ //swap(z.front(), z.back());
+ sort!("a[0] < b[0]")(zip(a, b));
+ assert(a == [1, 2, 3]);
+ assert(b == [2.0, 1.0, 3.0]);
+
+ z = zip(StoppingPolicy.requireSameLength, a, b);
+ assertNotThrown(z.popBack());
+ assertNotThrown(z.popBack());
+ assertNotThrown(z.popBack());
+ assert(z.empty);
+ assertThrown(z.popBack());
+
+ a = [ 1, 2, 3 ];
+ b = [ 1.0, 2.0, 3.0 ];
+ sort!("a[0] > b[0]")(zip(StoppingPolicy.requireSameLength, a, b));
+ assert(a == [3, 2, 1]);
+ assert(b == [3.0, 2.0, 1.0]);
+
+ a = [];
+ b = [];
+ assert(zip(StoppingPolicy.requireSameLength, a, b).empty);
+
+ // Test infiniteness propagation.
+ static assert(isInfinite!(typeof(zip(repeat(1), repeat(1)))));
+
+ // Test stopping policies with both value and reference.
+ auto a1 = [1, 2];
+ auto a2 = [1, 2, 3];
+ auto stuff = tuple(tuple(a1, a2),
+ tuple(filter!"a"(a1), filter!"a"(a2)));
+
+ alias FOO = Zip!(immutable(int)[], immutable(float)[]);
+
+ foreach (t; stuff.expand)
+ {
+ auto arr1 = t[0];
+ auto arr2 = t[1];
+ auto zShortest = zip(arr1, arr2);
+ assert(equal(map!"a[0]"(zShortest), [1, 2]));
+ assert(equal(map!"a[1]"(zShortest), [1, 2]));
+
+ try {
+ auto zSame = zip(StoppingPolicy.requireSameLength, arr1, arr2);
+ foreach (elem; zSame) {}
+ assert(0);
+ } catch (Throwable) { /* It's supposed to throw.*/ }
+
+ auto zLongest = zip(StoppingPolicy.longest, arr1, arr2);
+ assert(!zLongest.ranges[0].empty);
+ assert(!zLongest.ranges[1].empty);
+
+ zLongest.popFront();
+ zLongest.popFront();
+ assert(!zLongest.empty);
+ assert(zLongest.ranges[0].empty);
+ assert(!zLongest.ranges[1].empty);
+
+ zLongest.popFront();
+ assert(zLongest.empty);
+ }
+
+ // BUG 8900
+ 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[])));
+
+
+ // These unittests pass, but make the compiler consume an absurd amount
+ // of RAM and time. Therefore, they should only be run if explicitly
+ // uncommented when making changes to Zip. Also, running them using
+ // make -fwin32.mak unittest makes the compiler completely run out of RAM.
+ // You need to test just this module.
+ /+
+ foreach (DummyType1; AllDummyRanges)
+ {
+ DummyType1 d1;
+ 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 assert(isForwardRange!(typeof(r)));
+ }
+
+ static if (isBidirectionalRange!DummyType1 &&
+ isBidirectionalRange!DummyType2) {
+ static assert(isBidirectionalRange!(typeof(r)));
+ }
+ static if (isRandomAccessRange!DummyType1 &&
+ isRandomAccessRange!DummyType2) {
+ static assert(isRandomAccessRange!(typeof(r)));
+ }
+ }
+ }
+ +/
+}
+
+pure @safe unittest
+{
+ import std.algorithm.sorting : sort;
+
+ auto a = [5,4,3,2,1];
+ auto b = [3,1,2,5,6];
+ auto z = zip(a, b);
+
+ sort!"a[0] < b[0]"(z);
+
+ assert(a == [1, 2, 3, 4, 5]);
+ assert(b == [6, 5, 2, 1, 3]);
+}
+
+@safe pure unittest
+{
+ import std.algorithm.comparison : equal;
+ import std.typecons : tuple;
+
+ auto LL = iota(1L, 1000L);
+ auto z = zip(LL, [4]);
+
+ assert(equal(z, [tuple(1L,4)]));
+
+ auto LL2 = iota(0L, 500L);
+ auto z2 = zip([7], LL2);
+ assert(equal(z2, [tuple(7, 0L)]));
+}
+
+// Text for Issue 11196
+@safe pure unittest
+{
+ import std.exception : assertThrown;
+
+ static struct S { @disable this(); }
+ 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);
+}
+
+@safe pure unittest //12007
+{
+ static struct R
+ {
+ enum empty = false;
+ void popFront(){}
+ int front(){return 1;} @property
+ R save(){return this;} @property
+ void opAssign(R) @disable;
+ }
+ R r;
+ auto z = zip(r, r);
+ assert(z.save == z);
+}
+
+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, bool reverse)
+{
+ import std.format : format;
+
+ string[] params;
+ 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);
+ 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 %s(scope int delegate(%s) dg)
+ {
+ import std.exception : enforce;
+
+ auto ranges = _ranges;
+ int res;
+ %s
+
+ while (%s)
+ {
+ res = dg(%s);
+ if (res) break;
+ %s
+ %s
+ }
+
+ if (_stoppingPolicy == StoppingPolicy.requireSameLength)
+ {
+ foreach (range; ranges)
+ enforce(range.empty);
+ }
+ return res;
+ }
+ }, name, params.join(", "), indexDef,
+ emptyChecks.join(" && "), dgArgs.join(", "),
+ popFronts.join("\n "),
+ indexInc);
+}
+
+/**
+ 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
+ lengths and $(D s) == $(D StoppingPolicy.requireSameLength), throw an
+ 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).
+
+ See_Also: $(LREF zip)
+
+ `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))
+{
+ ///
+ this(R ranges, StoppingPolicy sp = StoppingPolicy.shortest)
+ {
+ import std.exception : enforce;
+
+ _ranges = ranges;
+ enforce(sp != StoppingPolicy.longest,
+ "Can't use StoppingPolicy.Longest on Lockstep.");
+ _stoppingPolicy = sp;
+ }
+
+ 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;
+ R _ranges;
+ 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)
+{
+ alias Lockstep = Range;
+}
+
+/// Ditto
+Lockstep!(Ranges) lockstep(Ranges...)(Ranges ranges)
+if (allSatisfy!(isInputRange, Ranges))
+{
+ return Lockstep!(Ranges)(ranges);
+}
+/// Ditto
+Lockstep!(Ranges) lockstep(Ranges...)(Ranges ranges, StoppingPolicy s)
+if (allSatisfy!(isInputRange, Ranges))
+{
+ static if (Ranges.length > 1)
+ return Lockstep!Ranges(ranges, s);
+ else
+ return ranges[0];
+}
+
+///
+@system unittest
+{
+ auto arr1 = [1,2,3,4,5,100];
+ auto arr2 = [6,7,8,9,10];
+
+ foreach (ref a, b; lockstep(arr1, arr2))
+ {
+ a += b;
+ }
+
+ 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);
+ }
+}
+
+@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;
+
+ // The filters are to make these the lowest common forward denominator ranges,
+ // i.e. w/o ref return, random access, length, etc.
+ auto foo = filter!"a"([1,2,3,4,5]);
+ immutable bar = [6f,7f,8f,9f,10f].idup;
+ auto l = lockstep(foo, bar);
+
+ // Should work twice. These are forward ranges with implicit save.
+ foreach (i; 0 .. 2)
+ {
+ uint[] res1;
+ float[] res2;
+
+ foreach (a, ref b; l)
+ {
+ res1 ~= a;
+ res2 ~= b;
+ }
+
+ assert(res1 == [1,2,3,4,5]);
+ assert(res2 == [6,7,8,9,10]);
+ assert(bar == [6f,7f,8f,9f,10f]);
+ }
+
+ // Doc example.
+ auto arr1 = [1,2,3,4,5];
+ auto arr2 = [6,7,8,9,10];
+
+ foreach (ref a, ref b; lockstep(arr1, arr2))
+ {
+ a += b;
+ }
+
+ assert(arr1 == [7,9,11,13,15]);
+
+ // Make sure StoppingPolicy.requireSameLength doesn't throw.
+ auto ls = lockstep(arr1, arr2, StoppingPolicy.requireSameLength);
+
+ int k = 1;
+ foreach (a, b; ls)
+ {
+ assert(a - b == k);
+ ++k;
+ }
+
+ // Make sure StoppingPolicy.requireSameLength throws.
+ arr2.popBack();
+ ls = lockstep(arr1, arr2, StoppingPolicy.requireSameLength);
+
+ try {
+ foreach (a, b; ls) {}
+ assert(0);
+ } catch (Exception) {}
+
+ // Just make sure 1-range case instantiates. This hangs the compiler
+ // when no explicit stopping policy is specified due to Bug 4652.
+ auto stuff = lockstep([1,2,3,4,5], StoppingPolicy.shortest);
+ foreach (int i, a; stuff)
+ {
+ assert(stuff[i] == a);
+ }
+
+ // Test with indexing.
+ uint[] res1;
+ float[] res2;
+ size_t[] indices;
+ foreach (i, a, b; lockstep(foo, bar))
+ {
+ indices ~= i;
+ res1 ~= a;
+ res2 ~= b;
+ }
+
+ assert(indices == to!(size_t[])([0, 1, 2, 3, 4]));
+ assert(res1 == [1,2,3,4,5]);
+ assert(res2 == [6f,7f,8f,9f,10f]);
+
+ // Make sure we've worked around the relevant compiler bugs and this at least
+ // compiles w/ >2 ranges.
+ lockstep(foo, foo, foo);
+
+ // Make sure it works with const.
+ const(int[])[] foo2 = [[1, 2, 3]];
+ const(int[])[] bar2 = [[4, 5, 6]];
+ auto c = chain(foo2, bar2);
+
+ 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
+values. The sequence comes in the form of an infinite forward
+range. The type $(D Recurrence) itself is seldom used directly; most
+often, recurrences are obtained by calling the function $(D
+recurrence).
+
+When calling $(D recurrence), the function that computes the next
+value is specified as a template argument, and the initial values in
+the recurrence are passed as regular arguments. For example, in a
+Fibonacci sequence, there are two initial values (and therefore a
+state size of 2) because computing the next Fibonacci value needs the
+past two values.
+
+The signature of this function should be:
+----
+auto fun(R)(R state, size_t n)
+----
+where $(D n) will be the index of the current value, and $(D state) will be an
+opaque state vector that can be indexed with array-indexing notation
+$(D state[i]), where valid values of $(D i) range from $(D (n - 1)) to
+$(D (n - State.length)).
+
+If the function is passed in string form, the state has name $(D "a")
+and the zero-based index in the recurrence has name $(D "n"). The
+given string must return the desired value for $(D a[n]) given $(D a[n
+- 1]), $(D a[n - 2]), $(D a[n - 3]),..., $(D a[n - stateSize]). The
+state size is dictated by the number of arguments passed to the call
+to $(D recurrence). The $(D Recurrence) struct itself takes care of
+managing the recurrence's state and shifting it appropriately.
+ */
+struct Recurrence(alias fun, StateType, size_t stateSize)
+{
+ import std.functional : binaryFun;
+
+ StateType[stateSize] _state;
+ size_t _n;
+
+ this(StateType[stateSize] initial) { _state = initial; }
+
+ void popFront()
+ {
+ static auto trustedCycle(ref typeof(_state) s) @trusted
+ {
+ return cycle(s);
+ }
+ // The cast here is reasonable because fun may cause integer
+ // promotion, but needs to return a StateType to make its operation
+ // closed. Therefore, we have no other choice.
+ _state[_n % stateSize] = cast(StateType) binaryFun!(fun, "a", "n")(
+ trustedCycle(_state), _n + stateSize);
+ ++_n;
+ }
+
+ @property StateType front()
+ {
+ return _state[_n % stateSize];
+ }
+
+ @property typeof(this) save()
+ {
+ return this;
+ }
+
+ enum bool empty = false;
+}
+
+///
+@safe unittest
+{
+ 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]
+ auto fib = recurrence!("a[n-1] + a[n-2]")(1, 1);
+ assert(fib.take(10).equal([1, 1, 2, 3, 5, 8, 13, 21, 34, 55]));
+
+ // The factorials, using function in lambda form:
+ auto fac = recurrence!((a,n) => a[n-1] * n)(1);
+ assert(take(fac, 10).equal([
+ 1, 1, 2, 6, 24, 120, 720, 5040, 40320, 362880
+ ]));
+
+ // The triangular numbers, using function in explicit form:
+ static size_t genTriangular(R)(R state, size_t n)
+ {
+ return state[n-1] + n;
+ }
+ auto tri = recurrence!genTriangular(0);
+ assert(take(tri, 10).equal([0, 1, 3, 6, 10, 15, 21, 28, 36, 45]));
+}
+
+/// Ditto
+Recurrence!(fun, CommonType!(State), State.length)
+recurrence(alias fun, State...)(State initial)
+{
+ CommonType!(State)[State.length] state;
+ foreach (i, Unused; State)
+ {
+ state[i] = initial[i];
+ }
+ return typeof(return)(state);
+}
+
+@safe unittest
+{
+ import std.algorithm.comparison : equal;
+
+ auto fib = recurrence!("a[n-1] + a[n-2]")(1, 1);
+ static assert(isForwardRange!(typeof(fib)));
+
+ int[] witness = [1, 1, 2, 3, 5, 8, 13, 21, 34, 55 ];
+ assert(equal(take(fib, 10), witness));
+ foreach (e; take(fib, 10)) {}
+ auto fact = recurrence!("n * a[n-1]")(1);
+ assert( equal(take(fact, 10), [1, 1, 2, 2*3, 2*3*4, 2*3*4*5, 2*3*4*5*6,
+ 2*3*4*5*6*7, 2*3*4*5*6*7*8, 2*3*4*5*6*7*8*9][]) );
+ auto piapprox = recurrence!("a[n] + (n & 1 ? 4.0 : -4.0) / (2 * n + 3)")(4.0);
+ foreach (e; take(piapprox, 20)) {}
+ // Thanks to yebblies for this test and the associated fix
+ auto r = recurrence!"a[n-2]"(1, 2);
+ witness = [1, 2, 1, 2, 1];
+ assert(equal(take(r, 5), witness));
+}
+
+/**
+ $(D Sequence) is similar to $(D Recurrence) except that iteration is
+ 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
+ range, as opposed to the regular $(D Recurrence), which only offers
+ forward iteration.
+
+ The state of the sequence is stored as a $(D Tuple) so it can be
+ heterogeneous.
+*/
+struct Sequence(alias fun, State)
+{
+private:
+ import std.functional : binaryFun;
+
+ alias compute = binaryFun!(fun, "a", "n");
+ alias ElementType = typeof(compute(State.init, cast(size_t) 1));
+ State _state;
+ size_t _n;
+
+ static struct DollarToken{}
+
+public:
+ this(State initial, size_t n = 0)
+ {
+ _state = initial;
+ _n = n;
+ }
+
+ @property ElementType front()
+ {
+ return compute(_state, _n);
+ }
+
+ void popFront()
+ {
+ ++_n;
+ }
+
+ enum opDollar = DollarToken();
+
+ auto opSlice(size_t lower, size_t upper)
+ in
+ {
+ assert(
+ upper >= lower,
+ "Attempting to slice a Sequence with a larger first argument than the second."
+ );
+ }
+ body
+ {
+ return typeof(this)(_state, _n + lower).take(upper - lower);
+ }
+
+ auto opSlice(size_t lower, DollarToken)
+ {
+ return typeof(this)(_state, _n + lower);
+ }
+
+ ElementType opIndex(size_t n)
+ {
+ return compute(_state, n + _n);
+ }
+
+ enum bool empty = false;
+
+ @property Sequence save() { return this; }
+}
+
+/// Ditto
+auto sequence(alias fun, State...)(State args)
+{
+ import std.typecons : Tuple, tuple;
+ alias Return = Sequence!(fun, Tuple!State);
+ return Return(tuple(args));
+}
+
+/// Odd numbers, using function in string form:
+@safe unittest
+{
+ auto odds = sequence!("a[0] + n * a[1]")(1, 2);
+ assert(odds.front == 1);
+ odds.popFront();
+ assert(odds.front == 3);
+ odds.popFront();
+ assert(odds.front == 5);
+}
+
+/// Triangular numbers, using function in lambda form:
+@safe unittest
+{
+ auto tri = sequence!((a,n) => n*(n+1)/2)();
+
+ // Note random access
+ assert(tri[0] == 0);
+ assert(tri[3] == 6);
+ assert(tri[1] == 1);
+ assert(tri[4] == 10);
+ assert(tri[2] == 3);
+}
+
+/// Fibonacci numbers, using function in explicit form:
+@safe unittest
+{
+ import std.math : pow, round, sqrt;
+ static ulong computeFib(S)(S state, size_t n)
+ {
+ // Binet's formula
+ return cast(ulong)(round((pow(state[0], n+1) - pow(state[1], n+1)) /
+ state[2]));
+ }
+ auto fib = sequence!computeFib(
+ (1.0 + sqrt(5.0)) / 2.0, // Golden Ratio
+ (1.0 - sqrt(5.0)) / 2.0, // Conjugate of Golden Ratio
+ sqrt(5.0));
+
+ // Note random access with [] operator
+ assert(fib[1] == 1);
+ assert(fib[4] == 5);
+ assert(fib[3] == 3);
+ assert(fib[2] == 2);
+ assert(fib[9] == 55);
+}
+
+@safe unittest
+{
+ import std.typecons : Tuple, tuple;
+ auto y = Sequence!("a[0] + n * a[1]", Tuple!(int, int))(tuple(0, 4));
+ static assert(isForwardRange!(typeof(y)));
+
+ //@@BUG
+ //auto y = sequence!("a[0] + n * a[1]")(0, 4);
+ //foreach (e; take(y, 15))
+ {} //writeln(e);
+
+ auto odds = Sequence!("a[0] + n * a[1]", Tuple!(int, int))(
+ tuple(1, 2));
+ for (int currentOdd = 1; currentOdd <= 21; currentOdd += 2)
+ {
+ assert(odds.front == odds[0]);
+ assert(odds[0] == currentOdd);
+ odds.popFront();
+ }
+}
+
+@safe unittest
+{
+ import std.algorithm.comparison : equal;
+
+ auto odds = sequence!("a[0] + n * a[1]")(1, 2);
+ static assert(hasSlicing!(typeof(odds)));
+
+ //Note: don't use drop or take as the target of an equal,
+ //since they'll both just forward to opSlice, making the tests irrelevant
+
+ // static slicing tests
+ assert(equal(odds[0 .. 5], [1, 3, 5, 7, 9]));
+ assert(equal(odds[3 .. 7], [7, 9, 11, 13]));
+
+ // relative slicing test, testing slicing is NOT agnostic of state
+ auto odds_less5 = odds.drop(5); //this should actually call odds[5 .. $]
+ assert(equal(odds_less5[0 .. 3], [11, 13, 15]));
+ assert(equal(odds_less5[0 .. 10], odds[5 .. 15]));
+
+ //Infinite slicing tests
+ odds = odds[10 .. $];
+ assert(equal(odds.take(3), [21, 23, 25]));
+}
+
+// Issue 5036
+@safe unittest
+{
+ auto s = sequence!((a, n) => new int)(0);
+ assert(s.front != s.front); // no caching
+}
+
+// iota
+/**
+ Creates a range of values that span the given starting and stopping
+ values.
+
+ Params:
+ begin = The starting value.
+ end = The value that serves as the stopping criterion. This value is not
+ included in the range.
+ step = The value to add to the current value at each iteration.
+
+ Returns:
+ A range that goes through the numbers $(D begin), $(D begin + step),
+ $(D begin + 2 * step), $(D ...), up to and excluding $(D end).
+
+ The two-argument overloads have $(D step = 1). If $(D begin < end && step <
+ 0) or $(D begin > end && step > 0) or $(D begin == end), then an empty range
+ is returned. If $(D step == 0) then $(D begin == end) is an error.
+
+ For built-in types, the range returned is a random access range. For
+ user-defined types that support $(D ++), the range is an input
+ range.
+
+ An integral iota also supports $(D in) operator from the right. It takes
+ the stepping into account, the integral won't be considered
+ contained if it falls between two consecutive values of the range.
+ $(D contains) does the same as in, but from lefthand side.
+
+ Example:
+ ---
+ void main()
+ {
+ import std.stdio;
+
+ // The following groups all produce the same output of:
+ // 0 1 2 3 4
+
+ foreach (i; 0 .. 5)
+ writef("%s ", i);
+ writeln();
+
+ import std.range : iota;
+ foreach (i; iota(0, 5))
+ writef("%s ", i);
+ writeln();
+
+ writefln("%(%s %|%)", iota(0, 5));
+
+ 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();
+ }
+ ---
+*/
+auto iota(B, E, S)(B begin, E end, S step)
+if ((isIntegral!(CommonType!(B, E)) || isPointer!(CommonType!(B, E)))
+ && isIntegral!S)
+{
+ import std.conv : unsigned;
+
+ alias Value = CommonType!(Unqual!B, Unqual!E);
+ alias StepType = Unqual!S;
+
+ assert(step != 0 || begin == end);
+
+ static struct Result
+ {
+ 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)
+ {
+ // 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) / (0 - step)) <= size_t.max);
+ // Cast below can't fail because current > pastLast
+ this.last = cast(Value) (pastLast + 1);
+ this.last += unsigned(current - this.last) % (0 - step);
+ }
+ else
+ {
+ // Initialize an empty range
+ this.step = 0;
+ return;
+ }
+ this.step = step;
+ this.current = current;
+ }
+
+ @property bool empty() const { return step == 0; }
+ @property inout(Value) front() inout { assert(!empty); return current; }
+ void popFront()
+ {
+ assert(!empty);
+ if (current == last) step = 0;
+ else current += 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; }
+
+ inout(Value) opIndex(ulong n) inout
+ {
+ assert(n < this.length);
+
+ // Just cast to Value here because doing so gives overflow behavior
+ // consistent with calling popFront() n times.
+ return cast(inout Value) (current + step * n);
+ }
+ auto opBinaryRight(string op)(Value val) const
+ if (op == "in")
+ {
+ if (empty) return false;
+ //cast to avoid becoming unsigned
+ auto supposedIndex = cast(StepType)(val - current) / step;
+ return supposedIndex < length && supposedIndex * step + current == val;
+ }
+ auto contains(Value x){return x in this;}
+ inout(Result) opSlice() inout { return this; }
+ inout(Result) opSlice(ulong lower, ulong upper) inout
+ {
+ assert(upper >= lower && upper <= this.length);
+
+ return cast(inout Result) Result(
+ cast(Value)(current + lower * step),
+ cast(Value)(current + upper * step),
+ step);
+ }
+ @property size_t length() const
+ {
+ if (step > 0)
+ return 1 + cast(size_t) (unsigned(last - current) / step);
+ if (step < 0)
+ return 1 + cast(size_t) (unsigned(current - last) / (0 - step));
+ return 0;
+ }
+
+ alias opDollar = length;
+ }
+
+ return Result(begin, end, step);
+}
+
+/// Ditto
+auto iota(B, E)(B begin, E end)
+if (isFloatingPoint!(CommonType!(B, E)))
+{
+ return iota(begin, end, CommonType!(B, E)(1));
+}
+
+/// Ditto
+auto iota(B, E)(B begin, E end)
+if (isIntegral!(CommonType!(B, E)) || isPointer!(CommonType!(B, E)))
+{
+ import std.conv : unsigned;
+
+ alias Value = CommonType!(Unqual!B, Unqual!E);
+
+ static struct Result
+ {
+ private Value current, pastLast;
+
+ this(Value current, Value pastLast)
+ {
+ if (current < pastLast)
+ {
+ assert(unsigned(pastLast - current) <= size_t.max);
+
+ this.current = current;
+ this.pastLast = pastLast;
+ }
+ else
+ {
+ // Initialize an empty range
+ this.current = this.pastLast = current;
+ }
+ }
+
+ @property bool empty() const { return current == pastLast; }
+ @property inout(Value) front() inout { assert(!empty); return current; }
+ void popFront() { assert(!empty); ++current; }
+
+ @property inout(Value) back() inout { assert(!empty); return cast(inout(Value))(pastLast - 1); }
+ void popBack() { assert(!empty); --pastLast; }
+
+ @property auto save() { return this; }
+
+ inout(Value) opIndex(size_t n) inout
+ {
+ assert(n < this.length);
+
+ // Just cast to Value here because doing so gives overflow behavior
+ // consistent with calling popFront() n times.
+ return cast(inout Value) (current + n);
+ }
+ auto opBinaryRight(string op)(Value val) const
+ if (op == "in")
+ {
+ return current <= val && val < pastLast;
+ }
+ auto contains(Value x){return x in this;}
+ inout(Result) opSlice() inout { return this; }
+ inout(Result) opSlice(ulong lower, ulong upper) inout
+ {
+ assert(upper >= lower && upper <= this.length);
+
+ return cast(inout Result) Result(cast(Value)(current + lower),
+ cast(Value)(pastLast - (length - upper)));
+ }
+ @property size_t length() const
+ {
+ return cast(size_t)(pastLast - current);
+ }
+
+ alias opDollar = length;
+ }
+
+ return Result(begin, end);
+}
+
+/// Ditto
+auto iota(E)(E end)
+if (is(typeof(iota(E(0), end))))
+{
+ E begin = E(0);
+ return iota(begin, end);
+}
+
+/// Ditto
+// Specialization for floating-point types
+auto iota(B, E, S)(B begin, E end, S step)
+if (isFloatingPoint!(CommonType!(B, E, S)))
+in
+{
+ assert(step != 0, "iota: step must not be 0");
+ assert((end - begin) / step >= 0, "iota: incorrect startup parameters");
+}
+body
+{
+ alias Value = Unqual!(CommonType!(B, E, S));
+ static struct Result
+ {
+ private Value start, step;
+ private size_t index, count;
+
+ this(Value start, Value end, Value step)
+ {
+ import std.conv : to;
+
+ this.start = start;
+ this.step = step;
+ immutable fcount = (end - start) / step;
+ count = to!size_t(fcount);
+ auto pastEnd = start + count * step;
+ if (step > 0)
+ {
+ if (pastEnd < end) ++count;
+ assert(start + count * step >= end);
+ }
+ else
+ {
+ if (pastEnd > end) ++count;
+ assert(start + count * step <= end);
+ }
+ }
+
+ @property bool empty() const { return index == count; }
+ @property Value front() const { assert(!empty); return start + step * index; }
+ void popFront()
+ {
+ assert(!empty);
+ ++index;
+ }
+ @property Value back() const
+ {
+ assert(!empty);
+ return start + step * (count - 1);
+ }
+ void popBack()
+ {
+ assert(!empty);
+ --count;
+ }
+
+ @property auto save() { return this; }
+
+ Value opIndex(size_t n) const
+ {
+ assert(n < count);
+ return start + step * (n + index);
+ }
+ inout(Result) opSlice() inout
+ {
+ return this;
+ }
+ inout(Result) opSlice(size_t lower, size_t upper) inout
+ {
+ assert(upper >= lower && upper <= count);
+
+ Result ret = this;
+ ret.index += lower;
+ ret.count = upper - lower + ret.index;
+ return cast(inout Result) ret;
+ }
+ @property size_t length() const
+ {
+ return count - index;
+ }
+
+ alias opDollar = length;
+ }
+
+ return Result(begin, end, step);
+}
+
+///
+@safe unittest
+{
+ import std.algorithm.comparison : equal;
+ import std.math : approxEqual;
+
+ auto r = iota(0, 10, 1);
+ assert(equal(r, [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]));
+ assert(equal(r, [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]));
+ assert(3 in r);
+ assert(r.contains(3)); //Same as above
+ assert(!(10 in r));
+ assert(!(-8 in r));
+ r = iota(0, 11, 3);
+ assert(equal(r, [0, 3, 6, 9]));
+ assert(r[2] == 6);
+ assert(!(2 in r));
+ auto rf = iota(0.0, 0.5, 0.1);
+ assert(approxEqual(rf, [0.0, 0.1, 0.2, 0.3, 0.4]));
+}
+
+nothrow @nogc @safe unittest
+{
+ //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 @system unittest
+{//check the contracts
+ 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));
+}
+
+@system unittest
+{
+ int[] a = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9];
+ auto r1 = iota(a.ptr, a.ptr + a.length, 1);
+ assert(r1.front == a.ptr);
+ assert(r1.back == a.ptr + a.length - 1);
+ assert(&a[4] in r1);
+}
+
+@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.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];
+ assert(equal(rSlice, [2, 3, 4, 5, 6, 7]));
+
+ rSlice.popFront();
+ assert(rSlice[0] == rSlice.front);
+ assert(rSlice.front == 3);
+
+ rSlice.popBack();
+ assert(rSlice[rSlice.length - 1] == rSlice.back);
+ assert(rSlice.back == 6);
+
+ rSlice = r[0 .. 4];
+ assert(equal(rSlice, [0, 1, 2, 3]));
+ assert(3 in rSlice);
+ assert(!(4 in rSlice));
+
+ auto rr = iota(10);
+ assert(equal(rr, [0, 1, 2, 3, 4, 5, 6, 7, 8, 9][]));
+
+ r = iota(0, -10, -1);
+ assert(equal(r, [0, -1, -2, -3, -4, -5, -6, -7, -8, -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];
+ assert(equal(rSlice, [-3]));
+
+ r = iota(0, -7, -3);
+ assert(equal(r, [0, -3, -6][]));
+ assert(0 in r);
+ assert(-6 in r);
+ rSlice = r[1 .. 3];
+ assert(equal(rSlice, [-3, -6]));
+ assert(!(0 in rSlice));
+ assert(!(-2 in rSlice));
+ assert(!(-5 in rSlice));
+ assert(!(3 in rSlice));
+ assert(!(-9 in rSlice));
+
+ r = iota(0, 11, 3);
+ assert(equal(r, [0, 3, 6, 9][]));
+ assert(r[2] == 6);
+ rSlice = r[1 .. 3];
+ assert(equal(rSlice, [3, 6]));
+
+ auto rf = iota(0.0, 0.5, 0.1);
+ assert(approxEqual(rf, [0.0, 0.1, 0.2, 0.3, 0.4][]));
+ assert(rf.length == 5);
+
+ rf.popFront();
+ assert(rf.length == 4);
+
+ auto rfSlice = rf[1 .. 4];
+ assert(rfSlice.length == 3);
+ assert(approxEqual(rfSlice, [0.2, 0.3, 0.4]));
+
+ rfSlice.popFront();
+ assert(approxEqual(rfSlice[0], 0.3));
+
+ rf.popFront();
+ assert(rf.length == 3);
+
+ rfSlice = rf[1 .. 3];
+ assert(rfSlice.length == 2);
+ assert(approxEqual(rfSlice, [0.3, 0.4]));
+ assert(approxEqual(rfSlice[0], 0.3));
+
+ // With something just above 0.5
+ rf = iota(0.0, nextUp(0.5), 0.1);
+ assert(approxEqual(rf, [0.0, 0.1, 0.2, 0.3, 0.4, 0.5][]));
+ rf.popBack();
+ assert(rf[rf.length - 1] == rf.back);
+ assert(approxEqual(rf.back, 0.4));
+ assert(rf.length == 5);
+
+ // 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];
+ assert(approxEqual(rfSlice, [-0.2, -0.3, -0.4]));
+
+ rf = iota(0.0, nextDown(-0.5), -0.1);
+ assert(approxEqual(rf, [0.0, -0.1, -0.2, -0.3, -0.4, -0.5][]));
+
+ // iota of longs
+ auto rl = iota(5_000_000L);
+ assert(rl.length == 5_000_000L);
+ assert(0 in rl);
+ assert(4_000_000L in rl);
+ assert(!(-4_000_000L in rl));
+ assert(!(5_000_000L in rl));
+
+ // iota of longs with steps
+ auto iota_of_longs_with_steps = iota(50L, 101L, 10);
+ assert(iota_of_longs_with_steps.length == 6);
+ assert(equal(iota_of_longs_with_steps, [50L, 60L, 70L, 80L, 90L, 100L]));
+
+ // iota of unsigned zero length (issue 6222, actually trying to consume it
+ // is the only way to find something is wrong because the public
+ // properties are all correct)
+ auto iota_zero_unsigned = iota(0, 0u, 3);
+ assert(count(iota_zero_unsigned) == 0);
+
+ // unsigned reverse iota can be buggy if .length doesn't take them into
+ // account (issue 7982).
+ assert(iota(10u, 0u, -1).length == 10);
+ assert(iota(10u, 0u, -2).length == 5);
+ assert(iota(uint.max, uint.max-10, -1).length == 10);
+ assert(iota(uint.max, uint.max-10, -2).length == 5);
+ assert(iota(uint.max, 0u, -1).length == uint.max);
+
+ assert(20 in iota(20u, 10u, -2));
+ assert(16 in iota(20u, 10u, -2));
+ assert(!(15 in iota(20u, 10u, -2)));
+ assert(!(10 in iota(20u, 10u, -2)));
+ assert(!(uint.max in iota(20u, 10u, -1)));
+ assert(!(int.min in iota(20u, 10u, -1)));
+ assert(!(int.max in iota(20u, 10u, -1)));
+
+
+ // Issue 8920
+ foreach (Type; AliasSeq!(byte, ubyte, short, ushort,
+ int, uint, long, ulong))
+ {
+ Type val;
+ foreach (i; iota(cast(Type) 0, cast(Type) 10)) { val++; }
+ assert(val == 10);
+ }
+}
+
+@safe unittest
+{
+ import std.algorithm.mutation : copy;
+ auto idx = new size_t[100];
+ copy(iota(0, idx.length), idx);
+}
+
+@safe unittest
+{
+ 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;
+ const f = cRange.front;
+ const b = cRange.back;
+ const i = cRange[2];
+ const s1 = cRange[];
+ const s2 = cRange[0 .. 3];
+ const l = cRange.length;
+ }
+}
+
+@system unittest
+{
+ //The ptr stuff can't be done at compile time, so we unfortunately end
+ //up with some code duplication here.
+ auto arr = [0, 5, 3, 5, 5, 7, 9, 2, 0, 42, 7, 6];
+
+ {
+ const cRange = iota(arr.ptr, arr.ptr + arr.length, 3);
+ const e = cRange.empty;
+ const f = cRange.front;
+ const b = cRange.back;
+ const i = cRange[2];
+ const s1 = cRange[];
+ const s2 = cRange[0 .. 3];
+ const l = cRange.length;
+ }
+
+ {
+ const cRange = iota(arr.ptr, arr.ptr + arr.length);
+ const e = cRange.empty;
+ const f = cRange.front;
+ const b = cRange.back;
+ const i = cRange[2];
+ const s1 = cRange[];
+ const s2 = cRange[0 .. 3];
+ const l = cRange.length;
+ }
+}
+
+@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))) )
+{
+ static struct Result
+ {
+ B current;
+ E end;
+
+ @property bool empty()
+ {
+ static if (is(typeof(B.init < E.init)))
+ return !(current < end);
+ else static if (is(typeof(B.init != E.init)))
+ return current == end;
+ else
+ static assert(0);
+ }
+ @property auto front() { return current; }
+ void popFront()
+ {
+ assert(!empty);
+ ++current;
+ }
+ }
+ return Result(begin, end);
+}
+
+@safe unittest
+{
+ import std.algorithm.comparison : equal;
+
+ // Test iota() for a type that only supports ++ and != but does not have
+ // '<'-ordering.
+ struct Cyclic(int wrapAround)
+ {
+ int current;
+
+ this(int start) { current = start % wrapAround; }
+
+ bool opEquals(Cyclic c) const { return current == c.current; }
+ bool opEquals(int i) const { return current == i; }
+ void opUnary(string op)() if (op == "++")
+ {
+ current = (current + 1) % wrapAround;
+ }
+ }
+ alias Cycle5 = Cyclic!5;
+
+ // Easy case
+ auto i1 = iota(Cycle5(1), Cycle5(4));
+ assert(i1.equal([1, 2, 3]));
+
+ // Wraparound case
+ auto i2 = iota(Cycle5(3), Cycle5(2));
+ assert(i2.equal([3, 4, 0, 1 ]));
+}
+
+/**
+ Options for the $(LREF FrontTransversal) and $(LREF Transversal) ranges
+ (below).
+*/
+enum TransverseOptions
+{
+/**
+ When transversed, the elements of a range of ranges are assumed to
+ have different lengths (e.g. a jagged array).
+*/
+ assumeJagged, //default
+ /**
+ The transversal enforces that the elements of a range of ranges have
+ all the same length (e.g. an array of arrays, all having the same
+ length). Checking is done once upon construction of the transversal
+ range.
+ */
+ enforceNotJagged,
+ /**
+ The transversal assumes, without verifying, that the elements of a
+ range of ranges have all the same length. This option is useful if
+ checking was already done from the outside of the range.
+ */
+ assumeNotJagged,
+ }
+
+/**
+ Given a range of ranges, iterate transversally through the first
+ elements of each of the enclosed ranges.
+*/
+struct FrontTransversal(Ror,
+ TransverseOptions opt = TransverseOptions.assumeJagged)
+{
+ alias RangeOfRanges = Unqual!(Ror);
+ alias RangeType = .ElementType!RangeOfRanges;
+ alias ElementType = .ElementType!RangeType;
+
+ private void prime()
+ {
+ static if (opt == TransverseOptions.assumeJagged)
+ {
+ while (!_input.empty && _input.front.empty)
+ {
+ _input.popFront();
+ }
+ static if (isBidirectionalRange!RangeOfRanges)
+ {
+ while (!_input.empty && _input.back.empty)
+ {
+ _input.popBack();
+ }
+ }
+ }
+ }
+
+/**
+ Construction from an input.
+*/
+ this(RangeOfRanges input)
+ {
+ _input = input;
+ prime();
+ static if (opt == TransverseOptions.enforceNotJagged)
+ // (isRandomAccessRange!RangeOfRanges
+ // && hasLength!RangeType)
+ {
+ import std.exception : enforce;
+
+ if (empty) return;
+ immutable commonLength = _input.front.length;
+ foreach (e; _input)
+ {
+ enforce(e.length == commonLength);
+ }
+ }
+ }
+
+/**
+ Forward range primitives.
+*/
+ static if (isInfinite!RangeOfRanges)
+ {
+ enum bool empty = false;
+ }
+ else
+ {
+ @property bool empty()
+ {
+ static if (opt != TransverseOptions.assumeJagged)
+ {
+ if (!_input.empty)
+ return _input.front.empty;
+ }
+
+ return _input.empty;
+ }
+ }
+
+ /// Ditto
+ @property auto ref front()
+ {
+ assert(!empty, "Attempting to fetch the front of an empty FrontTransversal");
+ return _input.front.front;
+ }
+
+ /// Ditto
+ static if (hasMobileElements!RangeType)
+ {
+ ElementType moveFront()
+ {
+ return _input.front.moveFront();
+ }
+ }
+
+ static if (hasAssignableElements!RangeType)
+ {
+ @property void front(ElementType val)
+ {
+ _input.front.front = val;
+ }
+ }
+
+ /// Ditto
+ void popFront()
+ {
+ assert(!empty, "Attempting to popFront an empty FrontTransversal");
+ _input.popFront();
+ prime();
+ }
+
+/**
+ Duplicates this $(D frontTransversal). Note that only the encapsulating
+ range of range will be duplicated. Underlying ranges will not be
+ duplicated.
+*/
+ static if (isForwardRange!RangeOfRanges)
+ {
+ @property FrontTransversal save()
+ {
+ return FrontTransversal(_input.save);
+ }
+ }
+
+ static if (isBidirectionalRange!RangeOfRanges)
+ {
+/**
+ Bidirectional primitives. They are offered if $(D
+ isBidirectionalRange!RangeOfRanges).
+*/
+ @property auto ref back()
+ {
+ assert(!empty, "Attempting to fetch the back of an empty FrontTransversal");
+ return _input.back.front;
+ }
+ /// Ditto
+ void popBack()
+ {
+ assert(!empty, "Attempting to popBack an empty FrontTransversal");
+ _input.popBack();
+ prime();
+ }
+
+ /// Ditto
+ static if (hasMobileElements!RangeType)
+ {
+ ElementType moveBack()
+ {
+ return _input.back.moveFront();
+ }
+ }
+
+ static if (hasAssignableElements!RangeType)
+ {
+ @property void back(ElementType val)
+ {
+ _input.back.front = val;
+ }
+ }
+ }
+
+ static if (isRandomAccessRange!RangeOfRanges &&
+ (opt == TransverseOptions.assumeNotJagged ||
+ opt == TransverseOptions.enforceNotJagged))
+ {
+/**
+ Random-access primitive. It is offered if $(D
+ isRandomAccessRange!RangeOfRanges && (opt ==
+ TransverseOptions.assumeNotJagged || opt ==
+ TransverseOptions.enforceNotJagged)).
+*/
+ auto ref opIndex(size_t n)
+ {
+ return _input[n].front;
+ }
+
+ /// Ditto
+ static if (hasMobileElements!RangeType)
+ {
+ ElementType moveAt(size_t n)
+ {
+ return _input[n].moveFront();
+ }
+ }
+ /// Ditto
+ static if (hasAssignableElements!RangeType)
+ {
+ void opIndexAssign(ElementType val, size_t n)
+ {
+ _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
+ conditions for supporting indexing are met.
+*/
+ static if (hasSlicing!RangeOfRanges)
+ {
+ typeof(this) opSlice(size_t lower, size_t upper)
+ {
+ return typeof(this)(_input[lower .. upper]);
+ }
+ }
+ }
+
+ auto opSlice() { return this; }
+
+private:
+ RangeOfRanges _input;
+}
+
+/// Ditto
+FrontTransversal!(RangeOfRanges, opt) frontTransversal(
+ TransverseOptions opt = TransverseOptions.assumeJagged,
+ RangeOfRanges)
+(RangeOfRanges rr)
+{
+ return typeof(return)(rr);
+}
+
+///
+@safe unittest
+{
+ import std.algorithm.comparison : equal;
+ int[][] x = new int[][2];
+ x[0] = [1, 2];
+ x[1] = [3, 4];
+ auto ror = frontTransversal(x);
+ assert(equal(ror, [ 1, 3 ][]));
+}
+
+@safe unittest
+{
+ import std.algorithm.comparison : equal;
+ import std.internal.test.dummyrange : AllDummyRanges, DummyRange, ReturnBy;
+
+ static assert(is(FrontTransversal!(immutable int[][])));
+
+ foreach (DummyType; AllDummyRanges)
+ {
+ auto dummies =
+ [DummyType.init, DummyType.init, DummyType.init, DummyType.init];
+
+ 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 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(ft.front == ft.moveFront());
+ assert(ft.back == ft.moveBack());
+ assert(ft.moveAt(1) == ft[1]);
+
+
+ // Test infiniteness propagation.
+ static assert(isInfinite!(typeof(frontTransversal(repeat("foo")))));
+
+ static if (DummyType.r == ReturnBy.Reference)
+ {
+ {
+ ft.front++;
+ scope(exit) ft.front--;
+ assert(dummies.front.front == 2);
+ }
+
+ {
+ ft.front = 5;
+ scope(exit) ft.front = 1;
+ assert(dummies[0].front == 5);
+ }
+
+ {
+ ft.back = 88;
+ scope(exit) ft.back = 4;
+ assert(dummies.back.front == 88);
+ }
+
+ {
+ ft[1] = 99;
+ scope(exit) ft[1] = 2;
+ assert(dummies[1].front == 99);
+ }
+ }
+ }
+}
+
+// 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
+ `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)
+{
+ private alias RangeOfRanges = Unqual!Ror;
+ private alias InnerRange = ElementType!RangeOfRanges;
+ private alias E = ElementType!InnerRange;
+
+ private void prime()
+ {
+ static if (opt == TransverseOptions.assumeJagged)
+ {
+ while (!_input.empty && _input.front.length <= _n)
+ {
+ _input.popFront();
+ }
+ static if (isBidirectionalRange!RangeOfRanges)
+ {
+ while (!_input.empty && _input.back.length <= _n)
+ {
+ _input.popBack();
+ }
+ }
+ }
+ }
+
+/**
+ Construction from an input and an index.
+*/
+ this(RangeOfRanges input, size_t n)
+ {
+ _input = input;
+ _n = n;
+ prime();
+ static if (opt == TransverseOptions.enforceNotJagged)
+ {
+ import std.exception : enforce;
+
+ if (empty) return;
+ immutable commonLength = _input.front.length;
+ foreach (e; _input)
+ {
+ enforce(e.length == commonLength);
+ }
+ }
+ }
+
+/**
+ Forward range primitives.
+*/
+ static if (isInfinite!(RangeOfRanges))
+ {
+ enum bool empty = false;
+ }
+ else
+ {
+ @property bool empty()
+ {
+ return _input.empty;
+ }
+ }
+
+ /// Ditto
+ @property auto ref front()
+ {
+ assert(!empty, "Attempting to fetch the front of an empty Transversal");
+ return _input.front[_n];
+ }
+
+ /// Ditto
+ static if (hasMobileElements!InnerRange)
+ {
+ E moveFront()
+ {
+ return _input.front.moveAt(_n);
+ }
+ }
+
+ /// Ditto
+ static if (hasAssignableElements!InnerRange)
+ {
+ @property void front(E val)
+ {
+ _input.front[_n] = val;
+ }
+ }
+
+
+ /// Ditto
+ void popFront()
+ {
+ assert(!empty, "Attempting to popFront an empty Transversal");
+ _input.popFront();
+ prime();
+ }
+
+ /// Ditto
+ static if (isForwardRange!RangeOfRanges)
+ {
+ @property typeof(this) save()
+ {
+ auto ret = this;
+ ret._input = _input.save;
+ return ret;
+ }
+ }
+
+ static if (isBidirectionalRange!RangeOfRanges)
+ {
+/**
+ Bidirectional primitives. They are offered if $(D
+ isBidirectionalRange!RangeOfRanges).
+*/
+ @property auto ref back()
+ {
+ assert(!empty, "Attempting to fetch the back of an empty Transversal");
+ return _input.back[_n];
+ }
+
+ /// Ditto
+ void popBack()
+ {
+ assert(!empty, "Attempting to popBack an empty Transversal");
+ _input.popBack();
+ prime();
+ }
+
+ /// Ditto
+ static if (hasMobileElements!InnerRange)
+ {
+ E moveBack()
+ {
+ return _input.back.moveAt(_n);
+ }
+ }
+
+ /// Ditto
+ static if (hasAssignableElements!InnerRange)
+ {
+ @property void back(E val)
+ {
+ _input.back[_n] = val;
+ }
+ }
+
+ }
+
+ static if (isRandomAccessRange!RangeOfRanges &&
+ (opt == TransverseOptions.assumeNotJagged ||
+ opt == TransverseOptions.enforceNotJagged))
+ {
+/**
+ Random-access primitive. It is offered if $(D
+ isRandomAccessRange!RangeOfRanges && (opt ==
+ TransverseOptions.assumeNotJagged || opt ==
+ TransverseOptions.enforceNotJagged)).
+*/
+ auto ref opIndex(size_t n)
+ {
+ return _input[n][_n];
+ }
+
+ /// Ditto
+ static if (hasMobileElements!InnerRange)
+ {
+ E moveAt(size_t n)
+ {
+ return _input[n].moveAt(_n);
+ }
+ }
+
+ /// Ditto
+ static if (hasAssignableElements!InnerRange)
+ {
+ void opIndexAssign(E val, size_t n)
+ {
+ _input[n][_n] = 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
+ conditions for supporting indexing are met.
+*/
+ static if (hasSlicing!RangeOfRanges)
+ {
+ typeof(this) opSlice(size_t lower, size_t upper)
+ {
+ return typeof(this)(_input[lower .. upper], _n);
+ }
+ }
+ }
+
+ auto opSlice() { return this; }
+
+private:
+ RangeOfRanges _input;
+ size_t _n;
+}
+
+/// Ditto
+Transversal!(RangeOfRanges, opt) transversal
+(TransverseOptions opt = TransverseOptions.assumeJagged, RangeOfRanges)
+(RangeOfRanges rr, size_t n)
+{
+ return typeof(return)(rr, n);
+}
+
+///
+@safe unittest
+{
+ import std.algorithm.comparison : equal;
+ int[][] x = new int[][2];
+ x[0] = [1, 2];
+ x[1] = [3, 4];
+ auto ror = transversal(x, 1);
+ assert(equal(ror, [ 2, 4 ][]));
+}
+
+@safe unittest
+{
+ import std.internal.test.dummyrange : DummyRange, Length, RangeType, ReturnBy;
+
+ int[][] x = new int[][2];
+ x[0] = [ 1, 2 ];
+ x[1] = [3, 4];
+ auto ror = transversal!(TransverseOptions.assumeNotJagged)(x, 1);
+ auto witness = [ 2, 4 ];
+ uint i;
+ foreach (e; ror) assert(e == witness[i++]);
+ assert(i == 2);
+ assert(ror.length == 2);
+
+ static assert(is(Transversal!(immutable int[][])));
+
+ // Make sure ref, assign is being propagated.
+ {
+ ror.front++;
+ scope(exit) ror.front--;
+ assert(x[0][1] == 3);
+ }
+ {
+ ror.front = 5;
+ scope(exit) ror.front = 2;
+ assert(x[0][1] == 5);
+ assert(ror.moveFront() == 5);
+ }
+ {
+ ror.back = 999;
+ scope(exit) ror.back = 4;
+ assert(x[1][1] == 999);
+ assert(ror.moveBack() == 999);
+ }
+ {
+ ror[0] = 999;
+ scope(exit) ror[0] = 2;
+ assert(x[0][1] == 999);
+ assert(ror.moveAt(0) == 999);
+ }
+
+ // Test w/o ref return.
+ alias D = DummyRange!(ReturnBy.Value, Length.Yes, RangeType.Random);
+ auto drs = [D.init, D.init];
+ foreach (num; 0 .. 10)
+ {
+ auto t = transversal!(TransverseOptions.enforceNotJagged)(drs, num);
+ assert(t[0] == t[1]);
+ assert(t[1] == num + 1);
+ }
+
+ static assert(isInfinite!(typeof(transversal(repeat([1,2,3]), 1))));
+
+ // 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];
+ assert(mat1[0] == 6);
+ assert(mat1[1] == 10);
+}
+
+struct Transposed(RangeOfRanges)
+if (isForwardRange!RangeOfRanges &&
+ isInputRange!(ElementType!RangeOfRanges) &&
+ hasAssignableElements!RangeOfRanges)
+{
+ //alias ElementType = typeof(map!"a.front"(RangeOfRanges.init));
+
+ this(RangeOfRanges input)
+ {
+ this._input = input;
+ }
+
+ @property auto front()
+ {
+ import std.algorithm.iteration : filter, map;
+ return _input.save
+ .filter!(a => !a.empty)
+ .map!(a => a.front);
+ }
+
+ void popFront()
+ {
+ // Advance the position of each subrange.
+ auto r = _input.save;
+ while (!r.empty)
+ {
+ auto e = r.front;
+ if (!e.empty)
+ {
+ e.popFront();
+ r.front = e;
+ }
+
+ r.popFront();
+ }
+ }
+
+ // ElementType opIndex(size_t n)
+ // {
+ // return _input[n].front;
+ // }
+
+ @property bool empty()
+ {
+ if (_input.empty) return true;
+ foreach (e; _input.save)
+ {
+ if (!e.empty) return false;
+ }
+ return true;
+ }
+
+ @property Transposed save()
+ {
+ return Transposed(_input.save);
+ }
+
+ auto opSlice() { return this; }
+
+private:
+ RangeOfRanges _input;
+}
+
+@safe unittest
+{
+ // Boundary case: transpose of empty range should be empty
+ int[][] ror = [];
+ assert(transposed(ror).empty);
+}
+
+// Issue 9507
+@safe unittest
+{
+ import std.algorithm.comparison : equal;
+
+ auto r = [[1,2], [3], [4,5], [], [6]];
+ assert(r.transposed.equal!equal([
+ [1, 3, 4, 6],
+ [2, 5]
+ ]));
+}
+
+/**
+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)
+{
+ return Transposed!RangeOfRanges(rr);
+}
+
+///
+@safe unittest
+{
+ import std.algorithm.comparison : equal;
+ int[][] ror = [
+ [1, 2, 3],
+ [4, 5, 6]
+ ];
+ auto xp = transposed(ror);
+ assert(equal!"a.equal(b)"(xp, [
+ [1, 4],
+ [2, 5],
+ [3, 6]
+ ]));
+}
+
+///
+@safe unittest
+{
+ int[][] x = new int[][2];
+ x[0] = [1, 2];
+ x[1] = [3, 4];
+ auto tr = transposed(x);
+ int[][] witness = [ [ 1, 3 ], [ 2, 4 ] ];
+ uint i;
+
+ foreach (e; tr)
+ {
+ assert(array(e) == witness[i++]);
+ }
+}
+
+// Issue 8764
+@safe unittest
+{
+ import std.algorithm.comparison : equal;
+ ulong[1] t0 = [ 123 ];
+
+ assert(!hasAssignableElements!(typeof(t0[].chunks(1))));
+ assert(!is(typeof(transposed(t0[].chunks(1)))));
+ assert(is(typeof(transposed(t0[].chunks(1).array()))));
+
+ auto t1 = transposed(t0[].chunks(1).array());
+ assert(equal!"a.equal(b)"(t1, [[123]]));
+}
+
+/**
+This struct takes two ranges, $(D source) and $(D indices), and creates a view
+of $(D source) as if its elements were reordered according to $(D indices).
+$(D indices) may include only a subset of the elements of $(D source) and
+may also repeat elements.
+
+$(D Source) must be a random access range. The returned range will be
+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])))
+{
+ this(Source source, Indices indices)
+ {
+ this._source = source;
+ this._indices = indices;
+ }
+
+ /// Range primitives
+ @property auto ref front()
+ {
+ assert(!empty, "Attempting to fetch the front of an empty Indexed");
+ return _source[_indices.front];
+ }
+
+ /// Ditto
+ void popFront()
+ {
+ assert(!empty, "Attempting to popFront an empty Indexed");
+ _indices.popFront();
+ }
+
+ static if (isInfinite!Indices)
+ {
+ enum bool empty = false;
+ }
+ else
+ {
+ /// Ditto
+ @property bool empty()
+ {
+ return _indices.empty;
+ }
+ }
+
+ static if (isForwardRange!Indices)
+ {
+ /// Ditto
+ @property typeof(this) save()
+ {
+ // Don't need to save _source because it's never consumed.
+ return typeof(this)(_source, _indices.save);
+ }
+ }
+
+ /// Ditto
+ static if (hasAssignableElements!Source)
+ {
+ @property auto ref front(ElementType!Source newVal)
+ {
+ assert(!empty);
+ return _source[_indices.front] = newVal;
+ }
+ }
+
+
+ static if (hasMobileElements!Source)
+ {
+ /// Ditto
+ auto moveFront()
+ {
+ assert(!empty);
+ return _source.moveAt(_indices.front);
+ }
+ }
+
+ static if (isBidirectionalRange!Indices)
+ {
+ /// Ditto
+ @property auto ref back()
+ {
+ assert(!empty, "Attempting to fetch the back of an empty Indexed");
+ return _source[_indices.back];
+ }
+
+ /// Ditto
+ void popBack()
+ {
+ assert(!empty, "Attempting to popBack an empty Indexed");
+ _indices.popBack();
+ }
+
+ /// Ditto
+ static if (hasAssignableElements!Source)
+ {
+ @property auto ref back(ElementType!Source newVal)
+ {
+ assert(!empty);
+ return _source[_indices.back] = newVal;
+ }
+ }
+
+
+ static if (hasMobileElements!Source)
+ {
+ /// Ditto
+ auto moveBack()
+ {
+ assert(!empty);
+ return _source.moveAt(_indices.back);
+ }
+ }
+ }
+
+ static if (hasLength!Indices)
+ {
+ /// Ditto
+ @property size_t length()
+ {
+ return _indices.length;
+ }
+
+ alias opDollar = length;
+ }
+
+ static if (isRandomAccessRange!Indices)
+ {
+ /// Ditto
+ auto ref opIndex(size_t index)
+ {
+ return _source[_indices[index]];
+ }
+
+ static if (hasSlicing!Indices)
+ {
+ /// Ditto
+ typeof(this) opSlice(size_t a, size_t b)
+ {
+ return typeof(this)(_source, _indices[a .. b]);
+ }
+ }
+
+
+ static if (hasAssignableElements!Source)
+ {
+ /// Ditto
+ auto opIndexAssign(ElementType!Source newVal, size_t index)
+ {
+ return _source[_indices[index]] = newVal;
+ }
+ }
+
+
+ static if (hasMobileElements!Source)
+ {
+ /// Ditto
+ auto moveAt(size_t index)
+ {
+ return _source.moveAt(_indices[index]);
+ }
+ }
+ }
+
+ // All this stuff is useful if someone wants to index an Indexed
+ // without adding a layer of indirection.
+
+ /**
+ Returns the source range.
+ */
+ @property Source source()
+ {
+ return _source;
+ }
+
+ /**
+ Returns the indices range.
+ */
+ @property Indices indices()
+ {
+ return _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.
+ */
+ 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:
+ Source _source;
+ Indices _indices;
+
+}
+
+/// Ditto
+Indexed!(Source, Indices) indexed(Source, Indices)(Source source, Indices indices)
+{
+ return typeof(return)(source, indices);
+}
+
+///
+@safe unittest
+{
+ 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);
+ assert(equal(ind, [5, 4, 2, 3, 1, 5]));
+ assert(equal(retro(ind), [5, 1, 3, 2, 4, 5]));
+}
+
+@safe unittest
+{
+ {
+ auto ind = indexed([1, 2, 3, 4, 5], [1, 3, 4]);
+ assert(ind.physicalIndex(0) == 1);
+ }
+
+ auto source = [1, 2, 3, 4, 5];
+ auto indices = [4, 3, 1, 2, 0, 4];
+ auto ind = indexed(source, indices);
+
+ // When elements of indices are duplicated and Source has lvalue elements,
+ // these are aliased in ind.
+ ind[0]++;
+ assert(ind[0] == 6);
+ assert(ind[5] == 6);
+}
+
+@safe unittest
+{
+ import std.internal.test.dummyrange : AllDummyRanges, propagatesLength,
+ propagatesRangeType, RangeType;
+
+ foreach (DummyType; AllDummyRanges)
+ {
+ auto d = DummyType.init;
+ auto r = indexed([1, 2, 3, 4, 5], d);
+ static assert(propagatesRangeType!(DummyType, typeof(r)));
+ static assert(propagatesLength!(DummyType, typeof(r)));
+ }
+}
+
+/**
+This range iterates over fixed-sized chunks of size $(D chunkSize) of a
+$(D source) range. $(D Source) must be an input 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.
+
+If `Source` is a forward range, the resulting range will be forward ranges as
+well. Otherwise, the resulting chunks will be input ranges consuming the same
+input: iterating over `front` will shrink the chunk such that subsequent
+invocations of `front` will no longer return the full chunk, and calling
+`popFront` on the outer range will invalidate any lingering references to
+previous values of `front`.
+
+Params:
+ source = Range from which the chunks will be selected
+ chunkSize = Chunk size
+
+See_Also: $(LREF slide)
+
+Returns: Range of chunks.
+*/
+struct Chunks(Source)
+if (isInputRange!Source)
+{
+ static if (isForwardRange!Source)
+ {
+ /// Standard constructor
+ this(Source source, size_t chunkSize)
+ {
+ assert(chunkSize != 0, "Cannot create a Chunk with an empty chunkSize");
+ _source = source;
+ _chunkSize = chunkSize;
+ }
+
+ /// Input range primitives. Always present.
+ @property auto front()
+ {
+ assert(!empty, "Attempting to fetch the front of an empty Chunks");
+ return _source.save.take(_chunkSize);
+ }
+
+ /// Ditto
+ void popFront()
+ {
+ assert(!empty, "Attempting to popFront and empty Chunks");
+ _source.popFrontN(_chunkSize);
+ }
+
+ static if (!isInfinite!Source)
+ /// Ditto
+ @property bool empty()
+ {
+ return _source.empty;
+ }
+ else
+ // undocumented
+ enum empty = false;
+
+ /// Forward range primitives. Only present if `Source` is a forward range.
+ @property typeof(this) save()
+ {
+ return typeof(this)(_source.save, _chunkSize);
+ }
+
+ static if (hasLength!Source)
+ {
+ /// Length. Only if $(D hasLength!Source) is $(D true)
+ @property size_t length()
+ {
+ // Note: _source.length + _chunkSize may actually overflow.
+ // We cast to ulong to mitigate the problem on x86 machines.
+ // For x64 machines, we just suppose we'll never overflow.
+ // The "safe" code would require either an extra branch, or a
+ // modulo operation, which is too expensive for such a rare case
+ return cast(size_t)((cast(ulong)(_source.length) + _chunkSize - 1) / _chunkSize);
+ }
+ //Note: No point in defining opDollar here without slicing.
+ //opDollar is defined below in the hasSlicing!Source section
+ }
+
+ static if (hasSlicing!Source)
+ {
+ //Used for various purposes
+ private enum hasSliceToEnd = is(typeof(Source.init[_chunkSize .. $]) == Source);
+
+ /**
+ Indexing and slicing operations. Provided only if
+ $(D hasSlicing!Source) is $(D true).
+ */
+ auto opIndex(size_t index)
+ {
+ immutable start = index * _chunkSize;
+ immutable end = start + _chunkSize;
+
+ static if (isInfinite!Source)
+ return _source[start .. end];
+ else
+ {
+ import std.algorithm.comparison : min;
+ immutable len = _source.length;
+ assert(start < len, "chunks index out of bounds");
+ return _source[start .. min(end, len)];
+ }
+ }
+
+ /// Ditto
+ static if (hasLength!Source)
+ typeof(this) opSlice(size_t lower, size_t upper)
+ {
+ 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);
+ }
+ else static if (hasSliceToEnd)
+ //For slicing an infinite chunk, we need to slice the source to the end.
+ typeof(takeExactly(this, 0)) opSlice(size_t lower, size_t upper)
+ {
+ assert(lower <= upper, "chunks slicing index out of bounds");
+ return chunks(_source[lower * _chunkSize .. $], _chunkSize).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 * _chunkSize .. $], _chunkSize);
+ }
+ }
+ }
+ else
+ {
+ //Dollar token carries a static type, with no extra information.
+ //It can lazily transform into _source.length on algorithmic
+ //operations such as : chunks[$/2, $-1];
+ private static struct DollarToken
+ {
+ Chunks!Source* mom;
+ @property size_t momLength()
+ {
+ return mom.length;
+ }
+ alias momLength this;
+ }
+ DollarToken opDollar()
+ {
+ return DollarToken(&this);
+ }
+
+ //Slice overloads optimized for using dollar. Without this, to slice to end, we would...
+ //1. Evaluate chunks.length
+ //2. Multiply by _chunksSize
+ //3. To finally just compare it (with min) to the original length of source (!)
+ //These overloads avoid that.
+ typeof(this) opSlice(DollarToken, DollarToken)
+ {
+ static if (hasSliceToEnd)
+ return chunks(_source[$ .. $], _chunkSize);
+ else
+ {
+ immutable len = _source.length;
+ return chunks(_source[len .. len], _chunkSize);
+ }
+ }
+ typeof(this) opSlice(size_t lower, DollarToken)
+ {
+ 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);
+ else
+ {
+ immutable len = _source.length;
+ return chunks(_source[min(lower * _chunkSize, len) .. len], _chunkSize);
+ }
+ }
+ typeof(this) opSlice(DollarToken, size_t upper)
+ {
+ assert(upper == length, "chunks slicing index out of bounds");
+ return this[$ .. $];
+ }
+ }
+ }
+
+ //Bidirectional range primitives
+ static if (hasSlicing!Source && hasLength!Source)
+ {
+ /**
+ Bidirectional range primitives. Provided only if both
+ $(D hasSlicing!Source) and $(D hasLength!Source) are $(D true).
+ */
+ @property auto back()
+ {
+ assert(!empty, "back called on empty chunks");
+ immutable len = _source.length;
+ immutable start = (len - 1) / _chunkSize * _chunkSize;
+ return _source[start .. len];
+ }
+
+ /// Ditto
+ void popBack()
+ {
+ assert(!empty, "popBack() called on empty chunks");
+ immutable end = (_source.length - 1) / _chunkSize * _chunkSize;
+ _source = _source[0 .. end];
+ }
+ }
+
+ private:
+ Source _source;
+ size_t _chunkSize;
+ }
+ else // is input range only
+ {
+ import std.typecons : RefCounted;
+
+ static struct Chunk
+ {
+ private RefCounted!Impl impl;
+
+ @property bool empty() { return impl.curSizeLeft == 0 || impl.r.empty; }
+ @property auto front() { return impl.r.front; }
+ void popFront()
+ {
+ assert(impl.curSizeLeft > 0 && !impl.r.empty);
+ impl.curSizeLeft--;
+ impl.r.popFront();
+ }
+ }
+
+ static struct Impl
+ {
+ private Source r;
+ private size_t chunkSize;
+ private size_t curSizeLeft;
+ }
+
+ private RefCounted!Impl impl;
+
+ private this(Source r, size_t chunkSize)
+ {
+ impl = RefCounted!Impl(r, r.empty ? 0 : chunkSize, chunkSize);
+ }
+
+ @property bool empty() { return impl.chunkSize == 0; }
+ @property Chunk front() return { return Chunk(impl); }
+
+ void popFront()
+ {
+ impl.curSizeLeft -= impl.r.popFrontN(impl.curSizeLeft);
+ if (!impl.r.empty)
+ impl.curSizeLeft = impl.chunkSize;
+ else
+ impl.chunkSize = 0;
+ }
+
+ static assert(isInputRange!(typeof(this)));
+ }
+}
+
+/// Ditto
+Chunks!Source chunks(Source)(Source source, size_t chunkSize)
+if (isInputRange!Source)
+{
+ return typeof(return)(source, chunkSize);
+}
+
+///
+@safe unittest
+{
+ 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]);
+ assert(chunks[1] == [5, 6, 7, 8]);
+ assert(chunks[2] == [9, 10]);
+ assert(chunks.back == chunks[2]);
+ assert(chunks.front == chunks[0]);
+ assert(chunks.length == 3);
+ assert(equal(retro(array(chunks)), array(retro(chunks))));
+}
+
+/// Non-forward input ranges are supported, but with limited semantics.
+@system /*@safe*/ unittest // FIXME: can't be @safe because RefCounted isn't.
+{
+ import std.algorithm.comparison : equal;
+
+ int i;
+
+ // The generator doesn't save state, so it cannot be a forward range.
+ auto inputRange = generate!(() => ++i).take(10);
+
+ // We can still process it in chunks, but it will be single-pass only.
+ auto chunked = inputRange.chunks(2);
+
+ assert(chunked.front.equal([1, 2]));
+ assert(chunked.front.empty); // Iterating the chunk has consumed it
+ chunked.popFront;
+ assert(chunked.front.equal([3, 4]));
+}
+
+@system /*@safe*/ unittest
+{
+ import std.algorithm.comparison : equal;
+ import std.internal.test.dummyrange : ReferenceInputRange;
+
+ auto data = [ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 ];
+ auto r = new ReferenceInputRange!int(data).chunks(3);
+ assert(r.equal!equal([
+ [ 1, 2, 3 ],
+ [ 4, 5, 6 ],
+ [ 7, 8, 9 ],
+ [ 10 ]
+ ]));
+
+ auto data2 = [ 1, 2, 3, 4, 5, 6 ];
+ auto r2 = new ReferenceInputRange!int(data2).chunks(3);
+ assert(r2.equal!equal([
+ [ 1, 2, 3 ],
+ [ 4, 5, 6 ]
+ ]));
+
+ auto data3 = [ 1, 2, 3, 4, 5 ];
+ auto r3 = new ReferenceInputRange!int(data3).chunks(2);
+ assert(r3.front.equal([1, 2]));
+ r3.popFront();
+ assert(!r3.empty);
+ r3.popFront();
+ assert(r3.front.equal([5]));
+}
+
+@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.
+ source = 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)
+*/
+// Explicitly set to private to delay the release until 2.076
+private
+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]] || <gap: 2> 6
+ * iota(7).slide(2, 4) = [[0, 1], [4, 5]] || <gap: 1> 6
+ * iota(7).slide(1, 5) = [[0], [5]] || <gap: 1> 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.algorithm.comparison : equal;
+ import std.array : array;
+
+ 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.algorithm.comparison : equal;
+ import std.array : array;
+
+ 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]]));
+}
+
+@safe 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
+{
+ import std.algorithm.comparison : equal;
+ import std.array : array;
+
+ // 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]]));
+}
+
+// test reverse
+@safe pure nothrow unittest
+{
+ import std.algorithm.comparison : equal;
+
+ auto e = iota(3).slide(2);
+ assert(e.retro.equal!equal([[1, 2], [0, 1]]));
+ assert(e.retro.array.equal(e.array.retro));
+
+ 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));
+
+ auto e3 = iota(3).slide(4);
+ assert(e3.retro.equal!equal([[0, 1, 2]]));
+ assert(e3.retro.array.equal(e3.array.retro));
+}
+
+// test reverse with different steps
+@safe pure nothrow unittest
+{
+ import std.algorithm.comparison : equal;
+
+ 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(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]]));
+}
+
+// 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.comparison : equal;
+ import std.internal.test.dummyrange : DummyRange, Length, RangeType, ReturnBy;
+ import std.meta : AliasSeq;
+
+ 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]);
+ }
+
+ static if (withInfiniteness)
+ {
+ enum empty = false;
+ }
+ else
+ {
+ @property bool empty() { return arr.empty; }
+ @property auto length() { return arr.length; }
+ }
+
+ static if (withOpDollar)
+ {
+ static if (withInfiniteness)
+ {
+ struct Dollar {}
+ Dollar opDollar() const { return Dollar.init; }
+
+ //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)
+{
+ private this(Values...)(auto ref Values values)
+ {
+ this.data = [values];
+ this.backIndex = arity;
+ }
+
+ bool empty() @property
+ {
+ return frontIndex >= backIndex;
+ }
+
+ T front() @property
+ {
+ assert(!empty, "Attempting to fetch the front of an empty Only range");
+ return data[frontIndex];
+ }
+
+ void popFront()
+ {
+ assert(!empty, "Attempting to popFront an empty Only range");
+ ++frontIndex;
+ }
+
+ T back() @property
+ {
+ assert(!empty, "Attempting to fetch the back of an empty Only range");
+ return data[backIndex - 1];
+ }
+
+ void popBack()
+ {
+ assert(!empty, "Attempting to popBack an empty Only range");
+ --backIndex;
+ }
+
+ OnlyResult save() @property
+ {
+ return this;
+ }
+
+ size_t length() const @property
+ {
+ return backIndex - frontIndex;
+ }
+
+ alias opDollar = length;
+
+ T opIndex(size_t idx)
+ {
+ // when i + idx points to elements popped
+ // with popBack
+ assert(idx < length, "Attempting to fetch an out of bounds index from an Only range");
+ return data[frontIndex + idx];
+ }
+
+ OnlyResult opSlice()
+ {
+ return this;
+ }
+
+ OnlyResult opSlice(size_t from, size_t to)
+ {
+ OnlyResult result = this;
+ result.frontIndex += from;
+ result.backIndex = this.frontIndex + to;
+ 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;
+ }
+
+ private size_t frontIndex = 0;
+ private size_t backIndex = 0;
+
+ // @@@BUG@@@ 10643
+ version (none)
+ {
+ import std.traits : hasElaborateAssign;
+ static if (hasElaborateAssign!T)
+ private T[arity] data;
+ else
+ private T[arity] data = void;
+ }
+ else
+ private T[arity] data;
+}
+
+// Specialize for single-element results
+private struct OnlyResult(T, size_t arity : 1)
+{
+ @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, "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)
+ {
+ this._value = value;
+ this._empty = false;
+ }
+
+ T opIndex(size_t i)
+ {
+ assert(!_empty && i == 0, "Attempting to fetch an out of bounds index from an Only range");
+ return _value;
+ }
+
+ OnlyResult opSlice()
+ {
+ return this;
+ }
+
+ OnlyResult opSlice(size_t from, size_t to)
+ {
+ 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;
+ }
+
+ private Unqual!T _value;
+ private bool _empty = true;
+}
+
+// Specialize for the empty range
+private struct OnlyResult(T, size_t arity : 0)
+{
+ private static struct EmptyElementType {}
+
+ bool empty() @property { return true; }
+ size_t length() const @property { return 0; }
+ alias opDollar = length;
+ EmptyElementType front() @property { assert(false); }
+ void popFront() { assert(false); }
+ EmptyElementType back() @property { assert(false); }
+ void popBack() { assert(false); }
+ OnlyResult save() @property { return this; }
+
+ EmptyElementType opIndex(size_t i)
+ {
+ assert(false);
+ }
+
+ OnlyResult opSlice() { return this; }
+
+ OnlyResult opSlice(size_t from, size_t to)
+ {
+ assert(from == 0 && to == 0);
+ return this;
+ }
+}
+
+/**
+Assemble $(D values) into a range that carries all its
+elements in-situ.
+
+Useful when a single value or multiple disconnected values
+must be passed to an algorithm expecting a range, without
+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)
+{
+ return OnlyResult!(CommonType!Values, Values.length)(values);
+}
+
+///
+@safe unittest
+{
+ 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]);
+
+ assert(only("one", "two", "three").joiner(" ").equal("one two three"));
+
+ string title = "The D Programming Language";
+ 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"));
+}
+
+@safe unittest
+{
+ // Verify that the same common type and same arity
+ // results in the same template instantiation
+ static assert(is(typeof(only(byte.init, int.init)) ==
+ typeof(only(int.init, byte.init))));
+
+ static assert(is(typeof(only((const(char)[]).init, string.init)) ==
+ typeof(only((const(char)[]).init, (const(char)[]).init))));
+}
+
+// Tests the zero-element result
+@safe unittest
+{
+ import std.algorithm.comparison : equal;
+
+ auto emptyRange = only();
+
+ alias EmptyRange = typeof(emptyRange);
+ static assert(isInputRange!EmptyRange);
+ static assert(isForwardRange!EmptyRange);
+ static assert(isBidirectionalRange!EmptyRange);
+ static assert(isRandomAccessRange!EmptyRange);
+ static assert(hasLength!EmptyRange);
+ static assert(hasSlicing!EmptyRange);
+
+ assert(emptyRange.empty);
+ assert(emptyRange.length == 0);
+ assert(emptyRange.equal(emptyRange[]));
+ assert(emptyRange.equal(emptyRange.save));
+ assert(emptyRange[0 .. 0].equal(emptyRange));
+}
+
+// Tests the single-element result
+@safe unittest
+{
+ import std.algorithm.comparison : equal;
+ import std.typecons : tuple;
+ foreach (x; tuple(1, '1', 1.0, "1", [1]))
+ {
+ auto a = only(x);
+ typeof(x)[] e = [];
+ assert(a.front == x);
+ assert(a.back == x);
+ 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(a[0] == x);
+
+ auto b = a.save;
+ assert(equal(a, b));
+ a.popFront();
+ assert(a.empty && a.length == 0 && a[].empty);
+ b.popBack();
+ assert(b.empty && b.length == 0 && b[].empty);
+
+ alias A = typeof(a);
+ static assert(isInputRange!A);
+ static assert(isForwardRange!A);
+ static assert(isBidirectionalRange!A);
+ static assert(isRandomAccessRange!A);
+ static assert(hasLength!A);
+ static assert(hasSlicing!A);
+ }
+
+ auto imm = only!(immutable int)(1);
+ immutable int[] imme = [];
+ assert(imm.front == 1);
+ assert(imm.back == 1);
+ assert(!imm.empty);
+ 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(imm[0] == 1);
+}
+
+// Tests multiple-element results
+@safe unittest
+{
+ 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)
+ assert(nums[i] == i + 1);
+
+ auto saved = nums.save;
+
+ foreach (i; 1 .. 4)
+ {
+ assert(nums.front == nums[0]);
+ assert(nums.front == i);
+ nums.popFront();
+ assert(nums.length == 3 - i);
+ }
+
+ assert(nums.empty);
+
+ assert(saved.equal(only(1, 2, 3)));
+ assert(saved.equal(saved[]));
+ assert(saved[0 .. 1].equal(only(1)));
+ assert(saved[0 .. 2].equal(only(1, 2)));
+ assert(saved[0 .. 3].equal(saved));
+ assert(saved[1 .. 3].equal(only(2, 3)));
+ assert(saved[2 .. 3].equal(only(3)));
+ assert(saved[0 .. 0].empty);
+ assert(saved[3 .. 3].empty);
+
+ alias data = AliasSeq!("one", "two", "three", "four");
+ static joined =
+ ["one two", "one two three", "one two three four"];
+ string[] joinedRange = joined;
+
+ foreach (argCount; AliasSeq!(2, 3, 4))
+ {
+ auto values = only(data[0 .. argCount]);
+ alias Values = typeof(values);
+ static assert(is(ElementType!Values == string));
+ static assert(isInputRange!Values);
+ static assert(isForwardRange!Values);
+ static assert(isBidirectionalRange!Values);
+ static assert(isRandomAccessRange!Values);
+ static assert(hasSlicing!Values);
+ static assert(hasLength!Values);
+
+ assert(values.length == argCount);
+ assert(values[0 .. $].equal(values[0 .. values.length]));
+ assert(values.joiner(" ").equal(joinedRange.front));
+ joinedRange.popFront();
+ }
+
+ assert(saved.retro.equal(only(3, 2, 1)));
+ assert(saved.length == 3);
+
+ assert(saved.back == 3);
+ saved.popBack();
+ assert(saved.length == 2);
+ assert(saved.back == 2);
+
+ assert(saved.front == 1);
+ saved.popFront();
+ assert(saved.length == 1);
+ assert(saved.front == 2);
+
+ saved.popBack();
+ assert(saved.empty);
+
+ auto imm = only!(immutable int, immutable int)(42, 24);
+ alias Imm = typeof(imm);
+ static assert(is(ElementType!Imm == immutable(int)));
+ assert(!imm.empty);
+ assert(imm.init.empty); // Issue 13441
+ assert(imm.front == 42);
+ imm.popFront();
+ assert(imm.front == 24);
+ imm.popFront();
+ assert(imm.empty);
+
+ static struct Test { int* a; }
+ immutable(Test) test;
+ cast(void) only(test, test); // Works with mutable indirection
+}
+
+/**
+Iterate over `range` with an attached index variable.
+
+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 `value`.
+
+The index starts at `start` and is incremented by one on every iteration.
+
+Overflow:
+ 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 `range` does not have length, and `popFront` is called when
+ `front.index == Enumerator.max`, the index will overflow and
+ continue from `Enumerator.min`.
+
+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;
+ import std.range : enumerate;
+
+ foreach (lineNum, line; stdin.byLine().enumerate(1))
+ stdout.writefln("line #%s: %s", lineNum, line);
+----
+*/
+auto enumerate(Enumerator = size_t, Range)(Range range, Enumerator start = 0)
+if (isIntegral!Enumerator && isInputRange!Range)
+in
+{
+ static if (hasLength!Range)
+ {
+ // TODO: core.checkedint supports mixed signedness yet?
+ import core.checkedint : adds, addu;
+ import std.conv : ConvException, to;
+ import std.traits : isSigned, Largest, Signed;
+
+ alias LengthType = typeof(range.length);
+ bool overflow;
+ static if (isSigned!Enumerator && isSigned!LengthType)
+ auto result = adds(start, range.length, overflow);
+ else static if (isSigned!Enumerator)
+ {
+ Largest!(Enumerator, Signed!LengthType) signedLength;
+ try signedLength = to!(typeof(signedLength))(range.length);
+ catch (ConvException)
+ overflow = true;
+ catch (Exception)
+ assert(false);
+
+ auto result = adds(start, signedLength, overflow);
+ }
+ else
+ {
+ static if (isSigned!LengthType)
+ assert(range.length >= 0);
+ auto result = addu(start, range.length, overflow);
+ }
+
+ assert(!overflow && result <= Enumerator.max);
+ }
+}
+body
+{
+ // TODO: Relax isIntegral!Enumerator to allow user-defined integral types
+ static struct Result
+ {
+ import std.typecons : Tuple;
+
+ private:
+ alias ElemType = Tuple!(Enumerator, "index", ElementType!Range, "value");
+ Range range;
+ Enumerator index;
+
+ public:
+ ElemType front() @property
+ {
+ assert(!range.empty, "Attempting to fetch the front of an empty enumerate");
+ return typeof(return)(index, range.front);
+ }
+
+ static if (isInfinite!Range)
+ enum bool empty = false;
+ else
+ {
+ bool empty() @property
+ {
+ return range.empty;
+ }
+ }
+
+ void popFront()
+ {
+ assert(!range.empty, "Attempting to popFront an empty enumerate");
+ range.popFront();
+ ++index; // When !hasLength!Range, overflow is expected
+ }
+
+ static if (isForwardRange!Range)
+ {
+ Result save() @property
+ {
+ return typeof(return)(range.save, index);
+ }
+ }
+
+ static if (hasLength!Range)
+ {
+ size_t length() @property
+ {
+ return range.length;
+ }
+
+ alias opDollar = length;
+
+ static if (isBidirectionalRange!Range)
+ {
+ ElemType back() @property
+ {
+ 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, "Attempting to popBack an empty enumerate");
+ range.popBack();
+ }
+ }
+ }
+
+ static if (isRandomAccessRange!Range)
+ {
+ ElemType opIndex(size_t i)
+ {
+ return typeof(return)(cast(Enumerator)(index + i), range[i]);
+ }
+ }
+
+ static if (hasSlicing!Range)
+ {
+ static if (hasLength!Range)
+ {
+ Result opSlice(size_t i, size_t j)
+ {
+ return typeof(return)(range[i .. j], cast(Enumerator)(index + i));
+ }
+ }
+ else
+ {
+ static struct DollarToken {}
+ enum opDollar = DollarToken.init;
+
+ Result opSlice(size_t i, DollarToken)
+ {
+ return typeof(return)(range[i .. $], cast(Enumerator)(index + i));
+ }
+
+ auto opSlice(size_t i, size_t j)
+ {
+ return this[i .. $].takeExactly(j - 1);
+ }
+ }
+ }
+ }
+
+ return Result(range, start);
+}
+
+/// Can start enumeration from a negative position:
+pure @safe nothrow unittest
+{
+ import std.array : assocArray;
+ import std.range : enumerate;
+
+ bool[int] aa = true.repeat(3).enumerate(-1).assocArray();
+ assert(aa[-1]);
+ assert(aa[0]);
+ assert(aa[1]);
+}
+
+pure @safe nothrow unittest
+{
+ import std.internal.test.dummyrange : AllDummyRanges;
+ import std.meta : AliasSeq;
+ import std.typecons : tuple;
+
+ static struct HasSlicing
+ {
+ typeof(this) front() @property { return typeof(this).init; }
+ bool empty() @property { return true; }
+ void popFront() {}
+
+ typeof(this) opSlice(size_t, size_t)
+ {
+ return typeof(this)();
+ }
+ }
+
+ foreach (DummyType; AliasSeq!(AllDummyRanges, HasSlicing))
+ {
+ alias R = typeof(enumerate(DummyType.init));
+ static assert(isInputRange!R);
+ static assert(isForwardRange!R == isForwardRange!DummyType);
+ static assert(isRandomAccessRange!R == isRandomAccessRange!DummyType);
+ static assert(!hasAssignableElements!R);
+
+ static if (hasLength!DummyType)
+ {
+ static assert(hasLength!R);
+ static assert(isBidirectionalRange!R ==
+ isBidirectionalRange!DummyType);
+ }
+
+ static assert(hasSlicing!R == hasSlicing!DummyType);
+ }
+
+ static immutable values = ["zero", "one", "two", "three"];
+ auto enumerated = values[].enumerate();
+ assert(!enumerated.empty);
+ assert(enumerated.front == tuple(0, "zero"));
+ assert(enumerated.back == tuple(3, "three"));
+
+ typeof(enumerated) saved = enumerated.save;
+ saved.popFront();
+ assert(enumerated.front == tuple(0, "zero"));
+ assert(saved.front == tuple(1, "one"));
+ assert(saved.length == enumerated.length - 1);
+ saved.popBack();
+ assert(enumerated.back == tuple(3, "three"));
+ assert(saved.back == tuple(2, "two"));
+ saved.popFront();
+ assert(saved.front == tuple(2, "two"));
+ assert(saved.back == tuple(2, "two"));
+ saved.popFront();
+ assert(saved.empty);
+
+ size_t control = 0;
+ foreach (i, v; enumerated)
+ {
+ static assert(is(typeof(i) == size_t));
+ static assert(is(typeof(v) == typeof(values[0])));
+ assert(i == control);
+ assert(v == values[i]);
+ assert(tuple(i, v) == enumerated[i]);
+ ++control;
+ }
+
+ assert(enumerated[0 .. $].front == tuple(0, "zero"));
+ assert(enumerated[$ - 1 .. $].front == tuple(3, "three"));
+
+ foreach (i; 0 .. 10)
+ {
+ auto shifted = values[0 .. 2].enumerate(i);
+ assert(shifted.front == tuple(i, "zero"));
+ assert(shifted[0] == shifted.front);
+
+ auto next = tuple(i + 1, "one");
+ assert(shifted[1] == next);
+ shifted.popFront();
+ assert(shifted.front == next);
+ shifted.popFront();
+ assert(shifted.empty);
+ }
+
+ foreach (T; AliasSeq!(ubyte, byte, uint, int))
+ {
+ auto inf = 42.repeat().enumerate(T.max);
+ alias Inf = typeof(inf);
+ static assert(isInfinite!Inf);
+ static assert(hasSlicing!Inf);
+
+ // test overflow
+ assert(inf.front == tuple(T.max, 42));
+ inf.popFront();
+ assert(inf.front == tuple(T.min, 42));
+
+ // test slicing
+ inf = inf[42 .. $];
+ assert(inf.front == tuple(T.min + 42, 42));
+ auto window = inf[0 .. 2];
+ assert(window.length == 1);
+ assert(window.front == inf.front);
+ window.popFront();
+ assert(window.empty);
+ }
+}
+
+pure @safe unittest
+{
+ import std.algorithm.comparison : equal;
+ import std.meta : AliasSeq;
+ static immutable int[] values = [0, 1, 2, 3, 4];
+ 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)
+ {
+ 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)));
+ }
+ }
+}
+
+version (none) // @@@BUG@@@ 10939
+{
+ // Re-enable (or remove) if 10939 is resolved.
+ /+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];
+
+ static struct SignedLengthRange
+ {
+ immutable(int)[] _values = values;
+
+ int front() @property { assert(false); }
+ bool empty() @property { assert(false); }
+ void popFront() { assert(false); }
+
+ int length() @property
+ {
+ return cast(int)_values.length;
+ }
+ }
+
+ SignedLengthRange svalues;
+ 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));
+ assertThrown!RangeError(values[].enumerate!Enumerator(Enumerator.max - values.length + 1));
+
+ assertThrown!RangeError(svalues.enumerate!Enumerator(Enumerator.max));
+ assertNotThrown!RangeError(svalues.enumerate!Enumerator(Enumerator.max - values.length));
+ assertThrown!RangeError(svalues.enumerate!Enumerator(Enumerator.max - values.length + 1));
+ }
+
+ foreach (Enumerator; AliasSeq!(byte, short, int))
+ {
+ assertThrown!RangeError(repeat(0, uint.max).enumerate!Enumerator());
+ }
+
+ assertNotThrown!RangeError(repeat(0, uint.max).enumerate!long());
+ }
+}
+
+/**
+ Returns true if $(D fn) accepts variables of type T1 and T2 in any order.
+ The following code should compile:
+ ---
+ T1 foo();
+ T2 bar();
+
+ fn(foo(), bar());
+ fn(bar(), foo());
+ ---
+*/
+template isTwoWayCompatible(alias fn, T1, T2)
+{
+ enum isTwoWayCompatible = is(typeof( (){
+ T1 foo();
+ T2 bar();
+
+ fn(foo(), bar());
+ fn(bar(), foo());
+ }
+ ));
+}
+
+
+/**
+ Policy used with the searching primitives $(D lowerBound), $(D
+ upperBound), and $(D equalRange) of $(LREF SortedRange) below.
+ */
+enum SearchPolicy
+{
+ /**
+ Searches in a linear fashion.
+ */
+ linear,
+
+ /**
+ Searches with a step that is grows linearly (1, 2, 3,...)
+ leading to a quadratic search schedule (indexes tried are 0, 1,
+ 3, 6, 10, 15, 21, 28,...) Once the search overshoots its target,
+ the remaining interval is searched using binary search. The
+ search is completed in $(BIGOH sqrt(n)) time. Use it when you
+ are reasonably confident that the value is around the beginning
+ of the range.
+ */
+ trot,
+
+ /**
+ 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
+ remaining interval is searched using binary search. A value is
+ found in $(BIGOH log(n)) time.
+ */
+ gallop,
+
+ /**
+ Searches using a classic interval halving policy. The search
+ starts in the middle of the range, and each search step cuts
+ the range in half. This policy finds a value in $(BIGOH log(n))
+ time but is less cache friendly than $(D gallop) for large
+ ranges. The $(D binarySearch) policy is used as the last step
+ of $(D trot), $(D gallop), $(D trotBackwards), and $(D
+ gallopBackwards) strategies.
+ */
+ binarySearch,
+
+ /**
+ Similar to $(D trot) but starts backwards. Use it when
+ confident that the value is around the end of the range.
+ */
+ trotBackwards,
+
+ /**
+ Similar to $(D gallop) but starts backwards. Use it when
+ confident that the value is around the end of the range.
+ */
+ gallopBackwards
+ }
+
+/**
+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
+$(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)
+{
+ import std.functional : binaryFun;
+
+ private alias predFun = binaryFun!pred;
+ private bool geq(L, R)(L lhs, R rhs)
+ {
+ return !predFun(lhs, rhs);
+ }
+ private bool gt(L, R)(L lhs, R rhs)
+ {
+ return predFun(rhs, lhs);
+ }
+ private Range _input;
+
+ // Undocummented because a clearer way to invoke is by calling
+ // assumeSorted.
+ this(Range input)
+ out
+ {
+ // moved out of the body as a workaround for Issue 12661
+ dbgVerifySorted();
+ }
+ body
+ {
+ this._input = input;
+ }
+
+ // Assertion only.
+ private void dbgVerifySorted()
+ {
+ if (!__ctfe)
+ debug
+ {
+ static if (isRandomAccessRange!Range && hasLength!Range)
+ {
+ 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;
+ auto st = stride(this._input, step);
+
+ assert(isSorted!pred(st), "Range is not sorted");
+ }
+ }
+ }
+
+ /// Range primitives.
+ @property bool empty() //const
+ {
+ return this._input.empty;
+ }
+
+ /// Ditto
+ static if (isForwardRange!Range)
+ @property auto save()
+ {
+ // Avoid the constructor
+ typeof(this) result = this;
+ result._input = _input.save;
+ return result;
+ }
+
+ /// Ditto
+ @property auto ref front()
+ {
+ return _input.front;
+ }
+
+ /// Ditto
+ void popFront()
+ {
+ _input.popFront();
+ }
+
+ /// Ditto
+ static if (isBidirectionalRange!Range)
+ {
+ @property auto ref back()
+ {
+ return _input.back;
+ }
+
+ /// Ditto
+ void popBack()
+ {
+ _input.popBack();
+ }
+ }
+
+ /// Ditto
+ static if (isRandomAccessRange!Range)
+ auto ref opIndex(size_t i)
+ {
+ return _input[i];
+ }
+
+ /// Ditto
+ static if (hasSlicing!Range)
+ auto opSlice(size_t a, size_t 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;
+ }
+
+ /// Ditto
+ static if (hasLength!Range)
+ {
+ @property size_t length() //const
+ {
+ return _input.length;
+ }
+ alias opDollar = length;
+ }
+
+/**
+ Releases the controlled range and returns it.
+*/
+ auto release()
+ {
+ import std.algorithm.mutation : move;
+ return move(_input);
+ }
+
+ // Assuming a predicate "test" that returns 0 for a left portion
+ // 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 && hasLength!Range)
+ {
+ size_t first = 0, count = _input.length;
+ while (count > 0)
+ {
+ immutable step = count / 2, it = first + step;
+ if (!test(_input[it], v))
+ {
+ first = it + 1;
+ count -= step + 1;
+ }
+ else
+ {
+ count = step;
+ }
+ }
+ return first;
+ }
+
+ // Specialization for trot and gallop
+ private size_t getTransitionIndex(SearchPolicy sp, alias test, V)(V v)
+ if ((sp == SearchPolicy.trot || sp == SearchPolicy.gallop)
+ && isRandomAccessRange!Range)
+ {
+ if (empty || test(front, v)) return 0;
+ immutable count = length;
+ if (count == 1) return 1;
+ size_t below = 0, above = 1, step = 2;
+ while (!test(_input[above], v))
+ {
+ // Still too small, update below and increase gait
+ below = above;
+ immutable next = above + step;
+ if (next >= count)
+ {
+ // Overshot - the next step took us beyond the end. So
+ // now adjust next and simply exit the loop to do the
+ // binary search thingie.
+ above = count;
+ break;
+ }
+ // Still in business, increase step and continue
+ above = next;
+ static if (sp == SearchPolicy.trot)
+ ++step;
+ else
+ step <<= 1;
+ }
+ return below + this[below .. above].getTransitionIndex!(
+ SearchPolicy.binarySearch, test, V)(v);
+ }
+
+ // Specialization for trotBackwards and gallopBackwards
+ private size_t getTransitionIndex(SearchPolicy sp, alias test, V)(V v)
+ if ((sp == SearchPolicy.trotBackwards || sp == SearchPolicy.gallopBackwards)
+ && isRandomAccessRange!Range)
+ {
+ immutable count = length;
+ if (empty || !test(back, v)) return count;
+ if (count == 1) return 0;
+ size_t below = count - 2, above = count - 1, step = 2;
+ while (test(_input[below], v))
+ {
+ // Still too large, update above and increase gait
+ above = below;
+ if (below < step)
+ {
+ // Overshot - the next step took us beyond the end. So
+ // now adjust next and simply fall through to do the
+ // binary search thingie.
+ below = 0;
+ break;
+ }
+ // Still in business, increase step and continue
+ below -= step;
+ static if (sp == SearchPolicy.trot)
+ ++step;
+ else
+ step <<= 1;
+ }
+ return below + this[below .. above].getTransitionIndex!(
+ SearchPolicy.binarySearch, test, V)(v);
+ }
+
+// lowerBound
+/**
+ This function uses a search with policy $(D sp) to find the
+ largest left subrange on which $(D pred(x, value)) is $(D true) for
+ all $(D x) (e.g., if $(D pred) is "less than", returns the portion of
+ the range with elements strictly smaller than $(D value)). The search
+ schedule and its complexity are documented in
+ $(LREF SearchPolicy). See also STL's
+ $(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)
+ && hasSlicing!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
+subrange on which $(D pred(value, x)) is $(D true) for all $(D x)
+(e.g., if $(D pred) is "less than", returns the portion of the range
+with elements strictly greater than $(D value)). The search schedule
+and its complexity are documented in $(LREF SearchPolicy).
+
+For ranges that do not offer random access, $(D SearchPolicy.linear)
+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 $(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))
+ {
+ static assert(hasSlicing!Range || sp == SearchPolicy.linear,
+ "Specify SearchPolicy.linear explicitly for "
+ ~ typeof(this).stringof);
+ static if (sp == SearchPolicy.linear)
+ {
+ for (; !_input.empty && !predFun(value, _input.front);
+ _input.popFront())
+ {
+ }
+ return this;
+ }
+ else
+ {
+ return this[getTransitionIndex!(sp, gt)(value) .. length];
+ }
+ }
+
+ ///
+ @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
+ pred(e, value)) and $(D pred(value, e)) evaluate to $(D false) (e.g.,
+ if $(D pred) is "less than", returns the portion of the range with
+ elements equal to $(D value)). Uses a classic binary search with
+ interval halving until it finds a value that satisfies the condition,
+ then uses $(D SearchPolicy.gallopBackwards) to find the left boundary
+ and $(D SearchPolicy.gallop) to find the right boundary. These
+ 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 $(HTTP sgi.com/tech/stl/equal_range.html, equal_range).
+*/
+ auto equalRange(V)(V value)
+ if (isTwoWayCompatible!(predFun, ElementType!Range, V)
+ && isRandomAccessRange!Range)
+ {
+ size_t first = 0, count = _input.length;
+ while (count > 0)
+ {
+ immutable step = count / 2;
+ auto 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
+ {
+ // Equal to value, do binary searches in the
+ // leftover portions
+ // Gallop towards the left end as it's likely nearby
+ immutable left = first
+ + this[first .. it]
+ .lowerBound!(SearchPolicy.gallopBackwards)(value).length;
+ first += count;
+ // Gallop towards the right end as it's likely nearby
+ immutable right = first
+ - this[it + 1 .. first]
+ .upperBound!(SearchPolicy.gallop)(value).length;
+ return this[left .. right];
+ }
+ }
+ 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
+of $(D lowerBound(value)), $(D r[1]) is the same as the result of $(D
+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.
+*/
+ auto trisect(V)(V value)
+ if (isTwoWayCompatible!(predFun, ElementType!Range, V)
+ && isRandomAccessRange!Range && hasLength!Range)
+ {
+ import std.typecons : tuple;
+ size_t first = 0, count = _input.length;
+ while (count > 0)
+ {
+ immutable step = count / 2;
+ auto 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
+ {
+ // Equal to value, do binary searches in the
+ // leftover portions
+ // Gallop towards the left end as it's likely nearby
+ immutable left = first
+ + this[first .. it]
+ .lowerBound!(SearchPolicy.gallopBackwards)(value).length;
+ first += count;
+ // Gallop towards the right end as it's likely nearby
+ immutable right = first
+ - this[it + 1 .. first]
+ .upperBound!(SearchPolicy.gallop)(value).length;
+ return tuple(this[0 .. left], this[left .. right],
+ this[right .. length]);
+ }
+ }
+ // No equal element was found
+ 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 $(HTTP
+sgi.com/tech/stl/binary_search.html, binary_search).
+ */
+
+ bool contains(V)(V value)
+ if (isRandomAccessRange!Range)
+ {
+ if (empty) return false;
+ immutable i = getTransitionIndex!(SearchPolicy.binarySearch, geq)(value);
+ if (i >= length) return false;
+ return !predFun(value, _input[i]);
+ }
+
+// groupBy
+/**
+Returns a range of subranges of elements that are equivalent according to the
+sorting relation.
+ */
+ auto groupBy()()
+ {
+ import std.algorithm.iteration : chunkBy;
+ return _input.chunkBy!((a, b) => !predFun(a, b) && !predFun(b, a));
+ }
+}
+
+///
+@safe unittest
+{
+ import std.algorithm.sorting : sort;
+ auto a = [ 1, 2, 3, 42, 52, 64 ];
+ auto r = assumeSorted(a);
+ assert(r.contains(3));
+ assert(!r.contains(32));
+ auto r1 = sort!"a > b"(a);
+ assert(r1.contains(3));
+ assert(!r1.contains(32));
+ assert(r1.release() == [ 64, 52, 42, 3, 2, 1 ]);
+}
+
+/**
+$(D SortedRange) could accept ranges weaker than random-access, but it
+is unable to provide interesting functionality for them. Therefore,
+$(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 sorted-ness, $(D SortedRange) will work erratically.
+*/
+@safe unittest
+{
+ import std.algorithm.mutation : swap;
+ auto a = [ 1, 2, 3, 42, 52, 64 ];
+ auto r = assumeSorted(a);
+ assert(r.contains(42));
+ swap(a[3], a[5]); // illegal to break sortedness of original range
+ assert(!r.contains(42)); // passes although it shouldn't
+}
+
+@safe unittest
+{
+ import std.algorithm.comparison : equal;
+
+ auto a = [ 10, 20, 30, 30, 30, 40, 40, 50, 60 ];
+ auto r = assumeSorted(a).trisect(30);
+ assert(equal(r[0], [ 10, 20 ]));
+ assert(equal(r[1], [ 30, 30, 30 ]));
+ assert(equal(r[2], [ 40, 40, 50, 60 ]));
+
+ r = assumeSorted(a).trisect(35);
+ assert(equal(r[0], [ 10, 20, 30, 30, 30 ]));
+ assert(r[1].empty);
+ assert(equal(r[2], [ 40, 40, 50, 60 ]));
+}
+
+@safe unittest
+{
+ 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" ]));
+ assert(equal(r[1], [ "B" ]));
+ assert(equal(r[2], [ "E", "F" ]));
+ r = assumeSorted!"cmp(a,b) < 0"(a).trisect("A"d);
+ assert(r[0].empty);
+ assert(equal(r[1], [ "A" ]));
+ assert(equal(r[2], [ "AG", "B", "E", "F" ]));
+}
+
+@safe unittest
+{
+ import std.algorithm.comparison : equal;
+ static void test(SearchPolicy pol)()
+ {
+ auto a = [ 1, 2, 3, 42, 52, 64 ];
+ auto r = assumeSorted(a);
+ assert(equal(r.lowerBound(42), [1, 2, 3]));
+
+ assert(equal(r.lowerBound!(pol)(42), [1, 2, 3]));
+ assert(equal(r.lowerBound!(pol)(41), [1, 2, 3]));
+ assert(equal(r.lowerBound!(pol)(43), [1, 2, 3, 42]));
+ assert(equal(r.lowerBound!(pol)(51), [1, 2, 3, 42]));
+ assert(equal(r.lowerBound!(pol)(3), [1, 2]));
+ assert(equal(r.lowerBound!(pol)(55), [1, 2, 3, 42, 52]));
+ assert(equal(r.lowerBound!(pol)(420), a));
+ assert(equal(r.lowerBound!(pol)(0), a[0 .. 0]));
+
+ assert(equal(r.upperBound!(pol)(42), [52, 64]));
+ assert(equal(r.upperBound!(pol)(41), [42, 52, 64]));
+ assert(equal(r.upperBound!(pol)(43), [52, 64]));
+ assert(equal(r.upperBound!(pol)(51), [52, 64]));
+ assert(equal(r.upperBound!(pol)(53), [64]));
+ assert(equal(r.upperBound!(pol)(55), [64]));
+ assert(equal(r.upperBound!(pol)(420), a[0 .. 0]));
+ assert(equal(r.upperBound!(pol)(0), a));
+ }
+
+ test!(SearchPolicy.trot)();
+ test!(SearchPolicy.gallop)();
+ test!(SearchPolicy.trotBackwards)();
+ test!(SearchPolicy.gallopBackwards)();
+ test!(SearchPolicy.binarySearch)();
+}
+
+@safe unittest
+{
+ // Check for small arrays
+ int[] a;
+ auto r = assumeSorted(a);
+ a = [ 1 ];
+ r = assumeSorted(a);
+ a = [ 1, 2 ];
+ r = assumeSorted(a);
+ a = [ 1, 2, 3 ];
+ r = assumeSorted(a);
+}
+
+@safe unittest
+{
+ import std.algorithm.mutation : swap;
+ auto a = [ 1, 2, 3, 42, 52, 64 ];
+ auto r = assumeSorted(a);
+ assert(r.contains(42));
+ swap(a[3], a[5]); // illegal to break sortedness of original range
+ assert(!r.contains(42)); // passes although it shouldn't
+}
+
+@safe unittest
+{
+ immutable(int)[] arr = [ 1, 2, 3 ];
+ auto s = assumeSorted(arr);
+}
+
+@system unittest
+{
+ import std.algorithm.comparison : equal;
+ int[] arr = [100, 101, 102, 200, 201, 300];
+ auto s = assumeSorted!((a, b) => a / 100 < b / 100)(arr);
+ assert(s.groupBy.equal!equal([[100, 101, 102], [200, 201], [300]]));
+}
+
+// Test on an input range
+@system unittest
+{
+ 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");
+ scope(exit) if (exists(name)) remove(name);
+ // write a sorted range of lines to the file
+ f.write("abc\ndef\nghi\njkl");
+ f.close();
+ f.open(name, "r");
+ auto r = assumeSorted(f.byLine());
+ auto r1 = r.upperBound!(SearchPolicy.linear)("def");
+ assert(r1.front == "ghi", r1.front);
+ f.close();
+}
+
+/**
+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 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 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))
+{
+ return SortedRange!(Unqual!R, pred)(r);
+}
+
+@safe unittest
+{
+ 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);
+ assert(equal(p, [0, 1, 2, 3]));
+ p = assumeSorted(a).lowerBound(5);
+ assert(equal(p, [0, 1, 2, 3, 4]));
+ p = assumeSorted(a).lowerBound(6);
+ assert(equal(p, [ 0, 1, 2, 3, 4, 5]));
+ p = assumeSorted(a).lowerBound(6.9);
+ assert(equal(p, [ 0, 1, 2, 3, 4, 5, 6]));
+}
+
+@safe unittest
+{
+ 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 ]));
+ p = assumeSorted(a).upperBound(4.2);
+ assert(equal(p, [ 5, 6 ]));
+}
+
+@safe unittest
+{
+ import std.algorithm.comparison : equal;
+ import std.conv : text;
+
+ int[] a = [ 1, 2, 3, 3, 3, 4, 4, 5, 6 ];
+ auto p = assumeSorted(a).equalRange(3);
+ assert(equal(p, [ 3, 3, 3 ]), text(p));
+ p = assumeSorted(a).equalRange(4);
+ assert(equal(p, [ 4, 4 ]), text(p));
+ p = assumeSorted(a).equalRange(2);
+ assert(equal(p, [ 2 ]));
+ p = assumeSorted(a).equalRange(0);
+ assert(p.empty);
+ p = assumeSorted(a).equalRange(7);
+ assert(p.empty);
+ p = assumeSorted(a).equalRange(3.0);
+ assert(equal(p, [ 3, 3, 3]));
+}
+
+@safe unittest
+{
+ int[] a = [ 1, 2, 3, 3, 3, 4, 4, 5, 6 ];
+ if (a.length)
+ {
+ auto b = a[a.length / 2];
+ //auto r = sort(a);
+ //assert(r.contains(b));
+ }
+}
+
+@safe unittest
+{
+ auto a = [ 5, 7, 34, 345, 677 ];
+ auto r = assumeSorted(a);
+ a = null;
+ r = assumeSorted(a);
+ a = [ 1 ];
+ r = assumeSorted(a);
+}
+
+@system unittest
+{
+ bool ok = true;
+ try
+ {
+ auto r2 = assumeSorted([ 677, 345, 34, 7, 5 ]);
+ debug ok = false;
+ }
+ catch (Throwable)
+ {
+ }
+ 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.
+ Both the original range and the RefRange will always have the exact same
+ elements. Any operation done on one will affect the other. So, for instance,
+ if it's passed to a function which would implicitly copy the original range
+ if it were passed to it, the original range is $(I not) copied but is
+ consumed as if it were a reference type.
+
+ 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 (isInputRange!R)
+{
+public:
+
+ /++ +/
+ this(R* range) @safe pure nothrow
+ {
+ _range = range;
+ }
+
+
+ /++
+ This does not assign the pointer of $(D rhs) to this $(D RefRange).
+ Rather it assigns the range pointed to by $(D rhs) to the range pointed
+ to by this $(D RefRange). This is because $(I any) operation on a
+ $(D RefRange) is the same is if it occurred to the original range. The
+ one exception is when a $(D RefRange) is assigned $(D null) either
+ directly or because $(D rhs) is $(D null). In that case, $(D RefRange)
+ no longer refers to the original range but is $(D null).
+ +/
+ auto opAssign(RefRange rhs)
+ {
+ if (_range && rhs._range)
+ *_range = *rhs._range;
+ else
+ _range = rhs._range;
+
+ return this;
+ }
+
+ /++ +/
+ void opAssign(typeof(null) rhs)
+ {
+ _range = null;
+ }
+
+
+ /++
+ A pointer to the wrapped range.
+ +/
+ @property inout(R*) ptr() @safe inout pure nothrow
+ {
+ return _range;
+ }
+
+
+ version (StdDdoc)
+ {
+ /++ +/
+ @property auto front() {assert(0);}
+ /++ Ditto +/
+ @property auto front() const {assert(0);}
+ /++ Ditto +/
+ @property auto front(ElementType!R value) {assert(0);}
+ }
+ else
+ {
+ @property auto front()
+ {
+ return (*_range).front;
+ }
+
+ 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)
+ {
+ return (*_range).front = value;
+ }
+ }
+
+
+ version (StdDdoc)
+ {
+ @property bool empty(); ///
+ @property bool empty() const; ///Ditto
+ }
+ else static if (isInfinite!R)
+ enum empty = false;
+ else
+ {
+ @property bool empty()
+ {
+ return (*_range).empty;
+ }
+
+ static if (is(typeof((*cast(const R*)_range).empty))) @property bool empty() const
+ {
+ return (*_range).empty;
+ }
+ }
+
+
+ /++ +/
+ void popFront()
+ {
+ return (*_range).popFront();
+ }
+
+
+ version (StdDdoc)
+ {
+ /++
+ Only defined if $(D isForwardRange!R) is $(D true).
+ +/
+ @property auto save() {assert(0);}
+ /++ Ditto +/
+ @property auto save() const {assert(0);}
+ /++ Ditto +/
+ auto opSlice() {assert(0);}
+ /++ Ditto +/
+ auto opSlice() const {assert(0);}
+ }
+ else static if (isForwardRange!R)
+ {
+ 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 RefRange!S save() @trusted
+ {
+ mixin(_genSave());
+ }
+
+ static if (is(typeof((*cast(const R*)_range).save))) @property RefRange!CS save() @trusted const
+ {
+ mixin(_genSave());
+ }
+ }
+ else
+ {
+ @property RefRange!S save()
+ {
+ mixin(_genSave());
+ }
+
+ static if (is(typeof((*cast(const R*)_range).save))) @property RefRange!CS save() const
+ {
+ mixin(_genSave());
+ }
+ }
+
+ auto opSlice()()
+ {
+ return save;
+ }
+
+ auto opSlice()() const
+ {
+ return save;
+ }
+
+ private static string _genSave() @safe pure nothrow
+ {
+ 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);`;
+ }
+
+ static assert(isForwardRange!RefRange);
+ }
+
+
+ version (StdDdoc)
+ {
+ /++
+ Only defined if $(D isBidirectionalRange!R) is $(D true).
+ +/
+ @property auto back() {assert(0);}
+ /++ Ditto +/
+ @property auto back() const {assert(0);}
+ /++ Ditto +/
+ @property auto back(ElementType!R value) {assert(0);}
+ }
+ else static if (isBidirectionalRange!R)
+ {
+ @property auto back()
+ {
+ return (*_range).back;
+ }
+
+ 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)
+ {
+ return (*_range).back = value;
+ }
+ }
+
+
+ /++ Ditto +/
+ static if (isBidirectionalRange!R) void popBack()
+ {
+ return (*_range).popBack();
+ }
+
+
+ version (StdDdoc)
+ {
+ /++
+ Only defined if $(D isRandomAccesRange!R) is $(D true).
+ +/
+ auto ref opIndex(IndexType)(IndexType index) {assert(0);}
+
+ /++ Ditto +/
+ auto ref opIndex(IndexType)(IndexType index) const {assert(0);}
+ }
+ else static if (isRandomAccessRange!R)
+ {
+ auto ref opIndex(IndexType)(IndexType index)
+ if (is(typeof((*_range)[index])))
+ {
+ return (*_range)[index];
+ }
+
+ auto ref opIndex(IndexType)(IndexType index) const
+ if (is(typeof((*cast(const R*)_range)[index])))
+ {
+ return (*_range)[index];
+ }
+ }
+
+
+ /++
+ Only defined if $(D hasMobileElements!R) and $(D isForwardRange!R) are
+ $(D true).
+ +/
+ static if (hasMobileElements!R && isForwardRange!R) auto moveFront()
+ {
+ return (*_range).moveFront();
+ }
+
+
+ /++
+ Only defined if $(D hasMobileElements!R) and $(D isBidirectionalRange!R)
+ are $(D true).
+ +/
+ static if (hasMobileElements!R && isBidirectionalRange!R) auto moveBack()
+ {
+ return (*_range).moveBack();
+ }
+
+
+ /++
+ Only defined if $(D hasMobileElements!R) and $(D isRandomAccessRange!R)
+ are $(D true).
+ +/
+ static if (hasMobileElements!R && isRandomAccessRange!R) auto moveAt(size_t index)
+ {
+ return (*_range).moveAt(index);
+ }
+
+
+ version (StdDdoc)
+ {
+ /++
+ Only defined if $(D hasLength!R) is $(D true).
+ +/
+ @property auto length() {assert(0);}
+
+ /++ Ditto +/
+ @property auto length() const {assert(0);}
+
+ /++ Ditto +/
+ alias opDollar = length;
+ }
+ else static if (hasLength!R)
+ {
+ @property auto length()
+ {
+ return (*_range).length;
+ }
+
+ static if (is(typeof((*cast(const R*)_range).length))) @property auto length() const
+ {
+ return (*_range).length;
+ }
+
+ alias opDollar = length;
+ }
+
+
+ version (StdDdoc)
+ {
+ /++
+ Only defined if $(D hasSlicing!R) is $(D true).
+ +/
+ auto opSlice(IndexType1, IndexType2)
+ (IndexType1 begin, IndexType2 end) {assert(0);}
+
+ /++ Ditto +/
+ auto opSlice(IndexType1, IndexType2)
+ (IndexType1 begin, IndexType2 end) const {assert(0);}
+ }
+ else static if (hasSlicing!R)
+ {
+ 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])))
+ {
+ mixin(_genOpSlice());
+ }
+
+ RefRange!CT opSlice(IndexType1, IndexType2)
+ (IndexType1 begin, IndexType2 end) const
+ if (is(typeof((*cast(const R*)_range)[begin .. end])))
+ {
+ mixin(_genOpSlice());
+ }
+
+ private static string _genOpSlice() @safe pure nothrow
+ {
+ 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);`;
+ }
+ }
+
+
+private:
+
+ R* _range;
+}
+
+/// Basic Example
+@system unittest
+{
+ import std.algorithm.searching : find;
+ ubyte[] buffer = [1, 9, 45, 12, 22];
+ auto found1 = find(buffer, 45);
+ assert(found1 == [45, 12, 22]);
+ assert(buffer == [1, 9, 45, 12, 22]);
+
+ auto wrapped1 = refRange(&buffer);
+ auto found2 = find(wrapped1, 45);
+ assert(*found2.ptr == [45, 12, 22]);
+ assert(buffer == [45, 12, 22]);
+
+ auto found3 = find(wrapped1.save, 22);
+ assert(*found3.ptr == [22]);
+ assert(buffer == [45, 12, 22]);
+
+ string str = "hello world";
+ auto wrappedStr = refRange(&str);
+ assert(str.front == 'h');
+ str.popFrontN(5);
+ assert(str == " world");
+ assert(wrappedStr.front == ' ');
+ assert(*wrappedStr.ptr == " world");
+}
+
+/// opAssign Example.
+@system unittest
+{
+ ubyte[] buffer1 = [1, 2, 3, 4, 5];
+ ubyte[] buffer2 = [6, 7, 8, 9, 10];
+ auto wrapped1 = refRange(&buffer1);
+ auto wrapped2 = refRange(&buffer2);
+ assert(wrapped1.ptr is &buffer1);
+ assert(wrapped2.ptr is &buffer2);
+ assert(wrapped1.ptr !is wrapped2.ptr);
+ assert(buffer1 != buffer2);
+
+ wrapped1 = wrapped2;
+
+ //Everything points to the same stuff as before.
+ assert(wrapped1.ptr is &buffer1);
+ assert(wrapped2.ptr is &buffer2);
+ assert(wrapped1.ptr !is wrapped2.ptr);
+
+ //But buffer1 has changed due to the assignment.
+ assert(buffer1 == [6, 7, 8, 9, 10]);
+ assert(buffer2 == [6, 7, 8, 9, 10]);
+
+ buffer2 = [11, 12, 13, 14, 15];
+
+ //Everything points to the same stuff as before.
+ assert(wrapped1.ptr is &buffer1);
+ assert(wrapped2.ptr is &buffer2);
+ assert(wrapped1.ptr !is wrapped2.ptr);
+
+ //But buffer2 has changed due to the assignment.
+ assert(buffer1 == [6, 7, 8, 9, 10]);
+ assert(buffer2 == [11, 12, 13, 14, 15]);
+
+ wrapped2 = null;
+
+ //The pointer changed for wrapped2 but not wrapped1.
+ assert(wrapped1.ptr is &buffer1);
+ assert(wrapped2.ptr is null);
+ assert(wrapped1.ptr !is wrapped2.ptr);
+
+ //buffer2 is not affected by the assignment.
+ assert(buffer1 == [6, 7, 8, 9, 10]);
+ assert(buffer2 == [11, 12, 13, 14, 15]);
+}
+
+@system unittest
+{
+ import std.algorithm.iteration : filter;
+ {
+ ubyte[] buffer = [1, 2, 3, 4, 5];
+ auto wrapper = refRange(&buffer);
+ auto p = wrapper.ptr;
+ auto f = wrapper.front;
+ wrapper.front = f;
+ auto e = wrapper.empty;
+ wrapper.popFront();
+ auto s = wrapper.save;
+ auto b = wrapper.back;
+ wrapper.back = b;
+ wrapper.popBack();
+ auto i = wrapper[0];
+ wrapper.moveFront();
+ wrapper.moveBack();
+ wrapper.moveAt(0);
+ auto l = wrapper.length;
+ auto sl = wrapper[0 .. 1];
+ assert(wrapper[0 .. $].length == buffer[0 .. $].length);
+ }
+
+ {
+ ubyte[] buffer = [1, 2, 3, 4, 5];
+ const wrapper = refRange(&buffer);
+ const p = wrapper.ptr;
+ const f = wrapper.front;
+ const e = wrapper.empty;
+ const s = wrapper.save;
+ const b = wrapper.back;
+ const i = wrapper[0];
+ const l = wrapper.length;
+ const sl = wrapper[0 .. 1];
+ }
+
+ {
+ ubyte[] buffer = [1, 2, 3, 4, 5];
+ auto filtered = filter!"true"(buffer);
+ auto wrapper = refRange(&filtered);
+ auto p = wrapper.ptr;
+ auto f = wrapper.front;
+ wrapper.front = f;
+ auto e = wrapper.empty;
+ wrapper.popFront();
+ auto s = wrapper.save;
+ wrapper.moveFront();
+ }
+
+ {
+ ubyte[] buffer = [1, 2, 3, 4, 5];
+ auto filtered = filter!"true"(buffer);
+ const wrapper = refRange(&filtered);
+ const p = wrapper.ptr;
+
+ //Cannot currently be const. filter needs to be updated to handle const.
+ /+
+ const f = wrapper.front;
+ const e = wrapper.empty;
+ const s = wrapper.save;
+ +/
+ }
+
+ {
+ string str = "hello world";
+ auto wrapper = refRange(&str);
+ auto p = wrapper.ptr;
+ auto f = wrapper.front;
+ auto e = wrapper.empty;
+ wrapper.popFront();
+ auto s = wrapper.save;
+ 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.
+@system unittest
+{
+ ubyte[] buffer1 = [1, 2, 3, 4, 5];
+ ubyte[] buffer2 = [6, 7, 8, 9, 10];
+ RefRange!(ubyte[]) wrapper1;
+ RefRange!(ubyte[]) wrapper2 = refRange(&buffer2);
+ assert(wrapper1.ptr is null);
+ assert(wrapper2.ptr is &buffer2);
+
+ wrapper1 = refRange(&buffer1);
+ assert(wrapper1.ptr is &buffer1);
+
+ wrapper1 = wrapper2;
+ assert(wrapper1.ptr is &buffer1);
+ assert(buffer1 == buffer2);
+
+ wrapper1 = RefRange!(ubyte[]).init;
+ assert(wrapper1.ptr is null);
+ assert(wrapper2.ptr is &buffer2);
+ assert(buffer1 == buffer2);
+ assert(buffer1 == [6, 7, 8, 9, 10]);
+
+ wrapper2 = null;
+ assert(wrapper2.ptr is null);
+ assert(buffer2 == [6, 7, 8, 9, 10]);
+}
+
+@system unittest
+{
+ 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.
+ {
+ int[] arr = [1, 42, 2, 41, 3, 40, 4, 42, 9];
+ auto wrapper = refRange(&arr);
+
+ assert(*find(wrapper, 41).ptr == [41, 3, 40, 4, 42, 9]);
+ assert(arr == [41, 3, 40, 4, 42, 9]);
+
+ assert(*drop(wrapper, 2).ptr == [40, 4, 42, 9]);
+ assert(arr == [40, 4, 42, 9]);
+
+ assert(equal(until(wrapper, 42), [40, 4]));
+ assert(arr == [42, 9]);
+
+ assert(find(wrapper, 12).empty);
+ assert(arr.empty);
+ }
+
+ {
+ string str = "Hello, world-like object.";
+ auto wrapper = refRange(&str);
+
+ assert(*find(wrapper, "l").ptr == "llo, world-like object.");
+ assert(str == "llo, world-like object.");
+
+ assert(equal(take(wrapper, 5), "llo, "));
+ assert(str == "world-like object.");
+ }
+
+ //Test that operating on saved ranges does not consume the original.
+ {
+ int[] arr = [1, 42, 2, 41, 3, 40, 4, 42, 9];
+ auto wrapper = refRange(&arr);
+ auto saved = wrapper.save;
+ saved.popFrontN(3);
+ assert(*saved.ptr == [41, 3, 40, 4, 42, 9]);
+ assert(arr == [1, 42, 2, 41, 3, 40, 4, 42, 9]);
+ }
+
+ {
+ string str = "Hello, world-like object.";
+ auto wrapper = refRange(&str);
+ auto saved = wrapper.save;
+ saved.popFrontN(13);
+ assert(*saved.ptr == "like object.");
+ assert(str == "Hello, world-like object.");
+ }
+
+ //Test that functions which use save work properly.
+ {
+ int[] arr = [1, 42];
+ auto wrapper = refRange(&arr);
+ assert(equal(commonPrefix(wrapper, [1, 27]), [1]));
+ }
+
+ {
+ int[] arr = [4, 5, 6, 7, 1, 2, 3];
+ auto wrapper = refRange(&arr);
+ assert(bringToFront(wrapper[0 .. 4], wrapper[4 .. arr.length]) == 3);
+ assert(arr == [1, 2, 3, 4, 5, 6, 7]);
+ }
+
+ //Test bidirectional functions.
+ {
+ int[] arr = [1, 42, 2, 41, 3, 40, 4, 42, 9];
+ auto wrapper = refRange(&arr);
+
+ assert(wrapper.back == 9);
+ assert(arr == [1, 42, 2, 41, 3, 40, 4, 42, 9]);
+
+ wrapper.popBack();
+ assert(arr == [1, 42, 2, 41, 3, 40, 4, 42]);
+ }
+
+ {
+ string str = "Hello, world-like object.";
+ auto wrapper = refRange(&str);
+
+ assert(wrapper.back == '.');
+ assert(str == "Hello, world-like object.");
+
+ wrapper.popBack();
+ assert(str == "Hello, world-like object");
+ }
+
+ //Test random access functions.
+ {
+ int[] arr = [1, 42, 2, 41, 3, 40, 4, 42, 9];
+ auto wrapper = refRange(&arr);
+
+ assert(wrapper[2] == 2);
+ assert(arr == [1, 42, 2, 41, 3, 40, 4, 42, 9]);
+
+ assert(*wrapper[3 .. 6].ptr != null, [41, 3, 40]);
+ assert(arr == [1, 42, 2, 41, 3, 40, 4, 42, 9]);
+ }
+
+ //Test move functions.
+ {
+ int[] arr = [1, 42, 2, 41, 3, 40, 4, 42, 9];
+ auto wrapper = refRange(&arr);
+
+ auto t1 = wrapper.moveFront();
+ auto t2 = wrapper.moveBack();
+ wrapper.front = t2;
+ wrapper.back = t1;
+ assert(arr == [9, 42, 2, 41, 3, 40, 4, 42, 1]);
+
+ sort(wrapper.save);
+ assert(arr == [1, 2, 3, 4, 9, 40, 41, 42, 42]);
+ }
+}
+
+@system unittest
+{
+ struct S
+ {
+ @property int front() @safe const pure nothrow { return 0; }
+ enum bool empty = false;
+ void popFront() @safe pure nothrow { }
+ @property auto save() @safe pure nothrow { return this; }
+ }
+
+ 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)
+{
+ static if (!is(R == class))
+ return RefRange!R(range);
+ else
+ 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.algorithm.comparison : equal;
+ import std.random : rndGen;
+
+ 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]);
+}
+
+// Test all range types over all integral types
+@safe pure nothrow unittest
+{
+ import std.internal.test.dummyrange;
+
+ alias IntegralTypes = AliasSeq!(byte, ubyte, short, ushort, int, uint,
+ long, ulong);
+ foreach (IntegralType; IntegralTypes)
+ {
+ foreach (T; AllDummyRangesType!(IntegralType[]))
+ {
+ T a;
+ auto bw = Bitwise!T(a);
+
+ static if (isForwardRange!T)
+ {
+ auto bwFwdSave = bw.save;
+ }
+
+ static if (isBidirectionalRange!T)
+ {
+ auto bwBack = bw.save;
+ auto bwBackSave = bw.save;
+ }
+
+ 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);
+ }
+ }
+
+ // Make sure front and back are not the mechanisms that modify the range
+ long numCalls = 42;
+ bool initialFrontValue;
+
+ if (!bw.empty)
+ {
+ initialFrontValue = bw.front;
+ }
+
+ 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);
+ }
+ }
+ }
+}
+
+// Test opIndex and opSlice
+@system unittest
+{
+ 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);
+
+ // 2 ^ (bitsNum - 1)
+ auto second = cast(IntegralType)(cast(IntegralType)(1) << (bitsNum - 2));
+
+ IntegralType[] a = [first, second];
+ auto bw = Bitwise!(IntegralType[])(a);
+
+ // Check against lsb of a[0]
+ assert(bw[0] == true);
+ // Check against msb - 1 of a[1]
+ assert(bw[2 * bitsNum - 2] == true);
+
+ bw.popFront();
+ assert(bw[2 * bitsNum - 3] == true);
+
+ import core.exception : Error;
+ import std.exception : assertThrown;
+
+ // Check out of bounds error
+ assertThrown!Error(bw[2 * bitsNum - 1]);
+
+ 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.
+ */
+struct NullSink
+{
+ void put(E)(E){}
+}
+
+///
+@safe unittest
+{
+ 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).
+
+ 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 $(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))
+{
+ static struct Result
+ {
+ private R1 _input;
+ private R2 _output;
+ static if (!pipeOnPop)
+ {
+ private bool _frontAccessed;
+ }
+
+ static if (hasLength!R1)
+ {
+ @property auto length()
+ {
+ return _input.length;
+ }
+ }
+
+ static if (isInfinite!R1)
+ {
+ enum bool empty = false;
+ }
+ else
+ {
+ @property bool empty() { return _input.empty; }
+ }
+
+ void popFront()
+ {
+ assert(!_input.empty, "Attempting to popFront an empty tee");
+ static if (pipeOnPop)
+ {
+ put(_output, _input.front);
+ }
+ else
+ {
+ _frontAccessed = false;
+ }
+ _input.popFront();
+ }
+
+ @property auto ref front()
+ {
+ assert(!_input.empty, "Attempting to fetch the front of an empty tee");
+ static if (!pipeOnPop)
+ {
+ if (!_frontAccessed)
+ {
+ _frontAccessed = true;
+ put(_output, _input.front);
+ }
+ }
+ return _input.front;
+ }
+ }
+
+ return Result(inputRange, 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
+ has no type, typeof(template) will always return void.
+ If it's a template lambda, it's first necessary to instantiate
+ it with $(D ElementType!R1).
+ */
+ static if (is(typeof(fun) == void))
+ alias _fun = fun!(ElementType!R1);
+ else
+ alias _fun = fun;
+
+ static if (isFunctionPointer!_fun || isDelegate!_fun)
+ {
+ return tee!pipeOnPop(inputRange, _fun);
+ }
+ else
+ {
+ return tee!pipeOnPop(inputRange, &_fun);
+ }
+}
+
+///
+@safe unittest
+{
+ import std.algorithm.comparison : equal;
+ import std.algorithm.iteration : filter, map;
+
+ // Sum values while copying
+ int[] values = [1, 4, 9, 16, 25];
+ 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++)
+ .map!(a => a + 1)
+ .filter!(a => a < 10);
+
+ //Fine, equal also evaluates any lazy ranges passed to it.
+ //count is not 3 until equal evaluates newValues4
+ assert(equal(newValues4, [2, 5]));
+ assert(count == 3);
+}
+
+//
+@safe unittest
+{
+ import std.algorithm.comparison : equal;
+ import std.algorithm.iteration : filter, map;
+
+ int[] values = [1, 4, 9, 16, 25];
+
+ int count = 0;
+ auto newValues = values.filter!(a => a < 10)
+ .tee!(a => count++, No.pipeOnPop)
+ .map!(a => a + 1)
+ .filter!(a => a < 10);
+
+ auto val = newValues.front;
+ assert(count == 1);
+ //front is only evaluated once per element
+ val = newValues.front;
+ assert(count == 1);
+
+ //popFront() called, fun will be called
+ //again on the next access to front
+ newValues.popFront();
+ newValues.front;
+ assert(count == 2);
+
+ int[] preMap = new int[](3), postMap = [];
+ auto mappedValues = values.filter!(a => a < 10)
+ //Note the two different ways of using tee
+ .tee(preMap)
+ .map!(a => a + 1)
+ .tee!(a => postMap ~= a)
+ .filter!(a => a < 10);
+ assert(equal(mappedValues, [2, 5]));
+ assert(equal(preMap, [1, 4, 9]));
+ assert(equal(postMap, [2, 5, 10]));
+}
+
+//
+@safe unittest
+{
+ import std.algorithm.comparison : equal;
+ import std.algorithm.iteration : filter, map;
+
+ char[] txt = "Line one, Line 2".dup;
+
+ bool isVowel(dchar c)
+ {
+ import std.string : indexOf;
+ return "AaEeIiOoUu".indexOf(c) != -1;
+ }
+
+ int vowelCount = 0;
+ int shiftedCount = 0;
+ auto removeVowels = txt.tee!(c => isVowel(c) ? vowelCount++ : 0)
+ .filter!(c => !isVowel(c))
+ .map!(c => (c == ' ') ? c : c + 1)
+ .tee!(c => isVowel(c) ? shiftedCount++ : 0);
+ assert(equal(removeVowels, "Mo o- Mo 3"));
+ assert(vowelCount == 6);
+ assert(shiftedCount == 3);
+}
+
+@safe unittest
+{
+ // Manually stride to test different pipe behavior.
+ void testRange(Range)(Range r)
+ {
+ const int strideLen = 3;
+ int i = 0;
+ ElementType!Range elem1;
+ ElementType!Range elem2;
+ while (!r.empty)
+ {
+ if (i % strideLen == 0)
+ {
+ //Make sure front is only
+ //evaluated once per item
+ elem1 = r.front;
+ elem2 = r.front;
+ assert(elem1 == elem2);
+ }
+ r.popFront();
+ i++;
+ }
+ }
+
+ string txt = "abcdefghijklmnopqrstuvwxyz";
+
+ int popCount = 0;
+ auto pipeOnPop = txt.tee!(a => popCount++);
+ testRange(pipeOnPop);
+ assert(popCount == 26);
+
+ int frontCount = 0;
+ auto pipeOnFront = txt.tee!(a => frontCount++, No.pipeOnPop);
+ testRange(pipeOnFront);
+ assert(frontCount == 9);
+}
+
+@safe unittest
+{
+ import std.algorithm.comparison : equal;
+ import std.meta : AliasSeq;
+
+ //Test diverting elements to an OutputRange
+ string txt = "abcdefghijklmnopqrstuvwxyz";
+
+ dchar[] asink1 = [];
+ auto fsink = (dchar c) { asink1 ~= c; };
+ auto result1 = txt.tee(fsink).array;
+ assert(equal(txt, result1) && (equal(result1, asink1)));
+
+ dchar[] _asink1 = [];
+ auto _result1 = txt.tee!((dchar c) { _asink1 ~= c; })().array;
+ assert(equal(txt, _result1) && (equal(_result1, _asink1)));
+
+ dchar[] asink2 = new dchar[](txt.length);
+ void fsink2(dchar c) { static int i = 0; asink2[i] = c; i++; }
+ auto result2 = txt.tee(&fsink2).array;
+ assert(equal(txt, result2) && equal(result2, asink2));
+
+ dchar[] asink3 = new dchar[](txt.length);
+ auto result3 = txt.tee(asink3).array;
+ assert(equal(txt, result3) && equal(result3, asink3));
+
+ 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; AliasSeq!(string, wstring, dstring))
+ {
+ auto appSink = appender!StringType();
+ auto appResult = txt.tee(appSink).array;
+ assert(equal(txt, appResult) && equal(appResult, appSink.data));
+ }
+}
+
+@safe unittest
+{
+ // Issue 13483
+ static void func1(T)(T x) {}
+ void func2(int x) {}
+
+ 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/libphobos/src/std/range/primitives.d b/libphobos/src/std/range/primitives.d
new file mode 100644
index 0000000..d602048
--- /dev/null
+++ b/libphobos/src/std/range/primitives.d
@@ -0,0 +1,2320 @@
+/**
+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 $(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 $(LREF isOutputRange))
+ $(TD Tests if something is an $(I output _range), defined to be
+ something to which one can sequentially write data using the
+ $(LREF put) primitive.
+ ))
+ $(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 $(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 $(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).
+ ))
+)
+
+It also provides number of templates that test for various _range capabilities:
+
+$(BOOKTABLE ,
+ $(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 $(LREF ElementType))
+ $(TD Returns the element type of a given _range.
+ ))
+ $(TR $(TD $(LREF ElementEncodingType))
+ $(TD Returns the encoding element type of a given _range.
+ ))
+ $(TR $(TD $(LREF hasSwappableElements))
+ $(TD Tests if a _range is a forward _range with swappable elements.
+ ))
+ $(TR $(TD $(LREF hasAssignableElements))
+ $(TD Tests if a _range is a forward _range with mutable elements.
+ ))
+ $(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 $(LREF hasLength))
+ $(TD Tests if a given _range has the $(D length) attribute.
+ ))
+ $(TR $(TD $(LREF isInfinite))
+ $(TD Tests if a given _range is an $(I infinite _range).
+ ))
+ $(TR $(TD $(LREF hasSlicing))
+ $(TD Tests if a given _range supports the array slicing operation $(D
+ R[x .. y]).
+ ))
+)
+
+Finally, it includes some convenience functions for manipulating ranges:
+
+$(BOOKTABLE ,
+ $(TR $(TD $(LREF popFrontN))
+ $(TD Advances a given _range by up to $(I n) elements.
+ ))
+ $(TR $(TD $(LREF popBackN))
+ $(TD Advances a given bidirectional _range from the right by up to
+ $(I n) elements.
+ ))
+ $(TR $(TD $(LREF popFrontExactly))
+ $(TD Advances a given _range by up exactly $(I n) elements.
+ ))
+ $(TR $(TD $(LREF popBackExactly))
+ $(TD Advances a given bidirectional _range from the right by exactly
+ $(I n) elements.
+ ))
+ $(TR $(TD $(LREF moveFront))
+ $(TD Removes the front element of a _range.
+ ))
+ $(TR $(TD $(LREF moveBack))
+ $(TD Removes the back element of a bidirectional _range.
+ ))
+ $(TR $(TD $(LREF moveAt))
+ $(TD Removes the $(I i)'th element of a random-access _range.
+ ))
+ $(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/_primitives.d)
+
+License: $(HTTP boost.org/LICENSE_1_0.txt, Boost License 1.0).
+
+Authors: $(HTTP erdani.com, Andrei Alexandrescu), David Simcha,
+and Jonathan M Davis. Credit for some of the ideas in building this module goes
+to $(HTTP fantascienza.net/leonardo/so/, Leonardo Maffi).
+*/
+module std.range.primitives;
+
+import std.traits;
+
+/**
+Returns $(D true) if $(D R) is an input range. An input range must
+define the primitives $(D empty), $(D popFront), and $(D front). The
+following code should compile for any input range.
+
+----
+R r; // can define a range object
+if (r.empty) {} // can test for empty
+r.popFront(); // can invoke popFront()
+auto h = r.front; // can get the front of the range of non-void type
+----
+
+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`.)
+)
+
+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
+
+Returns:
+ true if R is an InputRange, false if not
+ */
+enum bool isInputRange(R) =
+ is(typeof(R.init) == R)
+ && is(ReturnType!((R r) => r.empty) == bool)
+ && is(typeof((return ref R r) => r.front))
+ && !is(ReturnType!((R r) => r.front) == void)
+ && is(typeof((R r) => r.popFront));
+
+///
+@safe unittest
+{
+ struct A {}
+ struct B
+ {
+ void popFront();
+ @property bool empty();
+ @property int front();
+ }
+ static assert(!isInputRange!A);
+ static assert( isInputRange!B);
+ static assert( isInputRange!(int[]));
+ static assert( isInputRange!(char[]));
+ static assert(!isInputRange!(char[4]));
+ static assert( isInputRange!(inout(int)[]));
+
+ static struct NotDefaultConstructible
+ {
+ @disable this();
+ void popFront();
+ @property bool empty();
+ @property int front();
+ }
+ static assert( isInputRange!NotDefaultConstructible);
+
+ static struct NotDefaultConstructibleOrCopyable
+ {
+ @disable this();
+ @disable this(this);
+ void popFront();
+ @property bool empty();
+ @property int front();
+ }
+ static assert(isInputRange!NotDefaultConstructibleOrCopyable);
+
+ static struct Frontless
+ {
+ void popFront();
+ @property bool empty();
+ }
+ static assert(!isInputRange!Frontless);
+
+ static struct VoidFront
+ {
+ void popFront();
+ @property bool empty();
+ void front();
+ }
+ static assert(!isInputRange!VoidFront);
+}
+
+@safe unittest
+{
+ import std.algorithm.comparison : equal;
+
+ static struct R
+ {
+ static struct Front
+ {
+ R* impl;
+ @property int value() { return impl._front; }
+ alias value this;
+ }
+
+ int _front;
+
+ @property bool empty() { return _front >= 3; }
+ @property auto front() { return Front(&this); }
+ void popFront() { _front++; }
+ }
+ R r;
+
+ static assert(isInputRange!R);
+ assert(r.equal([ 0, 1, 2 ]));
+}
+
+/+
+puts the whole raw element $(D e) into $(D r). doPut will not attempt to
+iterate, slice or transcode $(D e) in any way shape or form. It will $(B only)
+call the correct primitive ($(D r.put(e)), $(D r.front = e) or
+$(D r(0)) once.
+
+This can be important when $(D e) needs to be placed in $(D r) unchanged.
+Furthermore, it can be useful when working with $(D InputRange)s, as doPut
+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))
+ enum usingPut = hasMember!(PointerTarget!R, "put");
+ else
+ enum usingPut = hasMember!(R, "put");
+
+ static if (usingPut)
+ {
+ static assert(is(typeof(r.put(e))),
+ "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 put a " ~ E.stringof ~ " into a " ~ R.stringof ~ ".");
+ r.front = e;
+ r.popFront();
+ }
+ else static if (is(typeof(r(e))))
+ {
+ r(e);
+ }
+ else
+ {
+ 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[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));
+
+}
+
+/++
+Outputs $(D e) to $(D r). The exact effect is dependent upon the two
+types. Several cases are accepted, as described below. The code snippets
+are attempted in order, and the first to compile "wins" and gets
+evaluated.
+
+In this table "doPut" is a method that places $(D e) into $(D r), using the
+correct primitive: $(D r.put(e)) if $(D R) defines $(D put), $(D r.front = e)
+if $(D r) is an input range (followed by $(D r.popFront())), or $(D r(e))
+otherwise.
+
+$(BOOKTABLE ,
+ $(TR
+ $(TH Code Snippet)
+ $(TH Scenario)
+ )
+ $(TR
+ $(TD $(D r.doPut(e);))
+ $(TD $(D R) specifically accepts an $(D E).)
+ )
+ $(TR
+ $(TD $(D r.doPut([ e ]);))
+ $(TD $(D R) specifically accepts an $(D E[]).)
+ )
+ $(TR
+ $(TD $(D r.putChar(e);))
+ $(TD $(D R) accepts some form of string or character. put will
+ transcode the character $(D e) accordingly.)
+ )
+ $(TR
+ $(TD $(D for (; !e.empty; e.popFront()) put(r, e.front);))
+ $(TD Copying range $(D E) into $(D R).)
+ )
+)
+
+Tip: $(D put) should $(I not) be used "UFCS-style", e.g. $(D r.put(e)).
+Doing this may call $(D R.put) directly, by-passing any transformation
+feature provided by $(D Range.put). $(D put(r, e)) is prefered.
+ +/
+void put(R, E)(ref R r, E e)
+{
+ //First level: simply straight up put.
+ static if (is(typeof(doPut(r, e))))
+ {
+ doPut(r, e);
+ }
+ //Optional optimization block for straight up array to array copy.
+ else static if (isDynamicArray!R && !isNarrowString!R && isDynamicArray!E && is(typeof(r[] = e[])))
+ {
+ immutable len = e.length;
+ r[0 .. len] = e[];
+ r = r[len .. $];
+ }
+ //Accepts E[] ?
+ else static if (is(typeof(doPut(r, [e]))) && !isDynamicArray!R)
+ {
+ if (__ctfe)
+ {
+ E[1] arr = [e];
+ doPut(r, arr[]);
+ }
+ else
+ 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))))
+ {
+ putChar(r, e);
+ }
+ //Extract each element from the range
+ //We can use "put" here, so we can recursively test a RoR of E.
+ else static if (isInputRange!E && is(typeof(put(r, e.front))))
+ {
+ //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 wchar[]) && is(typeof(doPut(r, wchar.max))) && !is(typeof(doPut(r, dchar.max)))) ) )
+ {
+ foreach (c; e)
+ doPut(r, c);
+ }
+ else
+ {
+ for (; !e.empty; e.popFront())
+ put(r, e.front);
+ }
+ }
+ else
+ {
+ static assert(false, "Cannot put a " ~ E.stringof ~ " into a " ~ R.stringof ~ ".");
+ }
+}
+
+@safe pure nothrow @nogc unittest
+{
+ static struct R() { void put(in char[]) {} }
+ R!() r;
+ put(r, 'a');
+}
+
+//Helper function to handle chars as quickly and as elegantly as possible
+//Assumes r.put(e)/r(e) has already been tested
+private void putChar(R, E)(ref R r, E e)
+if (isSomeChar!E)
+{
+ ////@@@9186@@@: Can't use (E[]).init
+ ref const( char)[] cstringInit();
+ ref const(wchar)[] wstringInit();
+ ref const(dchar)[] dstringInit();
+
+ enum csCond = !isDynamicArray!R && is(typeof(doPut(r, cstringInit())));
+ enum wsCond = !isDynamicArray!R && is(typeof(doPut(r, wstringInit())));
+ enum dsCond = !isDynamicArray!R && is(typeof(doPut(r, dstringInit())));
+
+ //Use "max" to avoid static type demotion
+ enum ccCond = is(typeof(doPut(r, char.max)));
+ enum wcCond = is(typeof(doPut(r, wchar.max)));
+ //enum dcCond = is(typeof(doPut(r, dchar.max)));
+
+ //Fast transform a narrow char into a wider string
+ static if ((wsCond && E.sizeof < wchar.sizeof) || (dsCond && E.sizeof < dchar.sizeof))
+ {
+ enum w = wsCond && E.sizeof < wchar.sizeof;
+ Select!(w, wchar, dchar) c = e;
+ typeof(c)[1] arr = [c];
+ doPut(r, arr[]);
+ }
+ //Encode a wide char into a narrower string
+ else static if (wsCond || csCond)
+ {
+ import std.utf : encode;
+ /+static+/ Select!(wsCond, wchar[2], char[4]) buf; //static prevents purity.
+ doPut(r, buf[0 .. encode(buf, e)]);
+ }
+ //Slowly encode a wide char into a series of narrower chars
+ else static if (wcCond || ccCond)
+ {
+ import std.encoding : encode;
+ alias C = Select!(wcCond, wchar, char);
+ encode!(C, R)(e, r);
+ }
+ else
+ {
+ static assert(false, "Cannot put a " ~ E.stringof ~ " into a " ~ R.stringof ~ ".");
+ }
+}
+
+pure @safe unittest
+{
+ auto f = delegate (const(char)[]) {};
+ putChar(f, cast(dchar)'a');
+}
+
+
+@safe pure unittest
+{
+ static struct R() { void put(in char[]) {} }
+ R!() r;
+ putChar(r, 'a');
+}
+
+@safe unittest
+{
+ struct A {}
+ static assert(!isInputRange!(A));
+ struct B
+ {
+ void put(int) {}
+ }
+ B b;
+ put(b, 5);
+}
+
+@safe unittest
+{
+ int[] a = [1, 2, 3], b = [10, 20];
+ auto c = a;
+ put(a, b);
+ assert(c == [10, 20, 3]);
+ assert(a == [3]);
+}
+
+@safe unittest
+{
+ int[] a = new int[10];
+ int b;
+ static assert(isInputRange!(typeof(a)));
+ put(a, b);
+}
+
+@safe unittest
+{
+ void myprint(in char[] s) { }
+ auto r = &myprint;
+ put(r, 'a');
+}
+
+@safe unittest
+{
+ int[] a = new int[10];
+ static assert(!__traits(compiles, put(a, 1.0L)));
+ put(a, 1);
+ assert(a.length == 9);
+ /*
+ * a[0] = 65; // OK
+ * a[0] = 'A'; // OK
+ * a[0] = "ABC"[0]; // OK
+ * put(a, "ABC"); // OK
+ */
+ put(a, "ABC");
+ assert(a.length == 6);
+}
+
+@safe unittest
+{
+ char[] a = new char[10];
+ static assert(!__traits(compiles, put(a, 1.0L)));
+ static assert(!__traits(compiles, put(a, 1)));
+ // char[] is NOT output range.
+ static assert(!__traits(compiles, put(a, 'a')));
+ static assert(!__traits(compiles, put(a, "ABC")));
+}
+
+@safe unittest
+{
+ int[][] a = new int[][10];
+ int[] b = new int[10];
+ int c;
+ put(b, c);
+ assert(b.length == 9);
+ put(a, b);
+ assert(a.length == 9);
+ static assert(!__traits(compiles, put(a, c)));
+}
+
+@safe unittest
+{
+ int[][] a = new int[][](3);
+ int[] b = [1];
+ auto aa = a;
+ put(aa, b);
+ assert(aa == [[], []]);
+ assert(a == [[1], [], []]);
+ int[][3] c = [2];
+ aa = a;
+ put(aa, c[]);
+ assert(aa.empty);
+ assert(a == [[2], [2], [2]]);
+}
+
+@safe unittest
+{
+ // Test fix for bug 7476.
+ struct LockingTextWriter
+ {
+ void put(dchar c){}
+ }
+ struct RetroResult
+ {
+ bool end = false;
+ @property bool empty() const { return end; }
+ @property dchar front(){ return 'a'; }
+ void popFront(){ end = true; }
+ }
+ LockingTextWriter w;
+ RetroResult r;
+ put(w, r);
+}
+
+@system unittest
+{
+ import std.conv : to;
+ import std.meta : AliasSeq;
+ import std.typecons : tuple;
+
+ static struct PutC(C)
+ {
+ string result;
+ void put(const(C) c) { result ~= to!string((&c)[0 .. 1]); }
+ }
+ static struct PutS(C)
+ {
+ string result;
+ void put(const(C)[] s) { result ~= to!string(s); }
+ }
+ static struct PutSS(C)
+ {
+ string result;
+ void put(const(C)[][] ss)
+ {
+ foreach (s; ss)
+ result ~= to!string(s);
+ }
+ }
+
+ PutS!char p;
+ putChar(p, cast(dchar)'a');
+
+ //Source Char
+ foreach (SC; AliasSeq!(char, wchar, dchar))
+ {
+ SC ch = 'I';
+ dchar dh = '♥';
+ immutable(SC)[] s = "日本語!";
+ immutable(SC)[][] ss = ["日本語", "が", "好き", "ですか", "?"];
+
+ //Target Char
+ foreach (TC; AliasSeq!(char, wchar, dchar))
+ {
+ //Testing PutC and PutS
+ foreach (Type; AliasSeq!(PutC!TC, PutS!TC))
+ (){ // avoid slow optimizations for large functions @@@BUG@@@ 2396
+ Type type;
+ auto sink = new Type();
+
+ //Testing put and sink
+ foreach (value ; tuple(type, sink))
+ {
+ put(value, ch);
+ assert(value.result == "I");
+ put(value, dh);
+ assert(value.result == "I♥");
+ put(value, s);
+ assert(value.result == "I♥日本語!");
+ put(value, ss);
+ assert(value.result == "I♥日本語!日本語が好きですか?");
+ }
+ }();
+ }
+ }
+}
+
+@safe unittest
+{
+ static struct CharRange
+ {
+ char c;
+ enum empty = false;
+ void popFront(){}
+ ref char front() return @property
+ {
+ return c;
+ }
+ }
+ CharRange c;
+ put(c, cast(dchar)'H');
+ put(c, "hello"d);
+}
+
+@system unittest
+{
+ // issue 9823
+ const(char)[] r;
+ void delegate(const(char)[]) dg = (s) { r = s; };
+ put(dg, ["ABC"]);
+ assert(r == "ABC");
+}
+
+@safe unittest
+{
+ // issue 10571
+ import std.format;
+ string buf;
+ formattedWrite((in char[] s) { buf ~= s; }, "%s", "hello");
+ assert(buf == "hello");
+}
+
+@safe unittest
+{
+ import std.format;
+ import std.meta : AliasSeq;
+ struct PutC(C)
+ {
+ void put(C){}
+ }
+ struct PutS(C)
+ {
+ void put(const(C)[]){}
+ }
+ struct CallC(C)
+ {
+ void opCall(C){}
+ }
+ struct CallS(C)
+ {
+ void opCall(const(C)[]){}
+ }
+ struct FrontC(C)
+ {
+ enum empty = false;
+ auto front()@property{return C.init;}
+ void front(C)@property{}
+ void popFront(){}
+ }
+ struct FrontS(C)
+ {
+ enum empty = false;
+ auto front()@property{return C[].init;}
+ void front(const(C)[])@property{}
+ void popFront(){}
+ }
+ void foo()
+ {
+ 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);
+ formattedWrite(PutC!C(), "", 1, 'a', cast(wchar)'a', cast(dchar)'a', "a"c, "a"w, "a"d);
+ formattedWrite(PutS!C(), "", 1, 'a', cast(wchar)'a', cast(dchar)'a', "a"c, "a"w, "a"d);
+ CallC!C callC;
+ CallS!C callS;
+ formattedWrite(callC, "", 1, 'a', cast(wchar)'a', cast(dchar)'a', "a"c, "a"w, "a"d);
+ formattedWrite(callS, "", 1, 'a', cast(wchar)'a', cast(dchar)'a', "a"c, "a"w, "a"d);
+ formattedWrite(FrontC!C(), "", 1, 'a', cast(wchar)'a', cast(dchar)'a', "a"c, "a"w, "a"d);
+ formattedWrite(FrontS!C(), "", 1, 'a', cast(wchar)'a', cast(dchar)'a', "a"c, "a"w, "a"d);
+ }
+ formattedWrite((dchar[]).init, "", 1, 'a', cast(wchar)'a', cast(dchar)'a', "a"c, "a"w, "a"d);
+ }
+}
+
+/+
+Returns $(D true) if $(D R) is a native output range for elements of type
+$(D E). An output range is defined functionally as a range that
+supports the operation $(D doPut(r, e)) as defined above. if $(D doPut(r, e))
+is valid, then $(D put(r,e)) will have the same behavior.
+
+The two guarantees isNativeOutputRange gives over the larger $(D isOutputRange)
+are:
+1: $(D e) is $(B exactly) what will be placed (not $(D [e]), for example).
+2: if $(D E) is a non $(empty) $(D InputRange), then placing $(D e) is
+guaranteed to not overflow the range.
+ +/
+package(std) enum bool isNativeOutputRange(R, E) =
+ is(typeof(doPut(lvalueOf!R, lvalueOf!E)));
+
+@safe unittest
+{
+ int[] r = new int[](4);
+ static assert(isInputRange!(int[]));
+ static assert( isNativeOutputRange!(int[], int));
+ static assert(!isNativeOutputRange!(int[], int[]));
+ static assert( isOutputRange!(int[], int[]));
+
+ if (!r.empty)
+ put(r, 1); //guaranteed to succeed
+ if (!r.empty)
+ put(r, [1, 2]); //May actually error out.
+}
+
+/++
+Returns $(D true) if $(D R) is an output range for elements of type
+$(D E). An output range is defined functionally as a range that
+supports the operation $(D put(r, e)) as defined above.
+ +/
+enum bool isOutputRange(R, E) =
+ is(typeof(put(lvalueOf!R, lvalueOf!E)));
+
+///
+@safe unittest
+{
+ void myprint(in char[] s) { }
+ static assert(isOutputRange!(typeof(&myprint), char));
+
+ static assert(!isOutputRange!(char[], char));
+ static assert( isOutputRange!(dchar[], wchar));
+ static assert( isOutputRange!(dchar[], dchar));
+}
+
+@safe unittest
+{
+ import std.array;
+ import std.stdio : writeln;
+
+ auto app = appender!string();
+ string s;
+ static assert( isOutputRange!(Appender!string, string));
+ static assert( isOutputRange!(Appender!string*, string));
+ static assert(!isOutputRange!(Appender!string, int));
+ static assert(!isOutputRange!(wchar[], wchar));
+ static assert( isOutputRange!(dchar[], char));
+ static assert( isOutputRange!(dchar[], string));
+ static assert( isOutputRange!(dchar[], wstring));
+ static assert( isOutputRange!(dchar[], dstring));
+
+ static assert(!isOutputRange!(const(int)[], int));
+ static assert(!isOutputRange!(inout(int)[], int));
+}
+
+
+/**
+Returns $(D true) if $(D R) is a forward range. A forward range is an
+input range $(D r) that can save "checkpoints" by saving $(D r.save)
+to another value of type $(D R). Notable examples of input ranges that
+are $(I not) forward ranges are file/socket ranges; copying such a
+range will not save the position in the stream, and they most likely
+reuse an internal buffer as the entire stream does not sit in
+memory. Subsequently, advancing either the original or the copy will
+advance the stream, so the copies are not independent.
+
+The following code should compile for any forward range.
+
+----
+static assert(isInputRange!R);
+R r1;
+auto s1 = r1.save;
+static assert(is(typeof(s1) == R));
+----
+
+Saving a range is not duplicating it; in the example above, $(D r1)
+and $(D r2) still refer to the same underlying data. They just
+navigate that data independently.
+
+The semantics of a forward range (not checkable during compilation)
+are the same as for an input range, with the additional requirement
+that backtracking must be possible by saving a copy of the range
+object with $(D save) and using it later.
+ */
+enum bool isForwardRange(R) = isInputRange!R
+ && is(ReturnType!((R r) => r.save) == R);
+
+///
+@safe unittest
+{
+ static assert(!isForwardRange!(int));
+ static assert( isForwardRange!(int[]));
+ 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
+$(D popBack). The following code should compile for any bidirectional
+range.
+
+The semantics of a bidirectional range (not checkable during
+compilation) are assumed to be the following ($(D r) is an object of
+type $(D R)):
+
+$(UL $(LI $(D r.back) returns (possibly a reference to) the last
+element in the range. Calling $(D r.back) is allowed only if calling
+$(D r.empty) has, or would have, returned $(D false).))
+ */
+enum bool isBidirectionalRange(R) = isForwardRange!R
+ && is(typeof((R r) => r.popBack))
+ && is(ReturnType!((R r) => r.back) == ElementType!R);
+
+///
+@safe unittest
+{
+ alias R = int[];
+ R r = [0,1];
+ static assert(isForwardRange!R); // is forward range
+ r.popBack(); // can invoke popBack
+ auto t = r.back; // can get the back of the range
+ auto w = r.front;
+ static assert(is(typeof(t) == typeof(w))); // same type for front and back
+}
+
+@safe unittest
+{
+ struct A {}
+ struct B
+ {
+ void popFront();
+ @property bool empty();
+ @property int front();
+ }
+ struct C
+ {
+ @property bool empty();
+ @property C save();
+ void popFront();
+ @property int front();
+ void popBack();
+ @property int back();
+ }
+ static assert(!isBidirectionalRange!(A));
+ static assert(!isBidirectionalRange!(B));
+ static assert( isBidirectionalRange!(C));
+ static assert( isBidirectionalRange!(int[]));
+ static assert( isBidirectionalRange!(char[]));
+ static assert( isBidirectionalRange!(inout(int)[]));
+}
+
+/**
+Returns $(D true) if $(D R) is a random-access range. A random-access
+range is a bidirectional range that also offers the primitive $(D
+opIndex), OR an infinite forward range that offers $(D opIndex). In
+either case, the range must either offer $(D length) or be
+infinite. The following code should compile for any random-access
+range.
+
+The semantics of a random-access range (not checkable during
+compilation) are assumed to be the following ($(D r) is an object of
+type $(D R)): $(UL $(LI $(D r.opIndex(n)) returns a reference to the
+$(D n)th element in the range.))
+
+Although $(D char[]) and $(D wchar[]) (as well as their qualified
+versions including $(D string) and $(D wstring)) are arrays, $(D
+isRandomAccessRange) yields $(D false) for them because they use
+variable-length encodings (UTF-8 and UTF-16 respectively). These types
+are bidirectional ranges only.
+ */
+enum bool isRandomAccessRange(R) =
+ is(typeof(lvalueOf!R[1]) == ElementType!R)
+ && !isNarrowString!R
+ && isForwardRange!R
+ && (isBidirectionalRange!R || isInfinite!R)
+ && (hasLength!R || isInfinite!R)
+ && (isInfinite!R || !is(typeof(lvalueOf!R[$ - 1]))
+ || is(typeof(lvalueOf!R[$ - 1]) == ElementType!R));
+
+///
+@safe unittest
+{
+ import std.traits : isNarrowString;
+
+ alias R = int[];
+
+ // range is finite and bidirectional or infinite and forward.
+ static assert(isBidirectionalRange!R ||
+ isForwardRange!R && isInfinite!R);
+
+ R r = [0,1];
+ auto e = r[1]; // can index
+ 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 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(f) == typeof(r[$ - 1])));
+ }
+}
+
+@safe unittest
+{
+ struct A {}
+ struct B
+ {
+ void popFront();
+ @property bool empty();
+ @property int front();
+ }
+ struct C
+ {
+ void popFront();
+ @property bool empty();
+ @property int front();
+ void popBack();
+ @property int back();
+ }
+ struct D
+ {
+ @property bool empty();
+ @property D save();
+ @property int front();
+ void popFront();
+ @property int back();
+ void popBack();
+ ref int opIndex(uint);
+ @property size_t length();
+ 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)[]));
+}
+
+@safe unittest
+{
+ // Test fix for bug 6935.
+ struct R
+ {
+ @disable this();
+
+ @property bool empty() const { return false; }
+ @property int front() const { return 0; }
+ void popFront() {}
+
+ @property R save() { return this; }
+
+ @property int back() const { return 0; }
+ void popBack(){}
+
+ int opIndex(size_t n) const { return 0; }
+ @property size_t length() const { return 0; }
+ alias opDollar = length;
+
+ void put(int e){ }
+ }
+ static assert(isInputRange!R);
+ static assert(isForwardRange!R);
+ static assert(isBidirectionalRange!R);
+ static assert(isRandomAccessRange!R);
+ static assert(isOutputRange!(R, int));
+}
+
+/**
+Returns $(D true) iff $(D R) is an input range that supports the
+$(D moveFront) primitive, as well as $(D moveBack) and $(D moveAt) if it's a
+bidirectional or random access range. These may be explicitly implemented, or
+may work via the default behavior of the module level functions $(D moveFront)
+and friends. The following code should compile for any range
+with mobile elements.
+
+----
+alias E = ElementType!R;
+R r;
+static assert(isInputRange!R);
+static assert(is(typeof(moveFront(r)) == E));
+static if (isBidirectionalRange!R)
+ static assert(is(typeof(moveBack(r)) == E));
+static if (isRandomAccessRange!R)
+ static assert(is(typeof(moveAt(r, 0)) == E));
+----
+ */
+enum bool hasMobileElements(R) =
+ isInputRange!R
+ && is(typeof(moveFront(lvalueOf!R)) == ElementType!R)
+ && (!isBidirectionalRange!R
+ || is(typeof(moveBack(lvalueOf!R)) == ElementType!R))
+ && (!isRandomAccessRange!R
+ || is(typeof(moveAt(lvalueOf!R, 0)) == ElementType!R));
+
+///
+@safe unittest
+{
+ import std.algorithm.iteration : map;
+ import std.range : iota, repeat;
+
+ static struct HasPostblit
+ {
+ this(this) {}
+ }
+
+ auto nonMobile = map!"a"(repeat(HasPostblit.init));
+ static assert(!hasMobileElements!(typeof(nonMobile)));
+ static assert( hasMobileElements!(int[]));
+ static assert( hasMobileElements!(inout(int)[]));
+ static assert( hasMobileElements!(typeof(iota(1000))));
+
+ static assert( hasMobileElements!( string));
+ static assert( hasMobileElements!(dstring));
+ static assert( hasMobileElements!( char[]));
+ static assert( hasMobileElements!(dchar[]));
+}
+
+/**
+The element type of $(D R). $(D R) does not have to be a range. The
+element type is determined as the type yielded by $(D r.front) for an
+object $(D r) of type $(D R). For example, $(D ElementType!(T[])) is
+$(D T) if $(D T[]) isn't a narrow string; if it is, the element type is
+$(D dchar). If $(D R) doesn't have $(D front), $(D ElementType!R) is
+$(D void).
+ */
+template ElementType(R)
+{
+ static if (is(typeof(R.init.front.init) T))
+ alias ElementType = T;
+ else
+ alias ElementType = void;
+}
+
+///
+@safe unittest
+{
+ import std.range : iota;
+
+ // Standard arrays: returns the type of the elements of the array
+ static assert(is(ElementType!(int[]) == int));
+
+ // Accessing .front retrieves the decoded dchar
+ static assert(is(ElementType!(char[]) == dchar)); // rvalue
+ static assert(is(ElementType!(dchar[]) == dchar)); // lvalue
+
+ // Ditto
+ static assert(is(ElementType!(string) == dchar));
+ static assert(is(ElementType!(dstring) == immutable(dchar)));
+
+ // For ranges it gets the type of .front.
+ auto range = iota(0, 10);
+ static assert(is(ElementType!(typeof(range)) == int));
+}
+
+@safe unittest
+{
+ static assert(is(ElementType!(byte[]) == byte));
+ static assert(is(ElementType!(wchar[]) == dchar)); // rvalue
+ static assert(is(ElementType!(wstring) == dchar));
+}
+
+@safe unittest
+{
+ enum XYZ : string { a = "foo" }
+ auto x = XYZ.a.front;
+ immutable char[3] a = "abc";
+ int[] i;
+ void[] buf;
+ static assert(is(ElementType!(XYZ) == dchar));
+ static assert(is(ElementType!(typeof(a)) == dchar));
+ static assert(is(ElementType!(typeof(i)) == int));
+ static assert(is(ElementType!(typeof(buf)) == void));
+ static assert(is(ElementType!(inout(int)[]) == inout(int)));
+ static assert(is(ElementType!(inout(int[])) == inout(int)));
+}
+
+@safe unittest
+{
+ static assert(is(ElementType!(int[5]) == int));
+ static assert(is(ElementType!(int[0]) == int));
+ static assert(is(ElementType!(char[5]) == dchar));
+ static assert(is(ElementType!(char[0]) == dchar));
+}
+
+@safe unittest //11336
+{
+ static struct S
+ {
+ this(this) @disable;
+ }
+ static assert(is(ElementType!(S[]) == S));
+}
+
+@safe unittest // 11401
+{
+ // ElementType should also work for non-@propety 'front'
+ struct E { ushort id; }
+ struct R
+ {
+ E front() { return E.init; }
+ }
+ static assert(is(ElementType!R == E));
+}
+
+/**
+The encoding element type of $(D R). For narrow strings ($(D char[]),
+$(D wchar[]) and their qualified variants including $(D string) and
+$(D wstring)), $(D ElementEncodingType) is the character type of the
+string. For all other types, $(D ElementEncodingType) is the same as
+$(D ElementType).
+ */
+template ElementEncodingType(R)
+{
+ static if (is(StringTypeOf!R) && is(R : E[], E))
+ alias ElementEncodingType = E;
+ else
+ alias ElementEncodingType = ElementType!R;
+}
+
+///
+@safe unittest
+{
+ import std.range : iota;
+ // internally the range stores the encoded type
+ static assert(is(ElementEncodingType!(char[]) == char));
+
+ static assert(is(ElementEncodingType!(wstring) == immutable(wchar)));
+
+ static assert(is(ElementEncodingType!(byte[]) == byte));
+
+ auto range = iota(0, 10);
+ static assert(is(ElementEncodingType!(typeof(range)) == int));
+}
+
+@safe unittest
+{
+ static assert(is(ElementEncodingType!(wchar[]) == wchar));
+ static assert(is(ElementEncodingType!(dchar[]) == dchar));
+ static assert(is(ElementEncodingType!(string) == immutable(char)));
+ static assert(is(ElementEncodingType!(dstring) == immutable(dchar)));
+ static assert(is(ElementEncodingType!(int[]) == int));
+}
+
+@safe unittest
+{
+ enum XYZ : string { a = "foo" }
+ auto x = XYZ.a.front;
+ immutable char[3] a = "abc";
+ int[] i;
+ void[] buf;
+ static assert(is(ElementType!(XYZ) : dchar));
+ static assert(is(ElementEncodingType!(char[]) == char));
+ static assert(is(ElementEncodingType!(string) == immutable char));
+ static assert(is(ElementType!(typeof(a)) : dchar));
+ static assert(is(ElementType!(typeof(i)) == int));
+ static assert(is(ElementEncodingType!(typeof(i)) == int));
+ static assert(is(ElementType!(typeof(buf)) : void));
+
+ static assert(is(ElementEncodingType!(inout char[]) : inout(char)));
+}
+
+@safe unittest
+{
+ static assert(is(ElementEncodingType!(int[5]) == int));
+ static assert(is(ElementEncodingType!(int[0]) == int));
+ static assert(is(ElementEncodingType!(char[5]) == char));
+ static assert(is(ElementEncodingType!(char[0]) == char));
+}
+
+/**
+Returns $(D true) if $(D R) is an input range and has swappable
+elements. The following code should compile for any range
+with swappable elements.
+
+----
+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[0], r.front);
+----
+ */
+template hasSwappableElements(R)
+{
+ import std.algorithm.mutation : swap;
+ enum bool hasSwappableElements = isInputRange!R
+ && is(typeof((ref R r) => swap(r.front, r.front)))
+ && (!isBidirectionalRange!R
+ || is(typeof((ref R r) => swap(r.back, r.front))))
+ && (!isRandomAccessRange!R
+ || is(typeof((ref R r) => swap(r[0], r.front))));
+}
+
+///
+@safe unittest
+{
+ static assert(!hasSwappableElements!(const int[]));
+ static assert(!hasSwappableElements!(const(int)[]));
+ static assert(!hasSwappableElements!(inout(int)[]));
+ static assert( hasSwappableElements!(int[]));
+
+ static assert(!hasSwappableElements!( string));
+ static assert(!hasSwappableElements!(dstring));
+ static assert(!hasSwappableElements!( char[]));
+ static assert( hasSwappableElements!(dchar[]));
+}
+
+/**
+Returns $(D true) if $(D R) is an input range and has mutable
+elements. The following code should compile for any range
+with assignable elements.
+
+----
+R r;
+static assert(isInputRange!R);
+r.front = r.front;
+static if (isBidirectionalRange!R) r.back = r.front;
+static if (isRandomAccessRange!R) r[0] = r.front;
+----
+ */
+enum bool hasAssignableElements(R) = isInputRange!R
+ && is(typeof(lvalueOf!R.front = lvalueOf!R.front))
+ && (!isBidirectionalRange!R
+ || is(typeof(lvalueOf!R.back = lvalueOf!R.back)))
+ && (!isRandomAccessRange!R
+ || is(typeof(lvalueOf!R[0] = lvalueOf!R.front)));
+
+///
+@safe unittest
+{
+ static assert(!hasAssignableElements!(const int[]));
+ static assert(!hasAssignableElements!(const(int)[]));
+ static assert( hasAssignableElements!(int[]));
+ static assert(!hasAssignableElements!(inout(int)[]));
+
+ static assert(!hasAssignableElements!( string));
+ static assert(!hasAssignableElements!(dstring));
+ static assert(!hasAssignableElements!( char[]));
+ static assert( hasAssignableElements!(dchar[]));
+}
+
+/**
+Tests whether the range $(D R) has lvalue elements. These are defined as
+elements that can be passed by reference and have their address taken.
+The following code should compile for any range with lvalue elements.
+----
+void passByRef(ref ElementType!R stuff);
+...
+static assert(isInputRange!R);
+passByRef(r.front);
+static if (isBidirectionalRange!R) passByRef(r.back);
+static if (isRandomAccessRange!R) passByRef(r[0]);
+----
+*/
+enum bool hasLvalueElements(R) = isInputRange!R
+ && is(typeof(((ref x) => x)(lvalueOf!R.front)))
+ && (!isBidirectionalRange!R
+ || is(typeof(((ref x) => x)(lvalueOf!R.back))))
+ && (!isRandomAccessRange!R
+ || is(typeof(((ref x) => x)(lvalueOf!R[0]))));
+
+///
+@safe unittest
+{
+ import std.range : iota, chain;
+
+ static assert( hasLvalueElements!(int[]));
+ static assert( hasLvalueElements!(const(int)[]));
+ static assert( hasLvalueElements!(inout(int)[]));
+ static assert( hasLvalueElements!(immutable(int)[]));
+ static assert(!hasLvalueElements!(typeof(iota(3))));
+
+ static assert(!hasLvalueElements!( string));
+ static assert( hasLvalueElements!(dstring));
+ static assert(!hasLvalueElements!( char[]));
+ static assert( hasLvalueElements!(dchar[]));
+
+ auto c = chain([1, 2, 3], [4, 5, 6]);
+ static assert( hasLvalueElements!(typeof(c)));
+}
+
+@safe unittest
+{
+ // bugfix 6336
+ struct S { immutable int value; }
+ static assert( isInputRange!(S[]));
+ static assert( hasLvalueElements!(S[]));
+}
+
+/**
+Yields `true` if `R` has a `length` member that returns a value of `size_t`
+type. `R` does not have to be a range. If `R` is a range, algorithms in the
+standard library are only guaranteed to support `length` with type `size_t`.
+
+Note that `length` is an optional primitive as no range must implement it. Some
+ranges do not store their length explicitly, some cannot compute it without
+actually exhausting the range (e.g. socket streams), and some other ranges may
+be infinite.
+
+Although narrow string types (`char[]`, `wchar[]`, and their qualified
+derivatives) do define a `length` property, `hasLength` yields `false` for them.
+This is because a narrow string's length does not reflect the number of
+characters, but instead the number of encoding units, and as such is not useful
+with range-oriented algorithms. To use strings as random-access ranges with
+length, use $(REF representation, std, string) or $(REF byCodeUnit, std, utf).
+
+Deprecation: Historically `hasLength!R` yielded `true` for types whereby
+`R.length` returns other types convertible to `ulong`, such as `int`, `ushort`,
+`const(size_t)`, user-defined types using `alias this`, or notably `ulong` on
+32-bit systems. This behavior has been deprecated. After December 2017,
+`hasLength` will yield `true` only if `R.length` yields the exact type `size_t`.
+*/
+template hasLength(R)
+{
+ static if (is(typeof(((R* r) => r.length)(null)) Length))
+ {
+ static if (is(Length == size_t))
+ {
+ enum bool hasLength = !isNarrowString!R;
+ }
+ else static if (is(Length : ulong))
+ {
+ // @@@DEPRECATED_2017-12@@@
+ // Uncomment the deprecated(...) message and take the pragma(msg)
+ // out once https://issues.dlang.org/show_bug.cgi?id=10181 is fixed.
+ pragma(msg, __FILE__ ~ "(" ~ __LINE__.stringof ~
+ "): Note: length must have type size_t on all systems" ~
+ ", please update your code by December 2017.");
+ //deprecated("length must have type size_t on all systems")
+ enum bool hasLength = true;
+ }
+ else
+ {
+ enum bool hasLength = false;
+ }
+ }
+ else
+ {
+ enum bool hasLength = false;
+ }
+}
+
+///
+@safe unittest
+{
+ static assert(!hasLength!(char[]));
+ static assert( hasLength!(int[]));
+ static assert( hasLength!(inout(int)[]));
+
+ struct A { ulong length; }
+ 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!(C));
+}
+
+/**
+Returns $(D true) if $(D R) is an infinite input range. An
+infinite input range is an input range that has a statically-defined
+enumerated member called $(D empty) that is always $(D false),
+for example:
+
+----
+struct MyInfiniteRange
+{
+ enum bool empty = false;
+ ...
+}
+----
+ */
+
+template isInfinite(R)
+{
+ static if (isInputRange!R && __traits(compiles, { enum e = R.empty; }))
+ enum bool isInfinite = !R.empty;
+ else
+ enum bool isInfinite = false;
+}
+
+///
+@safe unittest
+{
+ import std.range : Repeat;
+ static assert(!isInfinite!(int[]));
+ static assert( isInfinite!(Repeat!(int)));
+}
+
+/**
+Returns $(D true) if $(D R) offers a slicing operator with integral boundaries
+that returns a forward range type.
+
+For finite ranges, the result of $(D opSlice) must be of the same type as the
+original range type. If the range defines $(D opDollar), then it must support
+subtraction.
+
+For infinite ranges, when $(I not) using $(D opDollar), the result of
+$(D opSlice) must be the result of $(LREF take) or $(LREF takeExactly) on the
+original range (they both return the same type for infinite ranges). However,
+when using $(D opDollar), the result of $(D opSlice) must be that of the
+original range type.
+
+The following expression must be true for `hasSlicing` to be `true`:
+
+----
+ isForwardRange!R
+ && !isNarrowString!R
+ && is(ReturnType!((R r) => r[1 .. 1].length) == size_t)
+ && (is(typeof(lvalueOf!R[1 .. 1]) == R) || isInfinite!R)
+ && (!is(typeof(lvalueOf!R[0 .. $])) || is(typeof(lvalueOf!R[0 .. $]) == R))
+ && (!is(typeof(lvalueOf!R[0 .. $])) || isInfinite!R
+ || is(typeof(lvalueOf!R[0 .. $ - 1]) == R))
+ && is(typeof((ref R r)
+ {
+ static assert(isForwardRange!(typeof(r[1 .. 2])));
+ }));
+----
+ */
+enum bool hasSlicing(R) = isForwardRange!R
+ && !isNarrowString!R
+ && is(ReturnType!((R r) => r[1 .. 1].length) == size_t)
+ && (is(typeof(lvalueOf!R[1 .. 1]) == R) || isInfinite!R)
+ && (!is(typeof(lvalueOf!R[0 .. $])) || is(typeof(lvalueOf!R[0 .. $]) == R))
+ && (!is(typeof(lvalueOf!R[0 .. $])) || isInfinite!R
+ || is(typeof(lvalueOf!R[0 .. $ - 1]) == R))
+ && is(typeof((ref R r)
+ {
+ static assert(isForwardRange!(typeof(r[1 .. 2])));
+ }));
+
+///
+@safe unittest
+{
+ import std.range : takeExactly;
+ static assert( hasSlicing!(int[]));
+ static assert( hasSlicing!(const(int)[]));
+ static assert(!hasSlicing!(const int[]));
+ static assert( hasSlicing!(inout(int)[]));
+ static assert(!hasSlicing!(inout int []));
+ static assert( hasSlicing!(immutable(int)[]));
+ static assert(!hasSlicing!(immutable int[]));
+ static assert(!hasSlicing!string);
+ static assert( hasSlicing!dstring);
+
+ enum rangeFuncs = "@property int front();" ~
+ "void popFront();" ~
+ "@property bool empty();" ~
+ "@property auto save() { return this; }" ~
+ "@property size_t length();";
+
+ struct A { mixin(rangeFuncs); int opSlice(size_t, size_t); }
+ struct B { mixin(rangeFuncs); B opSlice(size_t, size_t); }
+ struct C { mixin(rangeFuncs); @disable this(); C opSlice(size_t, size_t); }
+ struct D { mixin(rangeFuncs); int[] opSlice(size_t, size_t); }
+ static assert(!hasSlicing!(A));
+ static assert( hasSlicing!(B));
+ static assert( hasSlicing!(C));
+ static assert(!hasSlicing!(D));
+
+ struct InfOnes
+ {
+ enum empty = false;
+ void popFront() {}
+ @property int front() { return 1; }
+ @property InfOnes save() { return this; }
+ auto opSlice(size_t i, size_t j) { return takeExactly(this, j - i); }
+ auto opSlice(size_t i, Dollar d) { return this; }
+
+ struct Dollar {}
+ Dollar opDollar() const { return Dollar.init; }
+ }
+
+ static assert(hasSlicing!InfOnes);
+}
+
+/**
+This is a best-effort implementation of $(D length) for any kind of
+range.
+
+If $(D hasLength!Range), simply returns $(D range.length) without
+checking $(D upTo) (when specified).
+
+Otherwise, walks the range through its length and returns the number
+of elements seen. Performes $(BIGOH n) evaluations of $(D range.empty)
+and $(D range.popFront()), where $(D n) is the effective length of $(D
+range).
+
+The $(D upTo) parameter is useful to "cut the losses" in case
+the interest is in seeing whether the range has at least some number
+of elements. If the parameter $(D upTo) is specified, stops if $(D
+upTo) steps have been taken and returns $(D upTo).
+
+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)
+{
+ static if (hasLength!Range)
+ return range.length;
+ else
+ {
+ size_t result;
+ for ( ; !range.empty ; range.popFront() )
+ ++result;
+ return result;
+ }
+}
+/// ditto
+auto walkLength(Range)(Range range, const size_t upTo)
+if (isInputRange!Range)
+{
+ static if (hasLength!Range)
+ return range.length;
+ else static if (isInfinite!Range)
+ return upTo;
+ else
+ {
+ size_t result;
+ for ( ; result < upTo && !range.empty ; range.popFront() )
+ ++result;
+ return result;
+ }
+}
+
+@safe unittest
+{
+ import std.algorithm.iteration : filter;
+ import std.range : recurrence, take;
+
+ //hasLength Range
+ int[] a = [ 1, 2, 3 ];
+ assert(walkLength(a) == 3);
+ assert(walkLength(a, 0) == 3);
+ assert(walkLength(a, 2) == 3);
+ assert(walkLength(a, 4) == 3);
+
+ //Forward Range
+ auto b = filter!"true"([1, 2, 3, 4]);
+ assert(b.walkLength() == 4);
+ assert(b.walkLength(0) == 0);
+ assert(b.walkLength(2) == 2);
+ assert(b.walkLength(4) == 4);
+ assert(b.walkLength(6) == 4);
+
+ //Infinite Range
+ auto fibs = recurrence!"a[n-1] + a[n-2]"(1, 1);
+ assert(!__traits(compiles, fibs.walkLength()));
+ assert(fibs.take(10).walkLength() == 10);
+ assert(fibs.walkLength(55) == 55);
+}
+
+/**
+ Eagerly advances $(D r) itself (not a copy) up to $(D n) times (by
+ calling $(D r.popFront)). $(D popFrontN) takes $(D r) by $(D ref),
+ so it mutates the original range. Completes in $(BIGOH 1) steps for ranges
+ that support slicing and have length.
+ Completes in $(BIGOH n) time for all other ranges.
+
+ Returns:
+ How much $(D r) was actually advanced, which may be less than $(D n) if
+ $(D r) did not have at least $(D n) elements.
+
+ $(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)
+{
+ static if (hasLength!Range)
+ {
+ n = cast(size_t) (n < r.length ? n : r.length);
+ }
+
+ static if (hasSlicing!Range && is(typeof(r = r[n .. $])))
+ {
+ r = r[n .. $];
+ }
+ else static if (hasSlicing!Range && hasLength!Range) //TODO: Remove once hasSlicing forces opDollar.
+ {
+ r = r[n .. r.length];
+ }
+ else
+ {
+ static if (hasLength!Range)
+ {
+ foreach (i; 0 .. n)
+ r.popFront();
+ }
+ else
+ {
+ foreach (i; 0 .. n)
+ {
+ if (r.empty) return i;
+ r.popFront();
+ }
+ }
+ }
+ return n;
+}
+
+/// ditto
+size_t popBackN(Range)(ref Range r, size_t n)
+if (isBidirectionalRange!Range)
+{
+ static if (hasLength!Range)
+ {
+ n = cast(size_t) (n < r.length ? n : r.length);
+ }
+
+ static if (hasSlicing!Range && is(typeof(r = r[0 .. $ - n])))
+ {
+ r = r[0 .. $ - n];
+ }
+ else static if (hasSlicing!Range && hasLength!Range) //TODO: Remove once hasSlicing forces opDollar.
+ {
+ r = r[0 .. r.length - n];
+ }
+ else
+ {
+ static if (hasLength!Range)
+ {
+ foreach (i; 0 .. n)
+ r.popBack();
+ }
+ else
+ {
+ foreach (i; 0 .. n)
+ {
+ if (r.empty) return i;
+ r.popBack();
+ }
+ }
+ }
+ return n;
+}
+
+///
+@safe unittest
+{
+ int[] a = [ 1, 2, 3, 4, 5 ];
+ a.popFrontN(2);
+ assert(a == [ 3, 4, 5 ]);
+ a.popFrontN(7);
+ assert(a == [ ]);
+}
+
+///
+@safe unittest
+{
+ import std.algorithm.comparison : equal;
+ import std.range : iota;
+ auto LL = iota(1L, 7L);
+ auto r = popFrontN(LL, 2);
+ assert(equal(LL, [3L, 4L, 5L, 6L]));
+ assert(r == 2);
+}
+
+///
+@safe unittest
+{
+ int[] a = [ 1, 2, 3, 4, 5 ];
+ a.popBackN(2);
+ assert(a == [ 1, 2, 3 ]);
+ a.popBackN(7);
+ assert(a == [ ]);
+}
+
+///
+@safe unittest
+{
+ import std.algorithm.comparison : equal;
+ import std.range : iota;
+ auto LL = iota(1L, 7L);
+ auto r = popBackN(LL, 2);
+ assert(equal(LL, [1L, 2L, 3L, 4L]));
+ assert(r == 2);
+}
+
+/**
+ Eagerly advances $(D r) itself (not a copy) exactly $(D n) times (by
+ calling $(D r.popFront)). $(D popFrontExactly) takes $(D r) by $(D ref),
+ so it mutates the original range. Completes in $(BIGOH 1) steps for ranges
+ that support slicing, and have either length or are infinite.
+ Completes in $(BIGOH n) time for all other ranges.
+
+ Note: Unlike $(LREF popFrontN), $(D popFrontExactly) will assume that the
+ range holds at least $(D n) elements. This makes $(D popFrontExactly)
+ faster than $(D popFrontN), but it also means that if $(D range) does
+ not contain at least $(D n) elements, it will attempt to call $(D popFront)
+ 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.
+
+ $(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)
+{
+ static if (hasLength!Range)
+ assert(n <= r.length, "range is smaller than amount of items to pop");
+
+ static if (hasSlicing!Range && is(typeof(r = r[n .. $])))
+ r = r[n .. $];
+ else static if (hasSlicing!Range && hasLength!Range) //TODO: Remove once hasSlicing forces opDollar.
+ r = r[n .. r.length];
+ else
+ foreach (i; 0 .. n)
+ r.popFront();
+}
+
+/// ditto
+void popBackExactly(Range)(ref Range r, size_t n)
+if (isBidirectionalRange!Range)
+{
+ static if (hasLength!Range)
+ assert(n <= r.length, "range is smaller than amount of items to pop");
+
+ static if (hasSlicing!Range && is(typeof(r = r[0 .. $ - n])))
+ r = r[0 .. $ - n];
+ else static if (hasSlicing!Range && hasLength!Range) //TODO: Remove once hasSlicing forces opDollar.
+ r = r[0 .. r.length - n];
+ else
+ foreach (i; 0 .. n)
+ r.popBack();
+}
+
+///
+@safe unittest
+{
+ import std.algorithm.comparison : equal;
+ import std.algorithm.iteration : filterBidirectional;
+
+ auto a = [1, 2, 3];
+ a.popFrontExactly(1);
+ assert(a == [2, 3]);
+ a.popBackExactly(1);
+ assert(a == [2]);
+
+ string s = "日本語";
+ s.popFrontExactly(1);
+ assert(s == "本語");
+ s.popBackExactly(1);
+ assert(s == "本");
+
+ auto bd = filterBidirectional!"true"([1, 2, 3]);
+ bd.popFrontExactly(1);
+ assert(bd.equal([2, 3]));
+ bd.popBackExactly(1);
+ assert(bd.equal([2]));
+}
+
+/**
+ Moves the front of $(D r) out and returns it. Leaves $(D r.front) in a
+ destroyable state that does not allocate any resources (usually equal
+ to its $(D .init) value).
+*/
+ElementType!R moveFront(R)(R r)
+{
+ static if (is(typeof(&r.moveFront)))
+ {
+ return r.moveFront();
+ }
+ else static if (!hasElaborateCopyConstructor!(ElementType!R))
+ {
+ return r.front;
+ }
+ else static if (is(typeof(&(r.front())) == ElementType!R*))
+ {
+ import std.algorithm.mutation : move;
+ return move(r.front);
+ }
+ else
+ {
+ static assert(0,
+ "Cannot move front of a range with a postblit and an rvalue front.");
+ }
+}
+
+///
+@safe unittest
+{
+ auto a = [ 1, 2, 3 ];
+ assert(moveFront(a) == 1);
+ assert(a.length == 3);
+
+ // define a perfunctory input range
+ struct InputRange
+ {
+ enum bool empty = false;
+ enum int front = 7;
+ void popFront() {}
+ int moveFront() { return 43; }
+ }
+ InputRange r;
+ // calls r.moveFront
+ assert(moveFront(r) == 43);
+}
+
+@safe unittest
+{
+ struct R
+ {
+ @property ref int front() { static int x = 42; return x; }
+ this(this){}
+ }
+ R r;
+ assert(moveFront(r) == 42);
+}
+
+/**
+ Moves the back of $(D r) out and returns it. Leaves $(D r.back) in a
+ destroyable state that does not allocate any resources (usually equal
+ to its $(D .init) value).
+*/
+ElementType!R moveBack(R)(R r)
+{
+ static if (is(typeof(&r.moveBack)))
+ {
+ return r.moveBack();
+ }
+ else static if (!hasElaborateCopyConstructor!(ElementType!R))
+ {
+ return r.back;
+ }
+ else static if (is(typeof(&(r.back())) == ElementType!R*))
+ {
+ import std.algorithm.mutation : move;
+ return move(r.back);
+ }
+ else
+ {
+ static assert(0,
+ "Cannot move back of a range with a postblit and an rvalue back.");
+ }
+}
+
+///
+@safe unittest
+{
+ struct TestRange
+ {
+ int payload = 5;
+ @property bool empty() { return false; }
+ @property TestRange save() { return this; }
+ @property ref int front() return { return payload; }
+ @property ref int back() return { return payload; }
+ void popFront() { }
+ void popBack() { }
+ }
+ static assert(isBidirectionalRange!TestRange);
+ TestRange r;
+ auto x = moveBack(r);
+ assert(x == 5);
+}
+
+/**
+ Moves element at index $(D i) of $(D r) out and returns it. Leaves $(D
+ r[i]) in a destroyable state that does not allocate any resources
+ (usually equal to its $(D .init) value).
+*/
+ElementType!R moveAt(R)(R r, size_t i)
+{
+ static if (is(typeof(&r.moveAt)))
+ {
+ return r.moveAt(i);
+ }
+ else static if (!hasElaborateCopyConstructor!(ElementType!(R)))
+ {
+ return r[i];
+ }
+ else static if (is(typeof(&r[i]) == ElementType!R*))
+ {
+ import std.algorithm.mutation : move;
+ return move(r[i]);
+ }
+ else
+ {
+ static assert(0,
+ "Cannot move element of a range with a postblit and rvalue elements.");
+ }
+}
+
+///
+@safe unittest
+{
+ auto a = [1,2,3,4];
+ foreach (idx, it; a)
+ {
+ assert(it == moveAt(a, idx));
+ }
+}
+
+@safe unittest
+{
+ import std.internal.test.dummyrange;
+
+ foreach (DummyType; AllDummyRanges)
+ {
+ auto d = DummyType.init;
+ assert(moveFront(d) == 1);
+
+ static if (isBidirectionalRange!DummyType)
+ {
+ assert(moveBack(d) == 10);
+ }
+
+ static if (isRandomAccessRange!DummyType)
+ {
+ assert(moveAt(d, 2) == 3);
+ }
+ }
+}
+
+/**
+Implements the range interface primitive $(D empty) for built-in
+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 @nogc
+{
+ return !a.length;
+}
+
+///
+@safe pure nothrow unittest
+{
+ auto a = [ 1, 2, 3 ];
+ assert(!a.empty);
+ assert(a[3 .. $].empty);
+}
+
+/**
+Implements the range interface primitive $(D save) for built-in
+arrays. Due to the fact that nonmember functions can be called with
+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 @nogc
+{
+ return a;
+}
+
+///
+@safe pure nothrow unittest
+{
+ auto a = [ 1, 2, 3 ];
+ 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
+the first argument using the dot notation, $(D array.popFront) is
+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 @nogc
+if (!isNarrowString!(T[]) && !is(T[] == void[]))
+{
+ assert(a.length, "Attempting to popFront() past the end of an array of " ~ T.stringof);
+ a = a[1 .. $];
+}
+
+///
+@safe pure nothrow unittest
+{
+ auto a = [ 1, 2, 3 ];
+ a.popFront();
+ assert(a == [ 2, 3 ]);
+}
+
+version (unittest)
+{
+ static assert(!is(typeof({ int[4] a; popFront(a); })));
+ static assert(!is(typeof({ immutable int[] a; popFront(a); })));
+ static assert(!is(typeof({ void[] a; popFront(a); })));
+}
+
+/// 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 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 < 192)
+ {
+ str = str.ptr[1 .. str.length];
+ }
+ else
+ {
+ str = str.ptr[min(str.length, charWidthTab.ptr[c - 192]) .. str.length];
+ }
+
+ }
+ else static if (is(Unqual!C == wchar))
+ {
+ immutable u = str[0];
+ 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.meta : AliasSeq;
+
+ 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'])
+ {
+ assert(str.front == c);
+ str.popFront();
+ }
+ assert(str.empty);
+
+ static assert(!is(typeof({ immutable S a; popFront(a); })));
+ static assert(!is(typeof({ typeof(S.init[0])[4] a; popFront(a); })));
+ }
+
+ C[] _eatString(C)(C[] str)
+ {
+ while (!str.empty)
+ str.popFront();
+
+ return str;
+ }
+ enum checkCTFE = _eatString("ウェブサイト@La_Verité.com");
+ static assert(checkCTFE.empty);
+ enum checkCTFEW = _eatString("ウェブサイト@La_Verité.com"w);
+ 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
+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 @nogc
+if (!isNarrowString!(T[]) && !is(T[] == void[]))
+{
+ assert(a.length);
+ a = a[0 .. $ - 1];
+}
+
+///
+@safe pure nothrow unittest
+{
+ auto a = [ 1, 2, 3 ];
+ a.popBack();
+ assert(a == [ 1, 2 ]);
+}
+
+version (unittest)
+{
+ static assert(!is(typeof({ immutable int[] a; popBack(a); })));
+ static assert(!is(typeof({ int[4] a; popBack(a); })));
+ static assert(!is(typeof({ void[] a; popBack(a); })));
+}
+
+/// 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 .. $ - strideBack(a, $)];
+}
+
+@safe pure unittest
+{
+ import std.meta : AliasSeq;
+
+ foreach (S; AliasSeq!(string, wstring, dstring))
+ {
+ S s = "hello\xE2\x89\xA0";
+ s.popBack();
+ assert(s == "hello");
+ S s3 = "\xE2\x89\xA0";
+ auto c = s3.back;
+ assert(c == cast(dchar)'\u2260');
+ s3.popBack();
+ assert(s3 == "");
+
+ S str = "\U00010143\u0100\U00010143hello";
+ foreach (dchar ch; ['o', 'l', 'l', 'e', 'h', '\U00010143', '\u0100', '\U00010143'])
+ {
+ assert(str.back == ch);
+ str.popBack();
+ }
+ assert(str.empty);
+
+ static assert(!is(typeof({ immutable S a; popBack(a); })));
+ static assert(!is(typeof({ typeof(S.init[0])[4] a; popBack(a); })));
+ }
+}
+
+/**
+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
+dchar).
+*/
+@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);
+ return a[0];
+}
+
+///
+@safe pure nothrow unittest
+{
+ int[] a = [ 1, 2, 3 ];
+ assert(a.front == 1);
+}
+
+@safe pure nothrow unittest
+{
+ auto a = [ 1, 2 ];
+ a.front = 4;
+ assert(a.front == 4);
+ assert(a == [ 4, 2 ]);
+
+ immutable b = [ 1, 2 ];
+ assert(b.front == 1);
+
+ int[2] c = [ 1, 2 ];
+ assert(c.front == 1);
+}
+
+/// 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);
+ size_t i = 0;
+ return decode(a, i);
+}
+
+/**
+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
+dchar).
+*/
+@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];
+}
+
+///
+@safe pure nothrow unittest
+{
+ int[] a = [ 1, 2, 3 ];
+ assert(a.back == 3);
+ a.back += 4;
+ assert(a.back == 7);
+}
+
+@safe pure nothrow unittest
+{
+ immutable b = [ 1, 2, 3 ];
+ assert(b.back == 3);
+
+ int[3] c = [ 1, 2, 3 ];
+ assert(c.back == 3);
+}
+
+/// ditto
+// Specialization for strings
+@property dchar back(T)(T[] a) @safe pure
+if (isNarrowString!(T[]))
+{
+ 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 - strideBack(a, a.length);
+ return decode(a, i);
+}
diff --git a/libphobos/src/std/regex/internal/backtracking.d b/libphobos/src/std/regex/internal/backtracking.d
new file mode 100644
index 0000000..ffc9779
--- /dev/null
+++ b/libphobos/src/std/regex/internal/backtracking.d
@@ -0,0 +1,1495 @@
+/*
+ Implementation of backtracking std.regex engine.
+ Contains both compile-time and run-time versions.
+*/
+module std.regex.internal.backtracking;
+
+package(std.regex):
+
+import core.stdc.stdlib, std.range.primitives, std.traits, std.typecons;
+import std.regex.internal.ir;
+
+/+
+ BacktrackingMatcher implements backtracking scheme of matching
+ regular expressions.
++/
+template BacktrackingMatcher(bool CTregex)
+{
+ @trusted struct BacktrackingMatcher(Char, Stream = Input!Char)
+ if (is(Char : dchar))
+ {
+ alias DataIndex = Stream.DataIndex;
+ struct State
+ {//top bit in pc is set if saved along with matches
+ DataIndex index;
+ uint pc, counter, infiniteNesting;
+ }
+ 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 String = const(Char)[];
+ alias RegEx = Regex!Char;
+ alias MatchFn = bool function (ref BacktrackingMatcher!(Char, Stream));
+ RegEx re; //regex program
+ static if (CTregex)
+ MatchFn nativeFn; //native code for that program
+ //Stream state
+ Stream s;
+ DataIndex index;
+ dchar front;
+ bool exhausted;
+ //backtracking machine state
+ uint pc, counter;
+ DataIndex lastState = 0; //top of state stack
+ 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"))
+ {
+ enum kicked = true;
+ }
+ else
+ enum kicked = false;
+
+ static size_t initialMemory(const ref RegEx re)
+ {
+ return stackSize(re)*size_t.sizeof + re.hotspotTableSize*Trace.sizeof;
+ }
+
+ static size_t stackSize(const ref RegEx re)
+ {
+ size_t itemSize = stateSize
+ + re.ngroup * (Group!DataIndex).sizeof / size_t.sizeof;
+ return initialStack * itemSize + 2;
+ }
+
+ @property bool atStart(){ return index == 0; }
+
+ @property bool atEnd(){ return index == s.lastIndex && s.atEnd; }
+
+ void next()
+ {
+ if (!s.nextChar(front, index))
+ index = s.lastIndex;
+ }
+
+ void search()
+ {
+ static if (kicked)
+ {
+ if (!s.search(re.kickstart, front, index))
+ {
+ index = s.lastIndex;
+ }
+ }
+ else
+ next();
+ }
+
+ //
+ void newStack()
+ {
+ auto chunk = mallocArray!(size_t)(stackSize(re));
+ chunk[0] = cast(size_t)(memory.ptr);
+ 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)
+ {
+ 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)
+ {
+ re = program;
+ s = stream;
+ exhausted = false;
+ initExternalMemory(memBlock);
+ backrefed = null;
+ }
+
+ auto dupTo(void[] memory)
+ {
+ typeof(this) tmp = this;
+ tmp.initExternalMemory(memory);
+ return tmp;
+ }
+
+ this(ref RegEx program, Stream stream, void[] memBlock, dchar ch, DataIndex idx)
+ {
+ initialize(program, stream, memBlock);
+ front = ch;
+ index = idx;
+ }
+
+ this(ref RegEx program, Stream stream, void[] memBlock)
+ {
+ initialize(program, stream, memBlock);
+ next();
+ }
+
+ auto fwdMatcher(ref BacktrackingMatcher matcher, void[] memBlock)
+ {
+ alias BackMatcherTempl = .BacktrackingMatcher!(CTregex);
+ alias BackMatcher = BackMatcherTempl!(Char, Stream);
+ auto fwdMatcher = BackMatcher(matcher.re, s, memBlock, front, index);
+ return fwdMatcher;
+ }
+
+ auto bwdMatcher(ref BacktrackingMatcher matcher, void[] memBlock)
+ {
+ alias BackMatcherTempl = .BacktrackingMatcher!(CTregex);
+ alias BackMatcher = BackMatcherTempl!(Char, typeof(s.loopBack(index)));
+ auto fwdMatcher =
+ BackMatcher(matcher.re, s.loopBack(index), memBlock);
+ return fwdMatcher;
+ }
+
+ //
+ int matchFinalize()
+ {
+ 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)
+ exhausted = true;
+ if (start == index)//empty match advances input
+ next();
+ return val;
+ }
+ else
+ return 0;
+ }
+
+ //lookup next match, fill matches with indices into input
+ int match(Group!DataIndex[] matches)
+ {
+ debug(std_regex_matcher)
+ {
+ writeln("------------------------------------------");
+ }
+ if (exhausted) //all matches collected
+ return false;
+ this.matches = matches;
+ if (re.flags & RegexInfo.oneShot)
+ {
+ exhausted = true;
+ const DataIndex start = index;
+ immutable m = matchImpl();
+ if (m)
+ {
+ matches[0].begin = start;
+ matches[0].end = index;
+ }
+ return m;
+ }
+ static if (kicked)
+ {
+ if (!re.kickstart.empty)
+ {
+ for (;;)
+ {
+ immutable val = matchFinalize();
+ if (val)
+ return val;
+ else
+ {
+ if (atEnd)
+ break;
+ search();
+ if (atEnd)
+ {
+ exhausted = true;
+ return matchFinalize();
+ }
+ }
+ }
+ exhausted = true;
+ return 0; //early return
+ }
+ }
+ //no search available - skip a char at a time
+ for (;;)
+ {
+ immutable val = matchFinalize();
+ if (val)
+ return val;
+ else
+ {
+ if (atEnd)
+ break;
+ next();
+ if (atEnd)
+ {
+ exhausted = true;
+ return matchFinalize();
+ }
+ }
+ }
+ exhausted = true;
+ return 0;
+ }
+
+ /+
+ match subexpression against input,
+ results are stored in matches
+ +/
+ int matchImpl()
+ {
+ static if (CTregex && is(typeof(nativeFn(this))))
+ {
+ debug(std_regex_ctr) writeln("using C-T matcher");
+ return nativeFn(this);
+ }
+ else
+ {
+ pc = 0;
+ counter = 0;
+ lastState = 0;
+ matches[] = Group!DataIndex.init;
+ auto start = s._index;
+ debug(std_regex_matcher)
+ 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)
+ {
+ case IR.OrChar://assumes IRL!(OrChar) == 1
+ 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)
+ {
+ for (pc = pc+2; pc < end; pc++)
+ if (re.ir[pc].data == front)
+ break;
+ if (pc == end)
+ goto L_backtrack;
+ }
+ pc = end;
+ next();
+ break;
+ case IR.Char:
+ if (atEnd || front != re.ir[pc].data)
+ goto L_backtrack;
+ pc += IRL!(IR.Char);
+ next();
+ break;
+ case IR.Any:
+ if (atEnd)
+ goto L_backtrack;
+ pc += IRL!(IR.Any);
+ next();
+ break;
+ case IR.CodepointSet:
+ 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.matchers[re.ir[pc].data][front])
+ goto L_backtrack;
+ next();
+ pc += IRL!(IR.Trie);
+ break;
+ case IR.Wordboundary:
+ dchar back;
+ DataIndex bi;
+ //at start & end of input
+ if (atStart && wordMatcher[front])
+ {
+ pc += IRL!(IR.Wordboundary);
+ break;
+ }
+ else if (atEnd && s.loopBack(index).nextChar(back, bi)
+ && wordMatcher[back])
+ {
+ pc += IRL!(IR.Wordboundary);
+ break;
+ }
+ else if (s.loopBack(index).nextChar(back, bi))
+ {
+ immutable af = wordMatcher[front];
+ immutable ab = wordMatcher[back];
+ if (af ^ ab)
+ {
+ pc += IRL!(IR.Wordboundary);
+ break;
+ }
+ }
+ goto L_backtrack;
+ case IR.Notwordboundary:
+ dchar back;
+ DataIndex bi;
+ //at start & end of input
+ if (atStart && wordMatcher[front])
+ goto L_backtrack;
+ else if (atEnd && s.loopBack(index).nextChar(back, bi)
+ && wordMatcher[back])
+ goto L_backtrack;
+ else if (s.loopBack(index).nextChar(back, bi))
+ {
+ 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)
+ pc += IRL!(IR.Bol);
+ else if (s.loopBack(index).nextChar(back,bi)
+ && endOfLine(back, front == '\n'))
+ {
+ pc += IRL!(IR.Bol);
+ }
+ 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]);
+ //no matching inside \r\n
+ if (atEnd || (endOfLine(front, s.loopBack(index).nextChar(back,bi)
+ && back == '\r')))
+ {
+ pc += IRL!(IR.Eol);
+ }
+ else
+ goto L_backtrack;
+ break;
+ case IR.InfiniteStart, IR.InfiniteQStart:
+ pc += re.ir[pc].data + IRL!(IR.InfiniteStart);
+ //now pc is at end IR.Infinite(Q)End
+ uint len = re.ir[pc].data;
+ if (re.ir[pc].code == IR.InfiniteEnd)
+ {
+ pushState(pc+IRL!(IR.InfiniteEnd), counter);
+ pc -= len;
+ }
+ else
+ {
+ 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
+ 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)
+ {
+ if (re.ir[pc].code == IR.RepeatEnd)
+ {
+ pushState(pc + IRL!(IR.RepeatEnd), counter%step);
+ counter += step;
+ pc -= len;
+ }
+ else
+ {
+ pushState(pc - len, counter + step);
+ counter = counter%step;
+ pc += IRL!(IR.RepeatEnd);
+ }
+ }
+ else
+ {
+ counter = counter%step;
+ pc += IRL!(IR.RepeatEnd);
+ }
+ break;
+ case IR.InfiniteEnd:
+ case IR.InfiniteQEnd:
+ 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;
+ if (re.ir[pc].code == IR.InfiniteEnd)
+ {
+ pushState(pc + IRL!(IR.InfiniteEnd), counter);
+ pc -= len;
+ }
+ else
+ {
+ 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:
+ 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
+ }
+ pc += IRL!(IR.Option);
+ break;
+ case IR.GotoEndOr:
+ pc = pc + re.ir[pc].data + IRL!(IR.GotoEndOr);
+ break;
+ case IR.GroupStart:
+ 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:
+ 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:
+ immutable len = re.ir[pc].data;
+ auto save = index;
+ 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)
+ {
+ auto matcher = bwdMatcher(this, mem);
+ }
+ else
+ {
+ auto matcher = fwdMatcher(this, mem);
+ }
+ 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)
+ ];
+ immutable match = (matcher.matchImpl() != 0) ^ (re.ir[pc].code == IR.NeglookaheadStart);
+ s.reset(save);
+ next();
+ if (!match)
+ goto L_backtrack;
+ else
+ {
+ pc += IRL!(IR.LookaheadStart)+len+IRL!(IR.LookaheadEnd);
+ }
+ break;
+ case IR.LookbehindStart:
+ case IR.NeglookbehindStart:
+ 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)
+ {
+ alias Matcher = BacktrackingMatcher!(Char, Stream);
+ auto matcher = Matcher(re, s, mem, front, index);
+ }
+ else
+ {
+ alias Matcher = BacktrackingMatcher!(Char, typeof(s.loopBack(index)));
+ 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.backrefed = backrefed.empty ? matches : backrefed;
+ immutable match = (matcher.matchImpl() != 0) ^ (re.ir[pc].code == IR.NeglookbehindStart);
+ if (!match)
+ goto L_backtrack;
+ else
+ {
+ pc += IRL!(IR.LookbehindStart)+len+IRL!(IR.LookbehindEnd);
+ }
+ break;
+ case IR.Backref:
+ 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)
+ {
+ next();
+ referenced.popFront();
+ }
+ if (referenced.empty)
+ pc++;
+ else
+ goto L_backtrack;
+ break;
+ case IR.Nop:
+ pc += IRL!(IR.Nop);
+ break;
+ case IR.LookaheadEnd:
+ case IR.NeglookaheadEnd:
+ case IR.LookbehindEnd:
+ case IR.NeglookbehindEnd:
+ case IR.End:
+ // 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())
+ {
+ s.reset(start);
+ return 0;
+ }
+ }
+ }
+ }
+ assert(0);
+ }
+
+ @property size_t stackAvail()
+ {
+ return memory.length - lastState;
+ }
+
+ void stackPush(T)(T val)
+ if (!isDynamicArray!T)
+ {
+ *cast(T*)&memory[lastState] = val;
+ enum delta = (T.sizeof+size_t.sizeof/2)/size_t.sizeof;
+ lastState += delta;
+ debug(std_regex_matcher) writeln("push element SP= ", lastState);
+ }
+
+ void stackPush(T)(T[] val)
+ {
+ static assert(T.sizeof % size_t.sizeof == 0);
+ (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)
+ {
+ enum delta = (T.sizeof+size_t.sizeof/2)/size_t.sizeof;
+ lastState -= delta;
+ val = *cast(T*)&memory[lastState];
+ debug(std_regex_matcher) writeln("pop element SP= ", lastState);
+ }
+
+ void stackPop(T)(T[] val)
+ {
+ stackPop(val); // call ref version
+ }
+ void stackPop(T)(ref T[] val)
+ {
+ lastState -= val.length*(T.sizeof/size_t.sizeof);
+ val[0..$] = (cast(T*)&memory[lastState])[0 .. val.length];
+ debug(std_regex_matcher) writeln("pop array SP= ", lastState);
+ }
+
+ static if (!CTregex)
+ {
+ //helper function, saves engine state
+ void pushState(uint pc, uint counter)
+ {
+ if (stateSize + 2 * matches.length > stackAvail)
+ {
+ newStack();
+ }
+ *cast(State*)&memory[lastState] =
+ State(index, pc, counter, infiniteNesting);
+ lastState += stateSize;
+ memory[lastState .. lastState + 2 * matches.length] = (cast(size_t[]) matches)[];
+ lastState += 2*matches.length;
+ debug(std_regex_matcher)
+ writefln("Saved(pc=%s) front: %s src: %s",
+ pc, front, s[index .. s.lastIndex]);
+ }
+
+ //helper function, restores engine state
+ bool popState()
+ {
+ if (!lastState && !prevStack())
+ return false;
+ lastState -= 2*matches.length;
+ auto pm = cast(size_t[]) matches;
+ pm[] = memory[lastState .. lastState + 2 * matches.length];
+ lastState -= stateSize;
+ State* state = cast(State*)&memory[lastState];
+ index = state.index;
+ pc = state.pc;
+ counter = state.counter;
+ infiniteNesting = state.infiniteNesting;
+ debug(std_regex_matcher)
+ {
+ writefln("Restored matches", front, s[index .. s.lastIndex]);
+ 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]);
+ return true;
+ }
+ }
+ }
+}
+
+//very shitty string formatter, $$ replaced with next argument converted to string
+@trusted string ctSub( U...)(string format, U args)
+{
+ import std.conv : to;
+ bool seenDollar;
+ foreach (i, ch; format)
+ {
+ if (ch == '$')
+ {
+ if (seenDollar)
+ {
+ static if (args.length > 0)
+ {
+ return format[0 .. i - 1] ~ to!string(args[0])
+ ~ ctSub(format[i + 1 .. $], args[1 .. $]);
+ }
+ else
+ assert(0);
+ }
+ else
+ seenDollar = true;
+ }
+ else
+ seenDollar = false;
+
+ }
+ return format;
+}
+
+alias Sequence(int B, int E) = staticIota!(B, E);
+
+struct CtContext
+{
+ import std.conv : to, text;
+ //dirty flags
+ bool counter;
+ //to mark the portion of matches to save
+ int match, total_matches;
+ int reserved;
+ CodepointSet[] charsets;
+
+
+ //state of codegenerator
+ static struct CtState
+ {
+ string code;
+ int addr;
+ }
+
+ this(Char)(Regex!Char re)
+ {
+ match = 1;
+ reserved = 1; //first match is skipped
+ total_matches = re.ngroup;
+ charsets = re.charsets;
+ }
+
+ CtContext lookaround(uint s, uint e)
+ {
+ CtContext ct;
+ ct.total_matches = e - s;
+ ct.match = 1;
+ return ct;
+ }
+
+ //restore state having current context
+ string restoreCode()
+ {
+ string text;
+ //stack is checked in L_backtrack
+ text ~= counter
+ ? "
+ stackPop(counter);"
+ : "
+ counter = 0;";
+ if (match < total_matches)
+ {
+ text ~= ctSub("
+ stackPop(matches[$$..$$]);", reserved, match);
+ text ~= ctSub("
+ matches[$$..$] = typeof(matches[0]).init;", match);
+ }
+ else
+ text ~= ctSub("
+ stackPop(matches[$$..$]);", reserved);
+ return text;
+ }
+
+ //save state having current context
+ string saveCode(uint pc, string count_expr="counter")
+ {
+ string text = ctSub("
+ if (stackAvail < $$*(Group!(DataIndex)).sizeof/size_t.sizeof + $$)
+ {
+ newStack();
+ }", match - reserved, cast(int) counter + 2);
+ if (match < total_matches)
+ text ~= ctSub("
+ stackPush(matches[$$..$$]);", reserved, match);
+ else
+ text ~= ctSub("
+ stackPush(matches[$$..$]);", reserved);
+ text ~= counter ? ctSub("
+ stackPush($$);", count_expr) : "";
+ text ~= ctSub("
+ stackPush(index); stackPush($$); \n", pc);
+ return text;
+ }
+
+ //
+ CtState ctGenBlock(Bytecode[] ir, int addr)
+ {
+ CtState result;
+ result.addr = addr;
+ while (!ir.empty)
+ {
+ auto n = ctGenGroup(ir, result.addr);
+ result.code ~= n.code;
+ result.addr = n.addr;
+ }
+ return result;
+ }
+
+ //
+ CtState ctGenGroup(ref Bytecode[] ir, int addr)
+ {
+ 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)
+ {
+ 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;
+ immutable len = ir[0].data;
+ auto nir = ir[ir[0].length .. ir[0].length+len];
+ r = ctGenBlock(nir, addr+1);
+ //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(nir, r.addr, addr+1);
+ r.addr += 2; //account end instruction + restore state
+ ir = nir;
+ break;
+ case IR.OrStart:
+ 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 .. $];
+ assert(ir[0].code == IR.OrEnd);
+ ir = ir[ir[0].length..$];
+ break;
+ case IR.LookaheadStart:
+ case IR.NeglookaheadStart:
+ case IR.LookbehindStart:
+ case 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)";
+ 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)
+ alias Lookaround = $$;
+ else
+ alias Lookaround = $$;
+ static bool matcher_$$(ref Lookaround matcher) @trusted
+ {
+ //(neg)lookaround piece start
+ $$
+ //(neg)lookaround piece ends
+ }
+ auto save = index;
+ auto mem = malloc(initialMemory(re))[0 .. initialMemory(re)];
+ scope(exit) free(mem.ptr);
+ 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
+ int match = $$;
+ s.reset(save);
+ next();
+ if (match)
+ $$
+ else
+ $$`, addr,
+ behind ? fwdType : bwdType, behind ? bwdType : fwdType,
+ addr, context.ctGenRegEx(slice),
+ behind ? fwdCreate : bwdCreate, behind ? bwdCreate : fwdCreate,
+ ir[1].raw, ir[2].raw, //start - end of matches slice
+ addr,
+ negative ? "!lookaround.matchImpl()" : "lookaround.matchImpl()",
+ nextInstr, bailOut);
+ ir = ir[end .. $];
+ r.addr = addr + 1;
+ break;
+ case IR.LookaheadEnd: case IR.NeglookaheadEnd:
+ case IR.LookbehindEnd: case IR.NeglookbehindEnd:
+ ir = ir[IRL!(IR.LookaheadEnd) .. $];
+ r.addr = addr;
+ break;
+ default:
+ assert(ir[0].isAtom, text(ir[0].mnemonic));
+ r = ctGenAtom(ir, addr);
+ }
+ return r;
+ }
+
+ //generate source for bytecode contained in OrStart ... OrEnd
+ CtState ctGenAlternation(Bytecode[] ir, int addr)
+ {
+ CtState[] pieces;
+ CtState r;
+ enum optL = IRL!(IR.Option);
+ 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
+ {
+ auto nir = ir[optL .. optL+len-IRL!(IR.GotoEndOr)];
+ r = ctGenBlock(nir, addr+2);//space for Option + restore state
+ //r.addr+1 to account GotoEndOr at end of branch
+ r.code = ctGenFixupCode(ir[0 .. ir[0].length], addr, r.addr+1) ~ r.code;
+ addr = r.addr+1;//leave space for GotoEndOr
+ pieces ~= r;
+ ir = ir[optL + len .. $];
+ }
+ else
+ {
+ pieces ~= ctGenBlock(ir[optL..$], addr);
+ addr = pieces[$-1].addr;
+ break;
+ }
+ }
+ r = pieces[0];
+ for (uint i = 1; i < pieces.length; i++)
+ {
+ r.code ~= ctSub(`
+ case $$:
+ goto case $$; `, pieces[i-1].addr, addr);
+ r.code ~= pieces[i].code;
+ }
+ r.addr = addr;
+ return r;
+ }
+
+ // generate fixup code for instruction in ir,
+ // fixup means it has an alternative way for control flow
+ string ctGenFixupCode(Bytecode[] ir, int addr, int fixup)
+ {
+ return ctGenFixupCode(ir, addr, fixup); // call ref Bytecode[] version
+ }
+ string ctGenFixupCode(ref Bytecode[] ir, int addr, int fixup)
+ {
+ string r;
+ string testCode;
+ r = ctSub(`
+ case $$: debug(std_regex_matcher) writeln("#$$");`,
+ addr, addr);
+ switch (ir[0].code)
+ {
+ case IR.InfiniteStart, IR.InfiniteQStart, IR.InfiniteBloomStart:
+ r ~= ctSub( `
+ goto case $$;`, fixup);
+ ir = ir[ir[0].length..$];
+ break;
+ case IR.InfiniteEnd:
+ testCode = ctQuickTest(ir[IRL!(IR.InfiniteEnd) .. $],addr + 1);
+ r ~= ctSub( `
+ 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;
+ }
+
+ $$
+ {
+ $$
+ }
+ 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.InfiniteQEnd:
+ testCode = ctQuickTest(ir[IRL!(IR.InfiniteEnd) .. $],addr + 1);
+ auto altCode = testCode.length ? ctSub("else goto case $$;", fixup) : "";
+ r ~= ctSub( `
+ if (merge[$$+counter].mark(index))
+ {
+ // merged!
+ goto L_backtrack;
+ }
+
+ $$
+ {
+ $$
+ goto case $$;
+ }
+ $$
+ case $$://restore state and go inside loop
+ $$
+ 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:
+ r ~= ctSub( `
+ goto case $$;`, fixup);
+ ir = ir[ir[0].length..$];
+ break;
+ case IR.RepeatEnd, IR.RepeatQEnd:
+ //len, step, min, max
+ immutable len = ir[0].data;
+ immutable step = ir[2].raw;
+ immutable min = ir[3].raw;
+ immutable max = ir[4].raw;
+ r ~= ctSub(`
+ if (merge[$$+counter].mark(index))
+ {
+ // merged!
+ goto L_backtrack;
+ }
+ if (counter < $$)
+ {
+ debug(std_regex_matcher) writeln("RepeatEnd min case pc=", $$);
+ counter += $$;
+ goto case $$;
+ }`, ir[1].raw, min, addr, step, fixup);
+ if (ir[0].code == IR.RepeatEnd)
+ {
+ string counter_expr = ctSub("counter % $$", step);
+ r ~= ctSub(`
+ else if (counter < $$)
+ {
+ $$
+ counter += $$;
+ goto case $$;
+ }`, max, saveCode(addr+1, counter_expr), step, fixup);
+ }
+ else
+ {
+ string counter_expr = ctSub("counter % $$", step);
+ r ~= ctSub(`
+ else if (counter < $$)
+ {
+ $$
+ counter = counter % $$;
+ goto case $$;
+ }`, max, saveCode(addr+1,counter_expr), step, addr+2);
+ }
+ r ~= ctSub(`
+ else
+ {
+ counter = counter % $$;
+ goto case $$;
+ }
+ case $$: //restore state
+ $$
+ goto case $$;`, step, addr+2, addr+1, restoreCode(),
+ ir[0].code == IR.RepeatEnd ? addr+2 : fixup );
+ ir = ir[ir[0].length..$];
+ break;
+ case IR.Option:
+ r ~= ctSub( `
+ {
+ $$
+ }
+ goto case $$;
+ case $$://restore thunk to go to the next group
+ $$
+ goto case $$;`, saveCode(addr+1), addr+2,
+ addr+1, restoreCode(), fixup);
+ ir = ir[ir[0].length..$];
+ break;
+ default:
+ assert(0, text(ir[0].mnemonic));
+ }
+ return r;
+ }
+
+
+ string ctQuickTest(Bytecode[] ir, int id)
+ {
+ uint pc = 0;
+ while (pc < ir.length && ir[pc].isAtom)
+ {
+ if (ir[pc].code == IR.GroupStart || ir[pc].code == IR.GroupEnd)
+ {
+ pc++;
+ }
+ else if (ir[pc].code == IR.Backref)
+ break;
+ else
+ {
+ auto code = ctAtomCode(ir[pc..$], -1);
+ return ctSub(`
+ int test_$$()
+ {
+ $$ //$$
+ }
+ if (test_$$() >= 0)`, id, code.ptr ? code : "return 0;",
+ ir[pc].mnemonic, id);
+ }
+ }
+ return "";
+ }
+
+ //process & generate source for simple bytecodes at front of ir using address addr
+ CtState ctGenAtom(ref Bytecode[] ir, int addr)
+ {
+ CtState result;
+ result.code = ctAtomCode(ir, addr);
+ ir.popFrontN(ir[0].code == IR.OrChar ? ir[0].sequence : ir[0].length);
+ result.addr = addr + 1;
+ return result;
+ }
+
+ //D code for atom at ir using address addr, addr < 0 means quickTest
+ string ctAtomCode(Bytecode[] ir, int addr)
+ {
+ string code;
+ string bailOut, nextInstr;
+ if (addr < 0)
+ {
+ bailOut = "return -1;";
+ nextInstr = "return 0;";
+ }
+ else
+ {
+ bailOut = "goto L_backtrack;";
+ nextInstr = ctSub("goto case $$;", addr+1);
+ code ~= ctSub( `
+ case $$: debug(std_regex_matcher) writeln("#$$");
+ `, addr, addr);
+ }
+ switch (ir[0].code)
+ {
+ case IR.OrChar://assumes IRL!(OrChar) == 1
+ code ~= ctSub(`
+ if (atEnd)
+ $$`, bailOut);
+ immutable len = ir[0].sequence;
+ for (uint i = 0; i < len; i++)
+ {
+ code ~= ctSub( `
+ if (front == $$)
+ {
+ $$
+ $$
+ }`, ir[i].data, addr >= 0 ? "next();" :"", nextInstr);
+ }
+ code ~= ctSub( `
+ $$`, bailOut);
+ break;
+ case IR.Char:
+ code ~= ctSub( `
+ if (atEnd || front != $$)
+ $$
+ $$
+ $$`, ir[0].data, bailOut, addr >= 0 ? "next();" :"", nextInstr);
+ break;
+ case IR.Any:
+ code ~= ctSub( `
+ if (atEnd || (!(re.flags & RegexOption.singleline)
+ && (front == '\r' || front == '\n')))
+ $$
+ $$
+ $$`, bailOut, addr >= 0 ? "next();" :"",nextInstr);
+ break;
+ case IR.CodepointSet:
+ 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.matchers[$$][front])
+ $$
+ $$
+ $$`, ir[0].data, bailOut, addr >= 0 ? "next();" :"", nextInstr);
+ break;
+ case IR.Wordboundary:
+ code ~= ctSub( `
+ dchar back;
+ DataIndex bi;
+ if (atStart && wordMatcher[front])
+ {
+ $$
+ }
+ else if (atEnd && s.loopBack(index).nextChar(back, bi)
+ && wordMatcher[back])
+ {
+ $$
+ }
+ else if (s.loopBack(index).nextChar(back, bi))
+ {
+ bool af = wordMatcher[front];
+ bool ab = wordMatcher[back];
+ if (af ^ ab)
+ {
+ $$
+ }
+ }
+ $$`, nextInstr, nextInstr, nextInstr, bailOut);
+ break;
+ case IR.Notwordboundary:
+ code ~= ctSub( `
+ dchar back;
+ DataIndex bi;
+ //at start & end of input
+ if (atStart && wordMatcher[front])
+ $$
+ else if (atEnd && s.loopBack(index).nextChar(back, bi)
+ && wordMatcher[back])
+ $$
+ else if (s.loopBack(index).nextChar(back, bi))
+ {
+ bool af = wordMatcher[front];
+ bool ab = wordMatcher[back];
+ if (af ^ ab)
+ $$
+ }
+ $$`, bailOut, bailOut, bailOut, nextInstr);
+
+ break;
+ case IR.Bol:
+ code ~= ctSub(`
+ dchar back;
+ DataIndex bi;
+ if (atStart || (s.loopBack(index).nextChar(back,bi)
+ && endOfLine(back, front == '\n')))
+ {
+ debug(std_regex_matcher) writeln("BOL matched");
+ $$
+ }
+ else
+ $$`, 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]);
+ //no matching inside \r\n
+ if (atEnd || (endOfLine(front, s.loopBack(index).nextChar(back,bi)
+ && back == '\r')))
+ {
+ debug(std_regex_matcher) writeln("EOL matched");
+ $$
+ }
+ 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(`
+ matches[$$].begin = index;
+ $$`, ir[0].data, nextInstr);
+ match = ir[0].data+1;
+ break;
+ case IR.GroupEnd:
+ code ~= ctSub(`
+ matches[$$].end = index;
+ $$`, ir[0].data, nextInstr);
+ break;
+ case IR.Backref:
+ string mStr = "auto referenced = ";
+ mStr ~= ir[0].localRef
+ ? ctSub("s[matches[$$].begin .. matches[$$].end];",
+ ir[0].data, ir[0].data)
+ : ctSub("s[backrefed[$$].begin .. backrefed[$$].end];",
+ ir[0].data, ir[0].data);
+ code ~= ctSub( `
+ $$
+ while (!atEnd && !referenced.empty && front == referenced.front)
+ {
+ next();
+ referenced.popFront();
+ }
+ if (referenced.empty)
+ $$
+ else
+ $$`, mStr, nextInstr, bailOut);
+ break;
+ case IR.Nop:
+ case IR.End:
+ break;
+ default:
+ assert(0, text(ir[0].mnemonic, " is not supported yet"));
+ }
+ return code;
+ }
+
+ //generate D code for the whole regex
+ public string ctGenRegEx(Bytecode[] ir)
+ {
+ auto bdy = ctGenBlock(ir, 0);
+ auto r = `
+ import core.stdc.stdlib;
+ with(matcher)
+ {
+ 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]);
+ L_backtrack:
+ if (lastState || prevStack())
+ {
+ stackPop(pc);
+ stackPop(index);
+ s.reset(index);
+ next();
+ }
+ else
+ {
+ s.reset(start);
+ return false;
+ }
+ StartLoop:
+ switch (pc)
+ {
+ `;
+ r ~= bdy.code;
+ r ~= ctSub(`
+ case $$: break;`,bdy.addr);
+ r ~= `
+ default:
+ assert(0);
+ }
+ // cleanup stale stack blocks
+ while (prevStack()) {}
+ return true;
+ }
+ `;
+ return r;
+ }
+
+}
+
+string ctGenRegExCode(Char)(Regex!Char re)
+{
+ auto context = CtContext(re);
+ return context.ctGenRegEx(re.ir);
+}
diff --git a/libphobos/src/std/regex/internal/generator.d b/libphobos/src/std/regex/internal/generator.d
new file mode 100644
index 0000000..6831e59
--- /dev/null
+++ b/libphobos/src/std/regex/internal/generator.d
@@ -0,0 +1,187 @@
+/*
+ Generators - components that generate strings for a given regex pattern.
+
+ For the moment undocumented, and is subject to change.
+*/
+module std.regex.internal.generator;
+
+/*
+ Useful utility for self-testing, an infinite range of string samples
+ that _have_ to match given compiled regex.
+ Caveats: supports only a simple subset of bytecode.
+*/
+@trusted private struct SampleGenerator(Char)
+{
+ import std.array : appender, Appender;
+ import std.format : formattedWrite;
+ import std.random : Xorshift;
+ import std.regex.internal.ir : Regex, IR, IRL;
+ import std.utf : isValidDchar, byChar;
+ Regex!Char re;
+ Appender!(char[]) app;
+ uint limit, seed;
+ Xorshift gen;
+ //generator for pattern r, with soft maximum of threshold elements
+ //and a given random seed
+ this(ref Regex!Char r, uint threshold, uint randomSeed)
+ {
+ re = r;
+ limit = threshold;
+ seed = randomSeed;
+ app = appender!(Char[])();
+ compose();
+ }
+
+ uint rand(uint x)
+ {
+ uint r = gen.front % x;
+ gen.popFront();
+ return r;
+ }
+
+ void compose()
+ {
+ uint pc = 0, counter = 0, dataLenOld = uint.max;
+ for (;;)
+ {
+ switch (re.ir[pc].code)
+ {
+ case IR.Char:
+ 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);
+ 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 y = rand(set.byInterval[x].b - set.byInterval[x].a);
+ formattedWrite(app, "%s", cast(dchar)(set.byInterval[x].a+y));
+ pc += IRL!(IR.CodepointSet);
+ break;
+ case IR.Any:
+ uint x;
+ do
+ {
+ x = rand(0x11_000);
+ }while (x == '\r' || x == '\n' || !isValidDchar(x));
+ formattedWrite(app, "%s", cast(dchar) x);
+ pc += IRL!(IR.Any);
+ break;
+ case IR.GotoEndOr:
+ pc += IRL!(IR.GotoEndOr)+re.ir[pc].data;
+ assert(re.ir[pc].code == IR.OrEnd);
+ goto case;
+ case IR.OrEnd:
+ pc += IRL!(IR.OrEnd);
+ break;
+ case IR.OrStart:
+ pc += IRL!(IR.OrStart);
+ goto case;
+ case IR.Option:
+ uint next = pc + re.ir[pc].data + IRL!(IR.Option);
+ uint nOpt = 0;
+ //queue next Option
+ while (re.ir[next].code == IR.Option)
+ {
+ nOpt++;
+ next += re.ir[next].data + IRL!(IR.Option);
+ }
+ nOpt++;
+ nOpt = rand(nOpt);
+ for (;nOpt; nOpt--)
+ {
+ pc += re.ir[pc].data + IRL!(IR.Option);
+ }
+ assert(re.ir[pc].code == IR.Option);
+ pc += IRL!(IR.Option);
+ break;
+ case IR.RepeatStart:case IR.RepeatQStart:
+ pc += IRL!(IR.RepeatStart)+re.ir[pc].data;
+ goto case IR.RepeatEnd;
+ case IR.RepeatEnd:
+ case IR.RepeatQEnd:
+ uint len = re.ir[pc].data;
+ uint step = re.ir[pc+2].raw;
+ uint min = re.ir[pc+3].raw;
+ if (counter < min)
+ {
+ counter += step;
+ pc -= len;
+ break;
+ }
+ uint max = re.ir[pc+4].raw;
+ if (counter < max)
+ {
+ if (app.data.length < limit && rand(3) > 0)
+ {
+ pc -= len;
+ counter += step;
+ }
+ else
+ {
+ counter = counter%step;
+ pc += IRL!(IR.RepeatEnd);
+ }
+ }
+ else
+ {
+ counter = counter%step;
+ pc += IRL!(IR.RepeatEnd);
+ }
+ break;
+ 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, IR.InfiniteBloomEnd, IR.InfiniteQEnd:
+ uint len = re.ir[pc].data;
+ if (app.data.length == dataLenOld)
+ {
+ pc += IRL!(IR.InfiniteEnd);
+ break;
+ }
+ dataLenOld = cast(uint) app.data.length;
+ if (app.data.length < limit && rand(3) > 0)
+ pc = pc - len;
+ else
+ pc = pc + re.ir[pc].length;
+ break;
+ case IR.GroupStart, IR.GroupEnd:
+ pc += IRL!(IR.GroupStart);
+ break;
+ case IR.Bol, IR.Wordboundary, IR.Notwordboundary:
+ case IR.LookaheadStart, IR.NeglookaheadStart, IR.LookbehindStart, IR.NeglookbehindStart:
+ default:
+ return;
+ }
+ }
+ }
+
+ @property Char[] front()
+ {
+ return app.data;
+ }
+
+ enum empty = false;
+
+ void popFront()
+ {
+ app.shrinkTo(0);
+ compose();
+ }
+}
+
+@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))
+ assert(v.match(re));
+}
diff --git a/libphobos/src/std/regex/internal/ir.d b/libphobos/src/std/regex/internal/ir.d
new file mode 100644
index 0000000..28b1998
--- /dev/null
+++ b/libphobos/src/std/regex/internal/ir.d
@@ -0,0 +1,788 @@
+/*
+ Implementation of std.regex IR, an intermediate representation
+ of a regular expression pattern.
+
+ This is a common ground between frontend regex component (parser)
+ and backend components - generators, matchers and other "filters".
+*/
+module std.regex.internal.ir;
+
+package(std.regex):
+
+import std.exception, std.meta, std.range.primitives, std.traits, std.uni;
+
+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 maxCachedMatchers = 8;
+
+alias Trie = CodepointSetTrie!(13, 8);
+alias makeTrie = codepointSetTrie!(13, 8);
+
+CharMatcher[CodepointSet] matcherCache;
+
+//accessor with caching
+@trusted CharMatcher getMatcher(CodepointSet set)
+{// @@@BUG@@@ 6357 almost all properties of AA are not @safe
+ if (__ctfe || maxCachedMatchers == 0)
+ return CharMatcher(set);
+ else
+ {
+ auto p = set in matcherCache;
+ if (p)
+ return *p;
+ if (matcherCache.length == maxCachedMatchers)
+ {
+ // flush enmatchers in trieCache
+ matcherCache = null;
+ }
+ return (matcherCache[set] = CharMatcher(set));
+ }
+}
+
+@trusted auto memoizeExpr(string expr)()
+{
+ if (__ctfe)
+ return mixin(expr);
+ alias T = typeof(mixin(expr));
+ static T slot;
+ static bool initialized;
+ if (!initialized)
+ {
+ slot = mixin(expr);
+ initialized = true;
+ }
+ return slot;
+}
+
+//property for \w character class
+@property CodepointSet wordCharacter()
+{
+ return memoizeExpr!("unicode.Alphabetic | unicode.Mn | unicode.Mc
+ | unicode.Me | unicode.Nd | unicode.Pc")();
+}
+
+@property CharMatcher wordMatcher()
+{
+ 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
+// freeform - ignore whitespace in pattern, to match space use [ ] or \s
+// multiline - switch ^, $ detect start and end of linesinstead of just start and end of input
+enum RegexOption: uint {
+ global = 0x1,
+ casefold = 0x2,
+ freeform = 0x4,
+ nonunicode = 0x8,
+ multiline = 0x10,
+ singleline = 0x20
+}
+//do not reorder this list
+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 }
+
+// IR bit pattern: 0b1_xxxxx_yy
+// where yy indicates class of instruction, xxxxx for actual operation code
+// 00: atom, a normal instruction
+// 01: open, opening of a group, has length of contained IR in the low bits
+// 10: close, closing of a group, has length of contained IR in the low bits
+// 11 unused
+//
+// Loops with Q (non-greedy, with ? mark) must have the same size / other properties as non Q version
+// Possible changes:
+//* merge group, option, infinite/repeat start (to never copy during parsing of (a|b){1,2})
+//* reorganize groups to make n args easier to find, or simplify the check for groups of similar ops
+// (like lookaround), or make it easier to identify hotspots.
+
+enum IR:uint {
+ Char = 0b1_00000_00, //a character
+ Any = 0b1_00001_00, //any character
+ CodepointSet = 0b1_00010_00, //a most generic CodepointSet [...]
+ Trie = 0b1_00011_00, //CodepointSet implemented as Trie
+ //match with any of a consecutive OrChar's in this sequence
+ //(used for case insensitive match)
+ //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 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)
+ //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)
+ 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_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
+template IRL(IR code)
+{
+ enum uint IRL = lengthOfIR(code);
+}
+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)
+ {
+ case IR.OrEnd,IR.InfiniteEnd,IR.InfiniteQEnd:
+ 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; // start-end of captures used
+ default:
+ return 0;
+ }
+}
+
+//full length of IR instruction inlcuding all parameters that might follow it
+int lengthOfIR(IR i)
+{
+ return 1 + immediateParamsIR(i);
+}
+
+//full length of the paired IR instruction inlcuding all parameters that might follow it
+int lengthOfPairedIR(IR i)
+{
+ return 1 + immediateParamsIR(pairedIR(i));
+}
+
+//if the operation has a merge point (this relies on the order of the ops)
+bool hasMerge(IR i)
+{
+ return (i&0b11)==0b10 && i <= IR.RepeatQEnd;
+}
+
+//is an IR that opens a "group"
+bool isStartIR(IR i)
+{
+ return (i&0b11)==0b01;
+}
+
+//is an IR that ends a "group"
+bool isEndIR(IR i)
+{
+ return (i&0b11)==0b10;
+}
+
+//is a standalone IR
+bool isAtomIR(IR i)
+{
+ return (i&0b11)==0b00;
+}
+
+//makes respective pair out of IR i, swapping start/end bits of instruction
+IR pairedIR(IR i)
+{
+ assert(isStartIR(i) || isEndIR(i));
+ return cast(IR)(i ^ 0b11);
+}
+
+//encoded IR instruction
+struct Bytecode
+{
+ uint raw;
+ //natural constraints
+ enum maxSequence = 2+4;
+ enum maxData = 1 << 22;
+ enum maxRaw = 1 << 31;
+
+ this(IR code, uint 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(seq >= 2 && seq < maxSequence);
+ raw = code << 24 | (seq - 2)<<22 | data;
+ }
+
+ //store raw data
+ static Bytecode fromRaw(uint data)
+ {
+ Bytecode t;
+ t.raw = data;
+ return t;
+ }
+
+ //bit twiddling helpers
+ //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); }
+
+ //ditto
+ @property bool hotspot() const { return hasMerge(code); }
+
+ //test the class of this instruction
+ @property bool isAtom() const { return isAtomIR(code); }
+
+ //ditto
+ @property bool isStart() const { return isStartIR(code); }
+
+ //ditto
+ @property bool isEnd() const { return isEndIR(code); }
+
+ //number of arguments for this instruction
+ @property int args() const { return immediateParamsIR(code); }
+
+ //mark this GroupStart or GroupEnd as referenced in backreference
+ void setBackrefence()
+ {
+ assert(code == IR.GroupStart || code == IR.GroupEnd);
+ raw = raw | 1 << 23;
+ }
+
+ //is referenced
+ @property bool backreference() const
+ {
+ assert(code == IR.GroupStart || code == IR.GroupEnd);
+ return cast(bool)(raw & 1 << 23);
+ }
+
+ //mark as local reference (for backrefs in lookarounds)
+ void setLocalRef()
+ {
+ assert(code == IR.Backref);
+ raw = raw | 1 << 23;
+ }
+
+ //is a local ref
+ @property bool localRef() const
+ {
+ assert(code == IR.Backref);
+ return cast(bool)(raw & 1 << 23);
+ }
+
+ //human readable name of instruction
+ @trusted @property string mnemonic()() const
+ {//@@@BUG@@@ to is @system
+ import std.conv : to;
+ return to!string(code);
+ }
+
+ //full length of instruction
+ @property uint length() const
+ {
+ return lengthOfIR(code);
+ }
+
+ //full length of respective start/end of this instruction
+ @property uint pairedLength() const
+ {
+ return lengthOfPairedIR(code);
+ }
+
+ //returns bytecode of paired instruction (assuming this one is start or end)
+ @property Bytecode paired() const
+ {//depends on bit and struct layout order
+ assert(isStart || isEnd);
+ return Bytecode.fromRaw(raw ^ 0b11 << 24);
+ }
+
+ //gets an index into IR block of the respective pair
+ uint indexOfPair(uint pc) const
+ {
+ assert(isStart || isEnd);
+ return isStart ? pc + data + length : pc - data - lengthOfPairedIR(code);
+ }
+}
+
+static assert(Bytecode.sizeof == 4);
+
+
+//index entry structure for name --> number of submatch
+struct NamedGroup
+{
+ string name;
+ uint group;
+}
+
+//holds pair of start-end markers for a submatch
+struct Group(DataIndex)
+{
+ DataIndex begin, end;
+ @trusted string toString()() const
+ {
+ import std.array : appender;
+ import std.format : formattedWrite;
+ auto a = appender!string();
+ formattedWrite(a, "%s..%s", begin, end);
+ return a.data;
+ }
+}
+
+//debugging tool, prints out instruction along with opcodes
+@trusted string disassemble(in Bytecode[] irb, uint pc, in NamedGroup[] dict=[])
+{
+ import std.array : appender;
+ import std.format : formattedWrite;
+ auto output = appender!string();
+ formattedWrite(output,"%s", irb[pc].mnemonic);
+ switch (irb[pc].code)
+ {
+ case IR.Char:
+ 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);
+ break;
+ 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));
+ break;
+ case IR.RepeatEnd, IR.RepeatQEnd: //backward-jump instructions
+ uint len = irb[pc].data;
+ 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.InfiniteBloomEnd, IR.OrEnd: //ditto
+ uint len = irb[pc].data;
+ formattedWrite(output, " pc=>%u", pc-len);
+ break;
+ case IR.LookaheadEnd, IR.NeglookaheadEnd: //ditto
+ uint len = irb[pc].data;
+ formattedWrite(output, " pc=>%u", pc-len);
+ break;
+ case IR.GroupStart, IR.GroupEnd:
+ uint n = irb[pc].data;
+ string name;
+ foreach (v;dict)
+ if (v.group == n)
+ {
+ name = "'"~v.name~"'";
+ break;
+ }
+ formattedWrite(output, " %s #%u " ~ (irb[pc].backreference ? "referenced" : ""),
+ name, n);
+ break;
+ case IR.LookaheadStart, IR.NeglookaheadStart, IR.LookbehindStart, IR.NeglookbehindStart:
+ uint len = irb[pc].data;
+ uint start = irb[pc+1].raw, end = irb[pc+2].raw;
+ formattedWrite(output, " pc=>%u [%u..%u]", pc + len + IRL!(IR.LookaheadStart), start, end);
+ break;
+ case IR.Backref: case IR.CodepointSet: case IR.Trie:
+ uint n = irb[pc].data;
+ formattedWrite(output, " %u", n);
+ if (irb[pc].code == IR.Backref)
+ formattedWrite(output, " %s", irb[pc].localRef ? "local" : "global");
+ break;
+ default://all data-free instructions
+ }
+ if (irb[pc].hotspot)
+ formattedWrite(output, " Hotspot %u", irb[pc+1].raw);
+ return output.data;
+}
+
+//disassemble the whole chunk
+@trusted void printBytecode()(in Bytecode[] slice, in NamedGroup[] dict=[])
+{
+ import std.stdio : writeln;
+ for (uint pc=0; pc<slice.length; pc += slice[pc].length)
+ writeln("\t", disassemble(slice, pc, dict));
+}
+
+/++
+ $(D Regex) object holds regular expression pattern in compiled form.
+ Instances of this object are constructed via calls to $(D regex).
+ This is an intended form for caching and storage of frequently
+ used regular expressions.
++/
+struct Regex(Char)
+{
+ //temporary workaround for identifier lookup
+ CodepointSet[] charsets; //
+ Bytecode[] ir; //compiled bytecode of pattern
+
+
+ @safe @property bool empty() const nothrow { return ir is null; }
+
+ @safe @property auto namedCaptures()
+ {
+ static struct NamedGroupRange
+ {
+ private:
+ NamedGroup[] groups;
+ size_t start;
+ size_t end;
+ public:
+ this(NamedGroup[] g, size_t s, size_t e)
+ {
+ assert(s <= e);
+ assert(e <= g.length);
+ groups = g;
+ start = s;
+ end = e;
+ }
+
+ @property string front() { return groups[start].name; }
+ @property string back() { return groups[end-1].name; }
+ @property bool empty() { return start >= end; }
+ @property size_t length() { return end - start; }
+ alias opDollar = length;
+ @property NamedGroupRange save()
+ {
+ return NamedGroupRange(groups, start, end);
+ }
+ void popFront() { assert(!empty); start++; }
+ void popBack() { assert(!empty); end--; }
+ string opIndex()(size_t i)
+ {
+ assert(start + i < end,
+ "Requested named group is out of range.");
+ return groups[start+i].name;
+ }
+ NamedGroupRange opSlice(size_t low, size_t high) {
+ assert(low <= high);
+ assert(start + high <= end);
+ return NamedGroupRange(groups, start + low, start + high);
+ }
+ NamedGroupRange opSlice() { return this.save; }
+ }
+ return NamedGroupRange(dict, 0, dict.length);
+ }
+
+package(std.regex):
+ 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)
+ return 0;
+ return backrefed[n / 32] & (1 << (n & 31));
+ }
+
+ //check if searching is not needed
+ void checkIfOneShot()
+ {
+ L_CheckLoop:
+ for (uint i = 0; i < ir.length; i += ir[i].length)
+ {
+ switch (ir[i].code)
+ {
+ case IR.Bof:
+ flags |= RegexInfo.oneShot;
+ break L_CheckLoop;
+ case IR.GroupStart, IR.GroupEnd, IR.Bol, IR.Eol, IR.Eof,
+ IR.Wordboundary, IR.Notwordboundary:
+ break;
+ default:
+ break L_CheckLoop;
+ }
+ }
+ }
+
+ //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)
+ {
+ writefln("%d\t%s ", i, disassemble(ir, i, dict));
+ }
+ writeln("Total merge table size: ", hotspotTableSize);
+ writeln("Max counter nesting depth: ", maxCounterDepth);
+ }
+
+}
+
+//@@@BUG@@@ (unreduced) - public makes it inaccessible in std.regex.package (!)
+/*public*/ struct StaticRegex(Char)
+{
+package(std.regex):
+ import std.regex.internal.backtracking : BacktrackingMatcher;
+ alias Matcher = BacktrackingMatcher!(true);
+ alias MatchFn = bool function(ref Matcher!Char) @trusted;
+ MatchFn nativeFn;
+public:
+ Regex!Char _regex;
+ alias _regex this;
+ this(Regex!Char re, MatchFn fn)
+ {
+ _regex = re;
+ nativeFn = fn;
+
+ }
+
+}
+
+// The stuff below this point is temporarrily part of IR module
+// but may need better place in the future (all internals)
+package(std.regex):
+
+//Simple UTF-string abstraction compatible with stream interface
+struct Input(Char)
+if (is(Char :dchar))
+{
+ import std.utf : decode;
+ alias DataIndex = size_t;
+ enum bool isLoopback = false;
+ alias String = const(Char)[];
+ String _origin;
+ size_t _index;
+
+ //constructs Input object out of plain string
+ this(String input, size_t idx = 0)
+ {
+ _origin = input;
+ _index = idx;
+ }
+
+ //codepoint at current stream position
+ pragma(inline, true) bool nextChar(ref dchar res, ref size_t pos)
+ {
+ pos = _index;
+ // 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;
+ }
+ bool search(Kickstart)(ref Kickstart kick, ref dchar res, ref size_t pos)
+ {
+ size_t idx = kick.search(_origin, _index);
+ _index = idx;
+ return nextChar(res, pos);
+ }
+
+ //index of at End position
+ @property size_t lastIndex(){ return _origin.length; }
+
+ //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]; }
+
+ auto loopBack(size_t index){ return BackLooper!Input(this, index); }
+}
+
+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)
+ {
+ _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 -= strideBack(_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; }
+
+ 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 : 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];
+ chunk = chunk[len * T.sizeof .. $];
+ return ret;
+}
+
+//
+@trusted uint lookupNamedGroup(String)(NamedGroup[] dict, String name)
+{//equal is @system?
+ import std.algorithm.comparison : equal;
+ import std.algorithm.iteration : map;
+ import std.conv : text;
+ import std.range : assumeSorted;
+
+ auto fnd = assumeSorted!"cmp(a,b) < 0"(map!"a.name"(dict)).lowerBound(name).length;
+ enforce(fnd < dict.length && equal(dict[fnd].name, name),
+ text("no submatch named ", name));
+ return dict[fnd].group;
+}
+
+//whether ch is one of unicode newline sequences
+//0-arg template due to @@@BUG@@@ 10985
+bool endOfLine()(dchar front, bool seenCr)
+{
+ return ((front == '\n') ^ seenCr) || front == '\r'
+ || front == NEL || front == LS || front == PS;
+}
+
+//
+//0-arg template due to @@@BUG@@@ 10985
+bool startOfLine()(dchar back, bool seenNl)
+{
+ return ((back == '\r') ^ seenNl) || back == '\n'
+ || back == NEL || back == LS || back == PS;
+}
+
+///Exception object thrown in case of errors during regex compilation.
+public class RegexException : Exception
+{
+ mixin basicExceptionCtors;
+}
+
+// simple 128-entry bit-table used with a hash function
+struct BitTable {
+ uint[4] filter;
+
+ this(CodepointSet set){
+ foreach (iv; set.byInterval)
+ {
+ 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;
+ }
+}
+
+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/libphobos/src/std/regex/internal/kickstart.d b/libphobos/src/std/regex/internal/kickstart.d
new file mode 100644
index 0000000..f303b43
--- /dev/null
+++ b/libphobos/src/std/regex/internal/kickstart.d
@@ -0,0 +1,579 @@
+/*
+ Kickstart is a coarse-grained "filter" engine that finds likely matches
+ to be verified by full-blown matcher.
+*/
+module std.regex.internal.kickstart;
+
+package(std.regex):
+
+import std.range.primitives, std.utf;
+import std.regex.internal.ir;
+
+//utility for shiftOr, returns a minimum number of bytes to test in a Char
+uint effectiveSize(Char)()
+{
+ static if (is(Char == char))
+ return 1;
+ else static if (is(Char == wchar))
+ return 2;
+ else static if (is(Char == dchar))
+ return 3;
+ else
+ static assert(0);
+}
+
+/*
+ Kickstart engine using ShiftOr algorithm,
+ a bit parallel technique for inexact string searching.
+*/
+struct ShiftOr(Char)
+{
+private:
+ uint[] table;
+ uint fChar;
+ uint n_length;
+ enum charSize = effectiveSize!Char();
+ //maximum number of chars in CodepointSet to process
+ enum uint charsetThreshold = 32_000;
+ static struct ShiftThread
+ {
+ uint[] tab;
+ uint mask;
+ uint idx;
+ uint pc, counter, hops;
+ this(uint newPc, uint newCounter, uint[] table)
+ {
+ pc = newPc;
+ counter = newCounter;
+ mask = 1;
+ idx = 0;
+ hops = 0;
+ tab = table;
+ }
+
+ void setMask(uint idx, uint mask)
+ {
+ tab[idx] |= mask;
+ }
+
+ void setInvMask(uint idx, uint mask)
+ {
+ tab[idx] &= ~mask;
+ }
+
+ void set(alias setBits = setInvMask)(dchar ch)
+ {
+ static if (charSize == 3)
+ {
+ uint val = ch, tmask = mask;
+ setBits(val&0xFF, tmask);
+ tmask <<= 1;
+ val >>= 8;
+ setBits(val&0xFF, tmask);
+ tmask <<= 1;
+ val >>= 8;
+ assert(val <= 0x10);
+ setBits(val, tmask);
+ tmask <<= 1;
+ }
+ else
+ {
+ 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)
+ {
+ static if (charSize == 1)
+ setBits(buf[i], tmask);
+ else static if (charSize == 2)
+ {
+ setBits(buf[i]&0xFF, tmask);
+ tmask <<= 1;
+ setBits(buf[i]>>8, tmask);
+ }
+ }
+ }
+ }
+ void add(dchar ch){ return set!setInvMask(ch); }
+ void advance(uint s)
+ {
+ mask <<= s;
+ idx += s;
+ }
+ @property bool full(){ return !mask; }
+ }
+
+ static ShiftThread fork(ShiftThread t, uint newPc, uint newCounter)
+ {
+ ShiftThread nt = t;
+ nt.pc = newPc;
+ nt.counter = newCounter;
+ return nt;
+ }
+
+ @trusted static ShiftThread fetch(ref ShiftThread[] worklist)
+ {
+ auto t = worklist[$-1];
+ worklist.length -= 1;
+ if (!__ctfe)
+ cast(void) worklist.assumeSafeAppend();
+ return t;
+ }
+
+ static uint charLen(uint ch)
+ {
+ assert(ch <= 0x10FFFF);
+ return codeLength!Char(cast(dchar) ch)*charSize;
+ }
+
+public:
+ @trusted this(ref Regex!Char re, uint[] memory)
+ {
+ 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;;)
+ {
+ switch (re.ir[i].code)
+ {
+ case IR.Char:
+ fChar = re.ir[i].data;
+ static if (charSize != 3)
+ {
+ Char[dchar.sizeof/Char.sizeof] buf;
+ encode(buf, fChar);
+ fChar = buf[0];
+ }
+ fChar = fChar & 0xFF;
+ break L_FindChar;
+ case IR.GroupStart, IR.GroupEnd:
+ i += IRL!(IR.GroupStart);
+ break;
+ case IR.Bof, IR.Bol, IR.Wordboundary, IR.Notwordboundary:
+ i += IRL!(IR.Bol);
+ break;
+ default:
+ break L_FindChar;
+ }
+ }
+ 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 (;;)
+ {
+ L_Eval_Thread:
+ for (;;)
+ {
+ switch (re.ir[t.pc].code)
+ {
+ case IR.Char:
+ uint s = charLen(re.ir[t.pc].data);
+ if (t.idx+s > n_length)
+ goto L_StopThread;
+ t.add(re.ir[t.pc].data);
+ t.advance(s);
+ t.pc += IRL!(IR.Char);
+ break;
+ case IR.OrChar://assumes IRL!(OrChar) == 1
+ uint len = re.ir[t.pc].sequence;
+ uint end = t.pc + len;
+ uint[Bytecode.maxSequence] s;
+ uint numS;
+ for (uint i = 0; i < len; i++)
+ {
+ auto x = charLen(re.ir[t.pc+i].data);
+ if (countUntil(s[0 .. numS], x) < 0)
+ s[numS++] = x;
+ }
+ for (uint i = t.pc; i < end; i++)
+ {
+ t.add(re.ir[i].data);
+ }
+ for (uint i = 0; i < numS; i++)
+ {
+ auto tx = fork(t, t.pc + len, t.counter);
+ if (tx.idx + s[i] <= n_length)
+ {
+ tx.advance(s[i]);
+ trs ~= tx;
+ }
+ }
+ if (!trs.empty)
+ t = fetch(trs);
+ else
+ goto L_StopThread;
+ break;
+ case IR.CodepointSet:
+ case IR.Trie:
+ auto set = re.charsets[re.ir[t.pc].data];
+ uint[4] s;
+ uint numS;
+ static if (charSize == 3)
+ {
+ s[0] = charSize;
+ numS = 1;
+ }
+ else
+ {
+
+ 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)
+ {
+ arr[ofs++] = ival.a;
+ arr[ofs++] = ival.b;
+ }
+ auto srange = assumeSorted!"a <= b"(arr);
+ 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)))
+ s[numS++] = (i+1)*charSize;
+ }
+ }
+ if (numS == 0 || t.idx + s[numS-1] > n_length)
+ goto L_StopThread;
+ auto chars = set.length;
+ if (chars > charsetThreshold)
+ goto L_StopThread;
+ foreach (ch; set.byCodepoint)
+ {
+ //avoid surrogate pairs
+ if (0xD800 <= ch && ch <= 0xDFFF)
+ continue;
+ t.add(ch);
+ }
+ 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)
+ t = fetch(trs);
+ else
+ goto L_StopThread;
+ break;
+ case IR.Any:
+ goto L_StopThread;
+
+ case IR.GotoEndOr:
+ t.pc += IRL!(IR.GotoEndOr)+re.ir[t.pc].data;
+ 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:
+ 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)
+ {
+ trs ~= fork(t, next, t.counter);
+ }
+ t.pc += IRL!(IR.Option);
+ break;
+ case IR.RepeatStart:case IR.RepeatQStart:
+ t.pc += IRL!(IR.RepeatStart)+re.ir[t.pc].data;
+ 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)
+ {
+ t.counter += step;
+ t.pc -= len;
+ break;
+ }
+ uint max = re.ir[t.pc+4].raw;
+ if (t.counter < max)
+ {
+ trs ~= fork(t, t.pc - len, t.counter + step);
+ t.counter = t.counter%step;
+ t.pc += IRL!(IR.RepeatEnd);
+ }
+ else
+ {
+ t.counter = t.counter%step;
+ t.pc += IRL!(IR.RepeatEnd);
+ }
+ 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.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)
+ goto L_StopThread;
+ pc1 = t.pc + IRL!(IR.InfiniteEnd);
+ pc2 = t.pc - len;
+ trs ~= fork(t, pc2, t.counter);
+ t.pc = pc1;
+ break;
+ case IR.GroupStart, IR.GroupEnd:
+ t.pc += IRL!(IR.GroupStart);
+ break;
+ case IR.Bof, IR.Bol, IR.Wordboundary, IR.Notwordboundary:
+ t.pc += IRL!(IR.Bol);
+ break;
+ case IR.LookaheadStart, IR.NeglookaheadStart, IR.LookbehindStart, IR.NeglookbehindStart:
+ t.pc += IRL!(IR.LookaheadStart) + IRL!(IR.LookaheadEnd) + re.ir[t.pc].data;
+ break;
+ default:
+ 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 = std.algorithm.comparison.min(t.idx, n_length);
+ break L_Eval_Thread;
+ }
+ }
+ if (trs.empty)
+ break;
+ t = fetch(trs);
+ }
+ debug(std_regex_search)
+ {
+ writeln("Min length: ", n_length);
+ }
+ }
+
+ @property bool empty() const { return n_length == 0; }
+
+ @property uint length() const{ return n_length/charSize; }
+
+ // lookup compatible bit pattern in haystack, return starting index
+ // has a useful trait: if supplied with valid UTF indexes,
+ // returns only valid UTF indexes
+ // (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 core.stdc.string : memchr;
+ import std.conv : text;
+ 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)
+ {
+ const(ubyte)* end = cast(ubyte*)(haystack.ptr + haystack.length);
+ const orginalAlign = cast(size_t) p & (Char.sizeof-1);
+ while (p != end)
+ {
+ if (!~state)
+ {//speed up seeking first matching place
+ for (;;)
+ {
+ assert(p <= end, text(p," vs ", end));
+ p = cast(ubyte*) memchr(p, fChar, end - p);
+ if (!p)
+ return haystack.length;
+ if ((cast(size_t) p & (Char.sizeof-1)) == orginalAlign)
+ break;
+ if (++p == end)
+ return haystack.length;
+ }
+ state = ~1u;
+ 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]];
+ p += 4;
+ }
+ else
+ p++;
+ //first char is tested, see if that's all
+ 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)
+ {
+ 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]];
+ p++;
+ }
+ if (!(state & limit))
+ return (p-cast(ubyte*) haystack.ptr)/Char.sizeof
+ -length;
+ }
+ debug(std_regex_search) writefln("State: %32b", state);
+ }
+ }
+ else
+ {
+ //normal path, partially unrolled for char/wchar
+ static if (charSize == 3)
+ {
+ const(ubyte)* end = cast(ubyte*)(haystack.ptr + haystack.length);
+ while (p != end)
+ {
+ 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
+ -length;
+ }
+ }
+ else
+ {
+ auto len = cast(ubyte*)(haystack.ptr + haystack.length) - p;
+ size_t i = 0;
+ if (len & 1)
+ {
+ state = (state << 1) | table[p[i++]];
+ if (!(state & limit))
+ return idx+i/Char.sizeof-length;
+ }
+ while (i < len)
+ {
+ state = (state << 1) | table[p[i++]];
+ if (!(state & limit))
+ return idx+i/Char.sizeof
+ -length;
+ state = (state << 1) | table[p[i++]];
+ if (!(state & limit))
+ return idx+i/Char.sizeof
+ -length;
+ debug(std_regex_search) writefln("State: %32b", state);
+ }
+ }
+ }
+ return haystack.length;
+ }
+
+ @system debug static void dump(uint[] table)
+ {//@@@BUG@@@ writef(ln) is @system
+ 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]);
+ }
+ }
+}
+
+@system unittest
+{
+ import std.conv, std.regex;
+ @trusted void test_fixed(alias Kick)()
+ {
+ foreach (i, v; AliasSeq!(char, wchar, dchar))
+ {
+ alias Char = v;
+ alias String = immutable(v)[];
+ auto r = regex(to!String(`abc$`));
+ auto kick = Kick!Char(r, new uint[256]);
+ assert(kick.length == 3, text(Kick.stringof," ",v.stringof, " == ", kick.length));
+ auto r2 = regex(to!String(`(abc){2}a+`));
+ kick = Kick!Char(r2, new uint[256]);
+ assert(kick.length == 7, text(Kick.stringof,v.stringof," == ", kick.length));
+ auto r3 = regex(to!String(`\b(a{2}b{3}){2,4}`));
+ kick = Kick!Char(r3, new uint[256]);
+ assert(kick.length == 10, text(Kick.stringof,v.stringof," == ", kick.length));
+ auto r4 = regex(to!String(`\ba{2}c\bxyz`));
+ kick = Kick!Char(r4, new uint[256]);
+ assert(kick.length == 6, text(Kick.stringof,v.stringof, " == ", kick.length));
+ auto r5 = regex(to!String(`\ba{2}c\b`));
+ kick = Kick!Char(r5, new uint[256]);
+ size_t x = kick.search("aabaacaa", 0);
+ assert(x == 3, text(Kick.stringof,v.stringof," == ", kick.length));
+ x = kick.search("aabaacaa", x+1);
+ assert(x == 8, text(Kick.stringof,v.stringof," == ", kick.length));
+ }
+ }
+ @trusted void test_flex(alias Kick)()
+ {
+ foreach (i, v; AliasSeq!(char, wchar, dchar))
+ {
+ alias Char = v;
+ alias String = immutable(v)[];
+ auto r = regex(to!String(`abc[a-z]`));
+ auto kick = Kick!Char(r, new uint[256]);
+ auto x = kick.search(to!String("abbabca"), 0);
+ assert(x == 3, text("real x is ", x, " ",v.stringof));
+
+ auto r2 = regex(to!String(`(ax|bd|cdy)`));
+ String s2 = to!String("abdcdyabax");
+ kick = Kick!Char(r2, new uint[256]);
+ x = kick.search(s2, 0);
+ assert(x == 1, text("real x is ", x));
+ x = kick.search(s2, x+1);
+ assert(x == 3, text("real x is ", x));
+ x = kick.search(s2, x+1);
+ assert(x == 8, text("real x is ", x));
+ auto rdot = regex(to!String(`...`));
+ kick = Kick!Char(rdot, new uint[256]);
+ assert(kick.length == 0);
+ auto rN = regex(to!String(`a(b+|c+)x`));
+ kick = Kick!Char(rN, new uint[256]);
+ assert(kick.length == 3, to!string(kick.length));
+ assert(kick.search("ababx",0) == 2);
+ assert(kick.search("abaacba",0) == 3);//expected inexact
+
+ }
+ }
+ test_fixed!(ShiftOr)();
+ test_flex!(ShiftOr)();
+}
+
+alias Kickstart = ShiftOr;
diff --git a/libphobos/src/std/regex/internal/parser.d b/libphobos/src/std/regex/internal/parser.d
new file mode 100644
index 0000000..8cabae5
--- /dev/null
+++ b/libphobos/src/std/regex/internal/parser.d
@@ -0,0 +1,1751 @@
+//Written in the D programming language
+/*
+ Regular expression pattern parser.
+*/
+module std.regex.internal.parser;
+
+static import std.ascii;
+import std.range.primitives, std.uni, std.meta,
+ std.traits, std.typecons, std.exception;
+import std.regex.internal.ir;
+
+// package relevant info from parser into a regex object
+auto makeRegex(S, CG)(Parser!(S, CG) p)
+{
+ Regex!(BasicElementOf!S) re;
+ auto g = p.g;
+ with(re)
+ {
+ ir = g.ir;
+ dict = g.dict;
+ ngroup = g.ngroup;
+ maxCounterDepth = g.counterDepth;
+ flags = p.re_flags;
+ charsets = g.charsets;
+ matchers = g.matchers;
+ backrefed = g.backrefed;
+ re.postprocess();
+ debug(std_regex_parser)
+ {
+ __ctfe || print();
+ }
+ //@@@BUG@@@ (not reduced)
+ //somehow just using validate _collides_ with std.utf.validate (!)
+ version (assert) re.validateRe();
+ }
+ return re;
+}
+
+// helper for unittest
+auto makeRegex(S)(S arg)
+if (isSomeString!S)
+{
+ return makeRegex(Parser!(S, CodeGen)(arg, ""));
+}
+
+@system unittest
+{
+ import std.algorithm.comparison : equal;
+ auto re = makeRegex(`(?P<name>\w+) = (?P<var>\d+)`);
+ auto nc = re.namedCaptures;
+ static assert(isRandomAccessRange!(typeof(nc)));
+ assert(!nc.empty);
+ assert(nc.length == 2);
+ assert(nc.equal(["name", "var"]));
+ assert(nc[0] == "name");
+ assert(nc[1..$].equal(["var"]));
+
+ re = makeRegex(`(\w+) (?P<named>\w+) (\w+)`);
+ nc = re.namedCaptures;
+ assert(nc.length == 1);
+ assert(nc[0] == "named");
+ assert(nc.front == "named");
+ assert(nc.back == "named");
+
+ re = makeRegex(`(\w+) (\w+)`);
+ nc = re.namedCaptures;
+ assert(nc.empty);
+
+ re = makeRegex(`(?P<year>\d{4})/(?P<month>\d{2})/(?P<day>\d{2})/`);
+ nc = re.namedCaptures;
+ auto cp = nc.save;
+ assert(nc.equal(cp));
+ nc.popFront();
+ assert(nc.equal(cp[1..$]));
+ nc.popBack();
+ assert(nc.equal(cp[1 .. $ - 1]));
+}
+
+
+@trusted void reverseBytecode()(Bytecode[] code)
+{
+ Bytecode[] rev = new Bytecode[code.length];
+ uint revPc = cast(uint) rev.length;
+ Stack!(Tuple!(uint, uint, uint)) stack;
+ uint start = 0;
+ uint end = cast(uint) code.length;
+ for (;;)
+ {
+ for (uint pc = start; pc < end; )
+ {
+ immutable len = code[pc].length;
+ if (code[pc].code == IR.GotoEndOr)
+ break; //pick next alternation branch
+ if (code[pc].isAtom)
+ {
+ rev[revPc - len .. revPc] = code[pc .. pc + len];
+ revPc -= len;
+ pc += len;
+ }
+ else if (code[pc].isStart || code[pc].isEnd)
+ {
+ //skip over other embedded lookbehinds they are reversed
+ if (code[pc].code == IR.LookbehindStart
+ || code[pc].code == IR.NeglookbehindStart)
+ {
+ immutable blockLen = len + code[pc].data
+ + code[pc].pairedLength;
+ rev[revPc - blockLen .. revPc] = code[pc .. pc + blockLen];
+ pc += blockLen;
+ revPc -= blockLen;
+ continue;
+ }
+ 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)
+ {
+ //we pass len bytes forward, but secLen in reverse
+ immutable revStart = revPc - (second + len - secLen - pc);
+ uint r = revStart;
+ uint i = pc + IRL!(IR.OrStart);
+ while (code[i].code == IR.Option)
+ {
+ if (code[i - 1].code != IR.OrStart)
+ {
+ assert(code[i - 1].code == IR.GotoEndOr);
+ rev[r - 1] = code[i - 1];
+ }
+ rev[r] = code[i];
+ 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)
+ {
+ newRpc--;
+ }
+ stack.push(tuple(newStart, newEnd, newRpc));
+ r += code[i].data + IRL!(IR.Option);
+ i += code[i].data + IRL!(IR.Option);
+ }
+ pc = i;
+ revPc = revStart;
+ assert(code[pc].code == IR.OrEnd);
+ }
+ else
+ pc += len;
+ }
+ }
+ if (stack.empty)
+ break;
+ start = stack.top[0];
+ end = stack.top[1];
+ revPc = stack.top[2];
+ stack.pop();
+ }
+ code[] = rev[];
+}
+
+//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)
+{
+ //std.conv.parse is both @system and bogus
+ enforce(str.length >= maxDigit,"incomplete escape sequence");
+ uint val;
+ for (int k = 0; k < maxDigit; k++)
+ {
+ 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')
+ val = val * 16 + current -'a' + 10;
+ else if ('A' <= current && current <= 'F')
+ val = val * 16 + current - 'A' + 10;
+ else
+ throw new Exception("invalid escape sequence");
+ }
+ enforce(val <= 0x10FFFF, "invalid codepoint");
+ str = str[maxDigit..$];
+ return val;
+}
+
+@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)
+ assert(collectException(parseUniHex(v, v.length)).msg
+ .canFind("invalid escape sequence"));
+ foreach (i, v; hex)
+ assert(parseUniHex(v, v.length) == value[i]);
+ string over = "0011FFFF";
+ assert(collectException(parseUniHex(over, over.length)).msg
+ .canFind("invalid codepoint"));
+}
+
+auto caseEnclose(CodepointSet set)
+{
+ auto cased = set & unicode.LC;
+ foreach (dchar ch; cased.byCodepoint)
+ {
+ foreach (c; simpleCaseFoldings(ch))
+ set |= c;
+ }
+ return set;
+}
+
+/+
+ fetch codepoint set corresponding to a name (InBlock or binary property)
++/
+@trusted CodepointSet getUnicodeSet(in char[] name, bool negated, bool casefold)
+{
+ CodepointSet s = unicode(name);
+ //FIXME: caseEnclose for new uni as Set | CaseEnclose(SET && LC)
+ if (casefold)
+ s = caseEnclose(s);
+ if (negated)
+ s = s.inverted;
+ return s;
+}
+
+//basic stack, just in case it gets used anywhere else then Parser
+@trusted struct Stack(T)
+{
+ T[] data;
+ @property bool empty(){ return data.empty; }
+
+ @property size_t length(){ return data.length; }
+
+ void push(T val){ data ~= val; }
+
+ T pop()
+ {
+ assert(!empty);
+ auto val = data[$ - 1];
+ data = data[0 .. $ - 1];
+ if (!__ctfe)
+ cast(void) data.assumeSafeAppend();
+ return val;
+ }
+
+ @property ref T top()
+ {
+ assert(!empty);
+ return data[$ - 1];
+ }
+}
+
+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: (?= (?! (?<= (?<!
+ void genLookaround(IR opcode)
+ {
+ nesting++;
+ pushFixup(length);
+ put(Bytecode(opcode, 0));
+ put(Bytecode.fromRaw(0));
+ put(Bytecode.fromRaw(0));
+ groupStack.push(0);
+ lookaroundNest++;
+ enforce(lookaroundNest <= maxLookaroundDepth,
+ "maximum lookaround depth is exceeded");
+ }
+
+ void endPattern(uint num)
+ {
+ import std.algorithm.comparison : max;
+ put(Bytecode(IR.End, num));
+ ngroup = max(ngroup, groupStack.top);
+ groupStack.top = 1; // reset group counter
+ }
+
+ //fixup lookaround with start at offset fix and append a proper *-End opcode
+ void fixLookaround(uint fix)
+ {
+ lookaroundNest--;
+ ir[fix] = Bytecode(ir[fix].code,
+ cast(uint) ir.length - fix - IRL!(IR.LookaheadStart));
+ auto g = groupStack.pop();
+ assert(!groupStack.empty);
+ ir[fix+1] = Bytecode.fromRaw(groupStack.top);
+ //groups are cumulative across lookarounds
+ ir[fix+2] = Bytecode.fromRaw(groupStack.top+g);
+ groupStack.top += g;
+ if (ir[fix].code == IR.LookbehindStart || ir[fix].code == IR.NeglookbehindStart)
+ {
+ reverseBytecode(ir[fix + IRL!(IR.LookbehindStart) .. $]);
+ }
+ put(ir[fix].paired);
+ }
+
+ // repetition of {1,1}
+ void fixRepetition(uint offset)
+ {
+ import std.algorithm.mutation : copy;
+ immutable replace = ir[offset].code == IR.Nop;
+ if (replace)
+ {
+ copy(ir[offset + 1 .. $], ir[offset .. $ - 1]);
+ ir.length -= 1;
+ }
+ }
+
+ // repetition of {x,y}
+ void fixRepetition(uint offset, uint min, uint max, bool greedy)
+ {
+ static import std.algorithm.comparison;
+ import std.algorithm.mutation : copy;
+ import std.array : insertInPlace;
+ immutable replace = ir[offset].code == IR.Nop;
+ immutable len = cast(uint) ir.length - offset - replace;
+ 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.comparison.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.comparison.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
+ }
+ }
+
+ void fixAlternation()
+ {
+ import std.array : insertInPlace;
+ uint 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));
+ 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
+enum maxCumulativeRepetitionLength = 2^^20;
+// marker to indicate infinite repetition
+enum infinite = ~0u;
+
+struct Parser(R, Generator)
+if (isForwardRange!R && is(ElementType!R : dchar))
+{
+ dchar _current;
+ bool empty;
+ R pat, origin; //keep full pattern for pretty printing error messages
+ uint re_flags = 0; //global flags e.g. multiline + internal ones
+ Generator g;
+
+ @trusted this(S)(R pattern, S flags)
+ if (isSomeString!S)
+ {
+ pat = origin = pattern;
+ //reserve slightly more then avg as sampled from unittests
+ parseFlags(flags);
+ _current = ' ';//a safe default for freeform parsing
+ next();
+ g.start(cast(uint) pat.length);
+ try
+ {
+ parseRegex();
+ }
+ catch (Exception e)
+ {
+ error(e.msg);//also adds pattern location
+ }
+ g.endPattern(1);
+ }
+
+ @property dchar current(){ return _current; }
+
+ bool _next()
+ {
+ if (pat.empty)
+ {
+ empty = true;
+ return false;
+ }
+ _current = pat.front;
+ pat.popFront();
+ return true;
+ }
+
+ void skipSpace()
+ {
+ while (isWhite(current) && _next()){ }
+ }
+
+ bool next()
+ {
+ if (re_flags & RegexOption.freeform)
+ {
+ immutable r = _next();
+ skipSpace();
+ return r;
+ }
+ else
+ return _next();
+ }
+
+ //parsing number with basic overflow check
+ uint parseDecimal()
+ {
+ uint r = 0;
+ while (std.ascii.isDigit(current))
+ {
+ if (r >= (uint.max/10))
+ error("Overflow in decimal number");
+ r = 10*r + cast(uint)(current-'0');
+ if (!next())
+ break;
+ }
+ return r;
+ }
+
+ //parse control code of form \cXXX, c assumed to be the current symbol
+ dchar parseControlCode()
+ {
+ enforce(next(), "Unfinished escape sequence");
+ enforce(('a' <= current && current <= 'z') || ('A' <= current && current <= 'Z'),
+ "Only letters are allowed after \\c");
+ return current & 0x1f;
+ }
+
+ //
+ @trusted void parseFlags(S)(S flags)
+ {//@@@BUG@@@ text is @system
+ import std.conv : text;
+ foreach (ch; flags)//flags are ASCII anyway
+ {
+ L_FlagSwitch:
+ switch (ch)
+ {
+
+ foreach (i, op; __traits(allMembers, RegexOption))
+ {
+ case RegexOptionNames[i]:
+ if (re_flags & mixin("RegexOption."~op))
+ throw new RegexException(text("redundant flag specified: ",ch));
+ re_flags |= mixin("RegexOption."~op);
+ break L_FlagSwitch;
+ }
+ default:
+ throw new RegexException(text("unknown regex flag '",ch,"'"));
+ }
+ }
+ }
+
+ //parse and store IR for regex pattern
+ @trusted void parseRegex()
+ {
+ uint fix;//fixup pointer
+
+ while (!empty)
+ {
+ debug(std_regex_parser)
+ __ctfe || writeln("*LR*\nSource: ", pat, "\nStack: ",fixupStack.data);
+ switch (current)
+ {
+ case '(':
+ next();
+ if (current == '?')
+ {
+ next();
+ switch (current)
+ {
+ case '#':
+ for (;;)
+ {
+ if (!next())
+ error("Unexpected end of pattern");
+ if (current == ')')
+ {
+ next();
+ break;
+ }
+ }
+ break;
+ case ':':
+ g.genLogicGroup();
+ next();
+ break;
+ case '=':
+ g.genLookaround(IR.LookaheadStart);
+ next();
+ break;
+ case '!':
+ g.genLookaround(IR.NeglookaheadStart);
+ next();
+ break;
+ case 'P':
+ next();
+ if (current != '<')
+ error("Expected '<' in named group");
+ string name;
+ if (!next() || !(isAlpha(current) || current == '_'))
+ error("Expected alpha starting a named group");
+ name ~= current;
+ while (next() && (isAlpha(current) ||
+ current == '_' || std.ascii.isDigit(current)))
+ {
+ name ~= current;
+ }
+ if (current != '>')
+ error("Expected '>' closing named group");
+ next();
+ g.genNamedGroup(name);
+ break;
+ case '<':
+ next();
+ if (current == '=')
+ g.genLookaround(IR.LookbehindStart);
+ else if (current == '!')
+ g.genLookaround(IR.NeglookbehindStart);
+ else
+ error("'!' or '=' expected after '<'");
+ next();
+ break;
+ default:
+ 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
+ {
+ g.genGroup();
+ }
+ break;
+ case ')':
+ enforce(g.nesting, "Unmatched ')'");
+ next();
+ auto pair = g.onClose();
+ if (pair[0])
+ parseQuantifier(pair[1]);
+ break;
+ case '|':
+ next();
+ g.fixAlternation();
+ break;
+ default://no groups or whatever
+ immutable start = g.length;
+ parseAtom();
+ parseQuantifier(start);
+ }
+ }
+
+ if (g.fixupLength != 1)
+ {
+ fix = g.popFixup();
+ g.finishAlternation(fix);
+ enforce(g.fixupLength == 1, "no matching ')'");
+ }
+ }
+
+
+ //parse and store IR for atom-quantifier pair
+ @trusted void parseQuantifier(uint offset)
+ {//copy is @system
+ if (empty)
+ return g.fixRepetition(offset);
+ uint min, max;
+ switch (current)
+ {
+ case '*':
+ min = 0;
+ max = infinite;
+ break;
+ case '?':
+ min = 0;
+ max = 1;
+ break;
+ case '+':
+ min = 1;
+ max = infinite;
+ break;
+ case '{':
+ enforce(next(), "Unexpected end of regex pattern");
+ enforce(std.ascii.isDigit(current), "First number required in repetition");
+ min = parseDecimal();
+ if (current == '}')
+ max = min;
+ else if (current == ',')
+ {
+ next();
+ if (std.ascii.isDigit(current))
+ max = parseDecimal();
+ else if (current == '}')
+ max = infinite;
+ else
+ error("Unexpected symbol in regex pattern");
+ skipSpace();
+ if (current != '}')
+ error("Unmatched '{' in regex pattern");
+ }
+ else
+ error("Unexpected symbol in regex pattern");
+ if (min > max)
+ error("Illegal {n,m} quantifier");
+ break;
+ default:
+ g.fixRepetition(offset);
+ return;
+ }
+ bool greedy = true;
+ //check only if we managed to get new symbol
+ if (next() && current == '?')
+ {
+ greedy = false;
+ next();
+ }
+ g.fixRepetition(offset, min, max, greedy);
+ }
+
+ //parse and store IR for atom
+ void parseAtom()
+ {
+ if (empty)
+ return;
+ switch (current)
+ {
+ case '*', '?', '+', '|', '{', '}':
+ error("'*', '+', '?', '{', '}' not allowed in atom");
+ break;
+ case '.':
+ 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 '[':
+ parseCharset();
+ break;
+ case '\\':
+ enforce(_next(), "Unfinished escape sequence");
+ parseEscape();
+ break;
+ case '^':
+ if (re_flags & RegexOption.multiline)
+ g.put(Bytecode(IR.Bol, 0));
+ else
+ g.put(Bytecode(IR.Bof, 0));
+ next();
+ break;
+ case '$':
+ 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)
+ {
+ auto range = simpleCaseFoldings(current);
+ assert(range.length <= 5);
+ if (range.length == 1)
+ g.put(Bytecode(IR.Char, range.front));
+ else
+ foreach (v; range)
+ g.put(Bytecode(IR.OrChar, v, cast(uint) range.length));
+ }
+ else
+ g.put(Bytecode(IR.Char, current));
+ next();
+ }
+ }
+
+
+
+ //CodepointSet operations relatively in order of priority
+ enum Operator:uint {
+ Open = 0, Negate, Difference, SymDifference, Intersection, Union, None
+ }
+
+ //parse unit of CodepointSet spec, most notably escape sequences and char ranges
+ //also fetches next set operation
+ Tuple!(CodepointSet,Operator) parseCharTerm()
+ {
+ enum State{ Start, Char, Escape, CharDash, CharDashEscape,
+ PotentialTwinSymbolOperator }
+ Operator op = Operator.None;
+ dchar last;
+ CodepointSet set;
+ State state = State.Start;
+
+ static void addWithFlags(ref CodepointSet set, uint ch, uint re_flags)
+ {
+ if (re_flags & RegexOption.casefold)
+ {
+ auto range = simpleCaseFoldings(ch);
+ foreach (v; range)
+ set |= v;
+ }
+ else
+ set |= ch;
+ }
+
+ static Operator twinSymbolOperator(dchar symbol)
+ {
+ switch (symbol)
+ {
+ case '|':
+ return Operator.Union;
+ case '-':
+ return Operator.Difference;
+ case '~':
+ return Operator.SymDifference;
+ case '&':
+ return Operator.Intersection;
+ default:
+ assert(false);
+ }
+ }
+
+ L_CharTermLoop:
+ for (;;)
+ {
+ final switch (state)
+ {
+ case State.Start:
+ switch (current)
+ {
+ case '|':
+ case '-':
+ case '~':
+ case '&':
+ state = State.PotentialTwinSymbolOperator;
+ last = current;
+ break;
+ case '[':
+ op = Operator.Union;
+ goto case;
+ case ']':
+ break L_CharTermLoop;
+ case '\\':
+ state = State.Escape;
+ break;
+ default:
+ state = State.Char;
+ last = current;
+ }
+ break;
+ case State.Char:
+ // xxx last current xxx
+ switch (current)
+ {
+ case '|':
+ case '~':
+ case '&':
+ // then last is treated as normal char and added as implicit union
+ state = State.PotentialTwinSymbolOperator;
+ addWithFlags(set, last, re_flags);
+ last = current;
+ break;
+ case '-': // still need more info
+ state = State.CharDash;
+ break;
+ case '\\':
+ set |= last;
+ state = State.Escape;
+ break;
+ case '[':
+ op = Operator.Union;
+ goto case;
+ case ']':
+ addWithFlags(set, last, re_flags);
+ break L_CharTermLoop;
+ default:
+ state = State.Char;
+ addWithFlags(set, last, re_flags);
+ last = current;
+ }
+ break;
+ case State.PotentialTwinSymbolOperator:
+ // xxx last current xxxx
+ // where last = [|-&~]
+ if (current == last)
+ {
+ op = twinSymbolOperator(last);
+ next();//skip second twin char
+ break L_CharTermLoop;
+ }
+ goto case State.Char;
+ case State.Escape:
+ // xxx \ current xxx
+ switch (current)
+ {
+ case 'f':
+ last = '\f';
+ state = State.Char;
+ break;
+ case 'n':
+ last = '\n';
+ state = State.Char;
+ break;
+ case 'r':
+ last = '\r';
+ state = State.Char;
+ break;
+ case 't':
+ last = '\t';
+ state = State.Char;
+ break;
+ case 'v':
+ last = '\v';
+ state = State.Char;
+ break;
+ case 'c':
+ last = parseControlCode();
+ state = State.Char;
+ break;
+ foreach (val; Escapables)
+ {
+ case val:
+ }
+ last = current;
+ state = State.Char;
+ break;
+ case 'p':
+ set.add(parseUnicodePropertySpec(false));
+ state = State.Start;
+ continue L_CharTermLoop; //next char already fetched
+ case 'P':
+ set.add(parseUnicodePropertySpec(true));
+ state = State.Start;
+ continue L_CharTermLoop; //next char already fetched
+ case 'x':
+ last = parseUniHex(pat, 2);
+ state = State.Char;
+ break;
+ case 'u':
+ last = parseUniHex(pat, 4);
+ state = State.Char;
+ break;
+ case 'U':
+ last = parseUniHex(pat, 8);
+ state = State.Char;
+ break;
+ case 'd':
+ set.add(unicode.Nd);
+ state = State.Start;
+ break;
+ case 'D':
+ set.add(unicode.Nd.inverted);
+ state = State.Start;
+ break;
+ case 's':
+ set.add(unicode.White_Space);
+ state = State.Start;
+ break;
+ case 'S':
+ set.add(unicode.White_Space.inverted);
+ state = State.Start;
+ break;
+ case 'w':
+ set.add(wordCharacter);
+ state = State.Start;
+ break;
+ case 'W':
+ set.add(wordCharacter.inverted);
+ state = State.Start;
+ break;
+ default:
+ if (current >= privateUseStart && current <= privateUseEnd)
+ enforce(false, "no matching ']' found while parsing character class");
+ enforce(false, "invalid escape sequence");
+ }
+ break;
+ case State.CharDash:
+ // xxx last - current xxx
+ switch (current)
+ {
+ case '[':
+ op = Operator.Union;
+ goto case;
+ case ']':
+ //means dash is a single char not an interval specifier
+ addWithFlags(set, last, re_flags);
+ addWithFlags(set, '-', re_flags);
+ break L_CharTermLoop;
+ case '-'://set Difference again
+ addWithFlags(set, last, re_flags);
+ op = Operator.Difference;
+ next();//skip '-'
+ break L_CharTermLoop;
+ case '\\':
+ state = State.CharDashEscape;
+ break;
+ default:
+ enforce(last <= current, "inverted range");
+ if (re_flags & RegexOption.casefold)
+ {
+ for (uint ch = last; ch <= current; ch++)
+ addWithFlags(set, ch, re_flags);
+ }
+ else
+ set.add(last, current + 1);
+ state = State.Start;
+ }
+ break;
+ case State.CharDashEscape:
+ //xxx last - \ current xxx
+ uint end;
+ switch (current)
+ {
+ case 'f':
+ end = '\f';
+ break;
+ case 'n':
+ end = '\n';
+ break;
+ case 'r':
+ end = '\r';
+ break;
+ case 't':
+ end = '\t';
+ break;
+ case 'v':
+ end = '\v';
+ break;
+ foreach (val; Escapables)
+ {
+ case val:
+ }
+ end = current;
+ break;
+ case 'c':
+ end = parseControlCode();
+ break;
+ case 'x':
+ end = parseUniHex(pat, 2);
+ break;
+ case 'u':
+ end = parseUniHex(pat, 4);
+ break;
+ case 'U':
+ end = parseUniHex(pat, 8);
+ break;
+ default:
+ if (current >= privateUseStart && current <= privateUseEnd)
+ enforce(false, "no matching ']' found while parsing character class");
+ error("invalid escape sequence");
+ }
+ // Lookahead to check if it's a \T
+ // where T is sub-pattern terminator in multi-pattern scheme
+ if (end == '\\' && !pat.empty)
+ {
+ if (pat.front >= privateUseStart && pat.front <= privateUseEnd)
+ enforce(false, "invalid escape sequence");
+ }
+ enforce(last <= end,"inverted range");
+ set.add(last, end + 1);
+ state = State.Start;
+ break;
+ }
+ enforce(next(), "unexpected end of CodepointSet");
+ }
+ return tuple(set, op);
+ }
+
+ alias ValStack = Stack!(CodepointSet);
+ alias OpStack = Stack!(Operator);
+
+ //parse and store IR for CodepointSet
+ void parseCharset()
+ {
+ const save = re_flags;
+ re_flags &= ~RegexOption.freeform; // stop ignoring whitespace if we did
+ parseCharsetImpl();
+ re_flags = save;
+ // Last next() in parseCharsetImp is executed w/o freeform flag
+ if (re_flags & RegexOption.freeform) skipSpace();
+ }
+
+ void parseCharsetImpl()
+ {
+ ValStack vstack;
+ OpStack opstack;
+ import std.functional : unaryFun;
+ //
+ static bool apply(Operator op, ref ValStack stack)
+ {
+ switch (op)
+ {
+ case Operator.Negate:
+ enforce(!stack.empty, "no operand for '^'");
+ stack.top = stack.top.inverted;
+ break;
+ case Operator.Union:
+ auto s = stack.pop();//2nd operand
+ enforce(!stack.empty, "no operand for '||'");
+ stack.top.add(s);
+ break;
+ case Operator.Difference:
+ auto s = stack.pop();//2nd operand
+ enforce(!stack.empty, "no operand for '--'");
+ stack.top.sub(s);
+ break;
+ case Operator.SymDifference:
+ auto s = stack.pop();//2nd operand
+ enforce(!stack.empty, "no operand for '~~'");
+ stack.top ~= s;
+ break;
+ case Operator.Intersection:
+ auto s = stack.pop();//2nd operand
+ enforce(!stack.empty, "no operand for '&&'");
+ stack.top.intersect(s);
+ break;
+ default:
+ return false;
+ }
+ return true;
+ }
+ static bool unrollWhile(alias cond)(ref ValStack vstack, ref OpStack opstack)
+ {
+ while (cond(opstack.top))
+ {
+ if (!apply(opstack.pop(),vstack))
+ return false;//syntax error
+ if (opstack.empty)
+ return false;
+ }
+ return true;
+ }
+
+ L_CharsetLoop:
+ do
+ {
+ switch (current)
+ {
+ case '[':
+ opstack.push(Operator.Open);
+ enforce(next(), "unexpected end of character class");
+ if (current == '^')
+ {
+ opstack.push(Operator.Negate);
+ enforce(next(), "unexpected end of character class");
+ }
+ else if (current == ']') // []...] is special cased
+ {
+ enforce(next(), "wrong character set");
+ auto pair = parseCharTerm();
+ pair[0].add(']', ']'+1);
+ if (pair[1] != Operator.None)
+ {
+ if (opstack.top == Operator.Union)
+ unrollWhile!(unaryFun!"a == a.Union")(vstack, opstack);
+ opstack.push(pair[1]);
+ }
+ vstack.push(pair[0]);
+ }
+ break;
+ case ']':
+ enforce(unrollWhile!(unaryFun!"a != a.Open")(vstack, opstack),
+ "character class syntax error");
+ enforce(!opstack.empty, "unmatched ']'");
+ opstack.pop();
+ if (!next() || opstack.empty)
+ break L_CharsetLoop;
+ auto pair = parseCharTerm();
+ if (!pair[0].empty)//not only operator e.g. -- or ~~
+ {
+ vstack.top.add(pair[0]);//apply union
+ }
+ if (pair[1] != Operator.None)
+ {
+ if (opstack.top == Operator.Union)
+ unrollWhile!(unaryFun!"a == a.Union")(vstack, opstack);
+ opstack.push(pair[1]);
+ }
+ break;
+ //
+ default://yet another pair of term(op)?
+ auto pair = parseCharTerm();
+ if (pair[1] != Operator.None)
+ {
+ if (opstack.top == Operator.Union)
+ unrollWhile!(unaryFun!"a == a.Union")(vstack, opstack);
+ opstack.push(pair[1]);
+ }
+ vstack.push(pair[0]);
+ }
+ }while (!empty || !opstack.empty);
+ while (!opstack.empty)
+ {
+ enforce(opstack.top != Operator.Open,
+ "no matching ']' found while parsing character class");
+ apply(opstack.pop(), vstack);
+ }
+ assert(vstack.length == 1);
+ g.charsetToIr(vstack.top);
+ }
+
+ //parse and generate IR for escape stand alone escape sequence
+ @trusted void parseEscape()
+ {//accesses array of appender
+ import std.algorithm.iteration : sum;
+ switch (current)
+ {
+ 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();
+ g.charsetToIr(unicode.Nd);
+ break;
+ case 'D':
+ next();
+ g.charsetToIr(unicode.Nd.inverted);
+ break;
+ case 'b': next(); g.put(Bytecode(IR.Wordboundary, 0)); break;
+ case 'B': next(); g.put(Bytecode(IR.Notwordboundary, 0)); break;
+ case 's':
+ next();
+ g.charsetToIr(unicode.White_Space);
+ break;
+ case 'S':
+ next();
+ g.charsetToIr(unicode.White_Space.inverted);
+ break;
+ case 'w':
+ next();
+ g.charsetToIr(wordCharacter);
+ break;
+ case 'W':
+ next();
+ g.charsetToIr(wordCharacter.inverted);
+ break;
+ case 'p': case 'P':
+ auto CodepointSet = parseUnicodePropertySpec(current == 'P');
+ g.charsetToIr(CodepointSet);
+ break;
+ case 'x':
+ immutable code = parseUniHex(pat, 2);
+ next();
+ g.put(Bytecode(IR.Char,code));
+ break;
+ case 'u': case 'U':
+ immutable code = parseUniHex(pat, current == 'u' ? 4 : 8);
+ next();
+ g.put(Bytecode(IR.Char, code));
+ break;
+ case 'c': //control codes
+ Bytecode code = Bytecode(IR.Char, parseControlCode());
+ next();
+ g.put(code);
+ break;
+ case '0':
+ next();
+ g.put(Bytecode(IR.Char, 0));//NUL character
+ break;
+ case '1': .. case '9':
+ 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))
+ {
+ nref = nref * 10 + current - '0';
+ }
+ if (nref >= maxBackref)
+ nref /= 10;
+ enforce(!g.isOpenGroup(nref), "Backref to open group");
+ uint localLimit = maxBackref - g.groupStack.top;
+ if (nref >= localLimit)
+ {
+ g.put(Bytecode(IR.Backref, nref-localLimit));
+ g.ir[$-1].setLocalRef();
+ }
+ else
+ g.put(Bytecode(IR.Backref, nref));
+ g.markBackref(nref);
+ break;
+ default:
+ // Lookahead to check if it's a \T
+ // where T is sub-pattern terminator in multi-pattern scheme
+ if (current == '\\' && !pat.empty)
+ {
+ if (pat.front >= privateUseStart && current <= privateUseEnd)
+ enforce(false, "invalid escape sequence");
+ }
+ if (current >= privateUseStart && current <= privateUseEnd)
+ {
+ g.endPattern(current - privateUseStart + 1);
+ break;
+ }
+ auto op = Bytecode(IR.Char, current);
+ next();
+ g.put(op);
+ }
+ }
+
+ //parse and return a CodepointSet for \p{...Property...} and \P{...Property..},
+ //\ - assumed to be processed, p - is current
+ CodepointSet parseUnicodePropertySpec(bool negated)
+ {
+ enum MAX_PROPERTY = 128;
+ char[MAX_PROPERTY] result;
+ uint k = 0;
+ 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);
+ 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;
+ }
+ auto s = getUnicodeSet(result[0 .. k], negated,
+ cast(bool)(re_flags & RegexOption.casefold));
+ enforce(!s.empty, "unrecognized unicode property spec");
+ next();
+ return s;
+ }
+
+ //
+ @trusted void error(string msg)
+ {
+ import std.array : appender;
+ import std.format : formattedWrite;
+ auto app = appender!string();
+ formattedWrite(app, "%s\nPattern with error: `%s` <--HERE-- `%s`",
+ msg, origin[0..$-pat.length], pat);
+ throw new RegexException(app.data);
+ }
+
+ alias Char = BasicElementOf!R;
+
+ @property program()
+ {
+ return makeRegex(this);
+ }
+}
+
+/+
+ Postproces the IR, then optimize.
++/
+@trusted void postprocess(Char)(ref Regex!Char zis)
+{//@@@BUG@@@ write is @system
+ with(zis)
+ {
+ struct FixedStack(T)
+ {
+ T[] arr;
+ uint _top;
+ //this(T[] storage){ arr = storage; _top = -1; }
+ @property ref T top(){ assert(!empty); return arr[_top]; }
+ void push(T x){ arr[++_top] = x; }
+ T pop() { assert(!empty); return arr[_top--]; }
+ @property bool empty(){ return _top == -1; }
+ }
+ 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)
+ {
+ 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)
+ {
+ case IR.RepeatStart, IR.RepeatQStart:
+ uint repEnd = cast(uint)(i + ir[i].data + IRL!(IR.RepeatStart));
+ assert(ir[repEnd].code == ir[i].paired.code);
+ immutable max = ir[repEnd + 4].raw;
+ ir[repEnd+2].raw = counterRange.top;
+ ir[repEnd+3].raw *= counterRange.top;
+ ir[repEnd+4].raw *= counterRange.top;
+ ulong cntRange = cast(ulong)(max)*counterRange.top;
+ cumRange += cntRange;
+ enforce(cumRange < maxCumulativeRepetitionLength,
+ "repetition length limit is exceeded");
+ counterRange.push(cast(uint) cntRange + counterRange.top);
+ threadCount += counterRange.top;
+ break;
+ case IR.RepeatEnd, IR.RepeatQEnd:
+ threadCount += counterRange.top;
+ counterRange.pop();
+ break;
+ case IR.GroupStart:
+ if (isBackref(ir[i].data))
+ ir[i].setBackrefence();
+ threadCount += counterRange.top;
+ break;
+ case IR.GroupEnd:
+ if (isBackref(ir[i].data))
+ ir[i].setBackrefence();
+ threadCount += counterRange.top;
+ break;
+ default:
+ threadCount += counterRange.top;
+ }
+ }
+ checkIfOneShot();
+ 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; i<ir.length; i+= ir[i].length)
+ {
+ if (ir[i].isStart || ir[i].code == Option)
+ fixups.push(i);
+ else if (ir[i].code == OrEnd)
+ {
+ // Alternatives need more care
+ auto j = fixups.pop(); // last Option
+ ir[j].data = i - j - ir[j].length;
+ j = fixups.pop(); // OrStart
+ ir[j].data = i - j - ir[j].length;
+ ir[i].data = ir[j].data;
+
+ // fixup all GotoEndOrs
+ j = j + IRL!(OrStart);
+ assert(ir[j].code == Option);
+ for (;;)
+ {
+ auto next = j + ir[j].data + IRL!(Option);
+ if (ir[next].code == IR.OrEnd)
+ break;
+ ir[next - IRL!(GotoEndOr)].data = i - next;
+ j = next;
+ }
+ }
+ else if (ir[i].code == GotoEndOr)
+ {
+ auto j = fixups.pop(); // Option
+ ir[j].data = i - j + IRL!(GotoEndOr)- IRL!(Option); // to the next option
+ }
+ else if (ir[i].isEnd)
+ {
+ auto j = fixups.pop();
+ ir[i].data = i - j - ir[j].length;
+ ir[j].data = ir[i].data;
+ }
+ }
+ assert(fixups.empty);
+}
+
+void optimize(Char)(ref Regex!Char zis)
+{
+ import std.array : insertInPlace;
+ CodepointSet nextSet(uint idx)
+ {
+ CodepointSet set;
+ with(zis) with(IR)
+ Outer:
+ for (uint i = idx; i < ir.length; i += ir[i].length)
+ {
+ switch (ir[i].code)
+ {
+ case Char:
+ set.add(ir[i].data, ir[i].data+1);
+ goto default;
+ //TODO: OrChar
+ case Trie, CodepointSet:
+ set = zis.charsets[ir[i].data];
+ goto default;
+ case GroupStart,GroupEnd:
+ break;
+ default:
+ break Outer;
+ }
+ }
+ return set;
+ }
+
+ with(zis) with(IR) for (uint i = 0; i < ir.length; i += ir[i].length)
+ {
+ if (ir[i].code == InfiniteEnd)
+ {
+ auto set = nextSet(i+IRL!(InfiniteEnd));
+ if (!set.empty && set.length < 10_000)
+ {
+ ir[i] = Bytecode(InfiniteBloomEnd, ir[i].data);
+ ir[i - ir[i].data - IRL!(InfiniteStart)] =
+ Bytecode(InfiniteBloomStart, ir[i].data);
+ ir.insertInPlace(i+IRL!(InfiniteEnd),
+ Bytecode.fromRaw(cast(uint) zis.filters.length));
+ zis.filters ~= BitTable(set);
+ fixupBytecode(ir);
+ }
+ }
+ }
+}
+
+//IR code validator - proper nesting, illegal instructions, etc.
+@trusted void validateRe(Char)(ref Regex!Char zis)
+{//@@@BUG@@@ text is @system
+ import std.conv : text;
+ with(zis)
+ {
+ for (uint pc = 0; pc < ir.length; pc += ir[pc].length)
+ {
+ if (ir[pc].isStart || ir[pc].isEnd)
+ {
+ immutable dest = ir[pc].indexOfPair(pc);
+ assert(dest < ir.length, text("Wrong length in opcode at pc=",
+ pc, " ", dest, " vs ", ir.length));
+ assert(ir[dest].paired == ir[pc],
+ text("Wrong pairing of opcodes at pc=", pc, "and pc=", dest));
+ }
+ else if (ir[pc].isAtom)
+ {
+
+ }
+ else
+ assert(0, text("Unknown type of instruction at pc=", pc));
+ }
+ }
+}
diff --git a/libphobos/src/std/regex/internal/tests.d b/libphobos/src/std/regex/internal/tests.d
new file mode 100644
index 0000000..1c4f295
--- /dev/null
+++ b/libphobos/src/std/regex/internal/tests.d
@@ -0,0 +1,1120 @@
+/*
+ Regualar expressions package test suite.
+*/
+module std.regex.internal.tests;
+
+package(std.regex):
+
+import std.conv, std.exception, std.meta, std.range,
+ std.typecons, std.regex;
+
+import std.regex.internal.parser : Escapables; // characters that need escaping
+
+alias Sequence(int B, int E) = staticIota!(B, E);
+
+@safe unittest
+{//sanity checks
+ regex("(a|b)*");
+ regex(`(?:([0-9A-F]+)\.\.([0-9A-F]+)|([0-9A-F]+))\s*;\s*(.*)\s*#`);
+ regex("abc|edf|ighrg");
+ auto r1 = regex("abc");
+ auto r2 = regex("(gylba)");
+ assert(match("abcdef", r1).hit == "abc");
+ assert(!match("wida",r2));
+ assert(bmatch("abcdef", r1).hit == "abc");
+ assert(!bmatch("wida", r2));
+ assert(match("abc", "abc".dup));
+ assert(bmatch("abc", "abc".dup));
+ Regex!char rc;
+ assert(rc.empty);
+ rc = regex("test");
+ assert(!rc.empty);
+}
+
+/* The test vectors in this file are altered from Henry Spencer's regexp
+ test code. His copyright notice is:
+
+ Copyright (c) 1986 by University of Toronto.
+ Written by Henry Spencer. Not derived from licensed software.
+
+ Permission is granted to anyone to use this software for any
+ purpose on any computer system, and to redistribute it freely,
+ subject to the following restrictions:
+
+ 1. The author is not responsible for the consequences of use of
+ this software, no matter how awful, even if they arise
+ from defects in it.
+
+ 2. The origin of this software must not be misrepresented, either
+ by explicit claim or by omission.
+
+ 3. Altered versions must be plainly marked as such, and must not
+ be misrepresented as being the original software.
+
+
+ */
+
+@safe unittest
+{
+ struct TestVectors
+ {
+ string pattern;
+ string input;
+ string result;
+ string format;
+ string replace;
+ string flags;
+ }
+
+ static immutable TestVectors[] tv = [
+ TestVectors( "a\\b", "a", "y", "$&", "a" ),
+ TestVectors( "(a)b\\1", "abaab","y", "$&", "aba" ),
+ TestVectors( "()b\\1", "aaab", "y", "$&", "b" ),
+ TestVectors( "abc", "abc", "y", "$&", "abc" ),
+ TestVectors( "abc", "xbc", "n", "-", "-" ),
+ TestVectors( "abc", "axc", "n", "-", "-" ),
+ TestVectors( "abc", "abx", "n", "-", "-" ),
+ TestVectors( "abc", "xabcy","y", "$&", "abc" ),
+ TestVectors( "abc", "ababc","y", "$&", "abc" ),
+ TestVectors( "ab*c", "abc", "y", "$&", "abc" ),
+ TestVectors( "ab*bc", "abc", "y", "$&", "abc" ),
+ TestVectors( "ab*bc", "abbc", "y", "$&", "abbc" ),
+ TestVectors( "ab*bc", "abbbbc","y", "$&", "abbbbc" ),
+ TestVectors( "ab+bc", "abbc", "y", "$&", "abbc" ),
+ TestVectors( "ab+bc", "abc", "n", "-", "-" ),
+ TestVectors( "ab+bc", "abq", "n", "-", "-" ),
+ TestVectors( "ab+bc", "abbbbc","y", "$&", "abbbbc" ),
+ TestVectors( "ab?bc", "abbc", "y", "$&", "abbc" ),
+ TestVectors( "ab?bc", "abc", "y", "$&", "abc" ),
+ TestVectors( "ab?bc", "abbbbc","n", "-", "-" ),
+ TestVectors( "ab?c", "abc", "y", "$&", "abc" ),
+ TestVectors( "^abc$", "abc", "y", "$&", "abc" ),
+ TestVectors( "^abc$", "abcc", "n", "-", "-" ),
+ TestVectors( "^abc", "abcc", "y", "$&", "abc" ),
+ TestVectors( "^abc$", "aabc", "n", "-", "-" ),
+ TestVectors( "abc$", "aabc", "y", "$&", "abc" ),
+ TestVectors( "^", "abc", "y", "$&", "" ),
+ TestVectors( "$", "abc", "y", "$&", "" ),
+ TestVectors( "a.c", "abc", "y", "$&", "abc" ),
+ TestVectors( "a.c", "axc", "y", "$&", "axc" ),
+ TestVectors( "a.*c", "axyzc","y", "$&", "axyzc" ),
+ TestVectors( "a.*c", "axyzd","n", "-", "-" ),
+ TestVectors( "a[bc]d", "abc", "n", "-", "-" ),
+ TestVectors( "a[bc]d", "abd", "y", "$&", "abd" ),
+ TestVectors( "a[b-d]e", "abd", "n", "-", "-" ),
+ TestVectors( "a[b-d]e", "ace", "y", "$&", "ace" ),
+ TestVectors( "a[b-d]", "aac", "y", "$&", "ac" ),
+ TestVectors( "a[-b]", "a-", "y", "$&", "a-" ),
+ TestVectors( "a[b-]", "a-", "y", "$&", "a-" ),
+ TestVectors( "a[b-a]", "-", "c", "-", "-" ),
+ TestVectors( "a[]b", "-", "c", "-", "-" ),
+ TestVectors( "a[", "-", "c", "-", "-" ),
+ TestVectors( "a]", "a]", "y", "$&", "a]" ),
+ TestVectors( "a[\\]]b", "a]b", "y", "$&", "a]b" ),
+ TestVectors( "a[^bc]d", "aed", "y", "$&", "aed" ),
+ TestVectors( "a[^bc]d", "abd", "n", "-", "-" ),
+ TestVectors( "a[^-b]c", "adc", "y", "$&", "adc" ),
+ TestVectors( "a[^-b]c", "a-c", "n", "-", "-" ),
+ TestVectors( "a[^\\]b]c", "adc", "y", "$&", "adc" ),
+ TestVectors( "ab|cd", "abc", "y", "$&", "ab" ),
+ TestVectors( "ab|cd", "abcd", "y", "$&", "ab" ),
+ TestVectors( "()ef", "def", "y", "$&-$1", "ef-" ),
+ TestVectors( "()*", "-", "y", "-", "-" ),
+ TestVectors( "*a", "-", "c", "-", "-" ),
+ TestVectors( "^*", "-", "y", "-", "-" ),
+ TestVectors( "$*", "-", "y", "-", "-" ),
+ TestVectors( "(*)b", "-", "c", "-", "-" ),
+ TestVectors( "$b", "b", "n", "-", "-" ),
+ TestVectors( "a\\", "-", "c", "-", "-" ),
+ TestVectors( "a\\(b", "a(b", "y", "$&-$1", "a(b-" ),
+ TestVectors( "a\\(*b", "ab", "y", "$&", "ab" ),
+ TestVectors( "a\\(*b", "a((b", "y", "$&", "a((b" ),
+ TestVectors( "a\\\\b", "a\\b", "y", "$&", "a\\b" ),
+ TestVectors( "abc)", "-", "c", "-", "-" ),
+ TestVectors( "(abc", "-", "c", "-", "-" ),
+ TestVectors( "((a))", "abc", "y", "$&-$1-$2", "a-a-a" ),
+ TestVectors( "(a)b(c)", "abc", "y", "$&-$1-$2", "abc-a-c" ),
+ TestVectors( "a+b+c", "aabbabc","y", "$&", "abc" ),
+ TestVectors( "a**", "-", "c", "-", "-" ),
+ TestVectors( "a*?a", "aa", "y", "$&", "a" ),
+ TestVectors( "(a*)*", "aaa", "y", "-", "-" ),
+ TestVectors( "(a*)+", "aaa", "y", "-", "-" ),
+ TestVectors( "(a|)*", "-", "y", "-", "-" ),
+ TestVectors( "(a*|b)*", "aabb", "y", "-", "-" ),
+ TestVectors( "(a|b)*", "ab", "y", "$&-$1", "ab-b" ),
+ TestVectors( "(a+|b)*", "ab", "y", "$&-$1", "ab-b" ),
+ TestVectors( "(a+|b)+", "ab", "y", "$&-$1", "ab-b" ),
+ TestVectors( "(a+|b)?", "ab", "y", "$&-$1", "a-a" ),
+ TestVectors( "[^ab]*", "cde", "y", "$&", "cde" ),
+ TestVectors( "(^)*", "-", "y", "-", "-" ),
+ TestVectors( "(ab|)*", "-", "y", "-", "-" ),
+ TestVectors( ")(", "-", "c", "-", "-" ),
+ TestVectors( "", "abc", "y", "$&", "" ),
+ TestVectors( "abc", "", "n", "-", "-" ),
+ TestVectors( "a*", "", "y", "$&", "" ),
+ TestVectors( "([abc])*d", "abbbcd", "y", "$&-$1", "abbbcd-c" ),
+ TestVectors( "([abc])*bcd", "abcd", "y", "$&-$1", "abcd-a" ),
+ TestVectors( "a|b|c|d|e", "e", "y", "$&", "e" ),
+ TestVectors( "(a|b|c|d|e)f", "ef", "y", "$&-$1", "ef-e" ),
+ TestVectors( "((a*|b))*", "aabb", "y", "-", "-" ),
+ TestVectors( "abcd*efg", "abcdefg", "y", "$&", "abcdefg" ),
+ TestVectors( "ab*", "xabyabbbz", "y", "$&", "ab" ),
+ TestVectors( "ab*", "xayabbbz", "y", "$&", "a" ),
+ TestVectors( "(ab|cd)e", "abcde", "y", "$&-$1", "cde-cd" ),
+ TestVectors( "[abhgefdc]ij", "hij", "y", "$&", "hij" ),
+ TestVectors( "^(ab|cd)e", "abcde", "n", "x$1y", "xy" ),
+ TestVectors( "(abc|)ef", "abcdef", "y", "$&-$1", "ef-" ),
+ TestVectors( "(a|b)c*d", "abcd", "y", "$&-$1", "bcd-b" ),
+ TestVectors( "(ab|ab*)bc", "abc", "y", "$&-$1", "abc-a" ),
+ TestVectors( "a([bc]*)c*", "abc", "y", "$&-$1", "abc-bc" ),
+ TestVectors( "a([bc]*)(c*d)", "abcd", "y", "$&-$1-$2", "abcd-bc-d" ),
+ TestVectors( "a([bc]+)(c*d)", "abcd", "y", "$&-$1-$2", "abcd-bc-d" ),
+ TestVectors( "a([bc]*)(c+d)", "abcd", "y", "$&-$1-$2", "abcd-b-cd" ),
+ TestVectors( "a[bcd]*dcdcde", "adcdcde", "y", "$&", "adcdcde" ),
+ TestVectors( "a[bcd]+dcdcde", "adcdcde", "n", "-", "-" ),
+ TestVectors( "(ab|a)b*c", "abc", "y", "$&-$1", "abc-ab" ),
+ TestVectors( "((a)(b)c)(d)", "abcd", "y", "$1-$2-$3-$4", "abc-a-b-d" ),
+ TestVectors( "[a-zA-Z_][a-zA-Z0-9_]*", "alpha", "y", "$&", "alpha" ),
+ TestVectors( "^a(bc+|b[eh])g|.h$", "abh", "y", "$&-$1", "bh-" ),
+ TestVectors( "(bc+d$|ef*g.|h?i(j|k))", "effgz", "y", "$&-$1-$2", "effgz-effgz-" ),
+ TestVectors( "(bc+d$|ef*g.|h?i(j|k))", "ij", "y", "$&-$1-$2", "ij-ij-j" ),
+ TestVectors( "(bc+d$|ef*g.|h?i(j|k))", "effg", "n", "-", "-" ),
+ TestVectors( "(bc+d$|ef*g.|h?i(j|k))", "bcdd", "n", "-", "-" ),
+ TestVectors( "(bc+d$|ef*g.|h?i(j|k))", "reffgz", "y", "$&-$1-$2", "effgz-effgz-" ),
+ TestVectors( "(((((((((a)))))))))", "a", "y", "$&", "a" ),
+ TestVectors( "multiple words of text", "uh-uh", "n", "-", "-" ),
+ TestVectors( "multiple words", "multiple words, yeah", "y", "$&", "multiple words" ),
+ TestVectors( "(.*)c(.*)", "abcde", "y", "$&-$1-$2", "abcde-ab-de" ),
+ TestVectors( "\\((.*), (.*)\\)", "(a, b)", "y", "($2, $1)", "(b, a)" ),
+ TestVectors( "abcd", "abcd", "y", "$&-&-$$$&", "abcd-&-$abcd" ),
+ TestVectors( "a(bc)d", "abcd", "y", "$1-$$1-$$$1", "bc-$1-$bc" ),
+ TestVectors( "[k]", "ab", "n", "-", "-" ),
+ TestVectors( "[ -~]*", "abc", "y", "$&", "abc" ),
+ TestVectors( "[ -~ -~]*", "abc", "y", "$&", "abc" ),
+ TestVectors( "[ -~ -~ -~]*", "abc", "y", "$&", "abc" ),
+ TestVectors( "[ -~ -~ -~ -~]*", "abc", "y", "$&", "abc" ),
+ TestVectors( "[ -~ -~ -~ -~ -~]*", "abc", "y", "$&", "abc" ),
+ TestVectors( "[ -~ -~ -~ -~ -~ -~]*", "abc", "y", "$&", "abc" ),
+ TestVectors( "[ -~ -~ -~ -~ -~ -~ -~]*", "abc", "y", "$&", "abc" ),
+ TestVectors( "a{2}", "candy", "n", "", "" ),
+ TestVectors( "a{2}", "caandy", "y", "$&", "aa" ),
+ TestVectors( "a{2}", "caaandy", "y", "$&", "aa" ),
+ TestVectors( "a{2,}", "candy", "n", "", "" ),
+ TestVectors( "a{2,}", "caandy", "y", "$&", "aa" ),
+ TestVectors( "a{2,}", "caaaaaandy", "y", "$&", "aaaaaa" ),
+ TestVectors( "a{1,3}", "cndy", "n", "", "" ),
+ TestVectors( "a{1,3}", "candy", "y", "$&", "a" ),
+ TestVectors( "a{1,3}", "caandy", "y", "$&", "aa" ),
+ TestVectors( "a{1,3}", "caaaaaandy", "y", "$&", "aaa" ),
+ TestVectors( "e?le?", "angel", "y", "$&", "el" ),
+ TestVectors( "e?le?", "angle", "y", "$&", "le" ),
+ TestVectors( "\\bn\\w", "noonday", "y", "$&", "no" ),
+ TestVectors( "\\wy\\b", "possibly yesterday", "y", "$&", "ly" ),
+ TestVectors( "\\w\\Bn", "noonday", "y", "$&", "on" ),
+ TestVectors( "y\\B\\w", "possibly yesterday", "y", "$&", "ye" ),
+ TestVectors( "\\cJ", "abc\ndef", "y", "$&", "\n" ),
+ TestVectors( "\\d", "B2 is", "y", "$&", "2" ),
+ TestVectors( "\\D", "B2 is", "y", "$&", "B" ),
+ TestVectors( "\\s\\w*", "foo bar", "y", "$&", " bar" ),
+ TestVectors( "\\S\\w*", "foo bar", "y", "$&", "foo" ),
+ TestVectors( "abc", "ababc", "y", "$&", "abc" ),
+ TestVectors( "apple(,)\\sorange\\1", "apple, orange, cherry, peach", "y", "$&", "apple, orange," ),
+ TestVectors( "(\\w+)\\s(\\w+)", "John Smith", "y", "$2, $1", "Smith, John" ),
+ TestVectors( "\\n\\f\\r\\t\\v", "abc\n\f\r\t\vdef", "y", "$&", "\n\f\r\t\v" ),
+ TestVectors( ".*c", "abcde", "y", "$&", "abc" ),
+ TestVectors( "^\\w+((;|=)\\w+)+$", "some=host=tld", "y", "$&-$1-$2", "some=host=tld-=tld-=" ),
+ TestVectors( "^\\w+((\\.|-)\\w+)+$", "some.host.tld", "y", "$&-$1-$2", "some.host.tld-.tld-." ),
+ TestVectors( "q(a|b)*q", "xxqababqyy", "y", "$&-$1", "qababq-b" ),
+ TestVectors( "^(a)(b){0,1}(c*)", "abcc", "y", "$1 $2 $3", "a b cc" ),
+ TestVectors( "^(a)((b){0,1})(c*)", "abcc", "y", "$1 $2 $3", "a b b" ),
+ TestVectors( "^(a)(b)?(c*)", "abcc", "y", "$1 $2 $3", "a b cc" ),
+ TestVectors( "^(a)((b)?)(c*)", "abcc", "y", "$1 $2 $3", "a b b" ),
+ TestVectors( "^(a)(b){0,1}(c*)", "acc", "y", "$1 $2 $3", "a cc" ),
+ TestVectors( "^(a)((b){0,1})(c*)", "acc", "y", "$1 $2 $3", "a " ),
+ TestVectors( "^(a)(b)?(c*)", "acc", "y", "$1 $2 $3", "a cc" ),
+ TestVectors( "^(a)((b)?)(c*)", "acc", "y", "$1 $2 $3", "a " ),
+ TestVectors( "(?:ab){3}", "_abababc","y", "$&-$1", "ababab-" ),
+ TestVectors( "(?:a(?:x)?)+", "aaxaxx", "y", "$&-$1-$2", "aaxax--" ),
+ TestVectors( `\W\w\W`, "aa b!ca", "y", "$&", " b!"),
+//more repetitions:
+ TestVectors( "(?:a{2,4}b{1,3}){1,2}", "aaabaaaabbb", "y", "$&", "aaabaaaabbb" ),
+ TestVectors( "(?:a{2,4}b{1,3}){1,2}?", "aaabaaaabbb", "y", "$&", "aaab" ),
+//groups:
+ TestVectors( "(abc)|(edf)|(xyz)", "xyz", "y", "$1-$2-$3","--xyz"),
+ TestVectors( "(?P<q>\\d+)/(?P<d>\\d+)", "2/3", "y", "${d}/${q}", "3/2"),
+//set operations:
+ TestVectors( "[a-z--d-f]", " dfa", "y", "$&", "a"),
+ TestVectors( "[abc[pq--acq]]{2}", "bqpaca", "y", "$&", "pa"),
+ TestVectors( "[a-z9&&abc0-9]{3}", "z90a0abc", "y", "$&", "abc"),
+ TestVectors( "[0-9a-f~~0-5a-z]{2}", "g0a58x", "y", "$&", "8x"),
+ TestVectors( "[abc[pq]xyz[rs]]{4}", "cqxr", "y", "$&", "cqxr"),
+ TestVectors( "[abcdf--[ab&&[bcd]][acd]]", "abcdefgh", "y", "$&", "f"),
+ TestVectors( "[a-c||d-f]+", "abcdef", "y", "$&", "abcdef"),
+ TestVectors( "[a-f--a-c]+", "abcdef", "y", "$&", "def"),
+ TestVectors( "[a-c&&b-f]+", "abcdef", "y", "$&", "bc"),
+ TestVectors( "[a-c~~b-f]+", "abcdef", "y", "$&", "a"),
+//unicode blocks & properties:
+ TestVectors( `\P{Inlatin1suppl ement}`, "\u00c2!", "y", "$&", "!"),
+ TestVectors( `\p{InLatin-1 Supplement}\p{in-mathematical-operators}\P{Inlatin1suppl ement}`,
+ "\u00c2\u2200\u00c3\u2203.", "y", "$&", "\u00c3\u2203."),
+ TestVectors( `[-+*/\p{in-mathematical-operators}]{2}`, "a+\u2212", "y", "$&", "+\u2212"),
+ TestVectors( `\p{Ll}+`, "XabcD", "y", "$&", "abc"),
+ TestVectors( `\p{Lu}+`, "абвГДЕ", "y", "$&", "ГДЕ"),
+ TestVectors( `^\p{Currency Symbol}\p{Sc}`, "$₤", "y", "$&", "$₤"),
+ TestVectors( `\p{Common}\p{Thai}`, "!ฆ", "y", "$&", "!ฆ"),
+ TestVectors( `[\d\s]*\D`, "12 \t3\U00001680\u0F20_2", "y", "$&", "12 \t3\U00001680\u0F20_"),
+ TestVectors( `[c-wф]фф`, "ффф", "y", "$&", "ффф"),
+//case insensitive:
+ TestVectors( `^abcdEf$`, "AbCdEF", "y", "$&", "AbCdEF", "i"),
+ TestVectors( `Русский язык`, "рУсскИй ЯзЫк", "y", "$&", "рУсскИй ЯзЫк", "i"),
+ TestVectors( `ⒶⒷⓒ` , "ⓐⓑⒸ", "y", "$&", "ⓐⓑⒸ", "i"),
+ TestVectors( "\U00010400{2}", "\U00010428\U00010400 ", "y", "$&", "\U00010428\U00010400", "i"),
+ TestVectors( `[adzУ-Я]{4}`, "DzюЯ", "y", "$&", "DzюЯ", "i"),
+ TestVectors( `\p{L}\p{Lu}{10}`, "абвгдеЖЗИКЛ", "y", "$&", "абвгдеЖЗИКЛ", "i"),
+ TestVectors( `(?:Dåb){3}`, "DåbDÅBdÅb", "y", "$&", "DåbDÅBdÅb", "i"),
+//escapes:
+ TestVectors( `\u0041\u005a\U00000065\u0001`, "AZe\u0001", "y", "$&", "AZe\u0001"),
+ TestVectors( `\u`, "", "c", "-", "-"),
+ TestVectors( `\U`, "", "c", "-", "-"),
+ TestVectors( `\u003`, "", "c", "-", "-"),
+ TestVectors( `[\x00-\x7f]{4}`, "\x00\x09ab", "y", "$&", "\x00\x09ab"),
+ TestVectors( `[\cJ\cK\cA-\cD]{3}\cQ`, "\x01\x0B\x0A\x11", "y", "$&", "\x01\x0B\x0A\x11"),
+ TestVectors( `\r\n\v\t\f\\`, "\r\n\v\t\f\\", "y", "$&", "\r\n\v\t\f\\"),
+ TestVectors( `[\u0003\u0001]{2}`, "\u0001\u0003", "y", "$&", "\u0001\u0003"),
+ TestVectors( `^[\u0020-\u0080\u0001\n-\r]{8}`, "abc\u0001\v\f\r\n", "y", "$&", "abc\u0001\v\f\r\n"),
+ TestVectors( `\w+\S\w+`, "ab7!44c", "y", "$&", "ab7!44c"),
+ TestVectors( `\b\w+\b`, " abde4 ", "y", "$&", "abde4"),
+ TestVectors( `\b\w+\b`, " abde4", "y", "$&", "abde4"),
+ TestVectors( `\b\w+\b`, "abde4 ", "y", "$&", "abde4"),
+ TestVectors( `\pL\pS`, "a\u02DA", "y", "$&", "a\u02DA"),
+ TestVectors( `\pX`, "", "c", "-", "-"),
+// ^, $, \b, \B, multiline :
+ TestVectors( `\r.*?$`, "abc\r\nxy", "y", "$&", "\r\nxy", "sm"),
+ TestVectors( `^a$^b$`, "a\r\nb\n", "n", "$&", "-", "m"),
+ TestVectors( `^a$\r\n^b$`,"a\r\nb\n", "y", "$&", "a\r\nb", "m"),
+ TestVectors( `^$`, "\r\n", "y", "$&", "", "m"),
+ TestVectors( `^a$\nx$`, "a\nx\u2028","y", "$&", "a\nx", "m"),
+ TestVectors( `^a$\nx$`, "a\nx\u2029","y", "$&", "a\nx", "m"),
+ TestVectors( `^a$\nx$`, "a\nx\u0085","y", "$&", "a\nx","m"),
+ TestVectors( `^x$`, "\u2028x", "y", "$&", "x", "m"),
+ TestVectors( `^x$`, "\u2029x", "y", "$&", "x", "m"),
+ TestVectors( `^x$`, "\u0085x", "y", "$&", "x", "m"),
+ TestVectors( `\b^.`, "ab", "y", "$&", "a"),
+ TestVectors( `\B^.`, "ab", "n", "-", "-"),
+ TestVectors( `^ab\Bc\B`, "\r\nabcd", "y", "$&", "abc", "m"),
+ TestVectors( `^.*$`, "12345678", "y", "$&", "12345678"),
+
+// luckily obtained regression on incremental matching in backtracker
+ TestVectors( `^(?:(?:([0-9A-F]+)\.\.([0-9A-F]+)|([0-9A-F]+))\s*;\s*([^ ]*)\s*#|# (?:\w|_)+=((?:\w|_)+))`,
+ "0020 ; White_Space # ", "y", "$1-$2-$3", "--0020"),
+//lookahead
+ TestVectors( "(foo.)(?=(bar))", "foobar foodbar", "y", "$&-$1-$2", "food-food-bar" ),
+ TestVectors( `\b(\d+)[a-z](?=\1)`, "123a123", "y", "$&-$1", "123a-123" ),
+ TestVectors( `\$(?!\d{3})\w+`, "$123 $abc", "y", "$&", "$abc"),
+ TestVectors( `(abc)(?=(ed(f))\3)`, "abcedff", "y", "-", "-"),
+ TestVectors( `\b[A-Za-z0-9.]+(?=(@(?!gmail)))`, "a@gmail,x@com", "y", "$&-$1", "x-@"),
+ TestVectors( `x()(abc)(?=(d)(e)(f)\2)`, "xabcdefabc", "y", "$&", "xabc"),
+ TestVectors( `x()(abc)(?=(d)(e)(f)()\3\4\5)`, "xabcdefdef", "y", "$&", "xabc"),
+//lookback
+ TestVectors( `(?<=(ab))\d`, "12ba3ab4", "y", "$&-$1", "4-ab", "i"),
+ TestVectors( `\w(?<!\d)\w`, "123ab24", "y", "$&", "ab"),
+ TestVectors( `(?<=Dåb)x\w`, "DåbDÅBxdÅb", "y", "$&", "xd", "i"),
+ TestVectors( `(?<=(ab*c))x`, "abbbbcxac", "y", "$&-$1", "x-abbbbc"),
+ TestVectors( `(?<=(ab*?c))x`, "abbbbcxac", "y", "$&-$1", "x-abbbbc"),
+ TestVectors( `(?<=(a.*?c))x`, "ababbcxac", "y", "$&-$1", "x-abbc"),
+ TestVectors( `(?<=(a{2,4}b{1,3}))x`, "yyaaaabx", "y", "$&-$1", "x-aaaab"),
+ TestVectors( `(?<=((?:a{2,4}b{1,3}){1,2}))x`, "aabbbaaaabx", "y", "$&-$1", "x-aabbbaaaab"),
+ TestVectors( `(?<=((?:a{2,4}b{1,3}){1,2}?))x`, "aabbbaaaabx", "y", "$&-$1", "x-aaaab"),
+ TestVectors( `(?<=(abc|def|aef))x`, "abcx", "y", "$&-$1", "x-abc"),
+ TestVectors( `(?<=(abc|def|aef))x`, "aefx", "y", "$&-$1", "x-aef"),
+ TestVectors( `(?<=(abc|dabc))(x)`, "dabcx", "y", "$&-$1-$2", "x-abc-x"),
+ TestVectors( `(?<=(|abc))x`, "dabcx", "y", "$&-$1", "x-"),
+ TestVectors( `(?<=((ab|da)*))x`, "abdaabx", "y", "$&-$2-$1", "x-ab-abdaab"),
+ TestVectors( `a(?<=(ba(?<=(aba)(?<=aaba))))`, "aabaa", "y", "$&-$1-$2", "a-ba-aba"),
+ TestVectors( `.(?<!b).`, "bax", "y", "$&", "ax"),
+ TestVectors( `(?<=b(?<!ab)).`, "abbx", "y", "$&", "x"),
+ TestVectors( `(?<=\.|[!?]+)X`, "Hey?!X", "y", "$&", "X"),
+ TestVectors( `(?<=\.|[!?]+)a{3}`, ".Nope.aaaX", "y", "$&", "aaa"),
+//mixed lookaround
+ TestVectors( `a(?<=a(?=b))b`, "ab", "y", "$&", "ab"),
+ TestVectors( `a(?<=a(?!b))c`, "ac", "y", "$&", "ac"),
+ TestVectors( `a(?i)bc`, "aBc", "y", "$&", "aBc"),
+ TestVectors( `a(?i)bc`, "Abc", "n", "$&", "-"),
+ TestVectors( `(?i)a(?-i)bc`, "aBcAbc", "y", "$&", "Abc"),
+ TestVectors( `(?s).(?-s).`, "\n\n\na", "y", "$&", "\na"),
+ TestVectors( `(?m)^a(?-m)$`, "\na", "y", "$&", "a")
+ ];
+ string produceExpected(M,String)(auto ref M m, String fmt)
+ {
+ auto app = appender!(String)();
+ replaceFmt(fmt, m.captures, app, true);
+ return app.data;
+ }
+ void run_tests(alias matchFn)()
+ {
+ int i;
+ foreach (Char; AliasSeq!( char, wchar, dchar))
+ (){ // avoid slow optimizations for large functions @@@BUG@@@ 2396
+ alias String = immutable(Char)[];
+ String produceExpected(M,Range)(auto ref M m, Range fmt)
+ {
+ auto app = appender!(String)();
+ replaceFmt(fmt, m.captures, app, true);
+ return app.data;
+ }
+ Regex!(Char) r;
+ foreach (a, tvd; tv)
+ {
+ uint c = tvd.result[0];
+ debug(std_regex_test) writeln(" Test #", a, " pattern: ", tvd.pattern, " with Char = ", Char.stringof);
+ try
+ {
+ i = 1;
+ r = regex(to!(String)(tvd.pattern), tvd.flags);
+ }
+ catch (RegexException e)
+ {
+ i = 0;
+ debug(std_regex_test) writeln(e.msg);
+ }
+
+ assert((c == 'c') ? !i : i, "failed to compile pattern "~tvd.pattern);
+
+ if (c != 'c')
+ {
+ auto m = matchFn(to!(String)(tvd.input), r);
+ i = !m.empty;
+ assert(
+ (c == 'y') ? i : !i,
+ text(matchFn.stringof ~": failed to match pattern #", a ,": ", tvd.pattern)
+ );
+ if (c == 'y')
+ {
+ auto result = produceExpected(m, to!(String)(tvd.format));
+ assert(result == to!String(tvd.replace),
+ text(matchFn.stringof ~": mismatch pattern #", a, ": ", tvd.pattern," expected: ",
+ tvd.replace, " vs ", result));
+ }
+ }
+ }
+ }();
+ debug(std_regex_test) writeln("!!! FReD bulk test done "~matchFn.stringof~" !!!");
+ }
+
+
+ void ct_tests()
+ {
+ import std.algorithm.comparison : equal;
+ version (std_regex_ct1)
+ {
+ pragma(msg, "Testing 1st part of ctRegex");
+ alias Tests = Sequence!(0, 155);
+ }
+ else version (std_regex_ct2)
+ {
+ pragma(msg, "Testing 2nd part of ctRegex");
+ alias Tests = Sequence!(155, 174);
+ }
+ //FIXME: #174-178 contains CTFE parser bug
+ else version (std_regex_ct3)
+ {
+ pragma(msg, "Testing 3rd part of ctRegex");
+ alias Tests = Sequence!(178, 220);
+ }
+ else version (std_regex_ct4)
+ {
+ pragma(msg, "Testing 4th part of ctRegex");
+ alias Tests = Sequence!(220, tv.length);
+ }
+ else
+ alias Tests = AliasSeq!(Sequence!(0, 30), Sequence!(235, tv.length-5));
+ foreach (a, v; Tests)
+ (){ // avoid slow optimizations for large functions @@@BUG@@@ 2396
+ enum tvd = tv[v];
+ static if (tvd.result == "c")
+ {
+ static assert(!__traits(compiles, (){
+ enum r = regex(tvd.pattern, tvd.flags);
+ }), "errornously compiles regex pattern: " ~ tvd.pattern);
+ }
+ else
+ {
+ //BUG: tv[v] is fine but tvd is not known at compile time?!
+ auto r = ctRegex!(tv[v].pattern, tv[v].flags);
+ auto nr = regex(tvd.pattern, tvd.flags);
+ assert(equal(r.ir, nr.ir),
+ text("!C-T regex! failed to compile pattern #", a ,": ", tvd.pattern));
+ auto m = match(tvd.input, r);
+ auto c = tvd.result[0];
+ bool ok = (c == 'y') ^ m.empty;
+ assert(ok, text("ctRegex: failed to match pattern #",
+ a ,": ", tvd.pattern));
+ if (c == 'y')
+ {
+ import std.stdio;
+ auto result = produceExpected(m, tvd.format);
+ if (result != tvd.replace)
+ writeln("ctRegex mismatch pattern #", a, ": ", tvd.pattern," expected: ",
+ tvd.replace, " vs ", result);
+ }
+ }
+ }();
+ debug(std_regex_test) writeln("!!! FReD C-T test done !!!");
+ }
+
+ ct_tests();
+ run_tests!bmatch(); //backtracker
+ run_tests!match(); //thompson VM
+}
+
+@safe unittest
+{
+ auto cr = ctRegex!("abc");
+ assert(bmatch("abc",cr).hit == "abc");
+ auto cr2 = ctRegex!("ab*c");
+ assert(bmatch("abbbbc",cr2).hit == "abbbbc");
+}
+@safe unittest
+{
+ auto cr3 = ctRegex!("^abc$");
+ assert(bmatch("abc",cr3).hit == "abc");
+ auto cr4 = ctRegex!(`\b(a\B[a-z]b)\b`);
+ assert(array(match("azb",cr4).captures) == ["azb", "azb"]);
+}
+
+@safe unittest
+{
+ auto cr5 = ctRegex!("(?:a{2,4}b{1,3}){1,2}");
+ assert(bmatch("aaabaaaabbb", cr5).hit == "aaabaaaabbb");
+ auto cr6 = ctRegex!("(?:a{2,4}b{1,3}){1,2}?"w);
+ assert(bmatch("aaabaaaabbb"w, cr6).hit == "aaab"w);
+}
+
+@safe unittest
+{
+ auto cr7 = ctRegex!(`\r.*?$`,"sm");
+ assert(bmatch("abc\r\nxy", cr7).hit == "\r\nxy");
+ auto greed = ctRegex!("<packet.*?/packet>");
+ assert(bmatch("<packet>text</packet><packet>text</packet>", greed).hit
+ == "<packet>text</packet>");
+}
+
+@safe unittest
+{
+ import std.algorithm.comparison : equal;
+ auto cr8 = ctRegex!("^(a)(b)?(c*)");
+ auto m8 = bmatch("abcc",cr8);
+ assert(m8);
+ assert(m8.captures[1] == "a");
+ assert(m8.captures[2] == "b");
+ assert(m8.captures[3] == "cc");
+ auto cr9 = ctRegex!("q(a|b)*q");
+ auto m9 = match("xxqababqyy",cr9);
+ assert(m9);
+ assert(equal(bmatch("xxqababqyy",cr9).captures, ["qababq", "b"]));
+}
+
+@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));
+ //CTFE parser BUG is triggered by group
+ //in the middle of alternation (at least not first and not last)
+ enum testCT = regex(`abc|(edf)|xyz`);
+ auto testRT = regex(`abc|(edf)|xyz`);
+ assert(equal(testCT.ir,testRT.ir));
+}
+
+@safe unittest
+{
+ import std.algorithm.comparison : equal;
+ import std.algorithm.iteration : map;
+ enum cx = ctRegex!"(A|B|C)";
+ auto mx = match("B",cx);
+ assert(mx);
+ assert(equal(mx.captures, [ "B", "B"]));
+ enum cx2 = ctRegex!"(A|B)*";
+ assert(match("BAAA",cx2));
+
+ enum cx3 = ctRegex!("a{3,4}","i");
+ auto mx3 = match("AaA",cx3);
+ assert(mx3);
+ assert(mx3.captures[0] == "AaA");
+ enum cx4 = ctRegex!(`^a{3,4}?[a-zA-Z0-9~]{1,2}`,"i");
+ auto mx4 = match("aaaabc", cx4);
+ assert(mx4);
+ assert(mx4.captures[0] == "aaaab");
+ auto cr8 = ctRegex!("(a)(b)?(c*)");
+ auto m8 = bmatch("abcc",cr8);
+ assert(m8);
+ assert(m8.captures[1] == "a");
+ assert(m8.captures[2] == "b");
+ assert(m8.captures[3] == "cc");
+ auto cr9 = ctRegex!(".*$", "gm");
+ auto m9 = match("First\rSecond", cr9);
+ assert(m9);
+ assert(equal(map!"a.hit"(m9), ["First", "", "Second"]));
+}
+
+@safe unittest
+{
+ import std.algorithm.comparison : equal;
+ import std.algorithm.iteration : map;
+//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))
+ test ~= m.hit;
+ assert(equal(test, [ "a", "quick", "brown", "fox", "jumps", "over", "a", "lazy", "dog"]));
+ auto free_reg = regex(`
+
+ abc
+ \s+
+ "
+ (
+ [^"]+
+ | \\ "
+ )+
+ "
+ z
+ `, "x");
+ auto m = match(`abc "quoted string with \" inside"z`,free_reg);
+ assert(m);
+ string mails = " hey@you.com no@spam.net ";
+ auto rm = regex(`@(?<=\S+@)\S+`,"g");
+ assert(equal(map!"a[0]"(matchFn(mails, rm)), ["@you.com", "@spam.net"]));
+ auto m2 = matchFn("First line\nSecond line",regex(".*$","gm"));
+ assert(equal(map!"a[0]"(m2), ["First line", "", "Second line"]));
+ auto m2a = matchFn("First line\nSecond line",regex(".+$","gm"));
+ assert(equal(map!"a[0]"(m2a), ["First line", "Second line"]));
+ auto m2b = matchFn("First line\nSecond line",regex(".+?$","gm"));
+ assert(equal(map!"a[0]"(m2b), ["First line", "Second line"]));
+ debug(std_regex_test) writeln("!!! FReD FLAGS test done "~matchFn.stringof~" !!!");
+ }
+ test_body!bmatch();
+ test_body!match();
+}
+
+//tests for accumulated std.regex issues and other regressions
+@safe unittest
+{
+ import std.algorithm.comparison : equal;
+ import std.algorithm.iteration : map;
+ void test_body(alias matchFn)()
+ {
+ //issue 5857
+ //matching goes out of control if ... in (...){x} has .*/.+
+ auto c = matchFn("axxxzayyyyyzd",regex("(a.*z){2}d")).captures;
+ assert(c[0] == "axxxzayyyyyzd");
+ assert(c[1] == "ayyyyyz");
+ auto c2 = matchFn("axxxayyyyyd",regex("(a.*){2}d")).captures;
+ assert(c2[0] == "axxxayyyyyd");
+ assert(c2[1] == "ayyyyy");
+ //issue 2108
+ //greedy vs non-greedy
+ auto nogreed = regex("<packet.*?/packet>");
+ assert(matchFn("<packet>text</packet><packet>text</packet>", nogreed).hit
+ == "<packet>text</packet>");
+ auto greed = regex("<packet.*/packet>");
+ assert(matchFn("<packet>text</packet><packet>text</packet>", greed).hit
+ == "<packet>text</packet><packet>text</packet>");
+ //issue 4574
+ //empty successful match still advances the input
+ string[] pres, posts, hits;
+ foreach (m; matchFn("abcabc", regex("","g")))
+ {
+ pres ~= m.pre;
+ posts ~= m.post;
+ assert(m.hit.empty);
+
+ }
+ auto heads = [
+ "abcabc",
+ "abcab",
+ "abca",
+ "abc",
+ "ab",
+ "a",
+ ""
+ ];
+ auto tails = [
+ "abcabc",
+ "bcabc",
+ "cabc",
+ "abc",
+ "bc",
+ "c",
+ ""
+ ];
+ assert(pres == array(retro(heads)));
+ assert(posts == tails);
+ //issue 6076
+ //regression on .*
+ auto re = regex("c.*|d");
+ auto m = matchFn("mm", re);
+ assert(!m);
+ debug(std_regex_test) writeln("!!! FReD REGRESSION test done "~matchFn.stringof~" !!!");
+ auto rprealloc = regex(`((.){5}.{1,10}){5}`);
+ auto arr = array(repeat('0',100));
+ auto m2 = matchFn(arr, rprealloc);
+ assert(m2);
+ assert(collectException(
+ regex(r"^(import|file|binary|config)\s+([^\(]+)\(?([^\)]*)\)?\s*$")
+ ) is null);
+ foreach (ch; [Escapables])
+ {
+ assert(match(to!string(ch),regex(`[\`~ch~`]`)));
+ assert(!match(to!string(ch),regex(`[^\`~ch~`]`)));
+ assert(match(to!string(ch),regex(`[\`~ch~`-\`~ch~`]`)));
+ }
+ //bugzilla 7718
+ string strcmd = "./myApp.rb -os OSX -path \"/GIT/Ruby Apps/sec\" -conf 'notimer'";
+ auto reStrCmd = regex (`(".*")|('.*')`, "g");
+ assert(equal(map!"a[0]"(matchFn(strcmd, reStrCmd)),
+ [`"/GIT/Ruby Apps/sec"`, `'notimer'`]));
+ }
+ test_body!bmatch();
+ test_body!match();
+}
+
+// tests for replace
+@safe unittest
+{
+ void test(alias matchFn)()
+ {
+ import std.uni : toUpper;
+
+ foreach (i, v; AliasSeq!(string, wstring, dstring))
+ {
+ auto baz(Cap)(Cap m)
+ if (is(Cap == Captures!(Cap.String)))
+ {
+ return toUpper(m.hit);
+ }
+ alias String = v;
+ assert(std.regex.replace!(matchFn)(to!String("ark rapacity"), regex(to!String("r")), to!String("c"))
+ == to!String("ack rapacity"));
+ assert(std.regex.replace!(matchFn)(to!String("ark rapacity"), regex(to!String("r"), "g"), to!String("c"))
+ == 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 :"));
+ 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.");
+ }
+ debug(std_regex_test) writeln("!!! Replace test done "~matchFn.stringof~" !!!");
+ }
+ test!(bmatch)();
+ test!(match)();
+}
+
+// tests for splitter
+@safe unittest
+{
+ import std.algorithm.comparison : equal;
+ auto s1 = ", abc, de, fg, hi, ";
+ auto sp1 = splitter(s1, regex(", *"));
+ auto w1 = ["", "abc", "de", "fg", "hi", ""];
+ assert(equal(sp1, w1));
+
+ auto s2 = ", abc, de, fg, hi";
+ auto sp2 = splitter(s2, regex(", *"));
+ auto w2 = ["", "abc", "de", "fg", "hi"];
+
+ uint cnt;
+ foreach (e; sp2)
+ {
+ assert(w2[cnt++] == e);
+ }
+ assert(equal(sp2, w2));
+}
+
+@safe unittest
+{
+ char[] s1 = ", abc, de, fg, hi, ".dup;
+ auto sp2 = splitter(s1, regex(", *"));
+}
+
+@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[]));
+}
+
+@safe unittest
+{ // bugzilla 7141
+ string pattern = `[a\--b]`;
+ assert(match("-", pattern));
+ assert(match("b", pattern));
+ string pattern2 = `[&-z]`;
+ assert(match("b", pattern2));
+}
+@safe unittest
+{//bugzilla 7111
+ assert(match("", regex("^")));
+}
+@safe unittest
+{//bugzilla 7300
+ assert(!match("a"d, "aa"d));
+}
+
+// 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\?");
+}
+@safe unittest
+{// bugzilla 7679
+ 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");
+ assert(equal(std.regex.splitter(str, re), [to!S("a"), to!S("b")]));
+ assert(split(str, re) == [to!S("a"), to!S("b")]);
+ }();
+}
+@safe unittest
+{//bugzilla 8203
+ string data = "
+ NAME = XPAW01_STA:STATION
+ NAME = XPAW01_STA
+ ";
+ auto uniFileOld = data;
+ auto r = regex(
+ r"^NAME = (?P<comp>[a-zA-Z0-9_]+):*(?P<blk>[a-zA-Z0-9_]*)","gm");
+ auto uniCapturesNew = match(uniFileOld, r);
+ for (int i = 0; i < 20; i++)
+ foreach (matchNew; uniCapturesNew) {}
+ //a second issue with same symptoms
+ auto r2 = regex(`([а-яА-Я\-_]+\s*)+(?<=[\s\.,\^])`);
+ match("аллея Театральная", r2);
+}
+@safe unittest
+{// bugzilla 8637 purity of enforce
+ auto m = match("hello world", regex("world"));
+ enforce(m);
+}
+
+// bugzilla 8725
+@safe unittest
+{
+ static italic = regex( r"\*
+ (?!\s+)
+ (.*?)
+ (?!\s+)
+ \*", "gx" );
+ string input = "this * is* interesting, *very* interesting";
+ assert(replace(input, italic, "<i>$1</i>") ==
+ "this * is* interesting, <i>very</i> interesting");
+}
+
+// bugzilla 8349
+@safe unittest
+{
+ enum peakRegexStr = r"\>(wgEncode.*Tfbs.*\.(?:narrow)|(?:broad)Peak.gz)</a>";
+ enum peakRegex = ctRegex!(peakRegexStr);
+ //note that the regex pattern itself is probably bogus
+ assert(match(r"\>wgEncode-blah-Tfbs.narrow</a>", peakRegex));
+}
+
+// bugzilla 9211
+@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"]));
+ auto rx_2 = regex(r"^([0-9])*(\d)");
+ auto m2 = match("1234", rx_2);
+ assert(equal(m2.front, ["1234", "3", "4"]));
+}
+
+// bugzilla 9280
+@safe unittest
+{
+ string tomatch = "a!b@c";
+ static r = regex(r"^(?P<nick>.*?)!(?P<ident>.*?)@(?P<host>.*?)$");
+ auto nm = match(tomatch, r);
+ assert(nm);
+ auto c = nm.captures;
+ assert(c[1] == "a");
+ assert(c["nick"] == "a");
+}
+
+
+// bugzilla 9579
+@safe unittest
+{
+ char[] input = ['a', 'b', 'c'];
+ string format = "($1)";
+ // used to give a compile error:
+ auto re = regex(`(a)`, "g");
+ auto r = replace(input, re, format);
+ assert(r == "(a)bc");
+}
+
+// bugzilla 9634
+@safe unittest
+{
+ auto re = ctRegex!"(?:a+)";
+ assert(match("aaaa", re).hit == "aaaa");
+}
+
+//bugzilla 10798
+@safe unittest
+{
+ auto cr = ctRegex!("[abcd--c]*");
+ auto m = "abc".match(cr);
+ assert(m);
+ assert(m.hit == "ab");
+}
+
+// bugzilla 10913
+@system unittest
+{
+ @system static string foo(const(char)[] s)
+ {
+ return s.dup;
+ }
+ @safe static string bar(const(char)[] s)
+ {
+ return s.dup;
+ }
+ () @system {
+ replace!((a) => foo(a.hit))("blah", regex(`a`));
+ }();
+ () @safe {
+ replace!((a) => bar(a.hit))("blah", regex(`a`));
+ }();
+}
+
+// bugzilla 11262
+@safe unittest
+{
+ enum reg = ctRegex!(r",", "g");
+ auto str = "This,List";
+ str = str.replace(reg, "-");
+ assert(str == "This-List");
+}
+
+// bugzilla 11775
+@safe unittest
+{
+ assert(collectException(regex("a{1,0}")));
+}
+
+// bugzilla 11839
+@safe unittest
+{
+ import std.algorithm.comparison : equal;
+ assert(regex(`(?P<var1>\w+)`).namedCaptures.equal(["var1"]));
+ assert(collectException(regex(`(?P<1>\w+)`)));
+ assert(regex(`(?P<v1>\w+)`).namedCaptures.equal(["v1"]));
+ assert(regex(`(?P<__>\w+)`).namedCaptures.equal(["__"]));
+ assert(regex(`(?P<я>\w+)`).namedCaptures.equal(["я"]));
+}
+
+// bugzilla 12076
+@safe unittest
+{
+ auto RE = ctRegex!(r"(?<!x[a-z]+)\s([a-z]+)");
+ string s = "one two";
+ auto m = match(s, RE);
+}
+
+// bugzilla 12105
+@safe unittest
+{
+ auto r = ctRegex!`.*?(?!a)`;
+ assert("aaab".matchFirst(r).hit == "aaa");
+ auto r2 = ctRegex!`.*(?!a)`;
+ assert("aaab".matchFirst(r2).hit == "aaab");
+}
+
+//bugzilla 11784
+@safe unittest
+{
+ assert("abcdefghijklmnopqrstuvwxyz"
+ .matchFirst("[a-z&&[^aeiuo]]").hit == "b");
+}
+
+//bugzilla 12366
+@safe unittest
+{
+ auto re = ctRegex!(`^((?=(xx+?)\2+$)((?=\2+$)(?=(x+)(\4+$))\5){2})*x?$`);
+ assert("xxxxxxxx".match(re).empty);
+ assert(!"xxxx".match(re).empty);
+}
+
+// bugzilla 12582
+@safe unittest
+{
+ auto r = regex(`(?P<a>abc)`);
+ assert(collectException("abc".matchFirst(r)["b"]));
+}
+
+// bugzilla 12691
+@safe unittest
+{
+ assert(bmatch("e@", "^([a-z]|)*$").empty);
+ assert(bmatch("e@", ctRegex!`^([a-z]|)*$`).empty);
+}
+
+//bugzilla 12713
+@safe unittest
+{
+ assertThrown(regex("[[a-z]([a-z]|(([[a-z])))"));
+}
+
+//bugzilla 12747
+@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.array : appender;
+ import std.regex : replaceFirst, replaceFirstInto, regex;
+ import std.stdio : writeln;
+
+ 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(`(<a (?:(?:\w+=\"[^"]*\")?\s*)*href="\.\.?)"`);
+}
+
+@safe unittest
+{
+ auto r = regex("(?# comment)abc(?# comment2)");
+ assert("abc".matchFirst(r));
+ assertThrown(regex("(?#..."));
+}
+
+// bugzilla 17075
+@safe unittest
+{
+ enum titlePattern = `<title>(.+)</title>`;
+ static titleRegex = ctRegex!titlePattern;
+ string input = "<title>" ~ "<".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));
+}
+
+// bugzilla 17667
+@safe unittest
+{
+ import std.algorithm.searching : canFind;
+ void willThrow(T, size_t line = __LINE__)(T arg, string msg)
+ {
+ auto e = collectException(regex(arg));
+ assert(e.msg.canFind(msg), to!string(line) ~ ": " ~ e.msg);
+ }
+ willThrow([r".", r"[\(\{[\]\}\)]"], "no matching ']' found while parsing character class");
+ willThrow([r"[\", r"123"], "no matching ']' found while parsing character class");
+ willThrow([r"[a-", r"123"], "no matching ']' found while parsing character class");
+ willThrow([r"[a-\", r"123"], "invalid escape sequence");
+ willThrow([r"\", r"123"], "invalid escape sequence");
+}
+
+// bugzilla 17668
+@safe unittest
+{
+ import std.algorithm.searching;
+ auto e = collectException!RegexException(regex(q"<[^]>"));
+ assert(e.msg.canFind("no operand for '^'"));
+}
+
+// bugzilla 17673
+@safe unittest
+{
+ string str = `<">`;
+ string[] regexps = ["abc", "\"|x"];
+ auto regexp = regex(regexps);
+ auto c = matchFirst(str, regexp);
+ assert(c);
+ assert(c.whichPattern == 2);
+}
+
diff --git a/libphobos/src/std/regex/internal/thompson.d b/libphobos/src/std/regex/internal/thompson.d
new file mode 100644
index 0000000..4d7deaa
--- /dev/null
+++ b/libphobos/src/std/regex/internal/thompson.d
@@ -0,0 +1,1188 @@
+//Written in the D programming language
+/*
+ Implementation of Thompson NFA std.regex engine.
+ Key point is evaluation of all possible threads (state) at each step
+ in a breadth-first manner, thereby geting some nice properties:
+ - looking at each character only once
+ - merging of equivalent threads, that gives matching process linear time complexity
+*/
+module std.regex.internal.thompson;
+
+package(std.regex):
+
+import std.range.primitives;
+import std.regex.internal.ir;
+
+//State of VM thread
+struct Thread(DataIndex)
+{
+ Thread* next; //intrusive linked list
+ uint pc;
+ uint counter; //loop counter
+ uint uopCounter; //counts micro operations inside one macro instruction (e.g. BackRef)
+ Group!DataIndex[1] matches;
+}
+
+//head-tail singly-linked list
+struct ThreadList(DataIndex)
+{
+ Thread!DataIndex* tip = null, toe = null;
+ //add new thread to the start of list
+ void insertFront(Thread!DataIndex* t)
+ {
+ if (tip)
+ {
+ t.next = tip;
+ tip = t;
+ }
+ else
+ {
+ t.next = null;
+ tip = toe = t;
+ }
+ }
+ //add new thread to the end of list
+ void insertBack(Thread!DataIndex* t)
+ {
+ if (toe)
+ {
+ toe.next = t;
+ toe = t;
+ }
+ else
+ tip = toe = t;
+ toe.next = null;
+ }
+ //move head element out of list
+ Thread!DataIndex* fetch()
+ {
+ auto t = tip;
+ if (tip == toe)
+ tip = toe = null;
+ else
+ tip = tip.next;
+ return t;
+ }
+ //non-destructive iteration of ThreadList
+ struct ThreadRange
+ {
+ const(Thread!DataIndex)* ct;
+ this(ThreadList tlist){ ct = tlist.tip; }
+ @property bool empty(){ return ct is null; }
+ @property const(Thread!DataIndex)* front(){ return ct; }
+ @property popFront()
+ {
+ assert(ct);
+ ct = ct.next;
+ }
+ }
+ @property bool empty()
+ {
+ return tip == null;
+ }
+ ThreadRange opSlice()
+ {
+ return ThreadRange(this);
+ }
+}
+
+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, 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;
+ Group!DataIndex[] backrefed;
+ Regex!Char re; //regex program
+ Stream s;
+ dchar front;
+ 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;
+ int matched;
+ bool exhausted;
+
+ 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;
+ }
+ else
+ enum kicked = false;
+
+ static size_t getThreadSize(const ref Regex!Char re)
+ {
+ return re.ngroup
+ ? (Thread!DataIndex).sizeof + (re.ngroup-1)*(Group!DataIndex).sizeof
+ : (Thread!DataIndex).sizeof - (Group!DataIndex).sizeof;
+ }
+
+ static size_t initialMemory(const ref Regex!Char re)
+ {
+ return getThreadSize(re)*re.threadCount + re.hotspotTableSize*size_t.sizeof
+ +4*OpFunc.sizeof*re.ir.length;
+ }
+
+ //true if it's start of input
+ @property bool atStart(){ return index == 0; }
+
+ //true if it's end of input
+ @property bool atEnd(){ return index == s.lastIndex && s.atEnd; }
+
+ bool next()
+ {
+ if (!s.nextChar(front, index))
+ {
+ index = s.lastIndex;
+ return false;
+ }
+ return true;
+ }
+
+ static if (kicked)
+ {
+ bool search()
+ {
+
+ if (!s.search(re.kickstart, front, index))
+ {
+ index = s.lastIndex;
+ return false;
+ }
+ return true;
+ }
+ }
+
+ void initExternalMemory(void[] memory)
+ {
+ threadSize = getThreadSize(re);
+ prepareFreeList(re.threadCount, memory);
+ 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)
+ {
+ re = program;
+ s = stream;
+ initExternalMemory(memory);
+ genCounter = 0;
+ }
+
+ this(ref ThompsonMatcher matcher, size_t lo, size_t hi, Stream stream)
+ {
+ s = stream;
+ re = matcher.re;
+ 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;
+ }
+
+ this(ref BackMatcher matcher, size_t lo, size_t hi, Stream stream)
+ {
+ 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()(size_t lo, size_t hi, size_t counter)
+ {
+ alias BackLooper = typeof(s.loopBack(index));
+ auto m = ThompsonMatcher!(Char, BackLooper)(this, lo, hi, s.loopBack(index));
+ m.genCounter = counter;
+ m.next();
+ return m;
+ }
+
+ auto dupTo(void[] memory)
+ {
+ typeof(this) tmp = this;//bitblit
+ tmp.initExternalMemory(memory);
+ tmp.genCounter = 0;
+ return tmp;
+ }
+
+ int match(Group!DataIndex[] matches)
+ {
+ debug(std_regex_matcher)
+ writeln("------------------------------------------");
+ if (exhausted)
+ {
+ return false;
+ }
+ if (re.flags & RegexInfo.oneShot)
+ {
+ next();
+ exhausted = true;
+ return matchOneShot(matches);
+ }
+ static if (kicked)
+ if (!re.kickstart.empty)
+ return matchImpl!(true)(matches);
+ return matchImpl!(false)(matches);
+ }
+
+ //match the input and fill matches
+ int matchImpl(bool withSearch)(Group!DataIndex[] matches)
+ {
+ if (!matched && clist.empty)
+ {
+ static if (withSearch)
+ search();
+ else
+ next();
+ }
+ else//char in question is fetched in prev call to match
+ {
+ matched = 0;
+ }
+ State state;
+ state.matches = matches;
+
+ if (!atEnd)//if no char
+ for (;;)
+ {
+ genCounter++;
+ debug(std_regex_matcher)
+ {
+ writefln("Threaded matching threads at %s", s[index .. s.lastIndex]);
+ foreach (t; clist[])
+ {
+ assert(t);
+ writef("pc=%s ",t.pc);
+ write(t.matches);
+ writeln();
+ }
+ }
+ for (state.t = clist.fetch(); state.t; state.t = clist.fetch())
+ {
+ eval!true(&state);
+ }
+ //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)
+ {
+ static if (withSearch)
+ {
+ if (!search())
+ break;
+ }
+ else
+ {
+ if (!next())
+ break;
+ }
+ }
+ else if (!next())
+ {
+ if (!atEnd) return false;
+ exhausted = true;
+ break;
+ }
+ }
+
+ genCounter++; //increment also on each end
+ debug(std_regex_matcher) writefln("Threaded matching threads at end");
+ //try out all zero-width posibilities
+ for (state.t = clist.fetch(); state.t; state.t = clist.fetch())
+ {
+ eval!false(&state);
+ }
+ if (!matched)
+ {
+ state.t = createStart(index);
+ eval!false(&state);//new thread starting at end of input
+ }
+ 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
+ next();//and reload front character
+ //--- 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)
+ next();
+ }
+ return matched;
+ }
+
+ /+
+ handle succesful threads
+ +/
+ void finish(const(Thread!DataIndex)* t, Group!DataIndex[] matches, int code)
+ {
+ 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)
+ writefln(": %s..%s", matches[0].begin, matches[0].end);
+ foreach (v; matches)
+ writefln("%d .. %d", v.begin, v.end);
+ }
+ 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)(State* state)
+ {
+ debug(std_regex_matcher) writeln("---- Evaluating thread");
+ 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
+ int matchOneShot(Group!DataIndex[] matches, uint startPc = 0)
+ {
+ debug(std_regex_matcher)
+ {
+ writefln("---------------single shot match ----------------- ");
+ }
+ alias evalFn = eval;
+ assert(clist == (ThreadList!DataIndex).init || startPc == RestartPc); // incorrect after a partial match
+ assert(nlist == (ThreadList!DataIndex).init || startPc == RestartPc);
+ State state;
+ state.matches = matches;
+ if (!atEnd)//if no char
+ {
+ debug(std_regex_matcher)
+ {
+ writefln("-- Threaded matching threads at %s", s[index .. s.lastIndex]);
+ }
+ if (startPc != RestartPc)
+ {
+ state.t = createStart(index, startPc);
+ genCounter++;
+ evalFn!true(&state);
+ }
+ for (;;)
+ {
+ debug(std_regex_matcher) writeln("\n-- Started iteration of main cycle");
+ genCounter++;
+ debug(std_regex_matcher)
+ {
+ foreach (t; clist[])
+ {
+ assert(t);
+ }
+ }
+ for (state.t = clist.fetch(); state.t; state.t = clist.fetch())
+ {
+ evalFn!true(&state);
+ }
+ 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())
+ 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 (state.t = clist.fetch(); state.t; state.t = clist.fetch())
+ {
+ evalFn!false(&state);
+ }
+ if (!matched)
+ {
+ state.t = createStart(index, startPc);
+ evalFn!false(&state);
+ }
+ return matched;
+ }
+
+ //get a dirty recycled Thread
+ Thread!DataIndex* allocate()
+ {
+ assert(freelist, "not enough preallocated memory");
+ Thread!DataIndex* t = freelist;
+ freelist = freelist.next;
+ return t;
+ }
+
+ //link memory into a free list of Threads
+ void prepareFreeList(size_t size, ref void[] memory)
+ {
+ void[] mem = memory[0 .. threadSize*size];
+ memory = memory[threadSize * size .. $];
+ freelist = cast(Thread!DataIndex*)&mem[0];
+ size_t i;
+ 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;
+ }
+
+ //dispose a thread
+ void recycle(Thread!DataIndex* t)
+ {
+ t.next = freelist;
+ freelist = t;
+ }
+
+ //dispose list of threads
+ void recycle(ref ThreadList!DataIndex list)
+ {
+ if (list.tip)
+ {
+ // just put this head-tail list in front of freelist
+ list.toe.next = freelist;
+ freelist = list.tip;
+ 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.pc = pc;
+ t.counter = counter;
+ t.uopCounter = 0;
+ return t;
+ }
+
+ //creates a start thread
+ Thread!DataIndex* createStart(DataIndex index, uint pc = 0)
+ {
+ auto t = allocate();
+ t.matches.ptr[0 .. re.ngroup] = (Group!DataIndex).init;
+ t.matches[0].begin = index;
+ t.pc = pc;
+ t.counter = 0;
+ t.uopCounter = 0;
+ return t;
+ }
+}
diff --git a/libphobos/src/std/regex/package.d b/libphobos/src/std/regex/package.d
new file mode 100644
index 0000000..bfc7d7f
--- /dev/null
+++ b/libphobos/src/std/regex/package.d
@@ -0,0 +1,1735 @@
+/++
+ $(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 $(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;
+ import std.stdio;
+ void main()
+ {
+ // 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)
+ {
+ // matchAll() returns a range that can be iterated
+ // to get all subsequent matches.
+ foreach (c; matchAll(line, r))
+ writeln(c.hit);
+ }
+ }
+ ...
+
+ // Create a static regex at compile-time, which contains fast native code.
+ auto ctr = ctRegex!(`^.*/([^/]+)/?$`);
+
+ // It works just like a normal regex:
+ auto c2 = matchFirst("foo/bar", ctr); // First match found here, if any
+ 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 `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.
+ As such it's ill-suited for tasks involving higher level invariants
+ like matching an integer number $(U bounded) in an [a,b] interval.
+ Checks of this sort of are better addressed by additional post-processing.
+
+ The basic syntax shouldn't surprise experienced users of regular expressions.
+ For an introduction to $(D std.regex) see a
+ $(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 $(HTTP www.regular-expressions.info, reference with tutorial)
+ can easily be found.
+
+ This library uses a remarkably common ECMAScript syntax flavor
+ with the following extensions:
+ $(UL
+ $(LI Named subexpressions, with Python syntax. )
+ $(LI Unicode properties such as Scripts, Blocks and common binary properties e.g Alphabetic, White_Space, Hex_Digit etc.)
+ $(LI Arbitrary length and complexity lookbehind, including lookahead in lookbehind and vise-versa.)
+ )
+
+ $(REG_START Pattern syntax )
+ $(I std.regex operates on codepoint level,
+ 'character' in this table denotes a single Unicode codepoint.)
+ $(REG_TABLE
+ $(REG_TITLE Pattern element, Semantics )
+ $(REG_TITLE Atoms, Match single characters )
+ $(REG_ROW any character except [{|*+?()^$, Matches the character itself. )
+ $(REG_ROW ., In single line mode matches any character.
+ Otherwise it matches any character except '\n' and '\r'. )
+ $(REG_ROW [class], Matches a single character
+ that belongs to this character class. )
+ $(REG_ROW [^class], Matches a single character that
+ does $(U not) belong to this character class.)
+ $(REG_ROW \cC, Matches the control character corresponding to letter C)
+ $(REG_ROW \xXX, Matches a character with hexadecimal value of XX. )
+ $(REG_ROW \uXXXX, Matches a character with hexadecimal value of XXXX. )
+ $(REG_ROW \U00YYYYYY, Matches a character with hexadecimal value of YYYYYY. )
+ $(REG_ROW \f, Matches a formfeed character. )
+ $(REG_ROW \n, Matches a linefeed character. )
+ $(REG_ROW \r, Matches a carriage return character. )
+ $(REG_ROW \t, Matches a tab character. )
+ $(REG_ROW \v, Matches a vertical tab character. )
+ $(REG_ROW \d, Matches any Unicode digit. )
+ $(REG_ROW \D, Matches any character except Unicode digits. )
+ $(REG_ROW \w, Matches any word character (note: this includes numbers).)
+ $(REG_ROW \W, Matches any non-word character.)
+ $(REG_ROW \s, Matches whitespace, same as \p{White_Space}.)
+ $(REG_ROW \S, Matches any character except those recognized as $(I \s ). )
+ $(REG_ROW \\, Matches \ character. )
+ $(REG_ROW \c where c is one of [|*+?(), Matches the character c itself. )
+ $(REG_ROW \p{PropertyName}, Matches a character that belongs
+ to the Unicode PropertyName set.
+ Single letter abbreviations can be used without surrounding {,}. )
+ $(REG_ROW \P{PropertyName}, Matches a character that does not belong
+ to the Unicode PropertyName set.
+ Single letter abbreviations can be used without surrounding {,}. )
+ $(REG_ROW \p{InBasicLatin}, Matches any character that is part of
+ the BasicLatin Unicode $(U block).)
+ $(REG_ROW \P{InBasicLatin}, Matches any character except ones in
+ the BasicLatin Unicode $(U block).)
+ $(REG_ROW \p{Cyrillic}, Matches any character that is part of
+ Cyrillic $(U script).)
+ $(REG_ROW \P{Cyrillic}, Matches any character except ones in
+ Cyrillic $(U script).)
+ $(REG_TITLE Quantifiers, Specify repetition of other elements)
+ $(REG_ROW *, Matches previous character/subexpression 0 or more times.
+ Greedy version - tries as many times as possible.)
+ $(REG_ROW *?, Matches previous character/subexpression 0 or more times.
+ Lazy version - stops as early as possible.)
+ $(REG_ROW +, Matches previous character/subexpression 1 or more times.
+ Greedy version - tries as many times as possible.)
+ $(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$(COMMA)}, Matches previous character/subexpression n times or more.
+ Greedy version - tries as many times as possible. )
+ $(REG_ROW {n$(COMMA)}?, Matches previous character/subexpression n times or more.
+ Lazy version - stops as early as possible.)
+ $(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$(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 $(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$(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.
+ )
+ $(REG_TITLE Assertions, Match position rather than character )
+ $(REG_ROW ^, Matches at the begining of input or line (in multiline mode).)
+ $(REG_ROW $, Matches at the end of input or line (in multiline mode). )
+ $(REG_ROW \b, Matches at word boundary. )
+ $(REG_ROW \B, Matches when $(U not) at word boundary. )
+ $(REG_ROW (?=regex), Zero-width lookahead assertion.
+ Matches at a point where the subexpression
+ regex could be matched starting from the current position.
+ )
+ $(REG_ROW (?!regex), Zero-width negative lookahead assertion.
+ Matches at a point where the subexpression
+ regex could $(U not) be matched starting from the current position.
+ )
+ $(REG_ROW (?<=regex), Zero-width lookbehind assertion. Matches at a point
+ where the subexpression regex could be matched ending
+ at the current position (matching goes backwards).
+ )
+ $(REG_ROW (?<!regex), Zero-width negative lookbehind assertion.
+ Matches at a point where the subexpression regex could $(U not)
+ be matched ending at the current position (matching goes backwards).
+ )
+ )
+
+ $(REG_START Character classes )
+ $(REG_TABLE
+ $(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]$(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.) )
+ )
+
+ $(REG_START Regex flags )
+ $(REG_TABLE
+ $(REG_TITLE Flag, Semantics )
+ $(REG_ROW g, Global regex, repeat over the whole input. )
+ $(REG_ROW i, Case insensitive matching. )
+ $(REG_ROW m, Multi-line mode, match ^, $ on start and end line separators
+ as well as start and end of input.)
+ $(REG_ROW s, Single-line mode, makes . match '\n' and '\r' as well. )
+ $(REG_ROW x, Free-form syntax, ignores whitespace in pattern,
+ useful for formatting complex regular expressions. )
+ )
+
+ $(SECTION Unicode support)
+
+ This library provides full Level 1 support* according to
+ $(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.)
+ $(LI 1.3 Character classes with set operations.)
+ $(LI 1.4 Word boundaries use the full set of "word" characters.)
+ $(LI 1.5 Using simple casefolding to match case
+ insensitively across the full range of codepoints.)
+ $(LI 1.6 Respecting line breaks as any of
+ \u000A | \u000B | \u000C | \u000D | \u0085 | \u2028 | \u2029 | \u000D\u000A.)
+ $(LI 1.7 Operating on codepoint level.)
+ )
+ *With exception of point 1.1.1, as of yet, normalization of input
+ is expected to be enforced by user.
+
+ $(SECTION Replace format string)
+
+ A set of functions in this module that do the substitution rely
+ on a simple format to guide the process. In particular the table below
+ applies to the $(D format) argument of
+ $(LREF replaceFirst) and $(LREF replaceAll).
+
+ The format string can reference parts of match using the following notation.
+ $(REG_TABLE
+ $(REG_TITLE Format specifier, Replaced by )
+ $(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 $(COMMA) where c is any character, the character c itself. )
+ $(REG_ROW \\, '\' character. )
+ $(REG_ROW $(DOLLAR)1 .. $(DOLLAR)99, submatch number 1 to 99 respectively. )
+ )
+
+ $(SECTION Slicing and zero memory allocations orientation)
+
+ All matches returned by pattern matching functionality in this library
+ are slices of the original input. The notable exception is the $(D replace)
+ family of functions that generate a new string from the input.
+
+ In cases where producing the replacement is the ultimate goal
+ $(LREF replaceFirstInto) and $(LREF replaceAllInto) could come in handy
+ as functions that avoid allocations even for replacement.
+
+ Copyright: Copyright Dmitry Olshansky, 2011-
+
+ 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/_package.d)
+
+Macros:
+ REG_ROW = $(TR $(TD $(I $1 )) $(TD $+) )
+ REG_TITLE = $(TR $(TD $(B $1)) $(TD $(B $2)) )
+ REG_TABLE = <table border="1" cellspacing="0" cellpadding="5" > $0 </table>
+ REG_START = <h3><div align="center"> $0 </div></h3>
+ SECTION = <h3><a id="$1" href="#$1" class="anchor">$0</a></h3>
+ S_LINK = <a href="#$1">$+</a>
+ +/
+module std.regex;
+
+import std.range.primitives, std.traits;
+import std.regex.internal.ir;
+import std.regex.internal.thompson; //TODO: get rid of this dependency
+import std.typecons; // : Flag, Yes, No;
+
+/++
+ $(D Regex) object holds regular expression pattern in compiled form.
+
+ Instances of this object are constructed via calls to $(D regex).
+ This is an intended form for caching and storage of frequently
+ used regular expressions.
+
+ Example:
+
+ Test if this object doesn't contain any compiled pattern.
+ ---
+ Regex!char r;
+ assert(r.empty);
+ r = regex(""); // Note: "" is a valid regex pattern.
+ assert(!r.empty);
+ ---
+
+ Getting a range of all the named captures in the regex.
+ ----
+ import std.range;
+ import std.algorithm;
+
+ auto re = regex(`(?P<name>\w+) = (?P<var>\d+)`);
+ auto nc = re.namedCaptures;
+ static assert(isRandomAccessRange!(typeof(nc)));
+ assert(!nc.empty);
+ assert(nc.length == 2);
+ assert(nc.equal(["name", "var"]));
+ assert(nc[0] == "name");
+ assert(nc[1..$].equal(["var"]));
+ ----
++/
+public alias Regex(Char) = std.regex.internal.ir.Regex!(Char);
+
+/++
+ A $(D StaticRegex) is $(D Regex) object that contains D code specially
+ generated at compile-time to speed up matching.
+
+ Implicitly convertible to normal $(D Regex),
+ however doing so will result in losing this additional capability.
++/
+public alias StaticRegex(Char) = std.regex.internal.ir.StaticRegex!(Char);
+
+/++
+ Compile regular expression pattern for the later execution.
+ Returns: $(D Regex) object that works on inputs having
+ the same character width as $(D pattern).
+
+ Params:
+ pattern = A single regular expression to match.
+ patterns = An array of regular expression strings.
+ The resulting `Regex` object will match any expression;
+ use $(LREF whichPattern) to know which.
+ 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[] patterns, const(char)[] flags="")
+if (isSomeString!(S))
+{
+ import std.array : appender;
+ import std.functional : memoize;
+ enum cacheSize = 8; //TODO: invent nice interface to control regex caching
+ 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]);
+ // terminator for the pattern
+ // to detect if the pattern unexpectedly ends
+ app.put("\\");
+ app.put(cast(dchar)(privateUseStart+i));
+ app.put(")");
+ // another one to return correct whichPattern
+ // for all of potential alternatives in the patterns[i]
+ app.put("\\");
+ app.put(cast(dchar)(privateUseStart+i));
+ }
+ 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))
+{
+ import std.regex.internal.parser : Parser, CodeGen;
+ auto parser = Parser!(Unqual!(typeof(pattern)), CodeGen)(pattern, flags);
+ auto r = parser.program;
+ return r;
+}
+
+
+template ctRegexImpl(alias pattern, string flags=[])
+{
+ import std.regex.internal.backtracking, std.regex.internal.parser;
+ enum r = regex(pattern, flags);
+ alias Char = BasicElementOf!(typeof(pattern));
+ enum source = ctGenRegExCode(r);
+ alias Matcher = BacktrackingMatcher!(true);
+ @trusted bool func(ref Matcher!Char matcher)
+ {
+ debug(std_regex_ctr) pragma(msg, source);
+ mixin(source);
+ }
+ enum nr = StaticRegex!Char(r, &func);
+}
+
+/++
+ Compile regular expression using CTFE
+ and generate optimized native machine code for matching it.
+
+ Returns: StaticRegex object for faster matching.
+
+ Params:
+ pattern = Regular expression
+ flags = The _attributes (g, i, m and x accepted)
++/
+public enum ctRegex(alias pattern, alias flags=[]) = ctRegexImpl!(pattern, flags).nr;
+
+enum isRegexFor(RegEx, R) = is(RegEx == Regex!(BasicElementOf!R))
+ || is(RegEx == StaticRegex!(BasicElementOf!R));
+
+
+/++
+ $(D Captures) object contains submatches captured during a call
+ to $(D match) or iteration over $(D RegexMatch) range.
+
+ First element of range is the whole match.
++/
+@trusted public struct Captures(R, DIndex = size_t)
+if (isSomeString!R)
+{//@trusted because of union inside
+ alias DataIndex = DIndex;
+ alias String = R;
+private:
+ import std.conv : text;
+ R _input;
+ 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 _refcount; // ref count or SMALL MASK + num groups
+ NamedGroup[] _names;
+
+ this()(R input, uint n, NamedGroup[] named)
+ {
+ _input = input;
+ _names = named;
+ newMatches(n);
+ _b = n;
+ _f = 0;
+ }
+
+ this(alias Engine)(ref RegexMatch!(R,Engine) rmatch)
+ {
+ _input = rmatch._input;
+ _names = rmatch._engine.re.dict;
+ immutable n = rmatch._engine.re.ngroup;
+ newMatches(n);
+ _b = n;
+ _f = 0;
+ }
+
+ @property inout(Group!DataIndex[]) matches() inout
+ {
+ return (_refcount & SMALL_MASK) ? small_matches[0 .. _refcount & 0xFF] : big_matches;
+ }
+
+ void newMatches(uint n)
+ {
+ 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 _nMatch == 0 ? _input[] : _input[0 .. matches[0].begin];
+ }
+
+ ///Slice of input immediately after the match.
+ @property R post()
+ {
+ return _nMatch == 0 ? _input[] : _input[matches[0].end .. $];
+ }
+
+ ///Slice of matched portion of input.
+ @property R hit()
+ {
+ assert(_nMatch, "attempted to get hit of an empty match");
+ return _input[matches[0].begin .. matches[0].end];
+ }
+
+ ///Range interface.
+ @property R front()
+ {
+ assert(_nMatch, "attempted to get front of an empty match");
+ return _input[matches[_f].begin .. matches[_f].end];
+ }
+
+ ///ditto
+ @property R back()
+ {
+ assert(_nMatch, "attempted to get back of an empty match");
+ return _input[matches[_b - 1].begin .. matches[_b - 1].end];
+ }
+
+ ///ditto
+ void popFront()
+ {
+ assert(!empty);
+ ++_f;
+ }
+
+ ///ditto
+ void popBack()
+ {
+ assert(!empty);
+ --_b;
+ }
+
+ ///ditto
+ @property bool empty() const { return _nMatch == 0 || _f >= _b; }
+
+ ///ditto
+ 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,
+ text("wrong match: ", matches[_f + i].begin, "..", matches[_f + i].end));
+ return _input[matches[_f + i].begin .. matches[_f + i].end];
+ }
+
+ /++
+ Explicit cast to bool.
+ Useful as a shorthand for !(x.empty) in if and assert statements.
+
+ ---
+ import std.regex;
+
+ assert(!matchFirst("nothing", "something"));
+ ---
+ +/
+
+ @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.
+
+ ---
+ import std.regex;
+ import std.range;
+
+ auto c = matchFirst("a = 42;", regex(`(?P<var>\w+)\s*=\s*(?P<value>\d+);`));
+ assert(c["var"] == "a");
+ assert(c["value"] == "42");
+ popFrontN(c, 2);
+ //named groups are unaffected by range primitives
+ assert(c["var"] =="a");
+ assert(c.front == "42");
+ ----
+ +/
+ R opIndex(String)(String i) /*const*/ //@@@BUG@@@
+ 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 _nMatch == 0 ? 0 : _b - _f; }
+
+ ///A hook for compatibility with original std.regex.
+ @property ref captures(){ return this; }
+}
+
+///
+@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
+ assert(c.hit == c[0] && c.hit == "abc"); // The whole match
+ assert(c[2] == "b");
+ assert(c.front == "abc");
+ c.popFront();
+ assert(c.front == "a");
+ assert(c.back == "c");
+ c.popBack();
+ assert(c.back == "b");
+ popFrontN(c, 2);
+ assert(c.empty);
+
+ assert(!matchFirst("nothing", "something"));
+}
+
+/++
+ A regex engine state, as returned by $(D match) family of functions.
+
+ Effectively it's a forward range of Captures!R, produced
+ by lazily searching for matches in a given input.
+
+ $(D alias Engine) specifies an engine type to use during matching,
+ and is automatically deduced in a call to $(D match)/$(D bmatch).
++/
+@trusted public struct RegexMatch(R, alias Engine = ThompsonMatcher)
+if (isSomeString!R)
+{
+private:
+ import core.stdc.stdlib : malloc, free;
+ alias Char = BasicElementOf!R;
+ alias EngineType = Engine!Char;
+ EngineType _engine;
+ R _input;
+ Captures!(R,EngineType.DataIndex) _captures;
+ void[] _memory;//is ref-counted
+
+ this(RegEx)(R input, RegEx prog)
+ {
+ import std.exception : enforce;
+ _input = input;
+ immutable size = EngineType.initialMemory(prog)+size_t.sizeof;
+ _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)))
+ _engine.nativeFn = prog.nativeFn;
+ _captures = Captures!(R,EngineType.DataIndex)(this);
+ _captures._nMatch = _engine.match(_captures.matches);
+ debug(std_regex_allocation) writefln("RefCount (ctor): %x %d", _memory.ptr, counter);
+ }
+
+ @property ref size_t counter(){ return *cast(size_t*)_memory.ptr; }
+public:
+ this(this)
+ {
+ if (_memory.ptr)
+ {
+ ++counter;
+ debug(std_regex_allocation) writefln("RefCount (postblit): %x %d",
+ _memory.ptr, *cast(size_t*)_memory.ptr);
+ }
+ }
+
+ ~this()
+ {
+ 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);
+ free(cast(void*)_memory.ptr);
+ }
+ }
+
+ ///Shorthands for front.pre, front.post, front.hit.
+ @property R pre()
+ {
+ return _captures.pre;
+ }
+
+ ///ditto
+ @property R post()
+ {
+ return _captures.post;
+ }
+
+ ///ditto
+ @property R hit()
+ {
+ return _captures.hit;
+ }
+
+ /++
+ Functionality for processing subsequent matches of global regexes via range interface:
+ ---
+ import std.regex;
+ auto m = matchAll("Hello, world!", regex(`\w+`));
+ assert(m.front.hit == "Hello");
+ m.popFront();
+ assert(m.front.hit == "world");
+ m.popFront();
+ assert(m.empty);
+ ---
+ +/
+ @property auto front()
+ {
+ return _captures;
+ }
+
+ ///ditto
+ void popFront()
+ {
+ 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), "malloc failed")[0 .. size]);
+ _engine = _engine.dupTo(_memory[size_t.sizeof .. size]);
+ counter = 1;//points to new chunk
+ }
+
+ 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() 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() inout { return _captures; }
+
+}
+
+private @trusted auto matchOnce(alias Engine, RegEx, R)(R input, RegEx re)
+{
+ 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), "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)))
+ engine.nativeFn = re.nativeFn;
+ captures._nMatch = engine.match(captures.matches);
+ return captures;
+}
+
+private auto matchMany(alias Engine, RegEx, R)(R input, RegEx re)
+{
+ re.flags |= RegexOption.global;
+ return RegexMatch!(R, Engine)(input, re);
+}
+
+@system unittest
+{
+ //sanity checks for new API
+ auto re = regex("abc");
+ assert(!"abc".matchOnce!(ThompsonMatcher)(re).empty);
+ assert("abc".matchOnce!(ThompsonMatcher)(re)[0] == "abc");
+}
+
+
+private enum isReplaceFunctor(alias fun, R) =
+ __traits(compiles, (Captures!R c) { fun(c); });
+
+// 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 (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))
+ sink.put(output(captures)); //"mutator" type of function
+ else
+ output(captures, sink); //"output" type of function
+ sink.put(captures.post);
+}
+
+// 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)
+{
+ size_t offset = 0;
+ foreach (cap; matches)
+ {
+ sink.put(cap.pre[offset .. $]);
+ // same hack, see replaceCapturesInto
+ static if (isReplaceFunctor!(output, R))
+ sink.put(output(cap)); //"mutator" type of function
+ else
+ output(cap, sink); //"output" type of function
+ offset = cap.pre.length + cap.hit.length;
+ }
+ sink.put(input[offset .. $]);
+}
+
+// a general skeleton of replaceFirst
+private R replaceFirstWith(alias output, R, RegEx)(R input, RegEx re)
+if (isSomeString!R && isRegexFor!(RegEx, R))
+{
+ import std.array : appender;
+ auto data = matchFirst(input, re);
+ if (data.empty)
+ return input;
+ auto app = appender!(R)();
+ replaceCapturesInto!output(app, input, data);
+ return app.data;
+}
+
+// ditto for replaceAll
+// 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))
+{
+ import std.array : appender;
+ auto matches = method(input, re); //inout(C)[] fails
+ if (matches.empty)
+ return input;
+ auto app = appender!(R)();
+ replaceMatchesInto!output(app, input, matches);
+ return app.data;
+}
+
+
+/++
+ Start matching $(D input) to regex pattern $(D re),
+ using Thompson NFA matching scheme.
+
+ The use of this function is $(RED discouraged) - use either of
+ $(LREF matchAll) or $(LREF matchFirst).
+
+ Delegating the kind of operation
+ to "g" flag is soon to be phased out along with the
+ ability to choose the exact matching scheme. The choice of
+ matching scheme to use depends highly on the pattern kind and
+ can done automatically on case by case basis.
+
+ Returns: a $(D RegexMatch) object holding engine state after first match.
++/
+
+public auto match(R, RegEx)(R input, RegEx re)
+if (isSomeString!R && is(RegEx == Regex!(BasicElementOf!R)))
+{
+ 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)
+{
+ 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)))
+{
+ import std.regex.internal.backtracking : BacktrackingMatcher;
+ return RegexMatch!(Unqual!(typeof(input)),BacktrackingMatcher!true)(input, re);
+}
+
+/++
+ Find the first (leftmost) slice of the $(D input) that
+ matches the pattern $(D re). This function picks the most suitable
+ regular expression engine depending on the pattern properties.
+
+ $(D re) parameter can be one of three types:
+ $(UL
+ $(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
+ compiled native machine code. )
+ )
+
+ Returns:
+ $(LREF Captures) containing the extent of a match together with all submatches
+ 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)))
+{
+ 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)
+{
+ 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 : ThompsonMatcher;
+ return matchOnce!ThompsonMatcher(input, regex(re));
+}
+
+public auto matchFirst(R, RegEx)(R input, RegEx re)
+if (isSomeString!R && is(RegEx == StaticRegex!(BasicElementOf!R)))
+{
+ import std.regex.internal.backtracking : BacktrackingMatcher;
+ return matchOnce!(BacktrackingMatcher!true)(input, re);
+}
+
+/++
+ Initiate a search for all non-overlapping matches to the pattern $(D re)
+ in the given $(D input). The result is a lazy range of matches generated
+ as they are encountered in the input going left to right.
+
+ This function picks the most suitable regular expression engine
+ depending on the pattern properties.
+
+ $(D re) parameter can be one of three types:
+ $(UL
+ $(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
+ compiled native machine code. )
+ )
+
+ Returns:
+ $(LREF RegexMatch) object that represents matcher state
+ 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)))
+{
+ 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)
+{
+ 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)))
+{
+ import std.regex.internal.backtracking : BacktrackingMatcher;
+ return matchMany!(BacktrackingMatcher!true)(input, re);
+}
+
+// another set of tests just to cover the new API
+@system unittest
+{
+ import std.algorithm.comparison : equal;
+ import std.algorithm.iteration : map;
+ import std.conv : to;
+
+ foreach (String; AliasSeq!(string, wstring, const(dchar)[]))
+ {
+ auto str1 = "blah-bleh".to!String();
+ auto pat1 = "bl[ae]h".to!String();
+ auto mf = matchFirst(str1, pat1);
+ assert(mf.equal(["blah".to!String()]));
+ auto mAll = matchAll(str1, pat1);
+ assert(mAll.equal!((a,b) => a.equal(b))
+ ([["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(), "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);
+ assert(mAll2.front.equal(mf2));
+ mAll2.popFront();
+ assert(mAll2.front.equal(["3/03/12", "3", "03", "12"].map!(to!String)()));
+ mf2.popFrontN(3);
+ assert(mf2.equal(["12".to!String()]));
+
+ auto ctPat = ctRegex!(`(?P<Quot>\d+)/(?P<Denom>\d+)`.to!String());
+ auto str = "2 + 34/56 - 6/1".to!String();
+ auto cmf = matchFirst(str, ctPat);
+ assert(cmf.equal(["34/56", "34", "56"].map!(to!String)()));
+ assert(cmf["Quot"] == "34".to!String());
+ assert(cmf["Denom"] == "56".to!String());
+
+ auto cmAll = matchAll(str, ctPat);
+ assert(cmAll.front.equal(cmf));
+ cmAll.popFront();
+ assert(cmAll.front.equal(["6/1", "6", "1"].map!(to!String)()));
+ }
+}
+
+/++
+ Start matching of $(D input) to regex pattern $(D re),
+ 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).
+
+ Delegating the kind of operation
+ to "g" flag is soon to be phased out along with the
+ ability to choose the exact matching scheme. The choice of
+ matching scheme to use depends highly on the pattern kind and
+ can done automatically on case by case basis.
+
+ Returns: a $(D RegexMatch) object holding engine
+ state after first match.
+
++/
+public auto bmatch(R, RegEx)(R input, RegEx re)
+if (isSomeString!R && is(RegEx == Regex!(BasicElementOf!R)))
+{
+ 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)
+{
+ 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)))
+{
+ 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)[]))
+{
+ import std.algorithm.searching : find;
+ import std.ascii : isDigit, isAlpha;
+ import std.conv : text, parse;
+ import std.exception : enforce;
+ enum State { Normal, Dollar }
+ auto state = State.Normal;
+ size_t offset;
+L_Replace_Loop:
+ while (!format.empty)
+ final switch (state)
+ {
+ case State.Normal:
+ for (offset = 0; offset < format.length; offset++)//no decoding
+ {
+ if (format[offset] == '$')
+ {
+ state = State.Dollar;
+ sink.put(format[0 .. offset]);
+ format = format[offset+1 .. $];//ditto
+ continue L_Replace_Loop;
+ }
+ }
+ sink.put(format[0 .. offset]);
+ format = format[offset .. $];
+ break;
+ case State.Dollar:
+ if (isDigit(format[0]))
+ {
+ uint digit = parse!uint(format);
+ enforce(ignoreBadSubs || digit < captures.length, text("invalid submatch number ", digit));
+ if (digit < captures.length)
+ sink.put(captures[digit]);
+ }
+ else if (format[0] == '{')
+ {
+ 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] == '&')
+ {
+ sink.put(captures[0]);
+ format = format[1 .. $];
+ }
+ else if (format[0] == '`')
+ {
+ sink.put(captures.pre);
+ format = format[1 .. $];
+ }
+ else if (format[0] == '\'')
+ {
+ sink.put(captures.post);
+ format = format[1 .. $];
+ }
+ else if (format[0] == '$')
+ {
+ sink.put(format[0 .. 1]);
+ format = format[1 .. $];
+ }
+ state = State.Normal;
+ break;
+ }
+ enforce(state == State.Normal, "invalid format string in regex replace");
+}
+
+/++
+ Construct a new string from $(D input) by replacing the first match with
+ a string generated from it according to the $(D format) specifier.
+
+ To replace all matches use $(LREF replaceAll).
+
+ Params:
+ input = string to search
+ re = compiled regular expression to use
+ format = _format string to generate replacements from,
+ see $(S_LINK Replace _format string, the _format string).
+
+ Returns:
+ A string of the same type with the first match (if any) replaced.
+ If no match is found returns the input string itself.
++/
+public R replaceFirst(R, C, RegEx)(R input, RegEx re, const(C)[] format)
+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
+ there is no format string instead captures are passed to
+ to a user-defined functor $(D fun) that returns a new string
+ to use as replacement.
+
+ This version replaces the first match in $(D input),
+ see $(LREF replaceAll) to replace the all of the matches.
+
+ Returns:
+ 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);
+}
+
+///
+@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");
+}
+
+/++
+ A variation on $(LREF replaceFirst) that instead of allocating a new string
+ on each call outputs the result piece-wise to the $(D sink). In particular
+ this enables efficient construction of a final output incrementally.
+
+ 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.
++/
+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))
+ {
+ replaceCapturesInto!((m, sink) => replaceFmt(format, m, sink))
+ (sink, input, matchFirst(input, re));
+ }
+
+///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))
+{
+ 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
+{
+ import std.conv;
+ 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");
+ 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");
+}
+
+/++
+ Construct a new string from $(D input) by replacing all of the
+ fragments that match a pattern $(D re) with a string generated
+ from the match according to the $(D format) specifier.
+
+ To replace only the first match use $(LREF replaceFirst).
+
+ Params:
+ input = string to search
+ re = compiled regular expression to use
+ format = _format string to generate replacements from,
+ see $(S_LINK Replace _format string, the _format string).
+
+ Returns:
+ 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.
++/
+public @trusted R replaceAll(R, C, RegEx)(R input, RegEx re, const(C)[] format)
+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
+ there is no format string instead captures are passed to
+ to a user-defined functor $(D fun) that returns a new string
+ to use as replacement.
+
+ This version replaces all of the matches found in $(D input),
+ see $(LREF replaceFirst) to replace the first match only.
+
+ Returns:
+ 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.
+
+ Params:
+ 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);
+}
+
+///
+@system unittest
+{
+ string baz(Captures!(string) m)
+ {
+ 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.");
+}
+
+/++
+ A variation on $(LREF replaceAll) that instead of allocating a new string
+ on each call outputs the result piece-wise to the $(D sink). In particular
+ this enables efficient construction of a final output incrementally.
+
+ As with $(LREF replaceAll) there are 2 overloads - one with a format string,
+ the other one with a user defined functor.
++/
+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))
+ {
+ replaceMatchesInto!((m, sink) => replaceFmt(format, m, sink))
+ (sink, input, matchAll(input, re));
+ }
+
+///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))
+{
+ replaceMatchesInto!fun(sink, input, matchAll(input, re));
+}
+
+///
+@system unittest
+{
+ // insert comma as thousands delimiter in fifty randomly produced big numbers
+ import std.array, std.conv, std.random, 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; AliasSeq!(string, wstring, dstring, char[], wchar[], dchar[]))
+ {
+ S s1 = "curt trial".to!S();
+ S s2 = "round dome".to!S();
+ S t1F = "court trial".to!S();
+ S t2F = "hound dome".to!S();
+ S t1A = "court trial".to!S();
+ S t2A = "hound home".to!S();
+ auto re1 = regex("curt".to!S());
+ auto re2 = regex("[dr]o".to!S());
+
+ assert(replaceFirst(s1, re1, "court") == t1F);
+ assert(replaceFirst(s2, re2, "ho") == t2F);
+ assert(replaceAll(s1, re1, "court") == t1A);
+ assert(replaceAll(s2, re2, "ho") == t2A);
+
+ auto rep1 = replaceFirst!(cap => cap[0][0]~"o".to!S()~cap[0][1..$])(s1, re1);
+ assert(rep1 == t1F);
+ assert(replaceFirst!(cap => "ho".to!S())(s2, re2) == t2F);
+ auto rep1A = replaceAll!(cap => cap[0][0]~"o".to!S()~cap[0][1..$])(s1, re1);
+ assert(rep1A == t1A);
+ assert(replaceAll!(cap => "ho".to!S())(s2, re2) == t2A);
+
+ auto sink = appender!S();
+ replaceFirstInto(sink, s1, re1, "court");
+ assert(sink.data == t1F);
+ replaceFirstInto(sink, s2, re2, "ho");
+ assert(sink.data == t1F~t2F);
+ replaceAllInto(sink, s1, re1, "court");
+ assert(sink.data == t1F~t2F~t1A);
+ replaceAllInto(sink, s2, re2, "ho");
+ assert(sink.data == t1F~t2F~t1A~t2A);
+ }
+}
+
+/++
+ Old API for replacement, operation depends on flags of pattern $(D re).
+ With "g" flag it performs the equivalent of $(LREF replaceAll) otherwise it
+ works the same as $(LREF replaceFirst).
+
+ The use of this function is $(RED discouraged), please use $(LREF replaceAll)
+ 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))
+{
+ 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))
+{
+ return replaceAllWith!(fun, match)(input, re);
+}
+
+/**
+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;
+ size_t _offset;
+ 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;
+ separator.flags |= RegexOption.global;
+ if (_input.empty)
+ {
+ //there is nothing to match at all, make _offset > 0
+ _offset = 1;
+ }
+ else
+ {
+ _match = Rx(_input, separator);
+
+ static if (keepSeparators)
+ if (_match.pre.empty)
+ popFront();
+ }
+ }
+
+public:
+ auto ref opSlice()
+ {
+ return this.save;
+ }
+
+ ///Forward range primitives.
+ @property Range front()
+ {
+ import std.algorithm.comparison : min;
+
+ assert(!empty && _offset <= _match.pre.length
+ && _match.pre.length <= _input.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()
+ {
+ static if (keepSeparators)
+ return _offset >= _input.length;
+ else
+ return _offset > _input.length;
+ }
+
+ ///ditto
+ void popFront()
+ {
+ assert(!empty);
+ if (_match.empty)
+ {
+ //No more separators, work is done here
+ _offset = _input.length + 1;
+ }
+ else
+ {
+ 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();
+ }
+ }
+ }
+
+ ///ditto
+ @property auto save()
+ {
+ return this;
+ }
+}
+
+/// 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!(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))
+{
+ import std.array : appender;
+ auto a = appender!(String[])();
+ 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.algorithm.comparison;
+ import std.regex;
+ string s = `This is {unfriendly} to *regex*`;
+ assert(s.escaper.equal(`This is \{unfriendly\} to \*regex\*`));
+}
+
+@system unittest
+{
+ import std.algorithm.comparison;
+ import std.conv;
+ foreach (S; AliasSeq!(string, wstring, dstring))
+ {
+ auto s = "^".to!S;
+ assert(s.escaper.equal(`\^`));
+ auto s2 = "";
+ assert(s2.escaper.equal(""));
+ }
+}
diff --git a/libphobos/src/std/signals.d b/libphobos/src/std/signals.d
new file mode 100644
index 0000000..071adca
--- /dev/null
+++ b/libphobos/src/std/signals.d
@@ -0,0 +1,708 @@
+// Written in the D programming language.
+
+/**
+ * Signals and Slots are an implementation of the Observer Pattern.
+ * Essentially, when a Signal is emitted, a list of connected Observers
+ * (called slots) are called.
+ *
+ * There have been several D implementations of Signals and Slots.
+ * This version makes use of several new features in D, which make
+ * using it simpler and less error prone. In particular, it is no
+ * longer necessary to instrument the slots.
+ *
+ * References:
+ * $(LUCKY A Deeper Look at Signals and Slots)$(BR)
+ * $(LINK2 http://en.wikipedia.org/wiki/Observer_pattern, Observer pattern)$(BR)
+ * $(LINK2 http://en.wikipedia.org/wiki/Signals_and_slots, Wikipedia)$(BR)
+ * $(LINK2 http://boost.org/doc/html/$(SIGNALS).html, Boost Signals)$(BR)
+ * $(LINK2 http://qt-project.org/doc/qt-5/signalsandslots.html, Qt)$(BR)
+ *
+ * There has been a great deal of discussion in the D newsgroups
+ * over this, and several implementations:
+ *
+ * $(LINK2 http://www.digitalmars.com/d/archives/digitalmars/D/announce/signal_slots_library_4825.html, signal slots library)$(BR)
+ * $(LINK2 http://www.digitalmars.com/d/archives/digitalmars/D/Signals_and_Slots_in_D_42387.html, Signals and Slots in D)$(BR)
+ * $(LINK2 http://www.digitalmars.com/d/archives/digitalmars/D/Dynamic_binding_--_Qt_s_Signals_and_Slots_vs_Objective-C_42260.html, Dynamic binding -- Qt's Signals and Slots vs Objective-C)$(BR)
+ * $(LINK2 http://www.digitalmars.com/d/archives/digitalmars/D/Dissecting_the_SS_42377.html, Dissecting the SS)$(BR)
+ * $(LINK2 http://www.digitalmars.com/d/archives/digitalmars/D/dwt/about_harmonia_454.html, about harmonia)$(BR)
+ * $(LINK2 http://www.digitalmars.com/d/archives/digitalmars/D/announce/1502.html, Another event handling module)$(BR)
+ * $(LINK2 http://www.digitalmars.com/d/archives/digitalmars/D/41825.html, Suggestion: signal/slot mechanism)$(BR)
+ * $(LINK2 http://www.digitalmars.com/d/archives/digitalmars/D/13251.html, Signals and slots?)$(BR)
+ * $(LINK2 http://www.digitalmars.com/d/archives/digitalmars/D/10714.html, Signals and slots ready for evaluation)$(BR)
+ * $(LINK2 http://www.digitalmars.com/d/archives/digitalmars/D/1393.html, Signals &amp; Slots for Walter)$(BR)
+ * $(LINK2 http://www.digitalmars.com/d/archives/28456.html, Signal/Slot mechanism?)$(BR)
+ * $(LINK2 http://www.digitalmars.com/d/archives/19470.html, Modern Features?)$(BR)
+ * $(LINK2 http://www.digitalmars.com/d/archives/16592.html, Delegates vs interfaces)$(BR)
+ * $(LINK2 http://www.digitalmars.com/d/archives/16583.html, The importance of component programming (properties$(COMMA) signals and slots$(COMMA) etc))$(BR)
+ * $(LINK2 http://www.digitalmars.com/d/archives/16368.html, signals and slots)$(BR)
+ *
+ * Bugs:
+ * Slots can only be delegates formed from class objects or
+ * interfaces to class objects. If a delegate to something else
+ * is passed to connect(), such as a struct member function,
+ * a nested function or a COM interface, undefined behavior
+ * will result.
+ *
+ * Not safe for multiple threads operating on the same signals
+ * or slots.
+ * Macros:
+ * SIGNALS=signals
+ *
+ * Copyright: Copyright Digital Mars 2000 - 2009.
+ * 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.
+ * (See accompanying file LICENSE_1_0.txt or copy at
+ * http://www.boost.org/LICENSE_1_0.txt)
+ */
+module std.signals;
+
+import core.exception : onOutOfMemoryError;
+import core.stdc.stdlib : calloc, realloc, free;
+import std.stdio;
+
+// Special function for internal use only.
+// Use of this is where the slot had better be a delegate
+// to an object or an interface that is part of an object.
+extern (C) Object _d_toObject(void* p);
+
+// Used in place of Object.notifyRegister and Object.notifyUnRegister.
+alias DisposeEvt = void delegate(Object);
+extern (C) void rt_attachDisposeEvent( Object obj, DisposeEvt evt );
+extern (C) void rt_detachDisposeEvent( Object obj, DisposeEvt evt );
+//debug=signal;
+
+/************************
+ * Mixin to create a signal within a class object.
+ *
+ * Different signals can be added to a class by naming the mixins.
+ */
+
+mixin template Signal(T1...)
+{
+ static import core.exception;
+ static import core.stdc.stdlib;
+ /***
+ * A slot is implemented as a delegate.
+ * The slot_t is the type of the delegate.
+ * The delegate must be to an instance of a class or an interface
+ * to a class instance.
+ * Delegates to struct instances or nested functions must not be
+ * used as slots.
+ */
+ alias slot_t = void delegate(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++;
+ }
+ }
+ }
+
+ /***
+ * Add a slot to the list of slots to be called when emit() is called.
+ */
+ final void connect(slot_t slot)
+ {
+ /* Do this:
+ * slots ~= slot;
+ * but use malloc() and friends instead
+ */
+ auto len = slots.length;
+ if (slots_idx == len)
+ {
+ if (slots.length == 0)
+ {
+ len = 4;
+ auto p = core.stdc.stdlib.calloc(slot_t.sizeof, len);
+ if (!p)
+ core.exception.onOutOfMemoryError();
+ slots = (cast(slot_t*) p)[0 .. len];
+ }
+ else
+ {
+ 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[slots_idx + 1 .. $] = null;
+ }
+ }
+ slots[slots_idx++] = slot;
+
+ L1:
+ Object o = _d_toObject(slot.ptr);
+ rt_attachDisposeEvent(o, &unhook);
+ }
+
+ /***
+ * Remove a slot from the list of slots to be called when emit() is called.
+ */
+ final void disconnect(slot_t slot)
+ {
+ debug (signal) writefln("Signal.disconnect(slot)");
+ size_t disconnectedSlots = 0;
+ size_t instancePreviousSlots = 0;
+ if (status >= ST.inemitting)
+ {
+ 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++;
+ }
+ }
+
+ // 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.
+ * It causes any slots dependent on o to be removed from the list
+ * of slots to be called by emit().
+ */
+ final void unhook(Object 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)
+ { slots_idx--;
+ slots[i] = slots[slots_idx];
+ slots[slots_idx] = null; // not strictly necessary
+ }
+ else
+ i++;
+ }
+ }
+
+ /* **
+ * There can be multiple destructors inserted by mixins.
+ */
+ ~this()
+ {
+ /* **
+ * When this object is destroyed, need to let every slot
+ * know that this object is destroyed so they are not left
+ * with dangling references to it.
+ */
+ if (slots.length)
+ {
+ foreach (slot; slots[0 .. slots_idx])
+ {
+ if (slot)
+ { Object o = _d_toObject(slot.ptr);
+ rt_detachDisposeEvent(o, &unhook);
+ }
+ }
+ core.stdc.stdlib.free(slots.ptr);
+ slots = null;
+ }
+ }
+
+ 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() { }
+
+@system unittest
+{
+ class Observer
+ {
+ void watch(string msg, int i)
+ {
+ //writefln("Observed msg '%s' and value %s", msg, i);
+ captured_value = i;
+ captured_msg = msg;
+ }
+
+ int captured_value;
+ string captured_msg;
+ }
+
+ class Foo
+ {
+ @property int value() { return _value; }
+
+ @property int value(int v)
+ {
+ if (v != _value)
+ { _value = v;
+ emit("setting new value", v);
+ }
+ return v;
+ }
+
+ mixin Signal!(string, int);
+
+ private:
+ int _value;
+ }
+
+ Foo a = new Foo;
+ Observer o = new Observer;
+
+ // check initial condition
+ assert(o.captured_value == 0);
+ assert(o.captured_msg == "");
+
+ // set a value while no observation is in place
+ a.value = 3;
+ assert(o.captured_value == 0);
+ assert(o.captured_msg == "");
+
+ // connect the watcher and trigger it
+ a.connect(&o.watch);
+ a.value = 4;
+ assert(o.captured_value == 4);
+ assert(o.captured_msg == "setting new value");
+
+ // disconnect the watcher and make sure it doesn't trigger
+ a.disconnect(&o.watch);
+ a.value = 5;
+ assert(o.captured_value == 4);
+ assert(o.captured_msg == "setting new value");
+
+ // reconnect the watcher and make sure it triggers
+ a.connect(&o.watch);
+ a.value = 6;
+ assert(o.captured_value == 6);
+ assert(o.captured_msg == "setting new value");
+
+ // destroy the underlying object and make sure it doesn't cause
+ // a crash or other problems
+ destroy(o);
+ a.value = 7;
+}
+
+@system unittest
+{
+ class Observer
+ {
+ int i;
+ long l;
+ string str;
+
+ void watchInt(string str, int i)
+ {
+ this.str = str;
+ this.i = i;
+ }
+
+ void watchLong(string str, long l)
+ {
+ this.str = str;
+ this.l = l;
+ }
+ }
+
+ class Bar
+ {
+ @property void value1(int v) { s1.emit("str1", v); }
+ @property void value2(int v) { s2.emit("str2", v); }
+ @property void value3(long v) { s3.emit("str3", v); }
+
+ mixin Signal!(string, int) s1;
+ mixin Signal!(string, int) s2;
+ mixin Signal!(string, long) s3;
+ }
+
+ void test(T)(T a) {
+ auto o1 = new Observer;
+ auto o2 = new Observer;
+ auto o3 = new Observer;
+
+ // connect the watcher and trigger it
+ a.s1.connect(&o1.watchInt);
+ a.s2.connect(&o2.watchInt);
+ a.s3.connect(&o3.watchLong);
+
+ assert(!o1.i && !o1.l && o1.str == null);
+ assert(!o2.i && !o2.l && o2.str == null);
+ assert(!o3.i && !o3.l && o3.str == null);
+
+ a.value1 = 11;
+ assert(o1.i == 11 && !o1.l && o1.str == "str1");
+ assert(!o2.i && !o2.l && o2.str == null);
+ assert(!o3.i && !o3.l && o3.str == null);
+ o1.i = -11; o1.str = "x1";
+
+ a.value2 = 12;
+ assert(o1.i == -11 && !o1.l && o1.str == "x1");
+ assert(o2.i == 12 && !o2.l && o2.str == "str2");
+ assert(!o3.i && !o3.l && o3.str == null);
+ o2.i = -12; o2.str = "x2";
+
+ a.value3 = 13;
+ assert(o1.i == -11 && !o1.l && o1.str == "x1");
+ assert(o2.i == -12 && !o1.l && o2.str == "x2");
+ assert(!o3.i && o3.l == 13 && o3.str == "str3");
+ o3.l = -13; o3.str = "x3";
+
+ // disconnect the watchers and make sure it doesn't trigger
+ a.s1.disconnect(&o1.watchInt);
+ a.s2.disconnect(&o2.watchInt);
+ a.s3.disconnect(&o3.watchLong);
+
+ a.value1 = 21;
+ a.value2 = 22;
+ a.value3 = 23;
+ assert(o1.i == -11 && !o1.l && o1.str == "x1");
+ assert(o2.i == -12 && !o1.l && o2.str == "x2");
+ assert(!o3.i && o3.l == -13 && o3.str == "x3");
+
+ // reconnect the watcher and make sure it triggers
+ a.s1.connect(&o1.watchInt);
+ a.s2.connect(&o2.watchInt);
+ a.s3.connect(&o3.watchLong);
+
+ a.value1 = 31;
+ a.value2 = 32;
+ a.value3 = 33;
+ assert(o1.i == 31 && !o1.l && o1.str == "str1");
+ assert(o2.i == 32 && !o1.l && o2.str == "str2");
+ assert(!o3.i && o3.l == 33 && o3.str == "str3");
+
+ // destroy observers
+ destroy(o1);
+ destroy(o2);
+ destroy(o3);
+ a.value1 = 41;
+ a.value2 = 42;
+ a.value3 = 43;
+ }
+
+ test(new Bar);
+
+ class BarDerived: Bar
+ {
+ @property void value4(int v) { s4.emit("str4", v); }
+ @property void value5(int v) { s5.emit("str5", v); }
+ @property void value6(long v) { s6.emit("str6", v); }
+
+ mixin Signal!(string, int) s4;
+ mixin Signal!(string, int) s5;
+ mixin Signal!(string, long) s6;
+ }
+
+ auto a = new BarDerived;
+
+ test!Bar(a);
+ test!BarDerived(a);
+
+ auto o4 = new Observer;
+ auto o5 = new Observer;
+ auto o6 = new Observer;
+
+ // connect the watcher and trigger it
+ a.s4.connect(&o4.watchInt);
+ a.s5.connect(&o5.watchInt);
+ a.s6.connect(&o6.watchLong);
+
+ assert(!o4.i && !o4.l && o4.str == null);
+ assert(!o5.i && !o5.l && o5.str == null);
+ assert(!o6.i && !o6.l && o6.str == null);
+
+ a.value4 = 44;
+ assert(o4.i == 44 && !o4.l && o4.str == "str4");
+ assert(!o5.i && !o5.l && o5.str == null);
+ assert(!o6.i && !o6.l && o6.str == null);
+ o4.i = -44; o4.str = "x4";
+
+ a.value5 = 45;
+ assert(o4.i == -44 && !o4.l && o4.str == "x4");
+ assert(o5.i == 45 && !o5.l && o5.str == "str5");
+ assert(!o6.i && !o6.l && o6.str == null);
+ o5.i = -45; o5.str = "x5";
+
+ a.value6 = 46;
+ assert(o4.i == -44 && !o4.l && o4.str == "x4");
+ assert(o5.i == -45 && !o4.l && o5.str == "x5");
+ assert(!o6.i && o6.l == 46 && o6.str == "str6");
+ o6.l = -46; o6.str = "x6";
+
+ // disconnect the watchers and make sure it doesn't trigger
+ a.s4.disconnect(&o4.watchInt);
+ a.s5.disconnect(&o5.watchInt);
+ a.s6.disconnect(&o6.watchLong);
+
+ a.value4 = 54;
+ a.value5 = 55;
+ a.value6 = 56;
+ assert(o4.i == -44 && !o4.l && o4.str == "x4");
+ assert(o5.i == -45 && !o4.l && o5.str == "x5");
+ assert(!o6.i && o6.l == -46 && o6.str == "x6");
+
+ // reconnect the watcher and make sure it triggers
+ a.s4.connect(&o4.watchInt);
+ a.s5.connect(&o5.watchInt);
+ a.s6.connect(&o6.watchLong);
+
+ a.value4 = 64;
+ a.value5 = 65;
+ a.value6 = 66;
+ assert(o4.i == 64 && !o4.l && o4.str == "str4");
+ assert(o5.i == 65 && !o4.l && o5.str == "str5");
+ assert(!o6.i && o6.l == 66 && o6.str == "str6");
+
+ // destroy observers
+ destroy(o4);
+ destroy(o5);
+ destroy(o6);
+ a.value4 = 44;
+ a.value5 = 45;
+ 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@@@
+@system unittest
+{
+ class A
+ {
+ mixin Signal!(string, int) s1;
+ }
+
+ class B : A
+ {
+ 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/libphobos/src/std/socket.d b/libphobos/src/std/socket.d
new file mode 100644
index 0000000..c008d62
--- /dev/null
+++ b/libphobos/src/std/socket.d
@@ -0,0 +1,3670 @@
+// Written in the D programming language
+
+/*
+ Copyright (C) 2004-2011 Christopher E. Miller
+
+ Boost Software License - Version 1.0 - August 17th, 2003
+
+ Permission is hereby granted, free of charge, to any person or organization
+ obtaining a copy of the software and accompanying documentation covered by
+ this license (the "Software") to use, reproduce, display, distribute,
+ execute, and transmit the Software, and to prepare derivative works of the
+ Software, and to permit third-parties to whom the Software is furnished to
+ do so, all subject to the following:
+
+ The copyright notices in the Software and this entire statement, including
+ the above license grant, this restriction and the following disclaimer,
+ must be included in all copies of the Software, in whole or in part, and
+ all derivative works of the Software, unless such copies or derivative
+ works are solely in the form of machine-executable object code generated by
+ a source language processor.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
+ SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
+ FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
+ ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ DEALINGS IN THE SOFTWARE.
+
+ socket.d 1.4
+ Jan 2011
+
+ Thanks to Benjamin Herr for his assistance.
+ */
+
+/**
+ * Socket primitives.
+ * Example: See $(SAMPLESRC listener.d) and $(SAMPLESRC htmlget.d)
+ * 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)
+ */
+
+module std.socket;
+
+import core.stdc.stdint, core.stdc.stdlib, core.stdc.string, std.conv, std.string;
+
+import core.stdc.config;
+import core.time : dur, Duration;
+import std.exception;
+
+import std.internal.cstring;
+
+
+@safe:
+
+version (Windows)
+{
+ version (GNU) {}
+ else
+ {
+ pragma (lib, "ws2_32.lib");
+ pragma (lib, "wsock32.lib");
+ }
+
+ import core.sys.windows.windows, std.windows.syserror;
+ public import core.sys.windows.winsock2;
+ private alias _ctimeval = core.sys.windows.winsock2.timeval;
+ private alias _clinger = core.sys.windows.winsock2.linger;
+
+ enum socket_t : SOCKET { INVALID_SOCKET }
+ private const int _SOCKET_ERROR = SOCKET_ERROR;
+
+
+ private int _lasterr() nothrow @nogc
+ {
+ return WSAGetLastError();
+ }
+}
+else version (Posix)
+{
+ version (linux)
+ {
+ enum : int
+ {
+ TCP_KEEPIDLE = 4,
+ TCP_KEEPINTVL = 5
+ }
+ }
+
+ import core.sys.posix.arpa.inet;
+ import core.sys.posix.fcntl;
+ import core.sys.posix.netdb;
+ import core.sys.posix.netinet.in_;
+ import core.sys.posix.netinet.tcp;
+ import core.sys.posix.sys.select;
+ import core.sys.posix.sys.socket;
+ import core.sys.posix.sys.time;
+ import core.sys.posix.sys.un : sockaddr_un;
+ import core.sys.posix.unistd;
+ private alias _ctimeval = core.sys.posix.sys.time.timeval;
+ private alias _clinger = core.sys.posix.sys.socket.linger;
+
+ import core.stdc.errno;
+
+ enum socket_t : int32_t { init = -1 }
+ private const int _SOCKET_ERROR = -1;
+
+ private enum : int
+ {
+ SD_RECEIVE = SHUT_RD,
+ SD_SEND = SHUT_WR,
+ SD_BOTH = SHUT_RDWR
+ }
+
+ private int _lasterr() nothrow @nogc
+ {
+ return errno;
+ }
+}
+else
+{
+ static assert(0); // No socket support yet.
+}
+
+version (unittest)
+{
+ static assert(is(uint32_t == uint));
+ static assert(is(uint16_t == ushort));
+
+ import std.stdio : writefln;
+
+ // Print a message on exception instead of failing the unittest.
+ private void softUnittest(void delegate() @safe test, int line = __LINE__) @trusted
+ {
+ try
+ test();
+ catch (Throwable e)
+ {
+ writefln(" --- std.socket(%d) test fails depending on environment ---", line);
+ writefln(" (%s)", e);
+ }
+ }
+}
+
+/// Base exception thrown by $(D std.socket).
+class SocketException: Exception
+{
+ 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.
+ */
+string formatSocketError(int err) @trusted
+{
+ version (Posix)
+ {
+ char[80] buf;
+ const(char)* cs;
+ version (CRuntime_Glibc)
+ {
+ cs = strerror_r(err, buf.ptr, buf.length);
+ }
+ else version (OSX)
+ {
+ auto errs = strerror_r(err, buf.ptr, buf.length);
+ if (errs == 0)
+ cs = buf.ptr;
+ else
+ return "Socket error " ~ to!string(err);
+ }
+ else version (FreeBSD)
+ {
+ auto errs = strerror_r(err, buf.ptr, buf.length);
+ if (errs == 0)
+ cs = buf.ptr;
+ 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);
+ if (errs == 0)
+ cs = buf.ptr;
+ else
+ return "Socket error " ~ to!string(err);
+ }
+ else version (CRuntime_Bionic)
+ {
+ auto errs = strerror_r(err, buf.ptr, buf.length);
+ if (errs == 0)
+ cs = buf.ptr;
+ else
+ return "Socket error " ~ to!string(err);
+ }
+ else
+ static assert(0);
+
+ auto len = strlen(cs);
+
+ if (cs[len - 1] == '\n')
+ len--;
+ if (cs[len - 1] == '\r')
+ len--;
+ return cs[0 .. len].idup;
+ }
+ else
+ version (Windows)
+ {
+ return sysErrorString(err);
+ }
+ else
+ return "Socket error " ~ to!string(err);
+}
+
+/// Retrieve the error message for the most recently encountered network error.
+@property string lastSocketError()
+{
+ return formatSocketError(_lasterr());
+}
+
+/**
+ * Socket exceptions representing network errors reported by the operating
+ * system.
+ */
+class SocketOSException: SocketException
+{
+ int errorCode; /// Platform-specific error code.
+
+ ///
+ this(string msg,
+ string file = __FILE__,
+ size_t line = __LINE__,
+ Throwable next = null,
+ int err = _lasterr(),
+ string function(int) @trusted errorFormatter = &formatSocketError)
+ {
+ errorCode = err;
+
+ if (msg.length)
+ super(msg ~ ": " ~ errorFormatter(err), file, line, next);
+ else
+ super(errorFormatter(err), file, line, next);
+ }
+
+ ///
+ this(string msg,
+ Throwable next,
+ string file = __FILE__,
+ size_t line = __LINE__,
+ int err = _lasterr(),
+ string function(int) @trusted errorFormatter = &formatSocketError)
+ {
+ this(msg, file, line, next, err, errorFormatter);
+ }
+
+ ///
+ this(string msg,
+ int err,
+ string function(int) @trusted errorFormatter = &formatSocketError,
+ string file = __FILE__,
+ size_t line = __LINE__,
+ Throwable next = null)
+ {
+ this(msg, file, line, next, err, errorFormatter);
+ }
+}
+
+/// Socket exceptions representing invalid parameters specified by user code.
+class SocketParameterException: SocketException
+{
+ mixin basicExceptionCtors;
+}
+
+/**
+ * Socket exceptions representing attempts to use network capabilities not
+ * available on the current system.
+ */
+class SocketFeatureException: SocketException
+{
+ mixin basicExceptionCtors;
+}
+
+
+/**
+ * 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)
+ return _lasterr() == WSAEWOULDBLOCK;
+ else version (Posix)
+ return _lasterr() == EAGAIN;
+ else
+ static assert(0);
+}
+
+
+private immutable
+{
+ typeof(&getnameinfo) getnameinfoPointer;
+ typeof(&getaddrinfo) getaddrinfoPointer;
+ typeof(&freeaddrinfo) freeaddrinfoPointer;
+}
+
+shared static this() @system
+{
+ version (Windows)
+ {
+ WSADATA wd;
+
+ // Winsock will still load if an older version is present.
+ // The version is just a request.
+ int val;
+ val = WSAStartup(0x2020, &wd);
+ 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.
+ // See the comment in InternetAddress.toHostNameString() for details.
+ auto ws2Lib = GetModuleHandleA("ws2_32.dll");
+ if (ws2Lib)
+ {
+ getnameinfoPointer = cast(typeof(getnameinfoPointer))
+ GetProcAddress(ws2Lib, "getnameinfo");
+ getaddrinfoPointer = cast(typeof(getaddrinfoPointer))
+ GetProcAddress(ws2Lib, "getaddrinfo");
+ freeaddrinfoPointer = cast(typeof(freeaddrinfoPointer))
+ GetProcAddress(ws2Lib, "freeaddrinfo");
+ }
+ }
+ else version (Posix)
+ {
+ getnameinfoPointer = &getnameinfo;
+ getaddrinfoPointer = &getaddrinfo;
+ freeaddrinfoPointer = &freeaddrinfo;
+ }
+}
+
+
+shared static ~this() @system nothrow @nogc
+{
+ version (Windows)
+ {
+ WSACleanup();
+ }
+}
+
+/**
+ * The communication domain used to resolve an address.
+ */
+enum AddressFamily: int
+{
+ UNSPEC = AF_UNSPEC, /// Unspecified address family
+ UNIX = AF_UNIX, /// Local communication
+ INET = AF_INET, /// Internet Protocol version 4
+ IPX = AF_IPX, /// Novell IPX
+ APPLETALK = AF_APPLETALK, /// AppleTalk
+ INET6 = AF_INET6, /// Internet Protocol version 6
+}
+
+
+/**
+ * Communication semantics
+ */
+enum SocketType: int
+{
+ STREAM = SOCK_STREAM, /// Sequenced, reliable, two-way communication-based byte streams
+ DGRAM = SOCK_DGRAM, /// Connectionless, unreliable datagrams with a fixed maximum length; data may be lost or arrive out of order
+ RAW = SOCK_RAW, /// Raw protocol access
+ RDM = SOCK_RDM, /// Reliably-delivered message datagrams
+ SEQPACKET = SOCK_SEQPACKET, /// Sequenced, reliable, two-way connection-based datagrams with a fixed maximum length
+}
+
+
+/**
+ * Protocol
+ */
+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
+}
+
+
+/**
+ * $(D Protocol) is a class for retrieving protocol information.
+ *
+ * Example:
+ * ---
+ * auto proto = new Protocol;
+ * writeln("About protocol TCP:");
+ * if (proto.getProtocolByType(ProtocolType.TCP))
+ * {
+ * writefln(" Name: %s", proto.name);
+ * foreach (string s; proto.aliases)
+ * writefln(" Alias: %s", s);
+ * }
+ * else
+ * writeln(" No information found");
+ * ---
+ */
+class Protocol
+{
+ /// These members are populated when one of the following functions are called successfully:
+ ProtocolType type;
+ string name; /// ditto
+ string[] aliases; /// ditto
+
+
+ void populate(protoent* proto) @system pure nothrow
+ {
+ type = cast(ProtocolType) proto.p_proto;
+ name = to!string(proto.p_name);
+
+ int i;
+ for (i = 0;; i++)
+ {
+ if (!proto.p_aliases[i])
+ break;
+ }
+
+ if (i)
+ {
+ aliases = new string[i];
+ for (i = 0; i != aliases.length; i++)
+ {
+ aliases[i] =
+ to!string(proto.p_aliases[i]);
+ }
+ }
+ else
+ {
+ aliases = null;
+ }
+ }
+
+ /** Returns: false on failure */
+ bool getProtocolByName(in char[] name) @trusted nothrow
+ {
+ protoent* proto;
+ proto = getprotobyname(name.tempCString());
+ if (!proto)
+ return false;
+ populate(proto);
+ return true;
+ }
+
+
+ /** Returns: false on failure */
+ // Same as getprotobynumber().
+ bool getProtocolByType(ProtocolType type) @trusted nothrow
+ {
+ protoent* proto;
+ proto = getprotobynumber(type);
+ if (!proto)
+ return false;
+ populate(proto);
+ return true;
+ }
+}
+
+
+// Skip this test on Android because getprotobyname/number are
+// unimplemented in bionic.
+version (CRuntime_Bionic) {} else
+@safe unittest
+{
+ softUnittest({
+ Protocol proto = new Protocol;
+ assert(proto.getProtocolByType(ProtocolType.TCP));
+ //writeln("About protocol TCP:");
+ //writefln("\tName: %s", proto.name);
+ // foreach (string s; proto.aliases)
+ // {
+ // writefln("\tAlias: %s", s);
+ // }
+ assert(proto.name == "tcp");
+ assert(proto.aliases.length == 1 && proto.aliases[0] == "TCP");
+ });
+}
+
+
+/**
+ * $(D Service) is a class for retrieving service information.
+ *
+ * Example:
+ * ---
+ * auto serv = new Service;
+ * writeln("About service epmap:");
+ * if (serv.getServiceByName("epmap", "tcp"))
+ * {
+ * writefln(" Service: %s", serv.name);
+ * writefln(" Port: %d", serv.port);
+ * writefln(" Protocol: %s", serv.protocolName);
+ * foreach (string s; serv.aliases)
+ * writefln(" Alias: %s", s);
+ * }
+ * else
+ * writefln(" No service for epmap.");
+ * ---
+ */
+class Service
+{
+ /// These members are populated when one of the following functions are called successfully:
+ string name;
+ string[] aliases; /// ditto
+ ushort port; /// ditto
+ string protocolName; /// ditto
+
+
+ void populate(servent* serv) @system pure nothrow
+ {
+ name = to!string(serv.s_name);
+ port = ntohs(cast(ushort) serv.s_port);
+ protocolName = to!string(serv.s_proto);
+
+ int i;
+ for (i = 0;; i++)
+ {
+ if (!serv.s_aliases[i])
+ break;
+ }
+
+ if (i)
+ {
+ aliases = new string[i];
+ for (i = 0; i != aliases.length; i++)
+ {
+ aliases[i] =
+ to!string(serv.s_aliases[i]);
+ }
+ }
+ else
+ {
+ aliases = null;
+ }
+ }
+
+ /**
+ * If a protocol name is omitted, any protocol will be matched.
+ * Returns: false on failure.
+ */
+ bool getServiceByName(in char[] name, in char[] protocolName = null) @trusted nothrow
+ {
+ servent* serv;
+ serv = getservbyname(name.tempCString(), protocolName.tempCString());
+ if (!serv)
+ return false;
+ populate(serv);
+ return true;
+ }
+
+
+ /// ditto
+ bool getServiceByPort(ushort port, in char[] protocolName = null) @trusted nothrow
+ {
+ servent* serv;
+ serv = getservbyport(port, protocolName.tempCString());
+ if (!serv)
+ return false;
+ populate(serv);
+ return true;
+ }
+}
+
+
+@safe unittest
+{
+ softUnittest({
+ Service serv = new Service;
+ 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)
+ // {
+ // writefln("\tAlias: %s", s);
+ // }
+ // For reasons unknown this is loc-srv on Wine and epmap on Windows
+ assert(serv.name == "loc-srv" || serv.name == "epmap", serv.name);
+ assert(serv.port == 135);
+ assert(serv.protocolName == "tcp");
+ }
+ else
+ {
+ writefln("No service for epmap.");
+ }
+ });
+}
+
+
+private mixin template socketOSExceptionCtors()
+{
+ ///
+ 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);
+ }
+}
+
+
+/**
+ * Class for exceptions thrown from an `InternetHost`.
+ */
+class HostException: SocketOSException
+{
+ mixin socketOSExceptionCtors;
+}
+
+/**
+ * `InternetHost` is a class for resolving IPv4 addresses.
+ *
+ * Consider using `getAddress`, `parseAddress` and `Address` methods
+ * instead of using this class directly.
+ */
+class InternetHost
+{
+ /// These members are populated when one of the following functions are called successfully:
+ string name;
+ string[] aliases; /// ditto
+ uint[] addrList; /// ditto
+
+
+ void validHostent(in hostent* he)
+ {
+ if (he.h_addrtype != cast(int) AddressFamily.INET || he.h_length != 4)
+ throw new HostException("Address family mismatch");
+ }
+
+
+ void populate(hostent* he) @system pure nothrow
+ {
+ int i;
+ char* p;
+
+ name = to!string(he.h_name);
+
+ for (i = 0;; i++)
+ {
+ p = he.h_aliases[i];
+ if (!p)
+ break;
+ }
+
+ if (i)
+ {
+ aliases = new string[i];
+ for (i = 0; i != aliases.length; i++)
+ {
+ aliases[i] =
+ to!string(he.h_aliases[i]);
+ }
+ }
+ else
+ {
+ aliases = null;
+ }
+
+ for (i = 0;; i++)
+ {
+ p = he.h_addr_list[i];
+ if (!p)
+ break;
+ }
+
+ if (i)
+ {
+ addrList = new uint[i];
+ for (i = 0; i != addrList.length; i++)
+ {
+ addrList[i] = ntohl(*(cast(uint*) he.h_addr_list[i]));
+ }
+ }
+ else
+ {
+ addrList = null;
+ }
+ }
+
+ private bool getHostNoSync(string opMixin, T)(T param) @system
+ {
+ mixin(opMixin);
+ if (!he)
+ return false;
+ validHostent(he);
+ populate(he);
+ return true;
+ }
+
+ version (Windows)
+ alias getHost = getHostNoSync;
+ else
+ {
+ // posix systems use global state for return value, so we
+ // must synchronize across all threads
+ private bool getHost(string opMixin, T)(T param) @system
+ {
+ synchronized(this.classinfo)
+ return getHostNoSync!(opMixin, T)(param);
+ }
+ }
+
+ /**
+ * Resolve host name.
+ * Returns: false if unable to resolve.
+ */
+ bool getHostByName(in char[] name) @trusted
+ {
+ static if (is(typeof(gethostbyname_r)))
+ {
+ return getHostNoSync!q{
+ hostent he_v;
+ hostent* he;
+ ubyte[256] buffer_v = void;
+ auto buffer = buffer_v[];
+ auto param_zTmp = param.tempCString();
+ while (true)
+ {
+ he = &he_v;
+ int errno;
+ if (gethostbyname_r(param_zTmp, he, buffer.ptr, buffer.length, &he, &errno) == ERANGE)
+ buffer.length = buffer.length * 2;
+ else
+ break;
+ }
+ }(name);
+ }
+ else
+ {
+ return getHost!q{
+ auto he = gethostbyname(param.tempCString());
+ }(name);
+ }
+ }
+
+ /**
+ * Resolve IPv4 address number.
+ *
+ * Params:
+ * addr = The IPv4 address to resolve, in host byte order.
+ * Returns:
+ * false if unable to resolve.
+ */
+ bool getHostByAddr(uint addr) @trusted
+ {
+ return getHost!q{
+ auto x = htonl(param);
+ auto he = gethostbyaddr(&x, 4, cast(int) AddressFamily.INET);
+ }(addr);
+ }
+
+ /**
+ * Same as previous, but addr is an IPv4 address string in the
+ * dotted-decimal form $(I a.b.c.d).
+ * Returns: false if unable to resolve.
+ */
+ bool getHostByAddr(in char[] addr) @trusted
+ {
+ return getHost!q{
+ auto x = inet_addr(param.tempCString());
+ enforce(x != INADDR_NONE,
+ new SocketParameterException("Invalid IPv4 address"));
+ auto he = gethostbyaddr(&x, 4, cast(int) AddressFamily.INET);
+ }(addr);
+ }
+}
+
+///
+@safe unittest
+{
+ InternetHost ih = new InternetHost;
+
+ ih.getHostByAddr(0x7F_00_00_01);
+ assert(ih.addrList[0] == 0x7F_00_00_01);
+ ih.getHostByAddr("127.0.0.1");
+ assert(ih.addrList[0] == 0x7F_00_00_01);
+
+ if (!ih.getHostByName("www.digitalmars.com"))
+ return; // don't fail if not connected to internet
+
+ 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);
+}
+
+
+/// Holds information about a socket _address retrieved by $(D getAddressInfo).
+struct AddressInfo
+{
+ AddressFamily family; /// Address _family
+ SocketType type; /// Socket _type
+ ProtocolType protocol; /// Protocol
+ Address address; /// Socket _address
+ 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).
+ */
+enum AddressInfoFlags: int
+{
+ /// The resulting addresses will be used in a call to $(D Socket.bind).
+ PASSIVE = AI_PASSIVE,
+
+ /// 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.
+ */
+ NUMERICHOST = AI_NUMERICHOST,
+}
+
+
+/**
+ * On POSIX, getaddrinfo uses its own error codes, and thus has its own
+ * formatting function.
+ */
+private string formatGaiError(int err) @trusted
+{
+ version (Windows)
+ {
+ return sysErrorString(err);
+ }
+ else
+ {
+ synchronized
+ return to!string(gai_strerror(err));
+ }
+}
+
+/**
+ * Provides _protocol-independent translation from host names to socket
+ * addresses. If advanced functionality is not required, consider using
+ * $(D getAddress) for compatibility with older systems.
+ *
+ * Returns: Array with one $(D AddressInfo) per socket address.
+ *
+ * Throws: $(D SocketOSException) on failure, or $(D SocketFeatureException)
+ * if this functionality is not available on the current system.
+ *
+ * Params:
+ * node = string containing host name or numeric address
+ * options = optional additional parameters, identified by type:
+ * $(UL $(LI $(D string) - service name or port number)
+ * $(LI $(D AddressInfoFlags) - option flags)
+ * $(LI $(D AddressFamily) - address family to filter by)
+ * $(LI $(D SocketType) - socket type to filter by)
+ * $(LI $(D ProtocolType) - protocol to filter by))
+ *
+ * Example:
+ * ---
+ * // Roundtrip DNS resolution
+ * auto results = getAddressInfo("www.digitalmars.com");
+ * assert(results[0].address.toHostNameString() ==
+ * "digitalmars.com");
+ *
+ * // Canonical name
+ * results = getAddressInfo("www.digitalmars.com",
+ * AddressInfoFlags.CANONNAME);
+ * assert(results[0].canonicalName == "digitalmars.com");
+ *
+ * // IPv6 resolution
+ * results = getAddressInfo("ipv6.google.com");
+ * assert(results[0].family == AddressFamily.INET6);
+ *
+ * // Multihomed resolution
+ * results = getAddressInfo("google.com");
+ * assert(results.length > 1);
+ *
+ * // Parsing IPv4
+ * results = getAddressInfo("127.0.0.1",
+ * AddressInfoFlags.NUMERICHOST);
+ * assert(results.length && results[0].family ==
+ * AddressFamily.INET);
+ *
+ * // Parsing IPv6
+ * results = getAddressInfo("::1",
+ * AddressInfoFlags.NUMERICHOST);
+ * assert(results.length && results[0].family ==
+ * AddressFamily.INET6);
+ * ---
+ */
+AddressInfo[] getAddressInfo(T...)(in char[] node, T options)
+{
+ const(char)[] service = null;
+ addrinfo hints;
+ hints.ai_family = AF_UNSPEC;
+
+ foreach (option; options)
+ {
+ static if (is(typeof(option) : const(char)[]))
+ service = option;
+ else
+ static if (is(typeof(option) == AddressInfoFlags))
+ hints.ai_flags |= option;
+ else
+ static if (is(typeof(option) == AddressFamily))
+ hints.ai_family = option;
+ else
+ static if (is(typeof(option) == SocketType))
+ hints.ai_socktype = option;
+ else
+ static if (is(typeof(option) == ProtocolType))
+ hints.ai_protocol = option;
+ else
+ static assert(0, "Unknown getAddressInfo option type: " ~ typeof(option).stringof);
+ }
+
+ 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
+{
+ import std.array : appender;
+
+ if (getaddrinfoPointer && freeaddrinfoPointer)
+ {
+ addrinfo* ai_res;
+
+ int ret = getaddrinfoPointer(
+ node.tempCString(),
+ service.tempCString(),
+ hints, &ai_res);
+ enforce(ret == 0, new SocketOSException("getaddrinfo error", ret, &formatGaiError));
+ scope(exit) freeaddrinfoPointer(ai_res);
+
+ auto result = appender!(AddressInfo[])();
+
+ // Use const to force UnknownAddressReference to copy the sockaddr.
+ for (const(addrinfo)* ai = ai_res; ai; ai = ai.ai_next)
+ result ~= AddressInfo(
+ cast(AddressFamily) ai.ai_family,
+ cast(SocketType ) ai.ai_socktype,
+ cast(ProtocolType ) ai.ai_protocol,
+ new UnknownAddressReference(ai.ai_addr, cast(socklen_t) ai.ai_addrlen),
+ ai.ai_canonname ? to!string(ai.ai_canonname) : null);
+
+ assert(result.data.length > 0);
+ return result.data;
+ }
+
+ throw new SocketFeatureException("Address info lookup is not available " ~
+ "on this system.");
+}
+
+
+@safe unittest
+{
+ softUnittest({
+ if (getaddrinfoPointer)
+ {
+ // Roundtrip DNS resolution
+ auto results = getAddressInfo("www.digitalmars.com");
+ assert(results[0].address.toHostNameString() == "digitalmars.com");
+
+ // Canonical name
+ results = getAddressInfo("www.digitalmars.com",
+ AddressInfoFlags.CANONNAME);
+ assert(results[0].canonicalName == "digitalmars.com");
+
+ // IPv6 resolution
+ //results = getAddressInfo("ipv6.google.com");
+ //assert(results[0].family == AddressFamily.INET6);
+
+ // Multihomed resolution
+ //results = getAddressInfo("google.com");
+ //assert(results.length > 1);
+
+ // Parsing IPv4
+ results = getAddressInfo("127.0.0.1", AddressInfoFlags.NUMERICHOST);
+ assert(results.length && results[0].family == AddressFamily.INET);
+
+ // Parsing IPv6
+ results = getAddressInfo("::1", AddressInfoFlags.NUMERICHOST);
+ 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");
+ }
+}
+
+
+private ushort serviceToPort(in char[] service)
+{
+ if (service == "")
+ return InternetAddress.PORT_ANY;
+ else
+ if (isNumeric(service))
+ return to!ushort(service);
+ else
+ {
+ auto s = new Service();
+ s.getServiceByName(service);
+ return s.port;
+ }
+}
+
+/**
+ * Provides _protocol-independent translation from host names to socket
+ * addresses. Uses $(D getAddressInfo) if the current system supports it,
+ * and $(D InternetHost) otherwise.
+ *
+ * Returns: Array with one $(D Address) instance per socket address.
+ *
+ * Throws: $(D SocketOSException) on failure.
+ *
+ * Example:
+ * ---
+ * writeln("Resolving www.digitalmars.com:");
+ * try
+ * {
+ * auto addresses = getAddress("www.digitalmars.com");
+ * foreach (address; addresses)
+ * writefln(" IP: %s", address.toAddrString());
+ * }
+ * catch (SocketException e)
+ * writefln(" Lookup failed: %s", e.msg);
+ * ---
+ */
+Address[] getAddress(in char[] hostname, in char[] service = null)
+{
+ if (getaddrinfoPointer && freeaddrinfoPointer)
+ {
+ // use getAddressInfo
+ auto infos = getAddressInfo(hostname, service);
+ Address[] results;
+ results.length = infos.length;
+ foreach (i, ref result; results)
+ result = infos[i].address;
+ return results;
+ }
+ else
+ return getAddress(hostname, serviceToPort(service));
+}
+
+/// ditto
+Address[] getAddress(in char[] hostname, ushort port)
+{
+ if (getaddrinfoPointer && freeaddrinfoPointer)
+ return getAddress(hostname, to!string(port));
+ else
+ {
+ // use getHostByName
+ auto ih = new InternetHost;
+ if (!ih.getHostByName(hostname))
+ throw new AddressException(
+ text("Unable to resolve host '", hostname, "'"));
+
+ Address[] results;
+ foreach (uint addr; ih.addrList)
+ results ~= new InternetAddress(addr, port);
+ return results;
+ }
+}
+
+
+@safe unittest
+{
+ softUnittest({
+ auto addresses = getAddress("63.105.9.61");
+ assert(addresses.length && addresses[0].toAddrString() == "63.105.9.61");
+
+ if (getaddrinfoPointer)
+ {
+ // test via gethostbyname
+ auto getaddrinfoPointerBackup = getaddrinfoPointer;
+ cast() getaddrinfoPointer = null;
+ scope(exit) cast() getaddrinfoPointer = getaddrinfoPointerBackup;
+
+ addresses = getAddress("63.105.9.61");
+ assert(addresses.length && addresses[0].toAddrString() == "63.105.9.61");
+ }
+ });
+}
+
+
+/**
+ * Provides _protocol-independent parsing of network addresses. Does not
+ * attempt name resolution. Uses $(D getAddressInfo) with
+ * $(D AddressInfoFlags.NUMERICHOST) if the current system supports it, and
+ * $(D InternetAddress) otherwise.
+ *
+ * Returns: An $(D Address) instance representing specified address.
+ *
+ * Throws: $(D SocketException) on failure.
+ *
+ * Example:
+ * ---
+ * writeln("Enter IP address:");
+ * string ip = readln().chomp();
+ * try
+ * {
+ * Address address = parseAddress(ip);
+ * writefln("Looking up reverse of %s:",
+ * address.toAddrString());
+ * try
+ * {
+ * string reverse = address.toHostNameString();
+ * if (reverse)
+ * writefln(" Reverse name: %s", reverse);
+ * else
+ * writeln(" Reverse hostname not found.");
+ * }
+ * catch (SocketException e)
+ * writefln(" Lookup error: %s", e.msg);
+ * }
+ * catch (SocketException e)
+ * {
+ * writefln(" %s is not a valid IP address: %s",
+ * ip, e.msg);
+ * }
+ * ---
+ */
+Address parseAddress(in char[] hostaddr, in char[] service = null)
+{
+ if (getaddrinfoPointer && freeaddrinfoPointer)
+ return getAddressInfo(hostaddr, service, AddressInfoFlags.NUMERICHOST)[0].address;
+ else
+ return parseAddress(hostaddr, serviceToPort(service));
+}
+
+/// ditto
+Address parseAddress(in char[] hostaddr, ushort port)
+{
+ if (getaddrinfoPointer && freeaddrinfoPointer)
+ return parseAddress(hostaddr, to!string(port));
+ else
+ {
+ auto in4_addr = InternetAddress.parse(hostaddr);
+ enforce(in4_addr != InternetAddress.ADDR_NONE,
+ new SocketParameterException("Invalid IP address"));
+ return new InternetAddress(in4_addr, port);
+ }
+}
+
+
+@safe unittest
+{
+ softUnittest({
+ auto address = parseAddress("63.105.9.61");
+ assert(address.toAddrString() == "63.105.9.61");
+
+ if (getaddrinfoPointer)
+ {
+ // test via inet_addr
+ auto getaddrinfoPointerBackup = getaddrinfoPointer;
+ cast() getaddrinfoPointer = null;
+ scope(exit) cast() getaddrinfoPointer = getaddrinfoPointerBackup;
+
+ address = parseAddress("63.105.9.61");
+ assert(address.toAddrString() == "63.105.9.61");
+ }
+
+ assert(collectException!SocketException(parseAddress("Invalid IP address")));
+ });
+}
+
+
+/**
+ * Class for exceptions thrown from an $(D Address).
+ */
+class AddressException: SocketOSException
+{
+ mixin socketOSExceptionCtors;
+}
+
+
+/**
+ * $(D Address) is an abstract class for representing a socket addresses.
+ *
+ * Example:
+ * ---
+ * writeln("About www.google.com port 80:");
+ * try
+ * {
+ * Address[] addresses = getAddress("www.google.com", 80);
+ * writefln(" %d addresses found.", addresses.length);
+ * foreach (int i, Address a; addresses)
+ * {
+ * writefln(" Address %d:", i+1);
+ * writefln(" IP address: %s", a.toAddrString());
+ * writefln(" Hostname: %s", a.toHostNameString());
+ * writefln(" Port: %s", a.toPortString());
+ * writefln(" Service name: %s",
+ * a.toServiceNameString());
+ * }
+ * }
+ * catch (SocketException e)
+ * writefln(" Lookup error: %s", e.msg);
+ * ---
+ */
+abstract class Address
+{
+ /// Returns pointer to underlying $(D sockaddr) structure.
+ abstract @property sockaddr* name() pure nothrow @nogc;
+ abstract @property const(sockaddr)* name() const pure nothrow @nogc; /// ditto
+
+ /// Returns actual size of underlying $(D sockaddr) structure.
+ abstract @property socklen_t nameLen() const pure nothrow @nogc;
+
+ // Socket.remoteAddress, Socket.localAddress, and Socket.receiveFrom
+ // use setNameLen to set the actual size of the address as returned by
+ // getsockname, getpeername, and recvfrom, respectively.
+ // The following implementation is sufficient for fixed-length addresses,
+ // and ensures that the length is not changed.
+ // Must be overridden for variable-length addresses.
+ protected void setNameLen(socklen_t len)
+ {
+ if (len != this.nameLen)
+ throw new AddressException(
+ format("%s expects address of length %d, not %d", typeid(this),
+ this.nameLen, len), 0);
+ }
+
+ /// Family of this address.
+ @property AddressFamily addressFamily() const pure nothrow @nogc
+ {
+ return cast(AddressFamily) name.sa_family;
+ }
+
+ // Common code for toAddrString and toHostNameString
+ 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
+ // on Windows XP and above, and not included with the WinSock import
+ // libraries shipped with DMD. Thus, we check for getnameinfo at
+ // runtime in the shared module constructor, and use it if it's
+ // available in the base class method. Classes for specific network
+ // families (e.g. InternetHost) override this method and use a
+ // deprecated, albeit commonly-available method when getnameinfo()
+ // is not available.
+ // http://technet.microsoft.com/en-us/library/aa450403.aspx
+ if (getnameinfoPointer)
+ {
+ auto buf = new char[NI_MAXHOST];
+ auto ret = getnameinfoPointer(
+ name, nameLen,
+ buf.ptr, cast(uint) buf.length,
+ null, 0,
+ numeric ? NI_NUMERICHOST : NI_NAMEREQD);
+
+ if (!numeric)
+ {
+ if (ret == EAI_NONAME)
+ return null;
+ version (Windows)
+ if (ret == WSANO_DATA)
+ return null;
+ }
+
+ enforce(ret == 0, new AddressException("Could not get " ~
+ (numeric ? "host address" : "host name")));
+ return assumeUnique(buf[0 .. strlen(buf.ptr)]);
+ }
+
+ throw new SocketFeatureException((numeric ? "Host address" : "Host name") ~
+ " lookup for this address family is not available on this system.");
+ }
+
+ // Common code for toPortString and toServiceNameString
+ private string toServiceString(bool numeric) @trusted const
+ {
+ // See toHostNameString() for details about getnameinfo().
+ if (getnameinfoPointer)
+ {
+ auto buf = new char[NI_MAXSERV];
+ enforce(getnameinfoPointer(
+ name, nameLen,
+ null, 0,
+ buf.ptr, cast(uint) buf.length,
+ numeric ? NI_NUMERICSERV : NI_NAMEREQD
+ ) == 0, new AddressException("Could not get " ~
+ (numeric ? "port number" : "service name")));
+ return assumeUnique(buf[0 .. strlen(buf.ptr)]);
+ }
+
+ throw new SocketFeatureException((numeric ? "Port number" : "Service name") ~
+ " lookup for this address family is not available on this system.");
+ }
+
+ /**
+ * Attempts to retrieve the host address as a human-readable string.
+ *
+ * Throws: $(D AddressException) on failure, or $(D SocketFeatureException)
+ * if address retrieval for this address family is not available on the
+ * current system.
+ */
+ string toAddrString() const
+ {
+ return toHostString(true);
+ }
+
+ /**
+ * Attempts to retrieve the host name as a fully qualified domain name.
+ *
+ * Returns: The FQDN corresponding to this $(D Address), or $(D null) if
+ * the host name did not resolve.
+ *
+ * Throws: $(D AddressException) on error, or $(D SocketFeatureException)
+ * if host name lookup for this address family is not available on the
+ * current system.
+ */
+ string toHostNameString() const
+ {
+ return toHostString(false);
+ }
+
+ /**
+ * Attempts to retrieve the numeric port number as a string.
+ *
+ * Throws: $(D AddressException) on failure, or $(D SocketFeatureException)
+ * if port number retrieval for this address family is not available on the
+ * current system.
+ */
+ string toPortString() const
+ {
+ return toServiceString(true);
+ }
+
+ /**
+ * Attempts to retrieve the service name as a string.
+ *
+ * Throws: $(D AddressException) on failure, or $(D SocketFeatureException)
+ * if service name lookup for this address family is not available on the
+ * current system.
+ */
+ string toServiceNameString() const
+ {
+ return toServiceString(false);
+ }
+
+ /// Human readable string representing this address.
+ override string toString() const
+ {
+ try
+ {
+ string host = toAddrString();
+ string port = toPortString();
+ if (host.indexOf(':') >= 0)
+ return "[" ~ host ~ "]:" ~ port;
+ else
+ return host ~ ":" ~ port;
+ }
+ catch (SocketException)
+ return "Unknown";
+ }
+}
+
+/**
+ * $(D UnknownAddress) encapsulates an unknown socket address.
+ */
+class UnknownAddress: Address
+{
+protected:
+ sockaddr sa;
+
+
+public:
+ override @property sockaddr* name()
+ {
+ return &sa;
+ }
+
+ override @property const(sockaddr)* name() const
+ {
+ return &sa;
+ }
+
+
+ override @property socklen_t nameLen() const
+ {
+ return cast(socklen_t) sa.sizeof;
+ }
+
+}
+
+
+/**
+ * $(D UnknownAddressReference) encapsulates a reference to an arbitrary
+ * socket address.
+ */
+class UnknownAddressReference: Address
+{
+protected:
+ sockaddr* sa;
+ socklen_t len;
+
+public:
+ /// Constructs an $(D Address) with a reference to the specified $(D sockaddr).
+ this(sockaddr* sa, socklen_t len) pure nothrow @nogc
+ {
+ this.sa = sa;
+ this.len = len;
+ }
+
+ /// 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.len = len;
+ }
+
+ override @property sockaddr* name()
+ {
+ return sa;
+ }
+
+ override @property const(sockaddr)* name() const
+ {
+ return sa;
+ }
+
+
+ override @property socklen_t nameLen() const
+ {
+ return cast(socklen_t) len;
+ }
+}
+
+
+/**
+ * $(D InternetAddress) encapsulates an IPv4 (Internet Protocol version 4)
+ * socket address.
+ *
+ * Consider using $(D getAddress), $(D parseAddress) and $(D Address) methods
+ * instead of using this class directly.
+ */
+class InternetAddress: Address
+{
+protected:
+ sockaddr_in sin;
+
+
+ this() pure nothrow @nogc
+ {
+ }
+
+
+public:
+ override @property sockaddr* name()
+ {
+ return cast(sockaddr*)&sin;
+ }
+
+ override @property const(sockaddr)* name() const
+ {
+ return cast(const(sockaddr)*)&sin;
+ }
+
+
+ override @property socklen_t nameLen() const
+ {
+ return cast(socklen_t) sin.sizeof;
+ }
+
+
+ enum uint ADDR_ANY = INADDR_ANY; /// Any IPv4 host address.
+ enum uint ADDR_NONE = INADDR_NONE; /// An invalid IPv4 host address.
+ enum ushort PORT_ANY = 0; /// Any IPv4 port number.
+
+ /// Returns the IPv4 _port number (in host byte order).
+ @property ushort port() const pure nothrow @nogc
+ {
+ return ntohs(sin.sin_port);
+ }
+
+ /// Returns the IPv4 address number (in host byte order).
+ @property uint addr() const pure nothrow @nogc
+ {
+ return ntohl(sin.sin_addr.s_addr);
+ }
+
+ /**
+ * Construct a new $(D InternetAddress).
+ * Params:
+ * addr = an IPv4 address string in the dotted-decimal form a.b.c.d,
+ * or a host name which will be resolved using an $(D InternetHost)
+ * object.
+ * port = port number, may be $(D PORT_ANY).
+ */
+ this(in char[] addr, ushort port)
+ {
+ uint uiaddr = parse(addr);
+ if (ADDR_NONE == uiaddr)
+ {
+ InternetHost ih = new InternetHost;
+ if (!ih.getHostByName(addr))
+ //throw new AddressException("Invalid internet address");
+ throw new AddressException(
+ text("Unable to resolve host '", addr, "'"));
+ uiaddr = ih.addrList[0];
+ }
+ sin.sin_family = AddressFamily.INET;
+ sin.sin_addr.s_addr = htonl(uiaddr);
+ sin.sin_port = htons(port);
+ }
+
+ /**
+ * Construct a new $(D InternetAddress).
+ * Params:
+ * addr = (optional) an IPv4 address in host byte order, may be $(D ADDR_ANY).
+ * port = port number, may be $(D PORT_ANY).
+ */
+ this(uint addr, ushort port) pure nothrow @nogc
+ {
+ sin.sin_family = AddressFamily.INET;
+ sin.sin_addr.s_addr = htonl(addr);
+ sin.sin_port = htons(port);
+ }
+
+ /// ditto
+ this(ushort port) pure nothrow @nogc
+ {
+ sin.sin_family = AddressFamily.INET;
+ sin.sin_addr.s_addr = ADDR_ANY;
+ 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
+ {
+ return to!string(inet_ntoa(sin.sin_addr));
+ }
+
+ /// Human readable string representing the IPv4 port.
+ override string toPortString() const
+ {
+ return std.conv.to!string(port);
+ }
+
+ /**
+ * Attempts to retrieve the host name as a fully qualified domain name.
+ *
+ * Returns: The FQDN corresponding to this $(D InternetAddress), or
+ * $(D null) if the host name did not resolve.
+ *
+ * Throws: $(D AddressException) on error.
+ */
+ override string toHostNameString() const
+ {
+ // getnameinfo() is the recommended way to perform a reverse (name)
+ // lookup on both Posix and Windows. However, it is only available
+ // on Windows XP and above, and not included with the WinSock import
+ // libraries shipped with DMD. Thus, we check for getnameinfo at
+ // runtime in the shared module constructor, and fall back to the
+ // deprecated getHostByAddr() if it could not be found. See also:
+ // http://technet.microsoft.com/en-us/library/aa450403.aspx
+
+ if (getnameinfoPointer)
+ return super.toHostNameString();
+ else
+ {
+ auto host = new InternetHost();
+ if (!host.getHostByAddr(ntohl(sin.sin_addr.s_addr)))
+ return null;
+ return host.name;
+ }
+ }
+
+ /**
+ * Compares with another InternetAddress of same type for equality
+ * Returns: true if the InternetAddresses share the same address and
+ * port number.
+ */
+ override bool opEquals(Object o) const
+ {
+ 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.
+ * Returns: If the string is not a legitimate IPv4 address,
+ * $(D ADDR_NONE) is returned.
+ */
+ static uint parse(in char[] addr) @trusted nothrow
+ {
+ return ntohl(inet_addr(addr.tempCString()));
+ }
+
+ /**
+ * Convert an IPv4 address number in host byte order to a human readable
+ * string representing the IPv4 address in dotted-decimal form.
+ */
+ static string addrToString(uint addr) @trusted nothrow
+ {
+ in_addr sin_addr;
+ sin_addr.s_addr = htonl(addr);
+ return to!string(inet_ntoa(sin_addr));
+ }
+}
+
+
+@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;
+ if (ih.getHostByName("digitalmars.com"))
+ {
+ const ia = new InternetAddress(ih.addrList[0], 80);
+ assert(ia.toHostNameString() == "digitalmars.com");
+
+ if (getnameinfoPointer)
+ {
+ // test reverse lookup, via gethostbyaddr
+ auto getnameinfoPointerBackup = getnameinfoPointer;
+ cast() getnameinfoPointer = null;
+ scope(exit) cast() getnameinfoPointer = getnameinfoPointerBackup;
+
+ assert(ia.toHostNameString() == "digitalmars.com");
+ }
+ }
+ });
+
+ version (SlowTests)
+ softUnittest({
+ // test failing reverse lookup
+ const InternetAddress ia = new InternetAddress("127.114.111.120", 80);
+ assert(ia.toHostNameString() is null);
+
+ if (getnameinfoPointer)
+ {
+ // test failing reverse lookup, via gethostbyaddr
+ auto getnameinfoPointerBackup = getnameinfoPointer;
+ getnameinfoPointer = null;
+ scope(exit) getnameinfoPointer = getnameinfoPointerBackup;
+
+ assert(ia.toHostNameString() is null);
+ }
+ });
+}
+
+
+/**
+ * $(D Internet6Address) encapsulates an IPv6 (Internet Protocol version 6)
+ * socket address.
+ *
+ * Consider using $(D getAddress), $(D parseAddress) and $(D Address) methods
+ * instead of using this class directly.
+ */
+class Internet6Address: Address
+{
+protected:
+ sockaddr_in6 sin6;
+
+
+ this() pure nothrow @nogc
+ {
+ }
+
+
+public:
+ override @property sockaddr* name()
+ {
+ return cast(sockaddr*)&sin6;
+ }
+
+ override @property const(sockaddr)* name() const
+ {
+ return cast(const(sockaddr)*)&sin6;
+ }
+
+
+ override @property socklen_t nameLen() const
+ {
+ return cast(socklen_t) sin6.sizeof;
+ }
+
+
+ /// Any IPv6 host address.
+ static @property ref const(ubyte)[16] ADDR_ANY() pure nothrow @nogc
+ {
+ const(ubyte)[16]* addr;
+ static if (is(typeof(IN6ADDR_ANY)))
+ {
+ addr = &IN6ADDR_ANY.s6_addr;
+ return *addr;
+ }
+ else static if (is(typeof(in6addr_any)))
+ {
+ addr = &in6addr_any.s6_addr;
+ return *addr;
+ }
+ else
+ static assert(0);
+ }
+
+ /// Any IPv6 port number.
+ enum ushort PORT_ANY = 0;
+
+ /// Returns the IPv6 port number.
+ @property ushort port() const pure nothrow @nogc
+ {
+ return ntohs(sin6.sin6_port);
+ }
+
+ /// Returns the IPv6 address.
+ @property ubyte[16] addr() const pure nothrow @nogc
+ {
+ return sin6.sin6_addr.s6_addr;
+ }
+
+ /**
+ * Construct a new $(D Internet6Address).
+ * Params:
+ * addr = an IPv6 host address string in the form described in RFC 2373,
+ * or a host name which will be resolved using $(D getAddressInfo).
+ * service = (optional) service name.
+ */
+ this(in char[] addr, in char[] service = null) @trusted
+ {
+ auto results = getAddressInfo(addr, service, AddressFamily.INET6);
+ assert(results.length && results[0].family == AddressFamily.INET6);
+ sin6 = *cast(sockaddr_in6*) results[0].address.name;
+ }
+
+ /**
+ * Construct a new $(D Internet6Address).
+ * Params:
+ * addr = an IPv6 host address string in the form described in RFC 2373,
+ * or a host name which will be resolved using $(D getAddressInfo).
+ * port = port number, may be $(D PORT_ANY).
+ */
+ this(in char[] addr, ushort port)
+ {
+ if (port == PORT_ANY)
+ this(addr);
+ else
+ this(addr, to!string(port));
+ }
+
+ /**
+ * Construct a new $(D Internet6Address).
+ * Params:
+ * addr = (optional) an IPv6 host address in host byte order, or
+ * $(D ADDR_ANY).
+ * port = port number, may be $(D PORT_ANY).
+ */
+ this(ubyte[16] addr, ushort port) pure nothrow @nogc
+ {
+ sin6.sin6_family = AddressFamily.INET6;
+ sin6.sin6_addr.s6_addr = addr;
+ sin6.sin6_port = htons(port);
+ }
+
+ /// ditto
+ this(ushort port) pure nothrow @nogc
+ {
+ sin6.sin6_family = AddressFamily.INET6;
+ sin6.sin6_addr.s6_addr = ADDR_ANY;
+ 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.
+ */
+ static ubyte[16] parse(in char[] addr) @trusted
+ {
+ // Although we could use inet_pton here, it's only available on Windows
+ // versions starting with Vista, so use getAddressInfo with NUMERICHOST
+ // 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;
+ throw new AddressException("Not an IPv6 address", 0);
+ }
+}
+
+
+@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)), i.e. a socket bound to a path name in the file system.
+ * Available only on supported systems.
+ *
+ * Linux also supports an abstract address namespace, in which addresses
+ * are independent of the file system. A socket address is abstract
+ * iff `path` starts with a _null byte (`'\0'`). Null bytes in other
+ * positions of an abstract address are allowed and have no special
+ * meaning.
+ *
+ * Example:
+ * ---
+ * auto addr = new UnixAddress("/var/run/dbus/system_bus_socket");
+ * auto abstractAddr = new UnixAddress("\0/tmp/dbus-OtHLWmCLPR");
+ * ---
+ *
+ * See_Also: $(HTTP http://man7.org/linux/man-pages/man7/unix.7.html, UNIX(7))
+ */
+ class UnixAddress: Address
+ {
+ private this() pure nothrow @nogc {}
+
+ /// Construct a new $(D UnixAddress) from the specified 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 { return null; }
+
+ /// ditto
+ 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
+static if (is(sockaddr_un))
+{
+ class UnixAddress: Address
+ {
+ protected:
+ socklen_t _nameLen;
+
+ 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 = '?';
+ _nameLen = sun.sizeof;
+ }
+
+ override void setNameLen(socklen_t len) @trusted
+ {
+ if (len > sun.sizeof)
+ throw new SocketParameterException("Not enough socket address storage");
+ _nameLen = len;
+ }
+
+ public:
+ override @property sockaddr* name()
+ {
+ return cast(sockaddr*)&sun;
+ }
+
+ override @property const(sockaddr)* name() const
+ {
+ return cast(const(sockaddr)*)&sun;
+ }
+
+ override @property socklen_t nameLen() @trusted const
+ {
+ return _nameLen;
+ }
+
+ this(in char[] path) @trusted pure
+ {
+ 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)[];
+ _nameLen = cast(socklen_t)
+ {
+ auto len = sockaddr_un.init.sun_path.offsetof + path.length;
+ // Pathname socket address must be terminated with '\0'
+ // which must be included in the address length.
+ if (sun.sun_path.ptr[0])
+ {
+ sun.sun_path.ptr[path.length] = 0;
+ ++len;
+ }
+ return len;
+ }();
+ }
+
+ this(sockaddr_un addr) pure nothrow @nogc
+ {
+ assert(addr.sun_family == AddressFamily.UNIX);
+ sun = addr;
+ }
+
+ @property string path() @trusted const pure
+ {
+ auto len = _nameLen - sockaddr_un.init.sun_path.offsetof;
+ // For pathname socket address we need to strip off the terminating '\0'
+ if (sun.sun_path.ptr[0])
+ --len;
+ return (cast(const(char)*) sun.sun_path.ptr)[0 .. len].idup;
+ }
+
+ override string toString() const pure
+ {
+ return path;
+ }
+ }
+
+ @safe unittest
+ {
+ import core.stdc.stdio : remove;
+ import std.file : deleteme;
+
+ immutable ubyte[] data = [1, 2, 3, 4];
+ Socket[2] pair;
+
+ auto names = [ deleteme ~ "-unix-socket" ];
+ version (linux)
+ names ~= "\0" ~ deleteme ~ "-abstract\0unix\0socket";
+ foreach (name; names)
+ {
+ auto address = new UnixAddress(name);
+
+ auto listener = new Socket(AddressFamily.UNIX, SocketType.STREAM);
+ scope(exit) listener.close();
+ listener.bind(address);
+ scope(exit) () @trusted { if (name[0]) remove(name.tempCString()); } ();
+ assert(listener.localAddress.toString == name);
+
+ listener.listen(1);
+
+ pair[0] = new Socket(AddressFamily.UNIX, SocketType.STREAM);
+ scope(exit) listener.close();
+
+ pair[0].connect(address);
+ scope(exit) pair[0].close();
+
+ pair[1] = listener.accept();
+ scope(exit) pair[1].close();
+
+ pair[0].send(data);
+
+ auto buf = new ubyte[data.length];
+ pair[1].receive(buf);
+ assert(buf == data);
+ }
+ }
+}
+
+
+/**
+ * Class for exceptions thrown by $(D Socket.accept).
+ */
+class SocketAcceptException: SocketOSException
+{
+ mixin socketOSExceptionCtors;
+}
+
+/// How a socket is shutdown:
+enum SocketShutdown: int
+{
+ RECEIVE = SD_RECEIVE, /// socket receives are disallowed
+ SEND = SD_SEND, /// socket sends are disallowed
+ BOTH = SD_BOTH, /// both RECEIVE and SEND
+}
+
+
+/// Flags may be OR'ed together:
+enum SocketFlags: int
+{
+ NONE = 0, /// no flags specified
+
+ OOB = MSG_OOB, /// out-of-band stream data
+ PEEK = MSG_PEEK, /// peek at incoming data without removing it from the queue, only for receiving
+ DONTROUTE = MSG_DONTROUTE, /// data should not be subject to routing; this flag may be ignored. Only for sending
+}
+
+
+private mixin template FieldProxy(string target, string field)
+{
+ mixin(`
+ @property typeof(`~target~`) `~field~`() const pure nothrow @nogc
+ {
+ return `~target~`;
+ }
+
+ /// ditto
+ @property typeof(`~target~`) `~field~`(typeof(`~target~`) value) pure nothrow @nogc
+ {
+ return `~target~` = value;
+ }
+ `);
+}
+
+
+/// Duration timeout value.
+struct TimeVal
+{
+ _ctimeval ctimeval;
+ alias tv_sec_t = typeof(ctimeval.tv_sec);
+ alias tv_usec_t = typeof(ctimeval.tv_usec);
+
+ version (StdDdoc) // no DDoc for string mixins, can't forward individual fields
+ {
+ tv_sec_t seconds; /// Number of _seconds.
+ tv_usec_t microseconds; /// Number of additional _microseconds.
+ }
+ else
+ {
+ // D interface
+ mixin FieldProxy!(`ctimeval.tv_sec`, `seconds`);
+ mixin FieldProxy!(`ctimeval.tv_usec`, `microseconds`);
+ }
+}
+
+
+/**
+ * A collection of sockets for use with $(D Socket.select).
+ *
+ * $(D SocketSet) wraps the platform $(D fd_set) type. However, unlike
+ * $(D fd_set), $(D SocketSet) is not statically limited to $(D FD_SETSIZE)
+ * or any other limit, and grows as needed.
+ */
+class SocketSet
+{
+private:
+ version (Windows)
+ {
+ // On Windows, fd_set is an array of socket handles,
+ // following a word containing the fd_set instance size.
+ // We use one dynamic array for everything, and use its first
+ // element(s) for the count.
+
+ alias fd_set_count_type = typeof(fd_set.init.fd_count);
+ alias fd_set_type = typeof(fd_set.init.fd_array[0]);
+ static assert(fd_set_type.sizeof == socket_t.sizeof);
+
+ // Number of fd_set_type elements at the start of our array that are
+ // used for the socket count and alignment
+
+ enum FD_SET_OFFSET = fd_set.fd_array.offsetof / fd_set_type.sizeof;
+ static assert(FD_SET_OFFSET);
+ static assert(fd_set.fd_count.offsetof % fd_set_type.sizeof == 0);
+
+ fd_set_type[] set;
+
+ void resize(size_t size) pure nothrow
+ {
+ set.length = FD_SET_OFFSET + size;
+ }
+
+ 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;
+ }
+
+ size_t capacity() @property const pure nothrow @nogc
+ {
+ return set.length - FD_SET_OFFSET;
+ }
+
+ inout(socket_t)[] fds() @trusted inout @property pure nothrow @nogc
+ {
+ return cast(inout(socket_t)[])set[FD_SET_OFFSET .. FD_SET_OFFSET+count];
+ }
+ }
+ else
+ version (Posix)
+ {
+ // On Posix, fd_set is a bit array. We assume that the fd_set
+ // 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);
+
+ // This is the type used in the fd_set array.
+ // Using the type of the correct size is important for big-endian
+ // architectures.
+
+ alias fd_set_type = typeof(fd_set.init.tupleof[0][0]);
+
+ // Number of file descriptors represented by one fd_set_type
+
+ enum FD_NFDBITS = 8 * fd_set_type.sizeof;
+
+ static fd_set_type mask(uint n) pure nothrow @nogc
+ {
+ return (cast(fd_set_type) 1) << (n % FD_NFDBITS);
+ }
+
+ // Array size to fit that many sockets
+
+ static size_t lengthFor(size_t size) pure nothrow @nogc
+ {
+ return (size + (FD_NFDBITS-1)) / FD_NFDBITS;
+ }
+
+ fd_set_type[] set;
+
+ void resize(size_t size) pure nothrow
+ {
+ set.length = lengthFor(size);
+ }
+
+ // Make sure we can fit that many sockets
+
+ void setMinCapacity(size_t size) pure nothrow
+ {
+ auto length = lengthFor(size);
+ if (set.length < length)
+ set.length = length;
+ }
+
+ size_t capacity() @property const pure nothrow @nogc
+ {
+ return set.length * FD_NFDBITS;
+ }
+
+ int maxfd;
+ }
+ else
+ static assert(false, "Unknown platform");
+
+public:
+
+ /**
+ * Create a SocketSet with a specific initial capacity (defaults to
+ * $(D FD_SETSIZE), the system's default capacity).
+ */
+ this(size_t size = FD_SETSIZE) pure nothrow
+ {
+ resize(size);
+ reset();
+ }
+
+ /// Reset the $(D SocketSet) so that there are 0 $(D Socket)s in the collection.
+ void reset() pure nothrow @nogc
+ {
+ version (Windows)
+ count = 0;
+ else
+ {
+ set[] = 0;
+ maxfd = -1;
+ }
+ }
+
+
+ void add(socket_t s) @trusted pure nothrow
+ {
+ version (Windows)
+ {
+ if (count == capacity)
+ {
+ set.length *= 2;
+ set.length = set.capacity;
+ }
+ ++count;
+ fds[$-1] = s;
+ }
+ else
+ {
+ auto index = s / FD_NFDBITS;
+ auto length = set.length;
+ if (index >= length)
+ {
+ while (index >= length)
+ length *= 2;
+ set.length = length;
+ set.length = set.capacity;
+ }
+ set[index] |= mask(s);
+ if (maxfd < s)
+ maxfd = s;
+ }
+ }
+
+ /**
+ * 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);
+ }
+
+ void remove(socket_t s) pure nothrow
+ {
+ version (Windows)
+ {
+ import std.algorithm.searching : countUntil;
+ auto fds = fds;
+ auto p = fds.countUntil(s);
+ if (p >= 0)
+ fds[p] = fds[--count];
+ }
+ else
+ {
+ auto index = s / FD_NFDBITS;
+ if (index >= set.length)
+ return;
+ set[index] &= ~mask(s);
+ // note: adjusting maxfd would require scanning the set, not worth it
+ }
+ }
+
+
+ /**
+ * 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);
+ }
+
+ int isSet(socket_t s) const pure nothrow @nogc
+ {
+ version (Windows)
+ {
+ import std.algorithm.searching : canFind;
+ return fds.canFind(s) ? 1 : 0;
+ }
+ else
+ {
+ if (s > maxfd)
+ return 0;
+ auto index = s / FD_NFDBITS;
+ return (set[index] & mask(s)) ? 1 : 0;
+ }
+ }
+
+
+ /// Return nonzero if this $(D Socket) is in the collection.
+ int isSet(Socket s) const pure nothrow @nogc
+ {
+ return isSet(s.sock);
+ }
+
+
+ /**
+ * 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;
+ }
+
+
+ fd_set* toFd_set() @trusted pure nothrow @nogc
+ {
+ return cast(fd_set*) set.ptr;
+ }
+
+
+ int selectn() const pure nothrow @nogc
+ {
+ version (Windows)
+ {
+ return count;
+ }
+ else version (Posix)
+ {
+ return maxfd + 1;
+ }
+ }
+}
+
+@safe unittest
+{
+ auto fds = cast(socket_t[])
+ [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);
+ foreach (fd; fds) assert(set.isSet(fd));
+
+ // 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))());
+
+ foreach (fd; fds)
+ {
+ assert(set.isSet(fd));
+ set.remove(fd);
+ assert(!set.isSet(fd));
+ }
+}
+
+@safe unittest
+{
+ softUnittest({
+ enum PAIRS = 768;
+ version (Posix)
+ () @trusted
+ {
+ enum LIMIT = 2048;
+ static assert(LIMIT > PAIRS*2);
+ import core.sys.posix.sys.resource;
+ rlimit fileLimit;
+ getrlimit(RLIMIT_NOFILE, &fileLimit);
+ assert(fileLimit.rlim_max > LIMIT, "Open file hard limit too low");
+ fileLimit.rlim_cur = LIMIT;
+ setrlimit(RLIMIT_NOFILE, &fileLimit);
+ } ();
+
+ Socket[2][PAIRS] pairs;
+ foreach (ref pair; pairs)
+ pair = socketPair();
+ scope(exit)
+ {
+ foreach (pair; pairs)
+ {
+ pair[0].close();
+ pair[1].close();
+ }
+ }
+
+ import std.random;
+ auto rng = Xorshift(42);
+ pairs[].randomShuffle(rng);
+
+ auto readSet = new SocketSet();
+ auto writeSet = new SocketSet();
+ auto errorSet = new SocketSet();
+
+ foreach (testPair; pairs)
+ {
+ void fillSets()
+ {
+ readSet.reset();
+ writeSet.reset();
+ errorSet.reset();
+ foreach (ref pair; pairs)
+ foreach (s; pair[])
+ {
+ readSet.add(s);
+ writeSet.add(s);
+ errorSet.add(s);
+ }
+ }
+
+ fillSets();
+ auto n = Socket.select(readSet, writeSet, errorSet);
+ assert(n == PAIRS*2); // All in writeSet
+ assert(writeSet.isSet(testPair[0]));
+ assert(writeSet.isSet(testPair[1]));
+ assert(!readSet.isSet(testPair[0]));
+ assert(!readSet.isSet(testPair[1]));
+ assert(!errorSet.isSet(testPair[0]));
+ assert(!errorSet.isSet(testPair[1]));
+
+ ubyte[1] b;
+ testPair[0].send(b[]);
+ fillSets();
+ n = Socket.select(readSet, null, null);
+ assert(n == 1); // testPair[1]
+ assert(readSet.isSet(testPair[1]));
+ assert(!readSet.isSet(testPair[0]));
+ testPair[1].receive(b[]);
+ }
+ });
+}
+
+@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);
+ assert(set.max >= LIMIT);
+}
+
+/// The level at which a socket option is defined:
+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
+}
+
+/// _Linger information for use with SocketOption.LINGER.
+struct Linger
+{
+ _clinger clinger;
+
+ version (StdDdoc) // no DDoc for string mixins, can't forward individual fields
+ {
+ private alias l_onoff_t = typeof(_clinger.init.l_onoff );
+ private alias l_linger_t = typeof(_clinger.init.l_linger);
+ l_onoff_t on; /// Nonzero for _on.
+ l_linger_t time; /// Linger _time.
+ }
+ else
+ {
+ // D interface
+ mixin FieldProxy!(`clinger.l_onoff`, `on`);
+ mixin FieldProxy!(`clinger.l_linger`, `time`);
+ }
+}
+
+/// Specifies a socket option:
+enum SocketOption: int
+{
+ DEBUG = SO_DEBUG, /// Record debugging information
+ BROADCAST = SO_BROADCAST, /// Allow transmission of broadcast messages
+ REUSEADDR = SO_REUSEADDR, /// Allow local reuse of address
+ LINGER = SO_LINGER, /// Linger on close if unsent data is present
+ OOBINLINE = SO_OOBINLINE, /// Receive out-of-band data in band
+ SNDBUF = SO_SNDBUF, /// Send buffer size
+ RCVBUF = SO_RCVBUF, /// Receive buffer size
+ DONTROUTE = SO_DONTROUTE, /// Do not route
+ SNDTIMEO = SO_SNDTIMEO, /// Send timeout
+ RCVTIMEO = SO_RCVTIMEO, /// Receive timeout
+ ERROR = SO_ERROR, /// Retrieve and clear error status
+ KEEPALIVE = SO_KEEPALIVE, /// Enable keep-alive packets
+ ACCEPTCONN = SO_ACCEPTCONN, /// Listen
+ RCVLOWAT = SO_RCVLOWAT, /// Minimum number of input bytes to process
+ SNDLOWAT = SO_SNDLOWAT, /// Minimum number of output bytes to process
+ TYPE = SO_TYPE, /// Socket type
+
+ // SocketOptionLevel.TCP:
+ TCP_NODELAY = .TCP_NODELAY, /// Disable the Nagle algorithm for send coalescing
+
+ // SocketOptionLevel.IPV6:
+ IPV6_UNICAST_HOPS = .IPV6_UNICAST_HOPS, /// IP unicast hop limit
+ IPV6_MULTICAST_IF = .IPV6_MULTICAST_IF, /// IP multicast interface
+ IPV6_MULTICAST_LOOP = .IPV6_MULTICAST_LOOP, /// IP multicast loopback
+ IPV6_MULTICAST_HOPS = .IPV6_MULTICAST_HOPS, /// IP multicast hops
+ IPV6_JOIN_GROUP = .IPV6_JOIN_GROUP, /// Add an IP group membership
+ IPV6_LEAVE_GROUP = .IPV6_LEAVE_GROUP, /// Drop an IP group membership
+ IPV6_V6ONLY = .IPV6_V6ONLY, /// Treat wildcard bind as AF_INET6-only
+}
+
+
+/**
+ * $(D Socket) is a class that creates a network communication endpoint using
+ * the Berkeley sockets interface.
+ */
+class Socket
+{
+private:
+ socket_t sock;
+ AddressFamily _family;
+
+ version (Windows)
+ bool _blocking = false; /// Property to get or set whether the socket is blocking or nonblocking.
+
+ // The WinSock timeouts seem to be effectively skewed by a constant
+ // offset of about half a second (value in milliseconds). This has
+ // been confirmed on updated (as of Jun 2011) Windows XP, Windows 7
+ // and Windows Server 2008 R2 boxes. The unittest below tests this
+ // behavior.
+ enum WINSOCK_TIMEOUT_SKEW = 500;
+
+ @safe unittest
+ {
+ version (SlowTests)
+ softUnittest({
+ import std.datetime;
+ import std.typecons;
+
+ enum msecs = 1000;
+ auto pair = socketPair();
+ auto sock = pair[0];
+ sock.setOption(SocketOptionLevel.SOCKET,
+ SocketOption.RCVTIMEO, dur!"msecs"(msecs));
+
+ auto sw = StopWatch(Yes.autoStart);
+ ubyte[1] buf;
+ sock.receive(buf);
+ sw.stop();
+
+ Duration readBack = void;
+ sock.getOption(SocketOptionLevel.SOCKET, SocketOption.RCVTIMEO, readBack);
+
+ assert(readBack.total!"msecs" == msecs);
+ assert(sw.peek().msecs > msecs-100 && sw.peek().msecs < msecs+100);
+ });
+ }
+
+ void setSock(socket_t handle)
+ {
+ assert(handle != socket_t.init);
+ sock = handle;
+
+ // Set the option to disable SIGPIPE on send() if the platform
+ // has it (e.g. on OS X).
+ static if (is(typeof(SO_NOSIGPIPE)))
+ {
+ setOption(SocketOptionLevel.SOCKET, cast(SocketOption) SO_NOSIGPIPE, true);
+ }
+ }
+
+
+ // For use with accepting().
+ protected this() pure nothrow @nogc
+ {
+ }
+
+
+public:
+
+ /**
+ * Create a blocking socket. If a single protocol type exists to support
+ * this socket type within the address family, the $(D ProtocolType) may be
+ * omitted.
+ */
+ this(AddressFamily af, SocketType type, ProtocolType protocol) @trusted
+ {
+ _family = af;
+ auto handle = cast(socket_t) socket(af, type, protocol);
+ if (handle == socket_t.init)
+ throw new SocketOSException("Unable to create socket");
+ setSock(handle);
+ }
+
+ /// ditto
+ this(AddressFamily af, SocketType type)
+ {
+ /* 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.
+ }
+
+
+ /// ditto
+ this(AddressFamily af, SocketType type, in char[] protocolName) @trusted
+ {
+ protoent* proto;
+ proto = getprotobyname(protocolName.tempCString());
+ if (!proto)
+ throw new SocketOSException("Unable to find the protocol");
+ this(af, type, cast(ProtocolType) proto.p_proto);
+ }
+
+
+ /**
+ * Create a blocking socket using the parameters from the specified
+ * $(D AddressInfo) structure.
+ */
+ this(in AddressInfo info)
+ {
+ this(info.family, info.type, info.protocol);
+ }
+
+ /// Use an existing socket handle.
+ this(socket_t sock, AddressFamily af) pure nothrow @nogc
+ {
+ assert(sock != socket_t.init);
+ this.sock = sock;
+ this._family = af;
+ }
+
+
+ ~this() nothrow @nogc
+ {
+ close();
+ }
+
+
+ /// Get underlying socket handle.
+ @property socket_t handle() const pure nothrow @nogc
+ {
+ return sock;
+ }
+
+ /**
+ * Get/set socket's blocking flag.
+ *
+ * When a socket is blocking, calls to receive(), accept(), and send()
+ * will block and wait for data/action.
+ * A non-blocking socket will immediately return instead of blocking.
+ */
+ @property bool blocking() @trusted const nothrow @nogc
+ {
+ version (Windows)
+ {
+ return _blocking;
+ }
+ else version (Posix)
+ {
+ return !(fcntl(handle, F_GETFL, 0) & O_NONBLOCK);
+ }
+ }
+
+ /// ditto
+ @property void blocking(bool byes) @trusted
+ {
+ version (Windows)
+ {
+ uint num = !byes;
+ if (_SOCKET_ERROR == ioctlsocket(sock, FIONBIO, &num))
+ goto err;
+ _blocking = byes;
+ }
+ else version (Posix)
+ {
+ int x = fcntl(sock, F_GETFL, 0);
+ if (-1 == x)
+ goto err;
+ if (byes)
+ x &= ~O_NONBLOCK;
+ else
+ x |= O_NONBLOCK;
+ if (-1 == fcntl(sock, F_SETFL, x))
+ goto err;
+ }
+ return; // Success.
+
+ err:
+ throw new SocketOSException("Unable to set socket blocking");
+ }
+
+
+ /// Get the socket's address family.
+ @property AddressFamily addressFamily()
+ {
+ return _family;
+ }
+
+ /// Property that indicates if this is a valid, alive socket.
+ @property bool isAlive() @trusted const
+ {
+ int type;
+ socklen_t typesize = cast(socklen_t) type.sizeof;
+ return !getsockopt(sock, SOL_SOCKET, SO_TYPE, cast(char*)&type, &typesize);
+ }
+
+ /// Associate a local address with this socket.
+ void bind(Address addr) @trusted
+ {
+ if (_SOCKET_ERROR == .bind(sock, addr.name, addr.nameLen))
+ throw new SocketOSException("Unable to bind socket");
+ }
+
+ /**
+ * Establish a connection. If the socket is blocking, connect waits for
+ * the connection to be made. If the socket is nonblocking, connect
+ * returns immediately and the connection attempt is still in progress.
+ */
+ void connect(Address to) @trusted
+ {
+ if (_SOCKET_ERROR == .connect(sock, to.name, to.nameLen))
+ {
+ int err;
+ err = _lasterr();
+
+ if (!blocking)
+ {
+ version (Windows)
+ {
+ if (WSAEWOULDBLOCK == err)
+ return;
+ }
+ else version (Posix)
+ {
+ if (EINPROGRESS == err)
+ return;
+ }
+ else
+ {
+ static assert(0);
+ }
+ }
+ throw new SocketOSException("Unable to connect socket", err);
+ }
+ }
+
+ /**
+ * Listen for an incoming connection. $(D bind) must be called before you
+ * can $(D listen). The $(D backlog) is a request of how many pending
+ * incoming connections are queued until $(D accept)ed.
+ */
+ void listen(int backlog) @trusted
+ {
+ if (_SOCKET_ERROR == .listen(sock, backlog))
+ throw new SocketOSException("Unable to listen on socket");
+ }
+
+ /**
+ * Called by $(D accept) when a new $(D Socket) must be created for a new
+ * connection. To use a derived class, override this method and return an
+ * 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.
+ */
+ protected Socket accepting() pure nothrow
+ {
+ return new Socket;
+ }
+
+ /**
+ * Accept an incoming connection. If the socket is blocking, $(D accept)
+ * waits for a connection request. Throws $(D SocketAcceptException) if
+ * unable to _accept. See $(D accepting) for use with derived classes.
+ */
+ Socket accept() @trusted
+ {
+ auto newsock = cast(socket_t).accept(sock, null, null);
+ if (socket_t.init == newsock)
+ throw new SocketAcceptException("Unable to accept socket connection");
+
+ Socket newSocket;
+ try
+ {
+ newSocket = accepting();
+ assert(newSocket.sock == socket_t.init);
+
+ newSocket.setSock(newsock);
+ version (Windows)
+ newSocket._blocking = _blocking; //inherits blocking mode
+ newSocket._family = _family; //same family
+ }
+ catch (Throwable o)
+ {
+ _close(newsock);
+ throw o;
+ }
+
+ return newSocket;
+ }
+
+ /// Disables sends and/or receives.
+ void shutdown(SocketShutdown how) @trusted nothrow @nogc
+ {
+ .shutdown(sock, cast(int) how);
+ }
+
+
+ private static void _close(socket_t sock) @system nothrow @nogc
+ {
+ version (Windows)
+ {
+ .closesocket(sock);
+ }
+ else version (Posix)
+ {
+ .close(sock);
+ }
+ }
+
+
+ /**
+ * Immediately drop any connections and release socket resources.
+ * 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.
+ */
+ void close() @trusted nothrow @nogc
+ {
+ _close(sock);
+ sock = socket_t.init;
+ }
+
+
+ /**
+ * 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))
+ throw new SocketOSException("Unable to obtain host name");
+ return to!string(result.ptr);
+ }
+
+ /// Remote endpoint $(D Address).
+ @property Address remoteAddress() @trusted
+ {
+ Address addr = createAddress();
+ socklen_t nameLen = addr.nameLen;
+ if (_SOCKET_ERROR == .getpeername(sock, addr.name, &nameLen))
+ throw new SocketOSException("Unable to obtain remote socket address");
+ addr.setNameLen(nameLen);
+ assert(addr.addressFamily == _family);
+ return addr;
+ }
+
+ /// Local endpoint $(D Address).
+ @property Address localAddress() @trusted
+ {
+ Address addr = createAddress();
+ socklen_t nameLen = addr.nameLen;
+ if (_SOCKET_ERROR == .getsockname(sock, addr.name, &nameLen))
+ throw new SocketOSException("Unable to obtain local socket address");
+ addr.setNameLen(nameLen);
+ assert(addr.addressFamily == _family);
+ return addr;
+ }
+
+ /**
+ * Send or receive error code. See $(D wouldHaveBlocked),
+ * $(D lastSocketError) and $(D Socket.getErrorText) for obtaining more
+ * information about the error.
+ */
+ 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.
+ */
+ ptrdiff_t send(const(void)[] buf, SocketFlags flags) @trusted
+ {
+ static if (is(typeof(MSG_NOSIGNAL)))
+ {
+ flags = cast(SocketFlags)(flags | MSG_NOSIGNAL);
+ }
+ version (Windows)
+ auto sent = .send(sock, buf.ptr, capToInt(buf.length), cast(int) flags);
+ else
+ auto sent = .send(sock, buf.ptr, buf.length, cast(int) flags);
+ return sent;
+ }
+
+ /// ditto
+ ptrdiff_t send(const(void)[] buf)
+ {
+ return send(buf, SocketFlags.NONE);
+ }
+
+ /**
+ * Send data to a specific destination Address. If the destination address is
+ * not specified, a connection must have been made and that address is used.
+ * If the socket is blocking and there is no buffer space left, $(D sendTo) waits.
+ * Returns: The number of bytes actually sent, or $(D Socket.ERROR) on
+ * failure.
+ */
+ ptrdiff_t sendTo(const(void)[] buf, SocketFlags flags, Address to) @trusted
+ {
+ static if (is(typeof(MSG_NOSIGNAL)))
+ {
+ flags = cast(SocketFlags)(flags | MSG_NOSIGNAL);
+ }
+ version (Windows)
+ return .sendto(
+ 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);
+ }
+
+ /// ditto
+ ptrdiff_t sendTo(const(void)[] buf, Address to)
+ {
+ return sendTo(buf, SocketFlags.NONE, to);
+ }
+
+
+ //assumes you connect()ed
+ /// ditto
+ ptrdiff_t sendTo(const(void)[] buf, SocketFlags flags) @trusted
+ {
+ static if (is(typeof(MSG_NOSIGNAL)))
+ {
+ flags = cast(SocketFlags)(flags | MSG_NOSIGNAL);
+ }
+ version (Windows)
+ 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);
+ }
+
+
+ //assumes you connect()ed
+ /// ditto
+ ptrdiff_t sendTo(const(void)[] buf)
+ {
+ return sendTo(buf, SocketFlags.NONE);
+ }
+
+
+ /**
+ * Receive data on the connection. If the socket is blocking, $(D receive)
+ * waits until there is data to be received.
+ * Returns: The number of bytes actually received, $(D 0) if the remote side
+ * has closed the connection, or $(D Socket.ERROR) on failure.
+ */
+ ptrdiff_t receive(void[] buf, SocketFlags flags) @trusted
+ {
+ version (Windows) // Does not use size_t
+ {
+ return buf.length
+ ? .recv(sock, buf.ptr, capToInt(buf.length), cast(int) flags)
+ : 0;
+ }
+ else
+ {
+ return buf.length
+ ? .recv(sock, buf.ptr, buf.length, cast(int) flags)
+ : 0;
+ }
+ }
+
+ /// ditto
+ ptrdiff_t receive(void[] buf)
+ {
+ return receive(buf, SocketFlags.NONE);
+ }
+
+ /**
+ * Receive data and get the remote endpoint $(D Address).
+ * If the socket is blocking, $(D receiveFrom) waits until there is data to
+ * be received.
+ * Returns: The number of bytes actually received, $(D 0) if the remote side
+ * has closed the connection, or $(D Socket.ERROR) on failure.
+ */
+ ptrdiff_t receiveFrom(void[] buf, SocketFlags flags, ref Address from) @trusted
+ {
+ 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, capToInt(buf.length), cast(int) flags, from.name, &nameLen);
+ from.setNameLen(nameLen);
+ assert(from.addressFamily == _family);
+ // if (!read) //connection closed
+ return read;
+ }
+ else
+ {
+ auto read = .recvfrom(sock, buf.ptr, buf.length, cast(int) flags, from.name, &nameLen);
+ from.setNameLen(nameLen);
+ assert(from.addressFamily == _family);
+ // if (!read) //connection closed
+ return read;
+ }
+ }
+
+
+ /// ditto
+ ptrdiff_t receiveFrom(void[] buf, ref Address from)
+ {
+ return receiveFrom(buf, SocketFlags.NONE, from);
+ }
+
+
+ //assumes you connect()ed
+ /// ditto
+ ptrdiff_t receiveFrom(void[] buf, SocketFlags flags) @trusted
+ {
+ if (!buf.length) //return 0 and don't think the connection closed
+ return 0;
+ version (Windows)
+ {
+ 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
+ return read;
+ }
+ }
+
+
+ //assumes you connect()ed
+ /// ditto
+ ptrdiff_t receiveFrom(void[] buf)
+ {
+ return receiveFrom(buf, SocketFlags.NONE);
+ }
+
+
+ /**
+ * 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))
+ throw new SocketOSException("Unable to get socket option");
+ return len;
+ }
+
+
+ /// Common case of getting integer and boolean options.
+ int getOption(SocketOptionLevel level, SocketOption option, out int32_t result) @trusted
+ {
+ return getOption(level, option, (&result)[0 .. 1]);
+ }
+
+
+ /// 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(level, option, (&result.clinger)[0 .. 1]);
+ }
+
+ /// Get a timeout (duration) option.
+ void getOption(SocketOptionLevel level, SocketOption option, out Duration result) @trusted
+ {
+ enforce(option == SocketOption.SNDTIMEO || option == SocketOption.RCVTIMEO,
+ new SocketParameterException("Not a valid timeout option: " ~ to!string(option)));
+ // WinSock returns the timeout values as a milliseconds DWORD,
+ // while Linux and BSD return a timeval struct.
+ version (Windows)
+ {
+ int msecs;
+ getOption(level, option, (&msecs)[0 .. 1]);
+ if (option == SocketOption.RCVTIMEO)
+ msecs += WINSOCK_TIMEOUT_SKEW;
+ result = dur!"msecs"(msecs);
+ }
+ else version (Posix)
+ {
+ TimeVal tv;
+ getOption(level, option, (&tv.ctimeval)[0 .. 1]);
+ result = dur!"seconds"(tv.seconds) + dur!"usecs"(tv.microseconds);
+ }
+ else static assert(false);
+ }
+
+ /// 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))
+ throw new SocketOSException("Unable to set socket option");
+ }
+
+
+ /// Common case for setting integer and boolean options.
+ void setOption(SocketOptionLevel level, SocketOption option, int32_t value) @trusted
+ {
+ setOption(level, option, (&value)[0 .. 1]);
+ }
+
+
+ /// Set the linger option.
+ void setOption(SocketOptionLevel level, SocketOption option, Linger value) @trusted
+ {
+ //setOption(cast(SocketOptionLevel) SocketOptionLevel.SOCKET, SocketOption.LINGER, (&value)[0 .. 1]);
+ setOption(level, option, (&value.clinger)[0 .. 1]);
+ }
+
+ /**
+ * Sets a timeout (duration) option, i.e. $(D SocketOption.SNDTIMEO) or
+ * $(D RCVTIMEO). Zero indicates no timeout.
+ *
+ * In a typical application, you might also want to consider using
+ * a non-blocking socket instead of setting a timeout on a blocking one.
+ *
+ * Note: While the receive timeout setting is generally quite accurate
+ * on *nix systems even for smaller durations, there are two issues to
+ * be aware of on Windows: First, although undocumented, the effective
+ * timeout duration seems to be the one set on the socket plus half
+ * a second. $(D setOption()) tries to compensate for that, but still,
+ * timeouts under 500ms are not possible on Windows. Second, be aware
+ * that the actual amount of time spent until a blocking call returns
+ * randomly varies on the order of 10ms.
+ *
+ * Params:
+ * level = The level at which a socket option is defined.
+ * option = Either $(D SocketOption.SNDTIMEO) or $(D SocketOption.RCVTIMEO).
+ * value = The timeout duration to set. Must not be negative.
+ *
+ * Throws: $(D SocketException) if setting the options fails.
+ *
+ * Example:
+ * ---
+ * import std.datetime;
+ * import std.typecons;
+ * auto pair = socketPair();
+ * scope(exit) foreach (s; pair) s.close();
+ *
+ * // Set a receive timeout, and then wait at one end of
+ * // the socket pair, knowing that no data will arrive.
+ * pair[0].setOption(SocketOptionLevel.SOCKET,
+ * SocketOption.RCVTIMEO, dur!"seconds"(1));
+ *
+ * auto sw = StopWatch(Yes.autoStart);
+ * ubyte[1] buffer;
+ * pair[0].receive(buffer);
+ * writefln("Waited %s ms until the socket timed out.",
+ * sw.peek.msecs);
+ * ---
+ */
+ void setOption(SocketOptionLevel level, SocketOption option, Duration value) @trusted
+ {
+ enforce(option == SocketOption.SNDTIMEO || option == SocketOption.RCVTIMEO,
+ new SocketParameterException("Not a valid timeout option: " ~ to!string(option)));
+
+ enforce(value >= dur!"hnsecs"(0), new SocketParameterException(
+ "Timeout duration must not be negative."));
+
+ 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);
+ setOption(level, option, msecs);
+ }
+ else version (Posix)
+ {
+ _ctimeval tv;
+ value.split!("seconds", "usecs")(tv.tv_sec, tv.tv_usec);
+ setOption(level, option, (&tv)[0 .. 1]);
+ }
+ else static assert(false);
+ }
+
+ /**
+ * Get a text description of this socket's error status, and clear the
+ * socket's error status.
+ */
+ string getErrorText()
+ {
+ int32_t error;
+ getOption(SocketOptionLevel.SOCKET, SocketOption.ERROR, error);
+ return formatSocketError(error);
+ }
+
+ /**
+ * Enables TCP keep-alive with the specified parameters.
+ *
+ * Params:
+ * time = Number of seconds with no activity until the first
+ * keep-alive packet is sent.
+ * interval = Number of seconds between when successive keep-alive
+ * packets are sent if no acknowledgement is received.
+ *
+ * Throws: $(D SocketOSException) if setting the options fails, or
+ * $(D SocketFeatureException) if setting keep-alive parameters is
+ * unsupported on the current platform.
+ */
+ void setKeepAlive(int time, int interval) @trusted
+ {
+ version (Windows)
+ {
+ tcp_keepalive options;
+ options.onoff = 1;
+ options.keepalivetime = time * 1000;
+ options.keepaliveinterval = interval * 1000;
+ uint cbBytesReturned;
+ enforce(WSAIoctl(sock, SIO_KEEPALIVE_VALS,
+ &options, options.sizeof,
+ null, 0,
+ &cbBytesReturned, null, null) == 0,
+ new SocketOSException("Error setting keep-alive"));
+ }
+ 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.SOCKET, SocketOption.KEEPALIVE, true);
+ }
+ else
+ throw new SocketFeatureException("Setting keep-alive options " ~
+ "is not supported on this platform");
+ }
+
+ /**
+ * Wait for a socket to change status. A wait timeout of $(REF Duration, core, time) or
+ * $(D TimeVal), may be specified; if a timeout is not specified or the
+ * $(D TimeVal) is $(D null), the maximum timeout is used. The $(D TimeVal)
+ * timeout has an unspecified value when $(D select) returns.
+ * Returns: The number of sockets with status changes, $(D 0) on timeout,
+ * or $(D -1) on interruption. If the return value is greater than $(D 0),
+ * the $(D SocketSets) are updated to only contain the sockets having status
+ * changes. For a connecting socket, a write status change means the
+ * 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
+ */
+ 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;
+ return select(checkRead, checkWrite, checkError, &tv);
+ }
+
+ /// ditto
+ //maximum timeout
+ static int select(SocketSet checkRead, SocketSet checkWrite, SocketSet checkError)
+ {
+ return select(checkRead, checkWrite, checkError, null);
+ }
+
+ /// Ditto
+ static int select(SocketSet checkRead, SocketSet checkWrite, SocketSet checkError, TimeVal* timeout) @trusted
+ in
+ {
+ //make sure none of the SocketSet's are the same object
+ if (checkRead)
+ {
+ assert(checkRead !is checkWrite);
+ assert(checkRead !is checkError);
+ }
+ if (checkWrite)
+ {
+ assert(checkWrite !is checkError);
+ }
+ }
+ body
+ {
+ fd_set* fr, fw, fe;
+ int n = 0;
+
+ version (Windows)
+ {
+ // Windows has a problem with empty fd_set`s that aren't null.
+ fr = checkRead && checkRead.count ? checkRead.toFd_set() : null;
+ fw = checkWrite && checkWrite.count ? checkWrite.toFd_set() : null;
+ fe = checkError && checkError.count ? checkError.toFd_set() : null;
+ }
+ else
+ {
+ if (checkRead)
+ {
+ fr = checkRead.toFd_set();
+ n = checkRead.selectn();
+ }
+ else
+ {
+ fr = null;
+ }
+
+ if (checkWrite)
+ {
+ fw = checkWrite.toFd_set();
+ int _n;
+ _n = checkWrite.selectn();
+ if (_n > n)
+ n = _n;
+ }
+ else
+ {
+ fw = null;
+ }
+
+ if (checkError)
+ {
+ fe = checkError.toFd_set();
+ int _n;
+ _n = checkError.selectn();
+ if (_n > n)
+ n = _n;
+ }
+ else
+ {
+ fe = null;
+ }
+
+ // Make sure the sets' capacity matches, to avoid select reading
+ // out of bounds just because one set was bigger than another
+ if (checkRead ) checkRead .setMinCapacity(n);
+ if (checkWrite) checkWrite.setMinCapacity(n);
+ if (checkError) checkError.setMinCapacity(n);
+ }
+
+ int result = .select(n, fr, fw, fe, &timeout.ctimeval);
+
+ version (Windows)
+ {
+ if (_SOCKET_ERROR == result && WSAGetLastError() == WSAEINTR)
+ return -1;
+ }
+ else version (Posix)
+ {
+ if (_SOCKET_ERROR == result && errno == EINTR)
+ return -1;
+ }
+ else
+ {
+ static assert(0);
+ }
+
+ if (_SOCKET_ERROR == result)
+ throw new SocketOSException("Socket select error");
+
+ return result;
+ }
+
+
+ /**
+ * 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)
+ {
+ static if (is(sockaddr_un))
+ {
+ case AddressFamily.UNIX:
+ result = new UnixAddress;
+ break;
+ }
+
+ case AddressFamily.INET:
+ result = new InternetAddress;
+ break;
+
+ case AddressFamily.INET6:
+ result = new Internet6Address;
+ break;
+
+ default:
+ result = new UnknownAddress;
+ }
+ return result;
+ }
+
+}
+
+
+/// $(D TcpSocket) is a shortcut class for a TCP Socket.
+class TcpSocket: Socket
+{
+ /// Constructs a blocking TCP Socket.
+ this(AddressFamily family)
+ {
+ super(family, SocketType.STREAM, ProtocolType.TCP);
+ }
+
+ /// Constructs a blocking IPv4 TCP Socket.
+ this()
+ {
+ this(AddressFamily.INET);
+ }
+
+
+ //shortcut
+ /// Constructs a blocking TCP Socket and connects to an $(D Address).
+ this(Address connectTo)
+ {
+ this(connectTo.addressFamily);
+ connect(connectTo);
+ }
+}
+
+
+/// $(D UdpSocket) is a shortcut class for a UDP Socket.
+class UdpSocket: Socket
+{
+ /// Constructs a blocking UDP Socket.
+ this(AddressFamily family)
+ {
+ super(family, SocketType.DGRAM, ProtocolType.UDP);
+ }
+
+
+ /// Constructs a blocking IPv4 UDP Socket.
+ this()
+ {
+ this(AddressFamily.INET);
+ }
+}
+
+// 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.
+ */
+Socket[2] socketPair() @trusted
+{
+ version (Posix)
+ {
+ int[2] socks;
+ if (socketpair(AF_UNIX, SOCK_STREAM, 0, socks) == -1)
+ throw new SocketOSException("Unable to create socket pair");
+
+ Socket toSocket(size_t id)
+ {
+ auto s = new Socket;
+ s.setSock(cast(socket_t) socks[id]);
+ s._family = AddressFamily.UNIX;
+ return s;
+ }
+
+ return [toSocket(0), toSocket(1)];
+ }
+ else version (Windows)
+ {
+ // We do not have socketpair() on Windows, just manually create a
+ // pair of sockets connected over some localhost port.
+ Socket[2] result;
+
+ auto listener = new TcpSocket();
+ listener.setOption(SocketOptionLevel.SOCKET, SocketOption.REUSEADDR, true);
+ listener.bind(new InternetAddress(INADDR_LOOPBACK, InternetAddress.PORT_ANY));
+ auto addr = listener.localAddress;
+ listener.listen(1);
+
+ result[0] = new TcpSocket(addr);
+ result[1] = listener.accept();
+
+ listener.close();
+ return result;
+ }
+ else
+ static assert(false);
+}
+
+///
+@safe unittest
+{
+ 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);
+}
diff --git a/libphobos/src/std/stdint.d b/libphobos/src/std/stdint.d
new file mode 100644
index 0000000..b4a5ff9
--- /dev/null
+++ b/libphobos/src/std/stdint.d
@@ -0,0 +1,131 @@
+// Written in the D programming language.
+
+/**
+ *
+ D constrains integral types to specific sizes. But efficiency
+ of different sizes varies from machine to machine,
+ pointer sizes vary, and the maximum integer size varies.
+ <b>stdint</b> offers a portable way of trading off size
+ vs efficiency, in a manner compatible with the <tt>stdint.h</tt>
+ definitions in C.
+
+ 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 $(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:
+
+ $(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 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)
+ ))
+
+ The ptr aliases are integral types guaranteed to be large enough
+ to hold a pointer without losing bits:
+
+ $(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:
+
+ $(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:
+ * ATABLE=<table border="1" cellspacing="0" cellpadding="5">$0</table>
+ *
+ * Copyright: Copyright Digital Mars 2000 - 2009.
+ * 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.
+ * 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.stdint;
+
+public import core.stdc.stdint;
diff --git a/libphobos/src/std/stdio.d b/libphobos/src/std/stdio.d
new file mode 100644
index 0000000..9683c98
--- /dev/null
+++ b/libphobos/src/std/stdio.d
@@ -0,0 +1,5159 @@
+// Written in the D programming language.
+
+/**
+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)
+Copyright: Copyright Digital Mars 2007-.
+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;
+
+import core.stdc.stddef; // wchar_t
+public import core.stdc.stdio;
+import std.algorithm.mutation; // copy
+import std.meta; // allSatisfy
+import std.range.primitives; // ElementEncodingType, empty, front,
+ // isBidirectionalRange, isInputRange, put
+import std.traits; // isSomeChar, isSomeString, Unqual, isPointer
+import std.typecons; // Flag
+
+/++
+If flag $(D KeepTerminator) is set to $(D KeepTerminator.yes), then the delimiter
+is included in the strings returned.
++/
+alias KeepTerminator = Flag!"keepTerminator";
+
+version (CRuntime_Microsoft)
+{
+ version = MICROSOFT_STDIO;
+}
+else version (CRuntime_DigitalMars)
+{
+ // Specific to the way Digital Mars C does stdio
+ version = DIGITAL_MARS_STDIO;
+}
+
+version (CRuntime_Glibc)
+{
+ // Specific to the way Gnu C does stdio
+ version = GCC_IO;
+ version = HAS_GETDELIM;
+}
+
+version (OSX)
+{
+ version = GENERIC_IO;
+ version = HAS_GETDELIM;
+}
+
+version (FreeBSD)
+{
+ version = GENERIC_IO;
+ version = HAS_GETDELIM;
+}
+
+version (NetBSD)
+{
+ version = GENERIC_IO;
+ version = HAS_GETDELIM;
+}
+
+version (Solaris)
+{
+ version = GENERIC_IO;
+ version = NO_GETDELIM;
+}
+
+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
+ // encoded in CP_ACP on Windows instead of UTF-8.
+ /+ 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;
+}
+
+version (DIGITAL_MARS_STDIO)
+{
+ extern (C)
+ {
+ /* **
+ * Digital Mars under-the-hood C I/O functions.
+ * Use _iobuf* for the unshared version of FILE*,
+ * usable when the FILE is locked.
+ */
+ nothrow:
+ @nogc:
+ int _fputc_nlock(int, _iobuf*);
+ int _fputwc_nlock(int, _iobuf*);
+ int _fgetc_nlock(_iobuf*);
+ int _fgetwc_nlock(_iobuf*);
+ int __fp_lock(FILE*);
+ void __fp_unlock(FILE*);
+
+ int setmode(int, int);
+ }
+ alias FPUTC = _fputc_nlock;
+ alias FPUTWC = _fputwc_nlock;
+ alias FGETC = _fgetc_nlock;
+ alias FGETWC = _fgetwc_nlock;
+
+ alias FLOCK = __fp_lock;
+ alias FUNLOCK = __fp_unlock;
+
+ alias _setmode = setmode;
+ enum _O_BINARY = 0x8000;
+ int _fileno(FILE* f) { return f._file; }
+ alias fileno = _fileno;
+}
+else version (MICROSOFT_STDIO)
+{
+ extern (C)
+ {
+ /* **
+ * Microsoft under-the-hood C I/O functions
+ */
+ nothrow:
+ @nogc:
+ int _fputc_nolock(int, _iobuf*);
+ int _fputwc_nolock(int, _iobuf*);
+ int _fgetc_nolock(_iobuf*);
+ int _fgetwc_nolock(_iobuf*);
+ void _lock_file(FILE*);
+ void _unlock_file(FILE*);
+ 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;
+ alias FGETC = _fgetc_nolock;
+ alias FGETWC = _fgetwc_nolock;
+
+ alias FLOCK = _lock_file;
+ alias FUNLOCK = _unlock_file;
+
+ alias setmode = _setmode;
+ alias fileno = _fileno;
+
+ enum
+ {
+ _O_RDONLY = 0x0000,
+ _O_APPEND = 0x0004,
+ _O_TEXT = 0x4000,
+ _O_BINARY = 0x8000,
+ }
+}
+else version (GCC_IO)
+{
+ /* **
+ * Gnu under-the-hood C I/O functions; see
+ * http://gnu.org/software/libc/manual/html_node/I_002fO-on-Streams.html
+ */
+ extern (C)
+ {
+ nothrow:
+ @nogc:
+ int fputc_unlocked(int, _iobuf*);
+ int fputwc_unlocked(wchar_t, _iobuf*);
+ int fgetc_unlocked(_iobuf*);
+ int fgetwc_unlocked(_iobuf*);
+ void flockfile(FILE*);
+ void funlockfile(FILE*);
+
+ private size_t fwrite_unlocked(const(void)* ptr,
+ size_t size, size_t n, _iobuf *stream);
+ }
+
+ alias FPUTC = fputc_unlocked;
+ alias FPUTWC = fputwc_unlocked;
+ alias FGETC = fgetc_unlocked;
+ alias FGETWC = fgetwc_unlocked;
+
+ alias FLOCK = flockfile;
+ alias FUNLOCK = funlockfile;
+}
+else version (GENERIC_IO)
+{
+ nothrow:
+ @nogc:
+
+ extern (C)
+ {
+ void flockfile(FILE*);
+ void funlockfile(FILE*);
+ }
+
+ int fputc_unlocked(int c, _iobuf* fp) { return fputc(c, cast(shared) fp); }
+ int fputwc_unlocked(wchar_t c, _iobuf* fp)
+ {
+ import core.stdc.wchar_ : fputwc;
+ return fputwc(c, cast(shared) fp);
+ }
+ int fgetc_unlocked(_iobuf* fp) { return fgetc(cast(shared) fp); }
+ int fgetwc_unlocked(_iobuf* fp)
+ {
+ import core.stdc.wchar_ : fgetwc;
+ return fgetwc(cast(shared) fp);
+ }
+
+ alias FPUTC = fputc_unlocked;
+ alias FPUTWC = fputwc_unlocked;
+ alias FGETC = fgetc_unlocked;
+ alias FGETWC = fgetwc_unlocked;
+
+ alias FLOCK = flockfile;
+ alias FUNLOCK = funlockfile;
+}
+else
+{
+ static assert(0, "unsupported C I/O system");
+}
+
+version (HAS_GETDELIM) extern(C) nothrow @nogc
+{
+ ptrdiff_t getdelim(char**, size_t*, int, FILE*);
+ // getline() always comes together with getdelim()
+ ptrdiff_t getline(char**, size_t*, FILE*);
+}
+
+//------------------------------------------------------------------------------
+struct ByRecord(Fields...)
+{
+private:
+ import std.typecons : Tuple;
+
+ File file;
+ char[] line;
+ Tuple!(Fields) current;
+ string format;
+
+public:
+ this(File f, string format)
+ {
+ assert(f.isOpen);
+ file = f;
+ this.format = format;
+ popFront(); // prime the range
+ }
+
+ /// Range primitive implementations.
+ @property bool empty()
+ {
+ return !file.isOpen;
+ }
+
+ /// Ditto
+ @property ref Tuple!(Fields) front()
+ {
+ return current;
+ }
+
+ /// Ditto
+ void popFront()
+ {
+ import std.conv : text;
+ import std.exception : enforce;
+ import std.format : formattedRead;
+ import std.string : chomp;
+
+ enforce(file.isOpen, "ByRecord: File must be open");
+ file.readln(line);
+ if (!line.length)
+ {
+ file.detach();
+ }
+ else
+ {
+ line = chomp(line);
+ formattedRead(line, format, &current);
+ enforce(line.empty, text("Leftover characters in record: `",
+ line, "'"));
+ }
+ }
+}
+
+template byRecord(Fields...)
+{
+ ByRecord!(Fields) byRecord(File f, string format)
+ {
+ return typeof(return)(f, format);
+ }
+}
+
+/**
+Encapsulates a $(D FILE*). Generally D does not attempt to provide
+thin wrappers over equivalent functions in the C standard library, but
+manipulating $(D FILE*) values directly is unsafe and error-prone in
+many ways. The $(D File) type ensures safe manipulation, automatic
+file closing, and a lot of convenience.
+
+The underlying $(D FILE*) handle is maintained in a reference-counted
+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.
+
+Example:
+----
+// test.d
+void main(string[] args)
+{
+ auto f = File("test.txt", "w"); // open for writing
+ f.write("Hello");
+ if (args.length > 1)
+ {
+ auto g = f; // now g and f write to the same file
+ // internal reference count is 2
+ g.write(", ", args[1]);
+ // g exits scope, reference count decreases to 1
+ }
+ f.writeln("!");
+ // f exits scope, reference count falls to zero,
+ // underlying `FILE*` is closed.
+}
+----
+$(CONSOLE
+% rdmd test.d Jimmy
+% cat test.txt
+Hello, Jimmy!
+% __
+)
+ */
+struct File
+{
+ import std.range.primitives : ElementEncodingType;
+ import std.traits : isScalarType, isArray;
+ enum Orientation { unknown, narrow, wide }
+
+ private struct Impl
+ {
+ FILE * handle = null; // Is null iff this Impl is closed by another File
+ uint refs = uint.max / 2;
+ bool isPopened; // true iff the stream has been created by popen()
+ Orientation orientation;
+ }
+ private Impl* _p;
+ private string _name;
+
+ package this(FILE* handle, string name, uint refs = 1, bool isPopened = false) @trusted
+ {
+ import core.stdc.stdlib : malloc;
+ import std.exception : enforce;
+
+ assert(!_p);
+ _p = cast(Impl*) enforce(malloc(Impl.sizeof), "Out of memory");
+ _p.handle = handle;
+ _p.refs = refs;
+ _p.isPopened = isPopened;
+ _p.orientation = Orientation.unknown;
+ _name = name;
+ }
+
+/**
+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.
+
+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
+ {
+ import std.conv : text;
+ import std.exception : errnoEnforce;
+
+ this(errnoEnforce(.fopen(name, stdioOpenmode),
+ 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
+ {
+ detach();
+ }
+
+ this(this) @safe nothrow
+ {
+ if (!_p) return;
+ assert(_p.refs);
+ ++_p.refs;
+ }
+
+/**
+Assigns a file to another. The target of the assignment gets detached
+from whatever file it was attached to, and attaches itself to the new
+file.
+ */
+ void opAssign(File rhs) @safe
+ {
+ import std.algorithm.mutation : swap;
+
+ swap(this, rhs);
+ }
+
+/**
+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 $(HTTP
+cplusplus.com/reference/clibrary/cstdio/fopen.html, fopen) function.
+
+Throws: $(D ErrnoException) in case of error.
+ */
+ void open(string name, in char[] stdioOpenmode = "rb") @safe
+ {
+ detach();
+ 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.conv : text;
+ import std.exception : enforce, errnoEnforce;
+ import std.internal.cstring : tempCString;
+
+ 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
+ {
+ import std.exception : assertThrown, assertNotThrown;
+ static import std.file;
+
+ 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
+ {
+ import std.exception : assertThrown, assertNotThrown;
+ static import std.file;
+
+ 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 $(HTTP
+opengroup.org/onlinepubs/007908799/xsh/_popen.html, _popen).
+
+Throws: $(D ErrnoException) in case of error.
+ */
+ version (Posix) void popen(string command, in char[] stdioOpenmode = "r") @safe
+ {
+ import std.exception : errnoEnforce;
+
+ detach();
+ this = File(errnoEnforce(.popen(command, stdioOpenmode),
+ "Cannot run command `"~command~"'"),
+ command, 1, true);
+ }
+
+/**
+First calls $(D detach) (throwing on failure), and then attempts to
+associate the given file descriptor with the $(D File). The mode must
+be compatible with the mode of the file descriptor.
+
+Throws: $(D ErrnoException) in case of error.
+ */
+ void fdopen(int fd, in char[] stdioOpenmode = "rb") @safe
+ {
+ fdopen(fd, stdioOpenmode, null);
+ }
+
+ package void fdopen(int fd, in char[] stdioOpenmode, string name) @trusted
+ {
+ import std.exception : errnoEnforce;
+ import std.internal.cstring : tempCString;
+
+ auto modez = stdioOpenmode.tempCString();
+ detach();
+
+ version (DIGITAL_MARS_STDIO)
+ {
+ // This is a re-implementation of DMC's fdopen, but without the
+ // mucking with the file descriptor. POSIX standard requires the
+ // new fdopen'd file to retain the given file descriptor's
+ // position.
+ import core.stdc.stdio : fopen;
+ auto fp = fopen("NUL", modez);
+ errnoEnforce(fp, "Cannot open placeholder NUL stream");
+ FLOCK(fp);
+ auto iob = cast(_iobuf*) fp;
+ .close(iob._file);
+ iob._file = fd;
+ iob._flag &= ~_IOTRAN;
+ FUNLOCK(fp);
+ }
+ else
+ {
+ version (Windows) // MSVCRT
+ auto fp = _fdopen(fd, modez);
+ else version (Posix)
+ {
+ import core.sys.posix.stdio : fdopen;
+ auto fp = fdopen(fd, modez);
+ }
+ errnoEnforce(fp);
+ }
+ this = File(fp, name);
+ }
+
+ // Declare a dummy HANDLE to allow generating documentation
+ // for Windows-only methods.
+ version (StdDdoc) { version (Windows) {} else alias HANDLE = int; }
+
+/**
+First calls $(D detach) (throwing on failure), and then attempts to
+associate the given Windows $(D HANDLE) with the $(D File). The mode must
+be compatible with the access attributes of the handle. Windows only.
+
+Throws: $(D ErrnoException) in case of error.
+*/
+ version (StdDdoc)
+ void windowsHandleOpen(HANDLE handle, in char[] stdioOpenmode);
+
+ version (Windows)
+ void windowsHandleOpen(HANDLE handle, in char[] stdioOpenmode)
+ {
+ import core.stdc.stdint : intptr_t;
+ import std.exception : errnoEnforce;
+ import std.format : format;
+
+ // Create file descriptors from the handles
+ version (DIGITAL_MARS_STDIO)
+ auto fd = _handleToFD(handle, FHND_DEVICE);
+ else // MSVCRT
+ {
+ int mode;
+ modeLoop:
+ foreach (c; stdioOpenmode)
+ switch (c)
+ {
+ case 'r': mode |= _O_RDONLY; break;
+ case '+': mode &=~_O_RDONLY; break;
+ case 'a': mode |= _O_APPEND; break;
+ case 'b': mode |= _O_BINARY; break;
+ case 't': mode |= _O_TEXT; break;
+ case ',': break modeLoop;
+ default: break;
+ }
+
+ auto fd = _open_osfhandle(cast(intptr_t) handle, mode);
+ }
+
+ errnoEnforce(fd >= 0, "Cannot open Windows HANDLE");
+ fdopen(fd, stdioOpenmode, "HANDLE(%s)".format(handle));
+ }
+
+
+/** Returns $(D true) if the file is opened. */
+ @property bool isOpen() const @safe pure nothrow
+ {
+ return _p !is null && _p.handle;
+ }
+
+/**
+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.
+ */
+ @property bool eof() const @trusted pure
+ {
+ import std.exception : enforce;
+
+ enforce(_p && _p.handle, "Calling eof() against an unopened file.");
+ return .feof(cast(FILE*) _p.handle) != 0;
+ }
+
+/** Returns the name of the last opened file, if any.
+If a $(D File) was created with $(LREF tmpfile) and $(LREF wrapFile)
+it has no name.*/
+ @property string name() const @safe pure nothrow
+ {
+ return _name;
+ }
+
+/**
+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
+ {
+ return !isOpen || .ferror(cast(FILE*) _p.handle);
+ }
+
+ @safe unittest
+ {
+ // Issue 12349
+ static import std.file;
+ auto deleteme = testFilename();
+ auto f = File(deleteme, "w");
+ scope(exit) std.file.remove(deleteme);
+
+ f.close();
+ assert(f.error);
+ }
+
+/**
+Detaches from the underlying file. If the sole owner, calls $(D close).
+
+Throws: $(D ErrnoException) on failure if closing the file.
+ */
+ void detach() @safe
+ {
+ if (!_p) return;
+ if (_p.refs == 1)
+ close();
+ else
+ {
+ assert(_p.refs);
+ --_p.refs;
+ _p = null;
+ }
+ }
+
+ @safe unittest
+ {
+ static import std.file;
+
+ auto deleteme = testFilename();
+ scope(exit) std.file.remove(deleteme);
+ auto f = File(deleteme, "w");
+ {
+ auto f2 = f;
+ f2.detach();
+ }
+ assert(f._p.refs == 1);
+ f.close();
+ }
+
+/**
+If the file was unopened, succeeds vacuously. Otherwise closes the
+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
+always closes the file; consequently, all other $(D File) objects
+referring to the same handle will see a closed file henceforth.
+
+Throws: $(D ErrnoException) on error.
+ */
+ void close() @trusted
+ {
+ import core.stdc.stdlib : free;
+ import std.exception : errnoEnforce;
+
+ if (!_p) return; // succeed vacuously
+ scope(exit)
+ {
+ assert(_p.refs);
+ if (!--_p.refs)
+ free(_p);
+ _p = null; // start a new life
+ }
+ if (!_p.handle) return; // Impl is closed by another File
+
+ scope(exit) _p.handle = null; // nullify the handle anyway
+ version (Posix)
+ {
+ import core.sys.posix.stdio : pclose;
+ import std.format : format;
+
+ if (_p.isPopened)
+ {
+ auto res = pclose(_p.handle);
+ errnoEnforce(res != -1,
+ "Could not close pipe `"~_name~"'");
+ errnoEnforce(res == 0, format("Command returned %d", res));
+ return;
+ }
+ }
+ errnoEnforce(.fclose(_p.handle) == 0,
+ "Could not close file `"~_name~"'");
+ }
+
+/**
+If the file is not opened, succeeds vacuously. Otherwise, returns
+$(HTTP cplusplus.com/reference/clibrary/cstdio/_clearerr.html,
+_clearerr) for the file handle.
+ */
+ void clearerr() @safe pure nothrow
+ {
+ _p is null || _p.handle is null ||
+ .clearerr(_p.handle);
+ }
+
+/**
+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.
+ */
+ void flush() @trusted
+ {
+ import std.exception : enforce, errnoEnforce;
+
+ enforce(isOpen, "Attempting to flush() in an unopened file");
+ errnoEnforce(.fflush(_p.handle) == 0);
+ }
+
+ @safe unittest
+ {
+ // Issue 12349
+ import std.exception : assertThrown;
+ static import std.file;
+
+ auto deleteme = testFilename();
+ auto f = File(deleteme, "w");
+ scope(exit) std.file.remove(deleteme);
+
+ f.close();
+ assertThrown(f.flush());
+ }
+
+/**
+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.
+
+Returns: The slice of $(D buffer) containing the data that was actually read.
+This will be shorter than $(D buffer) if EOF was reached before the buffer
+could be filled.
+
+Throws: $(D Exception) if $(D buffer) is empty.
+ $(D ErrnoException) if the file is not opened or the call to $(D fread) fails.
+
+$(D rawRead) always reads in binary mode on Windows.
+ */
+ T[] rawRead(T)(T[] buffer)
+ {
+ import std.exception : errnoEnforce;
+
+ if (!buffer.length)
+ throw new Exception("rawRead must take a non-empty buffer");
+ version (Windows)
+ {
+ immutable fd = ._fileno(_p.handle);
+ immutable mode = ._setmode(fd, _O_BINARY);
+ scope(exit) ._setmode(fd, mode);
+ version (DIGITAL_MARS_STDIO)
+ {
+ import core.atomic : atomicOp;
+
+ // @@@BUG@@@ 4243
+ immutable info = __fhnd_info[fd];
+ atomicOp!"&="(__fhnd_info[fd], ~FHND_TEXT);
+ scope(exit) __fhnd_info[fd] = info;
+ }
+ }
+ immutable freadResult = trustedFread(_p.handle, buffer);
+ assert(freadResult <= buffer.length); // fread return guarantee
+ if (freadResult != buffer.length) // error or eof
+ {
+ errnoEnforce(!error);
+ return buffer[0 .. freadResult];
+ }
+ return buffer;
+ }
+
+ ///
+ @system unittest
+ {
+ static import std.file;
+
+ 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");
+ }
+
+/**
+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.
+
+$(D rawWrite) always writes in binary mode on Windows.
+
+Throws: $(D ErrnoException) if the file is not opened or if the call to $(D fwrite) fails.
+ */
+ void rawWrite(T)(in T[] buffer)
+ {
+ import std.conv : text;
+ import std.exception : errnoEnforce;
+
+ version (Windows)
+ {
+ flush(); // before changing translation mode
+ immutable fd = ._fileno(_p.handle);
+ immutable mode = ._setmode(fd, _O_BINARY);
+ scope(exit) ._setmode(fd, mode);
+ version (DIGITAL_MARS_STDIO)
+ {
+ import core.atomic : atomicOp;
+
+ // @@@BUG@@@ 4243
+ immutable info = __fhnd_info[fd];
+ atomicOp!"&="(__fhnd_info[fd], ~FHND_TEXT);
+ scope(exit) __fhnd_info[fd] = info;
+ }
+ scope(exit) flush(); // before restoring translation mode
+ }
+ auto result = trustedFwrite(_p.handle, 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, "'"));
+ }
+
+ ///
+ @system unittest
+ {
+ static import std.file;
+
+ 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(testFile) == "\r\n\n\r\n");
+ }
+
+/**
+Calls $(HTTP cplusplus.com/reference/clibrary/cstdio/fseek.html, fseek)
+for the file handle.
+
+Throws: $(D Exception) if the file is not opened.
+ $(D ErrnoException) if the call to $(D fseek) fails.
+ */
+ void seek(long offset, int origin = SEEK_SET) @trusted
+ {
+ import std.conv : to, text;
+ import std.exception : enforce, errnoEnforce;
+
+ enforce(isOpen, "Attempting to seek() in an unopened file");
+ version (Windows)
+ {
+ 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;
+ alias fseekFun = fseeko;
+ }
+ errnoEnforce(fseekFun(_p.handle, to!off_t(offset), origin) == 0,
+ "Could not seek in file `"~_name~"'");
+ }
+
+ @system unittest
+ {
+ import std.conv : text;
+ static import std.file;
+
+ auto deleteme = testFilename();
+ auto f = File(deleteme, "w+");
+ scope(exit) { f.close(); std.file.remove(deleteme); }
+ f.rawWrite("abcdefghijklmnopqrstuvwxyz");
+ f.seek(7);
+ assert(f.readln() == "hijklmnopqrstuvwxyz");
+
+ 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 $(HTTP cplusplus.com/reference/clibrary/cstdio/ftell.html, ftell) for the
+managed file handle.
+
+Throws: $(D Exception) if the file is not opened.
+ $(D ErrnoException) if the call to $(D ftell) fails.
+ */
+ @property ulong tell() const @trusted
+ {
+ import std.exception : enforce, errnoEnforce;
+
+ enforce(isOpen, "Attempting to tell() in an unopened file");
+ version (Windows)
+ {
+ version (CRuntime_Microsoft)
+ immutable result = _ftelli64(cast(FILE*) _p.handle);
+ else
+ immutable result = ftell(cast(FILE*) _p.handle);
+ }
+ else version (Posix)
+ {
+ import core.sys.posix.stdio : ftello;
+ immutable result = ftello(cast(FILE*) _p.handle);
+ }
+ errnoEnforce(result != -1,
+ "Query ftell() failed for file `"~_name~"'");
+ return result;
+ }
+
+ ///
+ @system unittest
+ {
+ import std.conv : text;
+ static import std.file;
+
+ 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 $(HTTP cplusplus.com/reference/clibrary/cstdio/_rewind.html, _rewind)
+for the file handle.
+
+Throws: $(D Exception) if the file is not opened.
+ */
+ void rewind() @safe
+ {
+ import std.exception : enforce;
+
+ enforce(isOpen, "Attempting to rewind() an unopened file");
+ .rewind(_p.handle);
+ }
+
+/**
+Calls $(HTTP cplusplus.com/reference/clibrary/cstdio/_setvbuf.html, _setvbuf) for
+the file handle.
+
+Throws: $(D Exception) if the file is not opened.
+ $(D ErrnoException) if the call to $(D setvbuf) fails.
+ */
+ void setvbuf(size_t size, int mode = _IOFBF) @trusted
+ {
+ import std.exception : enforce, errnoEnforce;
+
+ enforce(isOpen, "Attempting to call setvbuf() on an unopened file");
+ errnoEnforce(.setvbuf(_p.handle, null, mode, size) == 0,
+ "Could not set buffering for file `"~_name~"'");
+ }
+
+/**
+Calls $(HTTP cplusplus.com/reference/clibrary/cstdio/_setvbuf.html,
+_setvbuf) for the file handle.
+
+Throws: $(D Exception) if the file is not opened.
+ $(D ErrnoException) if the call to $(D setvbuf) fails.
+*/
+ void setvbuf(void[] buf, int mode = _IOFBF) @trusted
+ {
+ import std.exception : enforce, errnoEnforce;
+
+ enforce(isOpen, "Attempting to call setvbuf() on an unopened file");
+ errnoEnforce(.setvbuf(_p.handle,
+ cast(char*) buf.ptr, mode, buf.length) == 0,
+ "Could not set buffering for file `"~_name~"'");
+ }
+
+
+ version (Windows)
+ {
+ import core.sys.windows.windows : ULARGE_INTEGER, OVERLAPPED, BOOL;
+
+ private BOOL lockImpl(alias F, Flags...)(ulong start, ulong length,
+ Flags flags)
+ {
+ if (!start && !length)
+ length = ulong.max;
+ ULARGE_INTEGER liStart = void, liLength = void;
+ liStart.QuadPart = start;
+ liLength.QuadPart = length;
+ OVERLAPPED overlapped;
+ overlapped.Offset = liStart.LowPart;
+ overlapped.OffsetHigh = liStart.HighPart;
+ overlapped.hEvent = null;
+ return F(windowsHandle, flags, 0, liLength.LowPart,
+ liLength.HighPart, &overlapped);
+ }
+
+ private static T wenforce(T)(T cond, string str)
+ {
+ import core.sys.windows.windows : GetLastError;
+ import std.windows.syserror : sysErrorString;
+
+ if (cond) return cond;
+ throw new Exception(str ~ ": " ~ sysErrorString(GetLastError()));
+ }
+ }
+ version (Posix)
+ {
+ private int lockImpl(int operation, short l_type,
+ ulong start, ulong length)
+ {
+ 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;
+ fl.l_whence = SEEK_SET;
+ fl.l_start = to!off_t(start);
+ fl.l_len = to!off_t(length);
+ fl.l_pid = getpid();
+ return fcntl(fileno, operation, &fl);
+ }
+ }
+
+/**
+Locks the specified file segment. If the file segment is already locked
+by another process, waits until the existing lock is released.
+If both $(D start) and $(D length) are zero, the entire file is locked.
+
+Locks created using $(D lock) and $(D tryLock) have the following properties:
+$(UL
+ $(LI All locks are automatically released when the process terminates.)
+ $(LI Locks are not inherited by child processes.)
+ $(LI Closing a file will release all locks associated with the file. On POSIX,
+ even locks acquired via a different $(D File) will be released as well.)
+ $(LI Not all NFS implementations correctly implement file locking.)
+)
+ */
+ void lock(LockType lockType = LockType.readWrite,
+ ulong start = 0, ulong length = 0)
+ {
+ import std.exception : enforce;
+
+ enforce(isOpen, "Attempting to call lock() on an unopened file");
+ version (Posix)
+ {
+ import core.sys.posix.fcntl : F_RDLCK, F_SETLKW, F_WRLCK;
+ import std.exception : errnoEnforce;
+ immutable short type = lockType == LockType.readWrite
+ ? F_WRLCK : F_RDLCK;
+ errnoEnforce(lockImpl(F_SETLKW, type, start, length) != -1,
+ "Could not set lock for file `"~_name~"'");
+ }
+ 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),
+ "Could not set lock for file `"~_name~"'");
+ }
+ else
+ static assert(false);
+ }
+
+/**
+Attempts to lock the specified file segment.
+If both $(D start) and $(D length) are zero, the entire file is locked.
+Returns: $(D true) if the lock was successful, and $(D false) if the
+specified file segment was already locked.
+ */
+ bool tryLock(LockType lockType = LockType.readWrite,
+ ulong start = 0, ulong length = 0)
+ {
+ import std.exception : enforce;
+
+ enforce(isOpen, "Attempting to call tryLock() on an unopened file");
+ version (Posix)
+ {
+ import core.stdc.errno : EACCES, EAGAIN, errno;
+ import core.sys.posix.fcntl : F_RDLCK, F_SETLK, F_WRLCK;
+ import std.exception : errnoEnforce;
+ immutable short type = lockType == LockType.readWrite
+ ? F_WRLCK : F_RDLCK;
+ immutable res = lockImpl(F_SETLK, type, start, length);
+ if (res == -1 && (errno == EACCES || errno == EAGAIN))
+ return false;
+ errnoEnforce(res != -1, "Could not set lock for file `"~_name~"'");
+ return true;
+ }
+ 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,
+ type | LOCKFILE_FAIL_IMMEDIATELY);
+ if (!res && (GetLastError() == ERROR_IO_PENDING
+ || GetLastError() == ERROR_LOCK_VIOLATION))
+ return false;
+ wenforce(res, "Could not set lock for file `"~_name~"'");
+ return true;
+ }
+ else
+ static assert(false);
+ }
+
+/**
+Removes the lock over the specified file segment.
+ */
+ void unlock(ulong start = 0, ulong length = 0)
+ {
+ import std.exception : enforce;
+
+ enforce(isOpen, "Attempting to call unlock() on an unopened file");
+ version (Posix)
+ {
+ import core.sys.posix.fcntl : F_SETLK, F_UNLCK;
+ import std.exception : errnoEnforce;
+ errnoEnforce(lockImpl(F_SETLK, F_UNLCK, start, length) != -1,
+ "Could not remove lock for file `"~_name~"'");
+ }
+ else
+ version (Windows)
+ {
+ import core.sys.windows.windows : UnlockFileEx;
+ wenforce(lockImpl!UnlockFileEx(start, length),
+ "Could not remove lock for file `"~_name~"'");
+ }
+ else
+ static assert(false);
+ }
+
+ version (Windows)
+ @system unittest
+ {
+ static import std.file;
+ auto deleteme = testFilename();
+ scope(exit) std.file.remove(deleteme);
+ auto f = File(deleteme, "wb");
+ assert(f.tryLock());
+ auto g = File(deleteme, "wb");
+ assert(!g.tryLock());
+ assert(!g.tryLock(LockType.read));
+ f.unlock();
+ f.lock(LockType.read);
+ assert(!g.tryLock());
+ assert(g.tryLock(LockType.read));
+ f.unlock();
+ g.unlock();
+ }
+
+ version (Posix)
+ @system unittest
+ {
+ static import std.file;
+ auto deleteme = testFilename();
+ scope(exit) std.file.remove(deleteme);
+
+ // Since locks are per-process, we cannot test lock failures within
+ // the same process. fork() is used to create a second process.
+ static void runForked(void delegate() code)
+ {
+ import core.stdc.stdlib : exit;
+ import core.sys.posix.sys.wait : wait;
+ import core.sys.posix.unistd : fork;
+ int child, status;
+ if ((child = fork()) == 0)
+ {
+ code();
+ exit(0);
+ }
+ else
+ {
+ assert(wait(&status) != -1);
+ assert(status == 0, "Fork crashed");
+ }
+ }
+
+ auto f = File(deleteme, "w+b");
+
+ runForked
+ ({
+ auto g = File(deleteme, "a+b");
+ assert(g.tryLock());
+ g.unlock();
+ assert(g.tryLock(LockType.read));
+ });
+
+ assert(f.tryLock());
+ runForked
+ ({
+ auto g = File(deleteme, "a+b");
+ assert(!g.tryLock());
+ assert(!g.tryLock(LockType.read));
+ });
+ f.unlock();
+
+ f.lock(LockType.read);
+ runForked
+ ({
+ auto g = File(deleteme, "a+b");
+ assert(!g.tryLock());
+ assert(g.tryLock(LockType.read));
+ g.unlock();
+ });
+ f.unlock();
+ }
+
+
+/**
+Writes its arguments in text format to the file.
+
+Throws: $(D Exception) if the file is not opened.
+ $(D ErrnoException) on an error writing to the file.
+*/
+ void write(S...)(S args)
+ {
+ import std.traits : isBoolean, isIntegral, isAggregateType;
+ auto w = lockingTextWriter();
+ foreach (arg; args)
+ {
+ alias A = typeof(arg);
+ static if (isAggregateType!A || is(A == enum))
+ {
+ import std.format : formattedWrite;
+
+ formattedWrite(w, "%s", arg);
+ }
+ else static if (isSomeString!A)
+ {
+ put(w, arg);
+ }
+ else static if (isIntegral!A)
+ {
+ import std.conv : toTextRange;
+
+ toTextRange(arg, w);
+ }
+ else static if (isBoolean!A)
+ {
+ put(w, arg ? "true" : "false");
+ }
+ else static if (isSomeChar!A)
+ {
+ put(w, arg);
+ }
+ else
+ {
+ import std.format : formattedWrite;
+
+ // Most general case
+ formattedWrite(w, "%s", arg);
+ }
+ }
+ }
+
+/**
+Writes its arguments in text format to the file, followed by a newline.
+
+Throws: $(D Exception) if the file is not opened.
+ $(D ErrnoException) on an error writing to the file.
+*/
+ void writeln(S...)(S args)
+ {
+ write(args, '\n');
+ }
+
+/**
+Writes its arguments in text format to the file, according to the
+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;
+
+ formattedWrite(lockingTextWriter(), fmt, args);
+ }
+
+ /// Equivalent to `file.writef(fmt, args, '\n')`.
+ 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 this.writefln(fmt, args);
+ }
+
+ /// ditto
+ void writefln(Char, A...)(in Char[] fmt, A args)
+ {
+ import std.format : formattedWrite;
+
+ auto w = lockingTextWriter();
+ formattedWrite(w, fmt, args);
+ w.put('\n');
+ }
+
+/**
+Read line from the file handle and return it as a specified type.
+
+This version manages its own read buffer, which means one memory allocation per call. If you are not
+retaining a reference to the read data, consider the $(D File.readln(buf)) version, which may offer
+better performance as it can reuse its read buffer.
+
+Params:
+ S = Template parameter; the type of the allocated buffer, and the type returned. Defaults to $(D string).
+ terminator = Line terminator (by default, $(D '\n')).
+
+Note:
+ String terminators are not supported due to ambiguity with readln(buf) below.
+
+Returns:
+ The line that was read, including the line terminator character.
+
+Throws:
+ $(D StdioException) on I/O error, or $(D UnicodeException) on Unicode conversion error.
+
+Example:
+---
+// Reads `stdin` and writes it to `stdout`.
+import std.stdio;
+
+void main()
+{
+ string line;
+ while ((line = stdin.readln()) !is null)
+ write(line);
+}
+---
+*/
+ S readln(S = string)(dchar terminator = '\n')
+ if (isSomeString!S)
+ {
+ Unqual!(ElementEncodingType!S)[] buf;
+ readln(buf, terminator);
+ return cast(S) buf;
+ }
+
+ @system unittest
+ {
+ import std.algorithm.comparison : equal;
+ static import std.file;
+ import std.meta : AliasSeq;
+
+ auto deleteme = testFilename();
+ std.file.write(deleteme, "hello\nworld\n");
+ scope(exit) std.file.remove(deleteme);
+ foreach (String; AliasSeq!(string, char[], wstring, wchar[], dstring, dchar[]))
+ {
+ auto witness = [ "hello\n", "world\n" ];
+ auto f = File(deleteme);
+ uint i = 0;
+ String buf;
+ while ((buf = f.readln!String()).length)
+ {
+ assert(i < witness.length);
+ assert(equal(buf, witness[i++]));
+ }
+ assert(i == witness.length);
+ }
+ }
+
+ @system unittest
+ {
+ static import std.file;
+ import std.typecons : Tuple;
+
+ auto deleteme = testFilename();
+ std.file.write(deleteme, "cześć \U0002000D");
+ scope(exit) std.file.remove(deleteme);
+ uint[] lengths = [12,8,7];
+ foreach (uint i, C; Tuple!(char, wchar, dchar).Types)
+ {
+ immutable(C)[] witness = "cześć \U0002000D";
+ auto buf = File(deleteme).readln!(immutable(C)[])();
+ assert(buf.length == lengths[i]);
+ assert(buf == witness);
+ }
+ }
+
+/**
+Read line from the file handle and write it to $(D buf[]), including
+terminating character.
+
+This can be faster than $(D line = File.readln()) because you can reuse
+the buffer for each call. Note that reusing the buffer means that you
+must copy the previous contents if you wish to retain them.
+
+Params:
+buf = Buffer used to store the resulting line data. buf is
+resized as necessary.
+terminator = Line terminator (by default, $(D '\n')). Use
+$(REF newline, std,ascii) for portability (unless the file was opened in
+text mode).
+
+Returns:
+0 for end of file, otherwise number of characters read
+
+Throws: $(D StdioException) on I/O error, or $(D UnicodeException) on Unicode
+conversion error.
+
+Example:
+---
+// Read lines from `stdin` into a string
+// Ignore lines starting with '#'
+// Write the string to `stdout`
+
+void main()
+{
+ string output;
+ char[] buf;
+
+ while (stdin.readln(buf))
+ {
+ if (buf[0] == '#')
+ continue;
+
+ output ~= buf;
+ }
+
+ write(output);
+}
+---
+
+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 `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))
+ {
+ import std.exception : enforce;
+
+ static if (is(C == char))
+ {
+ enforce(_p && _p.handle, "Attempt to read from an unopened file.");
+ if (_p.orientation == Orientation.unknown)
+ {
+ import core.stdc.wchar_ : fwide;
+ auto w = fwide(_p.handle, 0);
+ if (w < 0) _p.orientation = Orientation.narrow;
+ else if (w > 0) _p.orientation = Orientation.wide;
+ }
+ return readlnImpl(_p.handle, buf, terminator, _p.orientation);
+ }
+ else
+ {
+ // TODO: optimize this
+ string s = readln(terminator);
+ buf.length = 0;
+ if (!s.length) return 0;
+ foreach (C c; s)
+ {
+ buf ~= c;
+ }
+ return buf.length;
+ }
+ }
+
+ @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.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)
+ {
+ buf = buf2;
+ }
+ else
+ {
+ buf ~= buf2;
+ }
+ break;
+ }
+ buf ~= buf2;
+ }
+ return buf.length;
+ }
+
+ @system unittest
+ {
+ static import std.file;
+ import std.typecons : Tuple;
+
+ auto deleteme = testFilename();
+ std.file.write(deleteme, "hello\n\rworld\nhow\n\rare ya");
+ scope(exit) std.file.remove(deleteme);
+ foreach (C; Tuple!(char, wchar, dchar).Types)
+ {
+ immutable(C)[][] witness = [ "hello\n\r", "world\nhow\n\r", "are ya" ];
+ auto f = File(deleteme);
+ uint i = 0;
+ C[] buf;
+ while (f.readln(buf, "\n\r"))
+ {
+ assert(i < witness.length);
+ assert(buf == witness[i++]);
+ }
+ assert(buf.length == 0);
+ }
+ }
+
+ /**
+ * 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(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;
+
+ assert(isOpen);
+ auto input = LockingTextReader(this);
+ return formattedRead(input, format, data);
+ }
+
+ ///
+ @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);
+ string s;
+ auto f = File(deleteme);
+ f.readf("%s\n", &s);
+ assert(s == "hello", "["~s~"]");
+ f.readf("%s\n", &s);
+ assert(s == "world", "["~s~"]");
+
+ // Issue 11698
+ bool b1, b2;
+ f.readf("%s\n%s\n", &b1, &b2);
+ 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);
+ }
+
+ // Issue 12260 - Nice error of std.stdio.readf with newlines
+ @system unittest
+ {
+ static import std.file;
+
+ auto deleteme = testFilename();
+ std.file.write(deleteme, "1\n2");
+ scope(exit) std.file.remove(deleteme);
+ int input;
+ auto f = File(deleteme);
+ f.readf("%s", &input);
+
+ import std.conv : ConvException;
+ import std.exception : collectException;
+ assert(collectException!ConvException(f.readf("%s", &input)).msg ==
+ "Unexpected '\\n' when converting from type LockingTextReader to type int");
+ }
+
+/**
+ Returns a temporary file by calling
+ $(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;
+
+ return File(errnoEnforce(.tmpfile(),
+ "Could not create temporary file with tmpfile()"),
+ null);
+ }
+
+/**
+Unsafe function that wraps an existing $(D FILE*). The resulting $(D
+File) never takes the initiative in closing the file.
+Note that the created file has no $(LREF name)*/
+ /*private*/ static File wrapFile(FILE* f) @safe
+ {
+ import std.exception : enforce;
+
+ return File(enforce(f, "Could not wrap null FILE*"),
+ null, /*uint.max / 2*/ 9999);
+ }
+
+/**
+Returns the $(D FILE*) corresponding to this object.
+ */
+ FILE* getFP() @safe pure
+ {
+ import std.exception : enforce;
+
+ enforce(_p && _p.handle,
+ "Attempting to call getFP() on an unopened file");
+ return _p.handle;
+ }
+
+ @system unittest
+ {
+ static import core.stdc.stdio;
+ assert(stdout.getFP() == core.stdc.stdio.stdout);
+ }
+
+/**
+Returns the file number corresponding to this object.
+ */
+ @property int fileno() const @trusted
+ {
+ import std.exception : enforce;
+
+ enforce(isOpen, "Attempting to call fileno() on an unopened file");
+ return .fileno(cast(FILE*) _p.handle);
+ }
+
+/**
+Returns the underlying operating system $(D HANDLE) (Windows only).
+*/
+ version (StdDdoc)
+ @property HANDLE windowsHandle();
+
+ version (Windows)
+ @property HANDLE windowsHandle()
+ {
+ version (DIGITAL_MARS_STDIO)
+ return _fdToHandle(fileno);
+ else
+ return cast(HANDLE)_get_osfhandle(fileno);
+ }
+
+
+// Note: This was documented until 2013/08
+/*
+Range that reads one line at a time. Returned by $(LREF byLine).
+
+Allows to directly use range operations on lines of a file.
+*/
+ struct ByLine(Char, Terminator)
+ {
+ private:
+ 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.
+ * when accessing range.front, then using std.range.take,
+ * then accessing range.front again. */
+ alias PImpl = RefCounted!(Impl, RefCountedAutoInitialize.no);
+ PImpl impl;
+
+ static if (isScalarType!Terminator)
+ enum defTerm = '\n';
+ else
+ enum defTerm = cast(Terminator)"\n";
+
+ public:
+ this(File f, KeepTerminator kt = No.keepTerminator,
+ Terminator terminator = defTerm)
+ {
+ impl = PImpl(f, kt, terminator);
+ }
+
+ @property bool empty()
+ {
+ return impl.refCountedPayload.empty;
+ }
+
+ @property Char[] front()
+ {
+ return impl.refCountedPayload.front;
+ }
+
+ void popFront()
+ {
+ impl.refCountedPayload.popFront();
+ }
+
+ private:
+ struct Impl
+ {
+ private:
+ File file;
+ Char[] line;
+ Char[] buffer;
+ Terminator terminator;
+ KeepTerminator keepTerminator;
+
+ public:
+ this(File f, KeepTerminator kt, Terminator terminator)
+ {
+ file = f;
+ this.terminator = terminator;
+ keepTerminator = kt;
+ popFront();
+ }
+
+ // Range primitive implementations.
+ @property bool empty()
+ {
+ return line is null;
+ }
+
+ @property Char[] front()
+ {
+ return line;
+ }
+
+ void popFront()
+ {
+ import std.algorithm.searching : endsWith;
+ assert(file.isOpen);
+ line = buffer;
+ file.readln(line, terminator);
+ if (line.length > buffer.length)
+ {
+ buffer = line;
+ }
+ if (line.empty)
+ {
+ file.detach();
+ line = null;
+ }
+ else if (keepTerminator == No.keepTerminator
+ && endsWith(line, terminator))
+ {
+ static if (isScalarType!Terminator)
+ enum tlen = 1;
+ else static if (isArray!Terminator)
+ {
+ static assert(
+ is(Unqual!(ElementEncodingType!Terminator) == Char));
+ const tlen = terminator.length;
+ }
+ else
+ static assert(false);
+ line = line[0 .. line.length - tlen];
+ }
+ }
+ }
+ }
+
+/**
+Returns an input range set up to read from the file handle one line
+at a time.
+
+The element type for the range will be $(D Char[]). Range primitives
+may throw $(D StdioException) on I/O error.
+
+Note:
+Each $(D front) will not persist after $(D
+popFront) is called, so the caller must copy its contents (e.g. by
+calling $(D to!string)) when retention is needed. If the caller needs
+to retain a copy of every line, use the $(LREF byLineCopy) function
+instead.
+
+Params:
+Char = Character type for each line, defaulting to $(D char).
+keepTerminator = Use $(D Yes.keepTerminator) to include the
+terminator at the end of each line.
+terminator = Line separator ($(D '\n') by default). Use
+$(REF newline, std,ascii) for portability (unless the file was opened in
+text mode).
+
+Example:
+----
+import std.algorithm, std.stdio, std.string;
+// Count words in a file using ranges.
+void main()
+{
+ auto file = File("file.txt"); // Open for reading
+ const wordCount = file.byLine() // Read lines
+ .map!split // Split into words
+ .map!(a => a.length) // Count words per line
+ .sum(); // Total word count
+ writeln(wordCount);
+}
+----
+
+Example:
+----
+import std.range, std.stdio;
+// Read lines using foreach.
+void main()
+{
+ auto file = File("file.txt"); // Open for reading
+ auto range = file.byLine();
+ // Print first three lines
+ foreach (line; range.take(3))
+ writeln(line);
+ // Print remaining lines beginning with '#'
+ foreach (line; range)
+ {
+ if (!line.empty && line[0] == '#')
+ writeln(line);
+ }
+}
+----
+Notice that neither example accesses the line data returned by
+$(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 = No.keepTerminator,
+ Terminator terminator = '\n')
+ if (isScalarType!Terminator)
+ {
+ return ByLine!(Char, Terminator)(this, keepTerminator, terminator);
+ }
+
+/// ditto
+ auto byLine(Terminator, Char = char)
+ (KeepTerminator keepTerminator, Terminator terminator)
+ if (is(Unqual!(ElementEncodingType!Terminator) == Char))
+ {
+ return ByLine!(Char, Terminator)(this, keepTerminator, terminator);
+ }
+
+ @system unittest
+ {
+ static import std.file;
+ auto deleteme = testFilename();
+ std.file.write(deleteme, "hi");
+ scope(success) std.file.remove(deleteme);
+
+ import std.meta : AliasSeq;
+ foreach (T; AliasSeq!(char, wchar, dchar))
+ {
+ auto blc = File(deleteme).byLine!(T, T);
+ assert(blc.front == "hi");
+ // check front is cached
+ assert(blc.front is blc.front);
+ }
+ }
+
+ private struct ByLineCopy(Char, Terminator)
+ {
+ private:
+ 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.
+ * when accessing range.front, then using std.range.take,
+ * then accessing range.front again. */
+ alias Impl = RefCounted!(ByLineCopyImpl!(Char, Terminator),
+ RefCountedAutoInitialize.no);
+ Impl impl;
+
+ public:
+ this(File f, KeepTerminator kt, Terminator terminator)
+ {
+ impl = Impl(f, kt, terminator);
+ }
+
+ @property bool empty()
+ {
+ return impl.refCountedPayload.empty;
+ }
+
+ @property Char[] front()
+ {
+ return impl.refCountedPayload.front;
+ }
+
+ void popFront()
+ {
+ impl.refCountedPayload.popFront();
+ }
+ }
+
+ private struct ByLineCopyImpl(Char, Terminator)
+ {
+ ByLine!(Unqual!Char, Terminator).Impl impl;
+ bool gotFront;
+ Char[] line;
+
+ public:
+ this(File f, KeepTerminator kt, Terminator terminator)
+ {
+ impl = ByLine!(Unqual!Char, Terminator).Impl(f, kt, terminator);
+ }
+
+ @property bool empty()
+ {
+ return impl.empty;
+ }
+
+ @property front()
+ {
+ if (!gotFront)
+ {
+ line = impl.front.dup;
+ gotFront = true;
+ }
+ return line;
+ }
+
+ void popFront()
+ {
+ impl.popFront();
+ gotFront = false;
+ }
+ }
+
+/**
+Returns an input range set up to read from the file handle one line
+at a time. Each line will be newly allocated. $(D front) will cache
+its value to allow repeated calls without unnecessary allocations.
+
+Note: Due to caching byLineCopy can be more memory-efficient than
+$(D File.byLine.map!idup).
+
+The element type for the range will be $(D Char[]). Range
+primitives may throw $(D StdioException) on I/O error.
+
+Params:
+Char = Character type for each line, defaulting to $(D immutable char).
+keepTerminator = Use $(D Yes.keepTerminator) to include the
+terminator at the end of each line.
+terminator = Line separator ($(D '\n') by default). Use
+$(REF newline, std,ascii) for portability (unless the file was opened in
+text mode).
+
+Example:
+----
+import std.algorithm, std.array, std.stdio;
+// Print sorted lines of a file.
+void main()
+{
+ auto sortedLines = File("file.txt") // Open for reading
+ .byLineCopy() // Read persistent lines
+ .array() // into an array
+ .sort(); // then sort them
+ foreach (line; sortedLines)
+ writeln(line);
+}
+----
+See_Also:
+$(REF readText, std,file)
+*/
+ auto byLineCopy(Terminator = char, Char = immutable char)
+ (KeepTerminator keepTerminator = No.keepTerminator,
+ Terminator terminator = '\n')
+ if (isScalarType!Terminator)
+ {
+ return ByLineCopy!(Char, Terminator)(this, keepTerminator, terminator);
+ }
+
+/// ditto
+ auto byLineCopy(Terminator, Char = immutable char)
+ (KeepTerminator keepTerminator, Terminator terminator)
+ if (is(Unqual!(ElementEncodingType!Terminator) == Unqual!Char))
+ {
+ return ByLineCopy!(Char, Terminator)(this, keepTerminator, terminator);
+ }
+
+ @safe unittest
+ {
+ static assert(is(typeof(File("").byLine.front) == char[]));
+ static assert(is(typeof(File("").byLineCopy.front) == string));
+ static assert(
+ is(typeof(File("").byLineCopy!(char, char).front) == char[]));
+ }
+
+ @system unittest
+ {
+ import std.algorithm.comparison : equal;
+ static import std.file;
+
+ scope(failure) printf("Failed test at line %d\n", __LINE__);
+ auto deleteme = testFilename();
+ std.file.write(deleteme, "");
+ scope(success) std.file.remove(deleteme);
+
+ // Test empty file
+ auto f = File(deleteme);
+ foreach (line; f.byLine())
+ {
+ assert(false);
+ }
+ f.detach();
+ assert(!f.isOpen);
+
+ 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.range.primitives : walkLength;
+
+ uint i;
+ std.file.write(deleteme, txt);
+ auto f = File(deleteme);
+ scope(exit)
+ {
+ f.close();
+ assert(!f.isOpen);
+ }
+ auto lines = f.byLine(kt, term);
+ if (popFirstLine)
+ {
+ lines.popFront();
+ i = 1;
+ }
+ assert(lines.empty || lines.front is lines.front);
+ foreach (line; lines)
+ {
+ assert(line == witness[i++]);
+ }
+ assert(i == witness.length, text(i, " != ", witness.length));
+
+ // Issue 11830
+ auto walkedLength = File(deleteme).byLine(kt, term).walkLength;
+ assert(walkedLength == witness.length, text(walkedLength, " != ", witness.length));
+
+ // test persistent lines
+ assert(File(deleteme).byLineCopy(kt, term).array.sort() == witness.dup.sort());
+ }
+
+ KeepTerminator kt = No.keepTerminator;
+ test("", null, kt, '\n');
+ test("\n", [ "" ], kt, '\n');
+ test("asd\ndef\nasdf", [ "asd", "def", "asdf" ], kt, '\n');
+ test("asd\ndef\nasdf", [ "asd", "def", "asdf" ], kt, '\n', true);
+ test("asd\ndef\nasdf\n", [ "asd", "def", "asdf" ], kt, '\n');
+ test("foo", [ "foo" ], kt, '\n', true);
+ test("bob\r\nmarge\r\nsteve\r\n", ["bob", "marge", "steve"],
+ kt, "\r\n");
+ test("sue\r", ["sue"], kt, '\r');
+
+ kt = Yes.keepTerminator;
+ test("", null, kt, '\n');
+ test("\n", [ "\n" ], kt, '\n');
+ test("asd\ndef\nasdf", [ "asd\n", "def\n", "asdf" ], kt, '\n');
+ test("asd\ndef\nasdf\n", [ "asd\n", "def\n", "asdf\n" ], kt, '\n');
+ test("asd\ndef\nasdf\n", [ "asd\n", "def\n", "asdf\n" ], kt, '\n', true);
+ test("foo", [ "foo" ], kt, '\n');
+ test("bob\r\nmarge\r\nsteve\r\n", ["bob\r\n", "marge\r\n", "steve\r\n"],
+ kt, "\r\n");
+ test("sue\r", ["sue\r"], kt, '\r');
+ }
+
+ @system unittest
+ {
+ import std.algorithm.comparison : equal;
+ import std.range : drop, take;
+
+ version (Win64)
+ {
+ static import std.file;
+
+ /* the C function tmpfile doesn't seem to work, even when called from C */
+ auto deleteme = testFilename();
+ auto file = File(deleteme, "w+");
+ scope(success) std.file.remove(deleteme);
+ }
+ else version (CRuntime_Bionic)
+ {
+ static import std.file;
+
+ /* the C function tmpfile doesn't work when called from a shared
+ library apk:
+ https://code.google.com/p/android/issues/detail?id=66815 */
+ auto deleteme = testFilename();
+ auto file = File(deleteme, "w+");
+ scope(success) std.file.remove(deleteme);
+ }
+ else
+ auto file = File.tmpfile();
+ file.write("1\n2\n3\n");
+
+ // bug 9599
+ file.rewind();
+ File.ByLine!(char, char) fbl = file.byLine();
+ auto fbl2 = fbl;
+ assert(fbl.front == "1");
+ assert(fbl.front is fbl2.front);
+ assert(fbl.take(1).equal(["1"]));
+ assert(fbl.equal(["2", "3"]));
+ assert(fbl.empty);
+ assert(file.isOpen); // we still have a valid reference
+
+ file.rewind();
+ fbl = file.byLine();
+ assert(!fbl.drop(2).empty);
+ assert(fbl.equal(["3"]));
+ assert(fbl.empty);
+ assert(file.isOpen);
+
+ file.detach();
+ assert(!file.isOpen);
+ }
+
+ @system unittest
+ {
+ static import std.file;
+ auto deleteme = testFilename();
+ std.file.write(deleteme, "hi");
+ scope(success) std.file.remove(deleteme);
+
+ auto blc = File(deleteme).byLineCopy;
+ assert(!blc.empty);
+ // check front is cached
+ 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)
+ {
+ return typeof(return)(this, format);
+ }
+ }
+
+ ///
+ @system unittest
+ {
+ 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
+ /*
+ * Range that reads a chunk at a time.
+ */
+ struct ByChunk
+ {
+ private:
+ File file_;
+ ubyte[] chunk_;
+
+ void prime()
+ {
+ chunk_ = file_.rawRead(chunk_);
+ if (chunk_.length == 0)
+ file_.detach();
+ }
+
+ public:
+ this(File file, size_t size)
+ {
+ this(file, new ubyte[](size));
+ }
+
+ this(File file, ubyte[] buffer)
+ {
+ import std.exception : enforce;
+ enforce(buffer.length, "size must be larger than 0");
+ file_ = file;
+ chunk_ = buffer;
+ prime();
+ }
+
+ // $(D ByChunk)'s input range primitive operations.
+ @property nothrow
+ bool empty() const
+ {
+ return !file_.isOpen;
+ }
+
+ /// Ditto
+ @property nothrow
+ ubyte[] front()
+ {
+ version (assert)
+ {
+ import core.exception : RangeError;
+ if (empty)
+ throw new RangeError();
+ }
+ return chunk_;
+ }
+
+ /// Ditto
+ void popFront()
+ {
+ version (assert)
+ {
+ import core.exception : RangeError;
+ if (empty)
+ throw new RangeError();
+ }
+ prime();
+ }
+ }
+
+/**
+Returns an input range set up to read from the file handle a chunk at a
+time.
+
+The element type for the range will be $(D ubyte[]). Range primitives
+may throw $(D StdioException) on I/O error.
+
+Example:
+---------
+void main()
+{
+ // Read standard input 4KB at a time
+ foreach (ubyte[] buffer; stdin.byChunk(4096))
+ {
+ ... use buffer ...
+ }
+}
+---------
+
+The parameter may be a number (as shown in the example above) dictating the
+size of each chunk. Alternatively, $(D byChunk) accepts a
+user-provided buffer that it uses directly.
+
+Example:
+---------
+void main()
+{
+ // Read standard input 4KB at a time
+ foreach (ubyte[] buffer; stdin.byChunk(new ubyte[4096]))
+ {
+ ... use buffer ...
+ }
+}
+---------
+
+In either case, the content of the buffer is reused across calls. That means
+$(D front) will not persist after $(D popFront) is called, so if retention is
+needed, the caller must copy its contents (e.g. by calling $(D buffer.dup)).
+
+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 byChunk) works with any algorithm
+compatible with input ranges.
+
+Example:
+---
+// Efficient file copy, 1MB at a time.
+import std.algorithm, std.stdio;
+void main()
+{
+ stdin.byChunk(1024 * 1024).copy(stdout.lockingTextWriter());
+}
+---
+
+$(REF joiner, std,algorithm,iteration) can be used to join chunks together into
+a single range lazily.
+Example:
+---
+import std.algorithm, std.stdio;
+void main()
+{
+ //Range of ranges
+ static assert(is(typeof(stdin.byChunk(4096).front) == ubyte[]));
+ //Range of elements
+ static assert(is(typeof(stdin.byChunk(4096).joiner.front) == ubyte));
+}
+---
+
+Returns: A call to $(D byChunk) returns a range initialized with the $(D File)
+object and the appropriate buffer.
+
+Throws: If the user-provided size is zero or the user-provided buffer
+is empty, throws an $(D Exception). In case of an I/O error throws
+$(D StdioException).
+ */
+ auto byChunk(size_t chunkSize)
+ {
+ return ByChunk(this, chunkSize);
+ }
+/// Ditto
+ ByChunk byChunk(ubyte[] buffer)
+ {
+ return ByChunk(this, buffer);
+ }
+
+ @system unittest
+ {
+ static import std.file;
+
+ scope(failure) printf("Failed test at line %d\n", __LINE__);
+
+ auto deleteme = testFilename();
+ std.file.write(deleteme, "asd\ndef\nasdf");
+
+ auto witness = ["asd\n", "def\n", "asdf" ];
+ auto f = File(deleteme);
+ scope(exit)
+ {
+ f.close();
+ assert(!f.isOpen);
+ std.file.remove(deleteme);
+ }
+
+ uint i;
+ foreach (chunk; f.byChunk(4))
+ assert(chunk == cast(ubyte[]) witness[i++]);
+
+ assert(i == witness.length);
+ }
+
+ @system unittest
+ {
+ static import std.file;
+
+ scope(failure) printf("Failed test at line %d\n", __LINE__);
+
+ auto deleteme = testFilename();
+ std.file.write(deleteme, "asd\ndef\nasdf");
+
+ auto witness = ["asd\n", "def\n", "asdf" ];
+ auto f = File(deleteme);
+ scope(exit)
+ {
+ f.close();
+ assert(!f.isOpen);
+ std.file.remove(deleteme);
+ }
+
+ uint i;
+ foreach (chunk; f.byChunk(new ubyte[4]))
+ assert(chunk == cast(ubyte[]) witness[i++]);
+
+ assert(i == witness.length);
+ }
+
+ // Note: This was documented until 2013/08
+/*
+$(D Range) that locks the file and allows fast writing to it.
+ */
+ struct LockingTextWriter
+ {
+ private:
+ import std.range.primitives : ElementType, isInfinite, isInputRange;
+ // 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:
+
+ this(ref File f) @trusted
+ {
+ import core.stdc.wchar_ : fwide;
+ import std.exception : enforce;
+
+ enforce(f._p && f._p.handle, "Attempting to write to closed File");
+ fps_ = f._p.handle;
+ orientation_ = fwide(fps_, 0);
+ FLOCK(fps_);
+ }
+
+ ~this() @trusted
+ {
+ if (fps_)
+ {
+ FUNLOCK(fps_);
+ fps_ = null;
+ }
+ }
+
+ this(this) @trusted
+ {
+ if (fps_)
+ {
+ FLOCK(fps_);
+ }
+ }
+
+ /// Range primitive implementations.
+ void put(A)(A writeme)
+ if ((isSomeChar!(Unqual!(ElementType!A)) ||
+ is(ElementType!A : const(ubyte))) &&
+ isInputRange!A &&
+ !isInfinite!A)
+ {
+ import std.exception : errnoEnforce;
+
+ alias C = ElementEncodingType!A;
+ static assert(!is(C == void));
+ static if (isSomeString!A && C.sizeof == 1 || is(A : const(ubyte)[]))
+ {
+ if (orientation_ <= 0)
+ {
+ //file.write(writeme); causes infinite recursion!!!
+ //file.rawWrite(writeme);
+ auto result = trustedFwrite(fps_, writeme);
+ if (result != writeme.length) errnoEnforce(0);
+ return;
+ }
+ }
+
+ // put each element in turn.
+ alias Elem = Unqual!(ElementType!A);
+ foreach (Elem c; writeme)
+ {
+ put(c);
+ }
+ }
+
+ /// ditto
+ void put(C)(C c) @safe if (isSomeChar!C || is(C : const(ubyte)))
+ {
+ import std.traits : Parameters;
+ static auto trustedFPUTC(int ch, _iobuf* h) @trusted
+ {
+ return FPUTC(ch, h);
+ }
+ static auto trustedFPUTWC(Parameters!FPUTWC[0] ch, _iobuf* h) @trusted
+ {
+ return FPUTWC(ch, h);
+ }
+
+ static if (c.sizeof == 1)
+ {
+ // simple char
+ if (orientation_ <= 0) trustedFPUTC(c, handle_);
+ else trustedFPUTWC(c, handle_);
+ }
+ else static if (c.sizeof == 2)
+ {
+ import std.utf : encode, UseReplacementDchar;
+
+ if (orientation_ <= 0)
+ {
+ if (c <= 0x7F)
+ {
+ trustedFPUTC(c, handle_);
+ }
+ else
+ {
+ char[4] buf;
+ immutable size = encode!(UseReplacementDchar.yes)(buf, c);
+ foreach (i ; 0 .. size)
+ trustedFPUTC(buf[i], handle_);
+ }
+ }
+ else
+ {
+ trustedFPUTWC(c, handle_);
+ }
+ }
+ else // 32-bit characters
+ {
+ import std.utf : encode;
+
+ if (orientation_ <= 0)
+ {
+ if (c <= 0x7F)
+ {
+ trustedFPUTC(c, handle_);
+ }
+ else
+ {
+ char[4] buf = void;
+ immutable len = encode(buf, c);
+ foreach (i ; 0 .. len)
+ trustedFPUTC(buf[i], handle_);
+ }
+ }
+ else
+ {
+ version (Windows)
+ {
+ import std.utf : isValidDchar;
+
+ assert(isValidDchar(c));
+ if (c <= 0xFFFF)
+ {
+ trustedFPUTWC(c, handle_);
+ }
+ else
+ {
+ trustedFPUTWC(cast(wchar)
+ ((((c - 0x10000) >> 10) & 0x3FF)
+ + 0xD800), handle_);
+ trustedFPUTWC(cast(wchar)
+ (((c - 0x10000) & 0x3FF) + 0xDC00),
+ handle_);
+ }
+ }
+ else version (Posix)
+ {
+ trustedFPUTWC(c, handle_);
+ }
+ else
+ {
+ static assert(0);
+ }
+ }
+ }
+ }
+ }
+
+/** Returns an output range that locks the file and allows fast writing to it.
+
+See $(LREF byChunk) for an example.
+*/
+ auto lockingTextWriter() @safe
+ {
+ 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
+ {
+ import std.algorithm.mutation : reverse;
+ import std.exception : collectException;
+ static import std.file;
+ 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
+ {
+ import std.exception : collectException;
+
+ ulong pos = void;
+ if (collectException(pos = tell)) return ulong.max;
+ scope(exit) seek(pos);
+ seek(0, SEEK_END);
+ return tell;
+ }
+}
+
+@system unittest
+{
+ @system struct SystemToString
+ {
+ string toString()
+ {
+ return "system";
+ }
+ }
+
+ @trusted struct TrustedToString
+ {
+ string toString()
+ {
+ return "trusted";
+ }
+ }
+
+ @safe struct SafeToString
+ {
+ string toString()
+ {
+ return "safe";
+ }
+ }
+
+ @system void systemTests()
+ {
+ //system code can write to files/stdout with anything!
+ if (false)
+ {
+ auto f = File();
+
+ f.write("just a string");
+ f.write("string with arg: ", 47);
+ f.write(SystemToString());
+ f.write(TrustedToString());
+ f.write(SafeToString());
+
+ write("just a string");
+ write("string with arg: ", 47);
+ write(SystemToString());
+ write(TrustedToString());
+ write(SafeToString());
+
+ f.writeln("just a string");
+ f.writeln("string with arg: ", 47);
+ f.writeln(SystemToString());
+ f.writeln(TrustedToString());
+ f.writeln(SafeToString());
+
+ writeln("just a string");
+ writeln("string with arg: ", 47);
+ writeln(SystemToString());
+ writeln(TrustedToString());
+ writeln(SafeToString());
+
+ f.writef("string with arg: %s", 47);
+ f.writef("%s", SystemToString());
+ f.writef("%s", TrustedToString());
+ f.writef("%s", SafeToString());
+
+ writef("string with arg: %s", 47);
+ writef("%s", SystemToString());
+ writef("%s", TrustedToString());
+ writef("%s", SafeToString());
+
+ f.writefln("string with arg: %s", 47);
+ f.writefln("%s", SystemToString());
+ f.writefln("%s", TrustedToString());
+ f.writefln("%s", SafeToString());
+
+ writefln("string with arg: %s", 47);
+ writefln("%s", SystemToString());
+ writefln("%s", TrustedToString());
+ writefln("%s", SafeToString());
+ }
+ }
+
+ @safe void safeTests()
+ {
+ auto f = File();
+
+ //safe code can write to files only with @safe and @trusted code...
+ if (false)
+ {
+ f.write("just a string");
+ f.write("string with arg: ", 47);
+ f.write(TrustedToString());
+ f.write(SafeToString());
+
+ write("just a string");
+ write("string with arg: ", 47);
+ write(TrustedToString());
+ write(SafeToString());
+
+ f.writeln("just a string");
+ f.writeln("string with arg: ", 47);
+ f.writeln(TrustedToString());
+ f.writeln(SafeToString());
+
+ writeln("just a string");
+ writeln("string with arg: ", 47);
+ writeln(TrustedToString());
+ writeln(SafeToString());
+
+ f.writef("string with arg: %s", 47);
+ f.writef("%s", TrustedToString());
+ f.writef("%s", SafeToString());
+
+ writef("string with arg: %s", 47);
+ writef("%s", TrustedToString());
+ writef("%s", SafeToString());
+
+ f.writefln("string with arg: %s", 47);
+ f.writefln("%s", TrustedToString());
+ f.writefln("%s", SafeToString());
+
+ writefln("string with arg: %s", 47);
+ writefln("%s", TrustedToString());
+ writefln("%s", SafeToString());
+ }
+
+ static assert(!__traits(compiles, f.write(SystemToString().toString())));
+ static assert(!__traits(compiles, f.writeln(SystemToString())));
+ static assert(!__traits(compiles, f.writef("%s", SystemToString())));
+ static assert(!__traits(compiles, f.writefln("%s", SystemToString())));
+
+ static assert(!__traits(compiles, write(SystemToString().toString())));
+ static assert(!__traits(compiles, writeln(SystemToString())));
+ static assert(!__traits(compiles, writef("%s", SystemToString())));
+ static assert(!__traits(compiles, writefln("%s", SystemToString())));
+ }
+
+ systemTests();
+ safeTests();
+}
+
+@safe unittest
+{
+ import std.exception : collectException;
+ static import std.file;
+
+ auto deleteme = testFilename();
+ scope(exit) collectException(std.file.remove(deleteme));
+ std.file.write(deleteme, "1 2 3");
+ auto f = File(deleteme);
+ assert(f.size == 5);
+ assert(f.tell == 0);
+}
+
+@system unittest
+{
+ // @system due to readln
+ static import std.file;
+ import std.range : chain, only, repeat;
+ import std.range.primitives : isOutputRange;
+
+ auto deleteme = testFilename();
+ scope(exit) std.file.remove(deleteme);
+
+ {
+ File f = File(deleteme, "w");
+ auto writer = f.lockingTextWriter();
+ static assert(isOutputRange!(typeof(writer), dchar));
+ writer.put("日本語");
+ writer.put("日本語"w);
+ writer.put("日本語"d);
+ writer.put('日');
+ writer.put(chain(only('本'), only('語')));
+ writer.put(repeat('#', 12)); // BUG 11945
+ writer.put(cast(immutable(ubyte)[])"日本語"); // Bug 17229
+ }
+ 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.
+ */
+ 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.
+ */
+ readWrite
+}
+
+struct LockingTextReader
+{
+ private File _f;
+ private char _front;
+ private bool _hasChar;
+
+ this(File f)
+ {
+ import std.exception : enforce;
+ enforce(f.isOpen, "LockingTextReader: File must be open");
+ _f = f;
+ FLOCK(_f._p.handle);
+ }
+
+ this(this)
+ {
+ FLOCK(_f._p.handle);
+ }
+
+ ~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.mutation : swap;
+ swap(this, r);
+ }
+
+ @property bool empty()
+ {
+ if (!_hasChar)
+ {
+ if (!_f.isOpen || _f.eof)
+ return true;
+ immutable int c = FGETC(cast(_iobuf*) _f._p.handle);
+ if (c == EOF)
+ {
+ .destroy(_f);
+ return true;
+ }
+ _front = cast(char) c;
+ _hasChar = true;
+ }
+ return false;
+ }
+
+ @property char front()
+ {
+ if (!_hasChar)
+ {
+ version (assert)
+ {
+ import core.exception : RangeError;
+ if (empty)
+ throw new RangeError();
+ }
+ else
+ {
+ empty;
+ }
+ }
+ return _front;
+ }
+
+ void popFront()
+ {
+ if (!_hasChar)
+ empty;
+ _hasChar = false;
+ }
+}
+
+@system unittest
+{
+ // @system due to readf
+ static import std.file;
+ import std.range.primitives : isInputRange;
+
+ static assert(isInputRange!LockingTextReader);
+ auto deleteme = testFilename();
+ std.file.write(deleteme, "1 2 3");
+ scope(exit) std.file.remove(deleteme);
+ int x, y;
+ auto f = File(deleteme);
+ f.readf("%s ", &x);
+ assert(x == 1);
+ f.readf("%d ", &x);
+ assert(x == 2);
+ f.readf("%d ", &x);
+ assert(x == 3);
+}
+
+@system unittest // bugzilla 13686
+{
+ import std.algorithm.comparison : equal;
+ static import std.file;
+ import std.utf : byDchar;
+
+ auto deleteme = testFilename();
+ std.file.write(deleteme, "Тест");
+ scope(exit) std.file.remove(deleteme);
+
+ string s;
+ File(deleteme).readf("%s", &s);
+ assert(s == "Тест");
+
+ auto ltr = LockingTextReader(File(deleteme)).byDchar;
+ assert(equal(ltr, "Тест".byDchar));
+}
+
+@system unittest // bugzilla 12320
+{
+ static import std.file;
+ auto deleteme = testFilename();
+ std.file.write(deleteme, "ab");
+ scope(exit) std.file.remove(deleteme);
+ auto ltr = LockingTextReader(File(deleteme));
+ assert(ltr.front == 'a');
+ ltr.popFront();
+ assert(ltr.front == 'b');
+ ltr.popFront();
+ 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, 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)
+{
+ enum isFileHandle = is(T : FILE*) ||
+ is(T : File);
+}
+
+///
+@safe unittest
+{
+ static assert(isFileHandle!(FILE*));
+ static assert(isFileHandle!(File));
+}
+
+/**
+ * Property used by writeln/etc. so it can infer @safe since stdout is __gshared
+ */
+private @property File trustedStdout() @trusted
+{
+ return stdout;
+}
+
+/***********************************
+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))
+{
+ trustedStdout.write(args);
+}
+
+@system unittest
+{
+ static import std.file;
+
+ scope(failure) printf("Failed test at line %d\n", __LINE__);
+ void[] buf;
+ if (false) write(buf);
+ // test write
+ auto deleteme = testFilename();
+ auto f = File(deleteme, "w");
+ f.write("Hello, ", "world number ", 42, "!");
+ f.close();
+ scope(exit) { std.file.remove(deleteme); }
+ assert(cast(char[]) std.file.read(deleteme) == "Hello, world number 42!");
+}
+
+/***********************************
+ * 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)
+{
+ import std.traits : isAggregateType;
+ static if (T.length == 0)
+ {
+ import std.exception : enforce;
+
+ enforce(fputc('\n', .trustedStdout._p.handle) != EOF, "fputc failed");
+ }
+ else static if (T.length == 1 &&
+ is(typeof(args[0]) : const(char)[]) &&
+ !is(typeof(args[0]) == enum) &&
+ !is(Unqual!(typeof(args[0])) == typeof(null)) &&
+ !isAggregateType!(typeof(args[0])))
+ {
+ import std.traits : isStaticArray;
+
+ // Specialization for strings - a very frequent case
+ auto w = .trustedStdout.lockingTextWriter();
+
+ static if (isStaticArray!(typeof(args[0])))
+ {
+ w.put(args[0][]);
+ }
+ else
+ {
+ w.put(args[0]);
+ }
+ w.put('\n');
+ }
+ else
+ {
+ // Most general instance
+ trustedStdout.write(args, '\n');
+ }
+}
+
+@safe unittest
+{
+ // Just make sure the call compiles
+ if (false) writeln();
+
+ if (false) writeln("wyda");
+
+ // bug 8040
+ if (false) writeln(null);
+ if (false) writeln(">", null, "<");
+
+ // Bugzilla 14041
+ if (false)
+ {
+ char[8] a;
+ writeln(a);
+ }
+}
+
+@system unittest
+{
+ static import std.file;
+
+ scope(failure) printf("Failed test at line %d\n", __LINE__);
+
+ // test writeln
+ auto deleteme = testFilename();
+ auto f = File(deleteme, "w");
+ scope(exit) { std.file.remove(deleteme); }
+ f.writeln("Hello, ", "world number ", 42, "!");
+ f.close();
+ version (Windows)
+ assert(cast(char[]) std.file.read(deleteme) ==
+ "Hello, world number 42!\r\n");
+ else
+ assert(cast(char[]) std.file.read(deleteme) ==
+ "Hello, world number 42!\n");
+
+ // test writeln on stdout
+ auto saveStdout = stdout;
+ scope(exit) stdout = saveStdout;
+ stdout.open(deleteme, "w");
+ writeln("Hello, ", "world number ", 42, "!");
+ stdout.close();
+ version (Windows)
+ assert(cast(char[]) std.file.read(deleteme) ==
+ "Hello, world number 42!\r\n");
+ else
+ assert(cast(char[]) std.file.read(deleteme) ==
+ "Hello, world number 42!\n");
+
+ stdout.open(deleteme, "w");
+ writeln("Hello!"c);
+ writeln("Hello!"w); // bug 8386
+ writeln("Hello!"d); // bug 8386
+ writeln("embedded\0null"c); // bug 8730
+ stdout.close();
+ version (Windows)
+ assert(cast(char[]) std.file.read(deleteme) ==
+ "Hello!\r\nHello!\r\nHello!\r\nembedded\0null\r\n");
+ else
+ assert(cast(char[]) std.file.read(deleteme) ==
+ "Hello!\nHello!\nHello!\nembedded\0null\n");
+}
+
+@system unittest
+{
+ static import std.file;
+
+ auto deleteme = testFilename();
+ auto f = File(deleteme, "w");
+ scope(exit) { std.file.remove(deleteme); }
+
+ enum EI : int { A, B }
+ enum ED : double { A, B }
+ enum EC : char { A, B }
+ enum ES : string { A = "aaa", B = "bbb" }
+
+ f.writeln(EI.A); // false, but A on 2.058
+ f.writeln(EI.B); // true, but B on 2.058
+
+ f.writeln(ED.A); // A
+ f.writeln(ED.B); // B
+
+ f.writeln(EC.A); // A
+ f.writeln(EC.B); // B
+
+ f.writeln(ES.A); // A
+ f.writeln(ES.B); // B
+
+ f.close();
+ version (Windows)
+ assert(cast(char[]) std.file.read(deleteme) ==
+ "A\r\nB\r\nA\r\nB\r\nA\r\nB\r\nA\r\nB\r\n");
+ else
+ assert(cast(char[]) std.file.read(deleteme) ==
+ "A\nB\nA\nB\nA\nB\nA\nB\n");
+}
+
+@system unittest
+{
+ static auto useInit(T)(T ltw)
+ {
+ T val;
+ val = ltw;
+ val = T.init;
+ return val;
+ }
+ useInit(stdout.lockingTextWriter());
+}
+
+
+/***********************************
+Writes formatted data to standard output (without a trailing newline).
+
+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.
+
+Note: In older versions of Phobos, it used to be possible to write:
+
+------
+writef(stderr, "%s", "message");
+------
+
+to print a message to $(D stderr). This syntax is no longer supported, and has
+been superceded by:
+
+------
+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);
+}
+
+/// ditto
+void writef(Char, A...)(in Char[] fmt, A args)
+{
+ trustedStdout.writef(fmt, args);
+}
+
+@system unittest
+{
+ static import std.file;
+
+ 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.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);
+ stdout.close();
+ assert(cast(char[]) std.file.read(deleteme) == "Hello, nice world number 42!");
+}
+
+/***********************************
+ * Equivalent to $(D writef(fmt, args, '\n')).
+ */
+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(fmt, args);
+}
+
+@system unittest
+{
+ static import std.file;
+
+ scope(failure) printf("Failed test at line %d\n", __LINE__);
+
+ // 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.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",
+ cast(char[]) std.file.read(deleteme));
+
+ // 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");
+}
+
+/**
+ * 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(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);
+}
+
+@system unittest
+{
+ float f;
+ if (false) uint x = readf("%s", &f);
+
+ char a;
+ wchar b;
+ dchar 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);
+}
+
+/**********************************
+ * Read line from $(D stdin).
+ *
+ * This version manages its own read buffer, which means one memory allocation per call. If you are not
+ * retaining a reference to the read data, consider the $(D readln(buf)) version, which may offer
+ * better performance as it can reuse its read buffer.
+ *
+ * Returns:
+ * The line that was read, including the line terminator character.
+ * Params:
+ * S = Template parameter; the type of the allocated buffer, and the type returned. Defaults to $(D string).
+ * terminator = Line terminator (by default, $(D '\n')).
+ * Note:
+ * String terminators are not supported due to ambiguity with readln(buf) below.
+ * Throws:
+ * $(D StdioException) on I/O error, or $(D UnicodeException) on Unicode conversion error.
+ * Example:
+ * Reads $(D stdin) and writes it to $(D stdout).
+---
+import std.stdio;
+
+void main()
+{
+ string line;
+ while ((line = readln()) !is null)
+ write(line);
+}
+---
+*/
+S readln(S = string)(dchar terminator = '\n')
+if (isSomeString!S)
+{
+ return stdin.readln!S(terminator);
+}
+
+/**********************************
+ * Read line from $(D stdin) and write it to buf[], including terminating character.
+ *
+ * This can be faster than $(D line = readln()) because you can reuse
+ * the buffer for each call. Note that reusing the buffer means that you
+ * must copy the previous contents if you wish to retain them.
+ *
+ * Returns:
+ * $(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 $(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.
+ * Example:
+ * Reads $(D stdin) and writes it to $(D stdout).
+---
+import std.stdio;
+
+void main()
+{
+ char[] buf;
+ while (readln(buf))
+ write(buf);
+}
+---
+*/
+size_t readln(C)(ref C[] buf, dchar terminator = '\n')
+if (isSomeChar!C && is(Unqual!C == C) && !is(C == enum))
+{
+ return stdin.readln(buf, terminator);
+}
+
+/** 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)))
+{
+ return stdin.readln(buf, terminator);
+}
+
+@safe unittest
+{
+ import std.meta : AliasSeq;
+
+ //we can't actually test readln, so at the very least,
+ //we test compilability
+ void foo()
+ {
+ readln();
+ readln('\t');
+ foreach (String; AliasSeq!(string, char[], wstring, wchar[], dstring, dchar[]))
+ {
+ readln!String();
+ readln!String('\t');
+ }
+ foreach (String; AliasSeq!(char[], wchar[], dchar[]))
+ {
+ String buf;
+ readln(buf);
+ readln(buf, '\t');
+ readln(buf, "<br />");
+ }
+ }
+}
+
+/*
+ * Convenience function that forwards to $(D core.sys.posix.stdio.fopen)
+ * (to $(D _wfopen) on Windows)
+ * with appropriately-constructed C-style strings.
+ */
+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;
+
+ auto namez = name.tempCString!FSChar();
+ auto modez = mode.tempCString!FSChar();
+
+ static fopenImpl(const(FSChar)* namez, const(FSChar)* modez) @trusted nothrow @nogc
+ {
+ 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)
+{
+ /***********************************
+ * Convenience function that forwards to $(D core.sys.posix.stdio.popen)
+ * with appropriately-constructed C-style strings.
+ */
+ 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 std.internal.cstring : 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)
+ */
+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
+{
+ return fread(obj.ptr, T.sizeof, obj.length, f);
+}
+
+/**
+ * Iterates through the lines of a file by using $(D foreach).
+ *
+ * Example:
+ *
+---------
+void main()
+{
+ foreach (string line; lines(stdin))
+ {
+ ... use line ...
+ }
+}
+---------
+The line terminator ($(D '\n') by default) is part of the string read (it
+could be missing in the last line of the file). Several types are
+supported for $(D line), and the behavior of $(D lines)
+changes accordingly:
+
+$(OL $(LI If $(D line) has type $(D string), $(D
+wstring), or $(D dstring), a new string of the respective type
+is allocated every read.) $(LI If $(D line) has type $(D
+char[]), $(D wchar[]), $(D dchar[]), the line's content
+will be reused (overwritten) across reads.) $(LI If $(D line)
+has type $(D immutable(ubyte)[]), the behavior is similar to
+case (1), except that no UTF checking is attempted upon input.) $(LI
+If $(D line) has type $(D ubyte[]), the behavior is
+similar to case (2), except that no UTF checking is attempted upon
+input.))
+
+In all cases, a two-symbols versions is also accepted, in which case
+the first symbol (of integral type, e.g. $(D ulong) or $(D
+uint)) tracks the zero-based number of the current line.
+
+Example:
+----
+ foreach (ulong i, string line; lines(stdin))
+ {
+ ... use line ...
+ }
+----
+
+ In case of an I/O error, an $(D StdioException) is thrown.
+
+See_Also:
+$(LREF byLine)
+ */
+
+struct lines
+{
+ private File f;
+ private dchar terminator = '\n';
+
+ /**
+ Constructor.
+ Params:
+ f = File to read lines from.
+ terminator = Line separator ($(D '\n') by default).
+ */
+ this(File f, dchar terminator = '\n')
+ {
+ this.f = f;
+ this.terminator = terminator;
+ }
+
+ int opApply(D)(scope D dg)
+ {
+ import std.traits : Parameters;
+ alias Parms = Parameters!(dg);
+ static if (isSomeString!(Parms[$ - 1]))
+ {
+ enum bool duplicate = is(Parms[$ - 1] == string)
+ || is(Parms[$ - 1] == wstring) || is(Parms[$ - 1] == dstring);
+ int result = 0;
+ static if (is(Parms[$ - 1] : const(char)[]))
+ alias C = char;
+ else static if (is(Parms[$ - 1] : const(wchar)[]))
+ alias C = wchar;
+ else static if (is(Parms[$ - 1] : const(dchar)[]))
+ alias C = dchar;
+ C[] line;
+ static if (Parms.length == 2)
+ Parms[0] i = 0;
+ for (;;)
+ {
+ import std.conv : to;
+
+ if (!f.readln(line, terminator)) break;
+ auto copy = to!(Parms[$ - 1])(line);
+ static if (Parms.length == 2)
+ {
+ result = dg(i, copy);
+ ++i;
+ }
+ else
+ {
+ result = dg(copy);
+ }
+ if (result != 0) break;
+ }
+ return result;
+ }
+ else
+ {
+ // raw read
+ return opApplyRaw(dg);
+ }
+ }
+ // no UTF checking
+ int opApplyRaw(D)(scope D dg)
+ {
+ import std.conv : to;
+ import std.exception : assumeUnique;
+ import std.traits : Parameters;
+
+ alias Parms = Parameters!(dg);
+ enum duplicate = is(Parms[$ - 1] : immutable(ubyte)[]);
+ int result = 1;
+ int c = void;
+ FLOCK(f._p.handle);
+ scope(exit) FUNLOCK(f._p.handle);
+ ubyte[] buffer;
+ static if (Parms.length == 2)
+ Parms[0] line = 0;
+ while ((c = FGETC(cast(_iobuf*) f._p.handle)) != -1)
+ {
+ buffer ~= to!(ubyte)(c);
+ if (c == terminator)
+ {
+ static if (duplicate)
+ auto arg = assumeUnique(buffer);
+ else
+ alias arg = buffer;
+ // unlock the file while calling the delegate
+ FUNLOCK(f._p.handle);
+ scope(exit) FLOCK(f._p.handle);
+ static if (Parms.length == 1)
+ {
+ result = dg(arg);
+ }
+ else
+ {
+ result = dg(line, arg);
+ ++line;
+ }
+ if (result) break;
+ static if (!duplicate)
+ buffer.length = 0;
+ }
+ }
+ // can only reach when FGETC returned -1
+ if (!f.eof) throw new StdioException("Error in reading file"); // error occured
+ return result;
+ }
+}
+
+@system unittest
+{
+ static import std.file;
+ import std.meta : AliasSeq;
+
+ scope(failure) printf("Failed test at line %d\n", __LINE__);
+
+ auto deleteme = testFilename();
+ scope(exit) { std.file.remove(deleteme); }
+
+ alias 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");
+ foreach (T line; lines(f))
+ {
+ assert(false);
+ }
+ f.close();
+
+ // test looping with a file with three lines
+ std.file.write(deleteme, "Line one\nline two\nline three\n");
+ f.open(deleteme, "r");
+ uint i = 0;
+ foreach (T line; lines(f))
+ {
+ if (i == 0) assert(line == "Line one\n");
+ else if (i == 1) assert(line == "line two\n");
+ else if (i == 2) assert(line == "line three\n");
+ else assert(false);
+ ++i;
+ }
+ f.close();
+
+ // test looping with a file with three lines, last without a newline
+ std.file.write(deleteme, "Line one\nline two\nline three");
+ f.open(deleteme, "r");
+ i = 0;
+ foreach (T line; lines(f))
+ {
+ if (i == 0) assert(line == "Line one\n");
+ else if (i == 1) assert(line == "line two\n");
+ else if (i == 2) assert(line == "line three");
+ else assert(false);
+ ++i;
+ }
+ f.close();
+ }
+
+ // test with ubyte[] inputs
+ alias TestedWith2 = AliasSeq!(immutable(ubyte)[], ubyte[]);
+ foreach (T; TestedWith2)
+ {
+ // test looping with an empty file
+ std.file.write(deleteme, "");
+ auto f = File(deleteme, "r");
+ foreach (T line; lines(f))
+ {
+ assert(false);
+ }
+ f.close();
+
+ // test looping with a file with three lines
+ std.file.write(deleteme, "Line one\nline two\nline three\n");
+ f.open(deleteme, "r");
+ uint i = 0;
+ foreach (T line; lines(f))
+ {
+ if (i == 0) assert(cast(char[]) line == "Line one\n");
+ else if (i == 1) assert(cast(char[]) line == "line two\n",
+ T.stringof ~ " " ~ cast(char[]) line);
+ else if (i == 2) assert(cast(char[]) line == "line three\n");
+ else assert(false);
+ ++i;
+ }
+ f.close();
+
+ // test looping with a file with three lines, last without a newline
+ std.file.write(deleteme, "Line one\nline two\nline three");
+ f.open(deleteme, "r");
+ i = 0;
+ foreach (T line; lines(f))
+ {
+ if (i == 0) assert(cast(char[]) line == "Line one\n");
+ else if (i == 1) assert(cast(char[]) line == "line two\n");
+ else if (i == 2) assert(cast(char[]) line == "line three");
+ else assert(false);
+ ++i;
+ }
+ f.close();
+
+ }
+
+ foreach (T; AliasSeq!(ubyte[]))
+ {
+ // test looping with a file with three lines, last without a newline
+ // using a counter too this time
+ std.file.write(deleteme, "Line one\nline two\nline three");
+ auto f = File(deleteme, "r");
+ uint i = 0;
+ foreach (ulong j, T line; lines(f))
+ {
+ if (i == 0) assert(cast(char[]) line == "Line one\n");
+ else if (i == 1) assert(cast(char[]) line == "line two\n");
+ else if (i == 2) assert(cast(char[]) line == "line three");
+ else assert(false);
+ ++i;
+ }
+ f.close();
+ }
+}
+
+/**
+Iterates through a file a chunk at a time by using $(D foreach).
+
+Example:
+
+---------
+void main()
+{
+ foreach (ubyte[] buffer; chunks(stdin, 4096))
+ {
+ ... use buffer ...
+ }
+}
+---------
+
+The content of $(D buffer) is reused across calls. 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).
+
+ In case of an I/O error, an $(D StdioException) is thrown.
+*/
+auto chunks(File f, size_t size)
+{
+ return ChunksImpl(f, size);
+}
+private struct ChunksImpl
+{
+ private File f;
+ private size_t size;
+ // private string fileName; // Currently, no use
+
+ this(File f, size_t size)
+ in
+ {
+ assert(size, "size must be larger than 0");
+ }
+ body
+ {
+ this.f = f;
+ this.size = size;
+ }
+
+ int opApply(D)(scope D dg)
+ {
+ import core.stdc.stdlib : alloca;
+ enum maxStackSize = 1024 * 16;
+ ubyte[] buffer = void;
+ if (size < maxStackSize)
+ buffer = (cast(ubyte*) alloca(size))[0 .. size];
+ else
+ buffer = new ubyte[size];
+ size_t r = void;
+ int result = 1;
+ uint tally = 0;
+ while ((r = trustedFread(f._p.handle, buffer)) > 0)
+ {
+ assert(r <= size);
+ if (r != size)
+ {
+ // error occured
+ if (!f.eof) throw new StdioException(null);
+ buffer.length = r;
+ }
+ static if (is(typeof(dg(tally, buffer))))
+ {
+ if ((result = dg(tally, buffer)) != 0) break;
+ }
+ else
+ {
+ if ((result = dg(buffer)) != 0) break;
+ }
+ ++tally;
+ }
+ return result;
+ }
+}
+
+@system unittest
+{
+ static import std.file;
+
+ scope(failure) printf("Failed test at line %d\n", __LINE__);
+
+ auto deleteme = testFilename();
+ scope(exit) { std.file.remove(deleteme); }
+
+ // test looping with an empty file
+ std.file.write(deleteme, "");
+ auto f = File(deleteme, "r");
+ foreach (ubyte[] line; chunks(f, 4))
+ {
+ assert(false);
+ }
+ f.close();
+
+ // test looping with a file with three lines
+ std.file.write(deleteme, "Line one\nline two\nline three\n");
+ f = File(deleteme, "r");
+ uint i = 0;
+ foreach (ubyte[] line; chunks(f, 3))
+ {
+ if (i == 0) assert(cast(char[]) line == "Lin");
+ else if (i == 1) assert(cast(char[]) line == "e o");
+ else if (i == 2) assert(cast(char[]) line == "ne\n");
+ else break;
+ ++i;
+ }
+ 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.
+ */
+class StdioException : Exception
+{
+ static import core.stdc.errno;
+ /// Operating system error code.
+ uint errno;
+
+/**
+Initialize with a message and an error code.
+*/
+ this(string message, uint e = core.stdc.errno.errno) @trusted
+ {
+ import std.exception : errnoString;
+ errno = e;
+ auto sysmsg = errnoString(errno);
+ // 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 ? message ~ " (" ~ sysmsg ~ ")" : sysmsg));
+ }
+
+/** Convenience functions that throw an $(D StdioException). */
+ static void opCall(string msg)
+ {
+ throw new StdioException(msg);
+ }
+
+/// ditto
+ static void opCall()
+ {
+ throw new StdioException(null, core.stdc.errno.errno);
+ }
+}
+
+// Undocumented but public because the std* handles are aliasing it.
+@property ref File makeGlobal(alias handle)()
+{
+ __gshared File.Impl impl;
+ __gshared File result;
+
+ // Use an inline spinlock to make sure the initializer is only run once.
+ // We assume there will be at most uint.max / 2 threads trying to initialize
+ // `handle` at once and steal the high bit to indicate that the globals have
+ // been initialized.
+ static shared uint spinlock;
+ import core.atomic : atomicLoad, atomicOp, MemoryOrder;
+ if (atomicLoad!(MemoryOrder.acq)(spinlock) <= uint.max / 2)
+ {
+ for (;;)
+ {
+ if (atomicLoad!(MemoryOrder.acq)(spinlock) > uint.max / 2)
+ break;
+ if (atomicOp!"+="(spinlock, 1) == 1)
+ {
+ impl.handle = handle;
+ result._p = &impl;
+ atomicOp!"+="(spinlock, uint.max / 2);
+ break;
+ }
+ atomicOp!"-="(spinlock, 1);
+ }
+ }
+ return result;
+}
+
+/** 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.
+*/
+alias stdin = makeGlobal!(core.stdc.stdio.stdin);
+
+///
+@safe unittest
+{
+ // Read stdin, sort lines, write to stdout
+ import std.algorithm.mutation : copy;
+ import std.algorithm.sorting : sort;
+ import std.array : array;
+ import std.typecons : Yes;
+
+ void main() {
+ stdin // read from stdin
+ .byLineCopy(Yes.keepTerminator) // copying each line
+ .array() // convert to array of lines
+ .sort() // sort the lines
+ .copy( // copy output of .sort to an OutputRange
+ stdout.lockingTextWriter()); // the OutputRange
+ }
+}
+
+/**
+ 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.
+*/
+alias stdout = makeGlobal!(core.stdc.stdio.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.
+*/
+alias stderr = makeGlobal!(core.stdc.stdio.stderr);
+
+@system unittest
+{
+ static import std.file;
+ import std.typecons : tuple;
+
+ scope(failure) printf("Failed test at line %d\n", __LINE__);
+ auto deleteme = testFilename();
+
+ 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++]);
+ }
+ assert(i == 3);
+ }
+}
+
+@safe unittest
+{
+ // Retain backwards compatibility
+ // https://issues.dlang.org/show_bug.cgi?id=17472
+ static assert(is(typeof(stdin) == File));
+ static assert(is(typeof(stdout) == File));
+ static assert(is(typeof(stderr) == File));
+}
+
+// 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*/)
+{
+ FLOCK(fps);
+ scope(exit) FUNLOCK(fps);
+
+ /* Since fps is now locked, we can create an "unshared" version
+ * of fp.
+ */
+ 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);
+ for (int c = void; (c = FGETWC(fp)) != -1; )
+ {
+ if ((c & ~0x7F) == 0)
+ {
+ app.putchar(cast(char) c);
+ if (c == terminator)
+ break;
+ }
+ else
+ {
+ if (c >= 0xD800 && c <= 0xDBFF)
+ {
+ int c2 = void;
+ if ((c2 = FGETWC(fp)) != -1 ||
+ c2 < 0xDC00 && c2 > 0xDFFF)
+ {
+ StdioException("unpaired UTF-16 surrogate");
+ }
+ c = ((c - 0xD7C0) << 10) + (c2 - 0xDC00);
+ }
+ app.putdchar(cast(dchar) c);
+ }
+ }
+ if (ferror(fps))
+ StdioException();
+ }
+
+ else if (fp._flag & _IONBF)
+ {
+ /* Use this for unbuffered I/O, when running
+ * across buffer boundaries, or for any but the common
+ * cases.
+ */
+ L1:
+ int c;
+ while ((c = FGETC(fp)) != -1)
+ {
+ app.putchar(cast(char) c);
+ if (c == terminator)
+ {
+ buf = app.data;
+ return buf.length;
+ }
+
+ }
+
+ if (ferror(fps))
+ StdioException();
+ }
+ else
+ {
+ int u = fp._cnt;
+ char* p = fp._ptr;
+ int i;
+ if (fp._flag & _IOTRAN)
+ { /* Translated mode ignores \r and treats ^Z as end-of-file
+ */
+ char c;
+ while (1)
+ {
+ if (i == u) // if end of buffer
+ goto L1; // give up
+ c = p[i];
+ i++;
+ if (c != '\r')
+ {
+ if (c == terminator)
+ break;
+ if (c != 0x1A)
+ continue;
+ goto L1;
+ }
+ else
+ { if (i != u && p[i] == terminator)
+ break;
+ goto L1;
+ }
+ }
+ app.putonly(p[0 .. i]);
+ app.buf[i - 1] = cast(char) terminator;
+ if (terminator == '\n' && c == '\r')
+ i++;
+ }
+ else
+ {
+ while (1)
+ {
+ if (i == u) // if end of buffer
+ goto L1; // give up
+ auto c = p[i];
+ i++;
+ if (c == terminator)
+ break;
+ }
+ app.putonly(p[0 .. i]);
+ }
+ fp._cnt -= i;
+ fp._ptr += i;
+ }
+
+ buf = app.data;
+ return buf.length;
+}
+
+version (MICROSOFT_STDIO)
+private size_t readlnImpl(FILE* fps, ref char[] buf, dchar terminator, File.Orientation /*ignored*/)
+{
+ FLOCK(fps);
+ scope(exit) FUNLOCK(fps);
+
+ /* Since fps is now locked, we can create an "unshared" version
+ * of fp.
+ */
+ auto fp = cast(_iobuf*) fps;
+
+ ReadlnAppender app;
+ app.initialize(buf);
+
+ int c;
+ while ((c = FGETC(fp)) != -1)
+ {
+ app.putchar(cast(char) c);
+ if (c == terminator)
+ {
+ buf = app.data;
+ return buf.length;
+ }
+
+ }
+
+ if (ferror(fps))
+ StdioException();
+ buf = app.data;
+ return buf.length;
+}
+
+version (HAS_GETDELIM)
+private size_t readlnImpl(FILE* fps, ref char[] buf, dchar terminator, File.Orientation orientation)
+{
+ import core.stdc.stdlib : free;
+ import core.stdc.wchar_ : fwide;
+
+ if (orientation == File.Orientation.wide)
+ {
+ /* Stream is in wide characters.
+ * Read them and convert to chars.
+ */
+ FLOCK(fps);
+ scope(exit) FUNLOCK(fps);
+ auto fp = cast(_iobuf*) fps;
+ version (Windows)
+ {
+ buf.length = 0;
+ for (int c = void; (c = FGETWC(fp)) != -1; )
+ {
+ if ((c & ~0x7F) == 0)
+ { buf ~= c;
+ if (c == terminator)
+ break;
+ }
+ else
+ {
+ if (c >= 0xD800 && c <= 0xDBFF)
+ {
+ int c2 = void;
+ if ((c2 = FGETWC(fp)) != -1 ||
+ c2 < 0xDC00 && c2 > 0xDFFF)
+ {
+ StdioException("unpaired UTF-16 surrogate");
+ }
+ c = ((c - 0xD7C0) << 10) + (c2 - 0xDC00);
+ }
+ import std.utf : encode;
+ encode(buf, c);
+ }
+ }
+ if (ferror(fp))
+ StdioException();
+ return buf.length;
+ }
+ else version (Posix)
+ {
+ buf.length = 0;
+ for (int c; (c = FGETWC(fp)) != -1; )
+ {
+ import std.utf : encode;
+
+ if ((c & ~0x7F) == 0)
+ buf ~= cast(char) c;
+ else
+ encode(buf, cast(dchar) c);
+ if (c == terminator)
+ break;
+ }
+ if (ferror(fps))
+ StdioException();
+ return buf.length;
+ }
+ else
+ {
+ static assert(0);
+ }
+ }
+
+ static char *lineptr = null;
+ static size_t n = 0;
+ scope(exit)
+ {
+ if (n > 128 * 1024)
+ {
+ // Bound memory used by readln
+ free(lineptr);
+ lineptr = null;
+ n = 0;
+ }
+ }
+
+ auto s = getdelim(&lineptr, &n, terminator, fps);
+ if (s < 0)
+ {
+ if (ferror(fps))
+ StdioException();
+ buf.length = 0; // end of file
+ return 0;
+ }
+
+ if (s <= buf.length)
+ {
+ buf = buf[0 .. s];
+ buf[] = lineptr[0 .. s];
+ }
+ else
+ {
+ buf = lineptr[0 .. s].dup;
+ }
+ return s;
+}
+
+version (NO_GETDELIM)
+private size_t readlnImpl(FILE* fps, ref char[] buf, dchar terminator, File.Orientation orientation)
+{
+ import core.stdc.wchar_ : fwide;
+
+ FLOCK(fps);
+ scope(exit) FUNLOCK(fps);
+ auto fp = cast(_iobuf*) fps;
+ if (orientation == File.Orientation.wide)
+ {
+ /* Stream is in wide characters.
+ * Read them and convert to chars.
+ */
+ version (Windows)
+ {
+ buf.length = 0;
+ for (int c; (c = FGETWC(fp)) != -1; )
+ {
+ if ((c & ~0x7F) == 0)
+ { buf ~= c;
+ if (c == terminator)
+ break;
+ }
+ else
+ {
+ if (c >= 0xD800 && c <= 0xDBFF)
+ {
+ int c2 = void;
+ if ((c2 = FGETWC(fp)) != -1 ||
+ c2 < 0xDC00 && c2 > 0xDFFF)
+ {
+ StdioException("unpaired UTF-16 surrogate");
+ }
+ c = ((c - 0xD7C0) << 10) + (c2 - 0xDC00);
+ }
+ import std.utf : encode;
+ encode(buf, c);
+ }
+ }
+ if (ferror(fp))
+ StdioException();
+ return buf.length;
+ }
+ 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;
+ else
+ encode(buf, cast(dchar) c);
+ if (c == terminator)
+ break;
+ }
+ if (ferror(fps))
+ StdioException();
+ return buf.length;
+ }
+ else
+ {
+ static assert(0);
+ }
+ }
+
+ // Narrow stream
+ // First, fill the existing buffer
+ for (size_t bufPos = 0; bufPos < buf.length; )
+ {
+ immutable c = FGETC(fp);
+ if (c == -1)
+ {
+ buf.length = bufPos;
+ goto endGame;
+ }
+ buf[bufPos++] = cast(char) c;
+ if (c == terminator)
+ {
+ // No need to test for errors in file
+ buf.length = bufPos;
+ return bufPos;
+ }
+ }
+ // Then, append to it
+ for (int c; (c = FGETC(fp)) != -1; )
+ {
+ buf ~= cast(char) c;
+ if (c == terminator)
+ {
+ // No need to test for errors in file
+ return buf.length;
+ }
+ }
+
+ endGame:
+ if (ferror(fps))
+ StdioException();
+ 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
+
+ Opens a TCP connection to the given host and port, then returns
+ a File struct with read and write access through the same interface
+ as any other file (meaning writef and the byLine ranges work!).
+
+ Authors:
+ Adam D. Ruppe
+
+ Bugs:
+ Only works on Linux
+*/
+version (linux)
+{
+ File openNetwork(string host, ushort port)
+ {
+ import core.stdc.string : memcpy;
+ import core.sys.posix.arpa.inet : htons;
+ import core.sys.posix.netdb : gethostbyname;
+ import core.sys.posix.netinet.in_ : sockaddr_in;
+ static import core.sys.posix.unistd;
+ static import sock = core.sys.posix.sys.socket;
+ import std.conv : to;
+ import std.exception : enforce;
+ import std.internal.cstring : tempCString;
+
+ auto h = enforce( gethostbyname(host.tempCString()),
+ new StdioException("gethostbyname"));
+
+ int s = sock.socket(sock.AF_INET, sock.SOCK_STREAM, 0);
+ enforce(s != -1, new StdioException("socket"));
+
+ scope(failure)
+ {
+ // want to make sure it doesn't dangle if something throws. Upon
+ // normal exit, the File struct's reference counting takes care of
+ // closing, so we don't need to worry about success
+ core.sys.posix.unistd.close(s);
+ }
+
+ sockaddr_in addr;
+
+ addr.sin_family = sock.AF_INET;
+ addr.sin_port = htons(port);
+ memcpy(&addr.sin_addr.s_addr, h.h_addr, h.h_length);
+
+ enforce(sock.connect(s, cast(sock.sockaddr*) &addr, addr.sizeof) != -1,
+ new StdioException("Connect failed"));
+
+ File f;
+ f.fdopen(s, "w+", host ~ ":" ~ to!string(port));
+ return f;
+ }
+}
+
+version (unittest) string testFilename(string file = __FILE__, size_t line = __LINE__) @safe
+{
+ import std.conv : text;
+ import std.file : deleteme;
+ import std.path : baseName;
+
+ // filename intentionally contains non-ASCII (Russian) characters for test Issue 7648
+ return text(deleteme, "-детка.", baseName(file), ".", line);
+}
diff --git a/libphobos/src/std/string.d b/libphobos/src/std/string.d
new file mode 100644
index 0000000..34a1452
--- /dev/null
+++ b/libphobos/src/std/string.d
@@ -0,0 +1,6952 @@
+// Written in the D programming language.
+
+/**
+String handling functions.
+
+$(SCRIPT inhibitQuickIndex = 1;)
+
+$(DIVC quickindex,
+$(BOOKTABLE ,
+$(TR $(TH Category) $(TH Functions) )
+$(TR $(TDNW Searching)
+ $(TD
+ $(MYREF column)
+ $(MYREF indexOf)
+ $(MYREF indexOfAny)
+ $(MYREF indexOfNeither)
+ $(MYREF lastIndexOf)
+ $(MYREF lastIndexOfAny)
+ $(MYREF lastIndexOfNeither)
+ )
+)
+$(TR $(TDNW Comparison)
+ $(TD
+ $(MYREF isNumeric)
+ )
+)
+$(TR $(TDNW Mutation)
+ $(TD
+ $(MYREF capitalize)
+ )
+)
+$(TR $(TDNW Pruning and Filling)
+ $(TD
+ $(MYREF center)
+ $(MYREF chomp)
+ $(MYREF chompPrefix)
+ $(MYREF chop)
+ $(MYREF detabber)
+ $(MYREF detab)
+ $(MYREF entab)
+ $(MYREF entabber)
+ $(MYREF leftJustify)
+ $(MYREF outdent)
+ $(MYREF rightJustify)
+ $(MYREF strip)
+ $(MYREF stripLeft)
+ $(MYREF stripRight)
+ $(MYREF wrap)
+ )
+)
+$(TR $(TDNW Substitution)
+ $(TD
+ $(MYREF abbrev)
+ $(MYREF soundex)
+ $(MYREF soundexer)
+ $(MYREF succ)
+ $(MYREF tr)
+ $(MYREF translate)
+ )
+)
+$(TR $(TDNW Miscellaneous)
+ $(TD
+ $(MYREF assumeUTF)
+ $(MYREF fromStringz)
+ $(MYREF lineSplitter)
+ $(MYREF representation)
+ $(MYREF splitLines)
+ $(MYREF toStringz)
+ )
+)))
+
+Objects of types $(D _string), $(D wstring), and $(D dstring) are value types
+and cannot be mutated element-by-element. For using mutation during building
+strings, use $(D char[]), $(D wchar[]), or $(D dchar[]). The $(D xxxstring)
+types are preferable because they don't exhibit undesired aliasing, thus
+making code more robust.
+
+The following functions are publicly imported:
+
+$(BOOKTABLE ,
+$(TR $(TH Module) $(TH Functions) )
+$(LEADINGROW Publicly imported functions)
+ $(TR $(TD std.algorithm)
+ $(TD
+ $(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
+ $(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
+ $(REF_SHORT format, std,format)
+ $(REF_SHORT sformat, std,format)
+ ))
+ $(TR $(TD std.uni)
+ $(TD
+ $(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 $(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
+ $(MREF std, algorithm) and
+ $(MREF std, range)
+ for generic range algorithms
+ ,
+ $(MREF std, ascii)
+ for functions that work with ASCII strings
+ ,
+ $(MREF std, uni)
+ for functions that work with unicode strings
+ )
+
+Copyright: Copyright Digital Mars 2007-.
+
+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,
+ and David L. 'SpottedTiger' Davis
+
+Source: $(PHOBOSSRC std/_string.d)
+
+*/
+module std.string;
+
+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.format : format, sformat;
+import std.typecons : Flag, Yes, No;
+public import std.uni : icmp, toLower, toLowerInPlace, toUpper, toUpperInPlace;
+
+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.comparison : cmp;
+public import std.algorithm.searching : startsWith, endsWith, count;
+public import std.array : join, replace, replaceInPlace, split, empty;
+
+/* ************* Exceptions *************** */
+
+/++
+ Exception thrown on errors in std.string functions.
+ +/
+class StringException : Exception
+{
+ import std.exception : basicExceptionCtors;
+
+ ///
+ mixin basicExceptionCtors;
+}
+
+
+/++
+ 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) @nogc @system pure nothrow {
+ import core.stdc.string : strlen;
+ return cString ? cString[0 .. strlen(cString)] : null;
+}
+
+///
+@system pure unittest
+{
+ assert(fromStringz(null) == null);
+ assert(fromStringz("foo") == "foo");
+}
+
+/++
+ 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 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
+out (result)
+{
+ import core.stdc.string : strlen, memcmp;
+ if (result)
+ {
+ auto slen = s.length;
+ while (slen > 0 && s[slen-1] == 0) --slen;
+ assert(strlen(result) == slen);
+ assert(result[0 .. slen] == s[0 .. slen]);
+ }
+}
+body
+{
+ import std.exception : assumeUnique;
+ /+ Unfortunately, this isn't reliable.
+ We could make this work if string literals are put
+ in read-only memory and we test if s[] is pointing into
+ that.
+
+ /* Peek past end of s[], if it's 0, no conversion necessary.
+ * Note that the compiler will put a 0 past the end of static
+ * strings, and the storage allocator will put a 0 past the end
+ * of newly allocated char[]'s.
+ */
+ char* p = &s[0] + s.length;
+ if (*p == 0)
+ return s;
+ +/
+
+ // Need to make a copy
+ auto copy = new char[s.length + 1];
+ copy[0 .. s.length] = s[];
+ copy[s.length] = 0;
+
+ return &assumeUnique(copy)[0];
+}
+
+/++ Ditto +/
+immutable(char)* toStringz(in string s) @trusted pure nothrow
+{
+ if (s.empty) return "".ptr;
+ /* Peek past end of s[], if it's 0, no conversion necessary.
+ * Note that the compiler will put a 0 past the end of static
+ * strings, and the storage allocator will put a 0 past the end
+ * of newly allocated char[]'s.
+ */
+ immutable p = s.ptr + s.length;
+ // Is p dereferenceable? A simple test: if the p points to an
+ // address multiple of 4, then conservatively assume the pointer
+ // 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 &s[0];
+ return toStringz(cast(const char[]) s);
+}
+
+///
+pure nothrow @system unittest
+{
+ import core.stdc.string : strlen;
+ import std.conv : to;
+
+ auto p = toStringz("foo");
+ assert(strlen(p) == 3);
+ const(char)[] foo = "abbzxyzzy";
+ p = toStringz(foo[3 .. 5]);
+ assert(strlen(p) == 2);
+
+ string test = "";
+ p = toStringz(test);
+ assert(*p == 0);
+
+ test = "\0";
+ p = toStringz(test);
+ assert(*p == 0);
+
+ test = "foo\0";
+ p = toStringz(test);
+ assert(p[0] == 'f' && p[1] == 'o' && p[2] == 'o' && p[3] == 0);
+
+ const string test2 = "";
+ p = toStringz(test2);
+ assert(*p == 0);
+}
+
+
+/**
+ Flag indicating whether a search is case-sensitive.
+*/
+alias CaseSensitive = Flag!"caseSensitive";
+
+/++
+ Searches for character in range.
+
+ 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(Range)(Range s, in dchar c,
+ in CaseSensitive cs = Yes.caseSensitive)
+if (isInputRange!Range && isSomeChar!(ElementEncodingType!Range) &&
+ !isConvertibleToString!Range)
+{
+ 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 && isSomeString!Range)
+ {
+ if (std.ascii.isASCII(c) && !__ctfe)
+ { // Plain old ASCII
+ 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);
+ }
+ }
+
+ 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)
+ {
+ 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
+ immutable c1 = cast(char) std.ascii.toLower(c);
+
+ ptrdiff_t i;
+ foreach (const c2; s.byCodeUnit())
+ {
+ if (c1 == std.ascii.toLower(c2))
+ return i;
+ ++i;
+ }
+ }
+ else
+ { // c is a universal character
+ immutable c1 = std.uni.toLower(c);
+
+ ptrdiff_t i;
+ foreach (const c2; s.byDchar())
+ {
+ 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;
+
+ assertCTFEable!(
+ {
+ foreach (S; AliasSeq!(string, wstring, dstring))
+ {
+ 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', 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', 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)
+ {
+ 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);
+ });
+}
+
+@safe pure unittest
+{
+ assert(testAliasedString!indexOf("std/string.d", '/', 3));
+}
+
+@safe pure unittest
+{
+ import std.conv : to;
+ import std.traits : EnumMembers;
+ import std.utf : byCodeUnit, byChar, byWchar;
+
+ 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(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,
+ No.caseSensitive) == -1);
+ assert(indexOf(to!S("def"), cast(dchar)'a', 1,
+ No.caseSensitive) == -1);
+ assert(indexOf(to!S("def"), cast(dchar)'a', 12,
+ No.caseSensitive) == -1);
+ assert(indexOf(to!S("AbbA"), cast(dchar)'a', 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,
+ 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)
+ {
+ assert(indexOf("hello\U00010143\u0100\U00010143", '\u0100', 2, cs)
+ == 9);
+ assert(indexOf("hello\U00010143\u0100\U00010143"w, '\u0100', 3, cs)
+ == 7);
+ assert(indexOf("hello\U00010143\u0100\U00010143"d, '\u0100', 6, cs)
+ == 6);
+ }
+}
+
+/++
+ Searches for substring in $(D s).
+
+ 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(Range, Char)(Range s, const(Char)[] sub,
+ in CaseSensitive cs = Yes.caseSensitive)
+if (isForwardRange!Range && isSomeChar!(ElementEncodingType!Range) &&
+ isSomeChar!Char)
+{
+ alias Char1 = Unqual!(ElementEncodingType!Range);
+
+ static if (isSomeString!Range)
+ {
+ 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
+ {
+ 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 -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;
+
+ assertCTFEable!(
+ {
+ foreach (S; AliasSeq!(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(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"), 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"), 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"), 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"), No.caseSensitive) == -1);
+ }();
+
+ foreach (cs; EnumMembers!CaseSensitive)
+ {
+ assert(indexOf("hello\U00010143\u0100\U00010143", to!S("\u0100"), cs) == 9);
+ assert(indexOf("hello\U00010143\u0100\U00010143"w, to!S("\u0100"), cs) == 7);
+ assert(indexOf("hello\U00010143\u0100\U00010143"d, to!S("\u0100"), cs) == 6);
+ }
+ }
+ });
+}
+
+@safe pure @nogc nothrow
+unittest
+{
+ import std.traits : EnumMembers;
+ import std.utf : byWchar;
+
+ foreach (cs; EnumMembers!CaseSensitive)
+ {
+ 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);
+ }
+}
+
+@safe pure unittest
+{
+ import std.conv : to;
+ import std.traits : EnumMembers;
+
+ foreach (S; AliasSeq!(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(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, 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,
+ 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, 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, 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)
+ {
+ assert(indexOf("hello\U00010143\u0100\U00010143", to!S("\u0100"),
+ 3, cs) == 9);
+ assert(indexOf("hello\U00010143\u0100\U00010143"w, to!S("\u0100"),
+ 3, cs) == 7);
+ assert(indexOf("hello\U00010143\u0100\U00010143"d, to!S("\u0100"),
+ 3, cs) == 6);
+ }
+ }
+}
+
+/++
+ 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 = Yes.caseSensitive) @safe pure
+if (isSomeChar!Char)
+{
+ static import std.ascii, std.uni;
+ import std.utf : canSearchInCodeUnits;
+ if (cs == Yes.caseSensitive)
+ {
+ if (canSearchInCodeUnits!Char(c))
+ {
+ foreach_reverse (i, it; s)
+ {
+ if (it == c)
+ {
+ return i;
+ }
+ }
+ }
+ else
+ {
+ foreach_reverse (i, dchar it; s)
+ {
+ if (it == c)
+ {
+ return i;
+ }
+ }
+ }
+ }
+ else
+ {
+ if (std.ascii.isASCII(c))
+ {
+ immutable c1 = std.ascii.toLower(c);
+
+ foreach_reverse (i, it; s)
+ {
+ immutable c2 = std.ascii.toLower(it);
+ if (c1 == c2)
+ {
+ return i;
+ }
+ }
+ }
+ else
+ {
+ immutable c1 = std.uni.toLower(c);
+
+ foreach_reverse (i, dchar it; s)
+ {
+ immutable c2 = std.uni.toLower(it);
+ if (c1 == c2)
+ {
+ return i;
+ }
+ }
+ }
+ }
+
+ 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;
+
+ assertCTFEable!(
+ {
+ 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);
+ assert(lastIndexOf(to!S("def"), 'f') == 2);
+ assert(lastIndexOf(to!S("ödef"), 'ö') == 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"),
+ No.caseSensitive) == 1);
+
+ S sPlts = "Mars: the fourth Rock (Planet) from the Sun.";
+
+ 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)
+ {
+ assert(lastIndexOf("\U00010143\u0100\U00010143hello", '\u0100', cs) == 4);
+ assert(lastIndexOf("\U00010143\u0100\U00010143hello"w, '\u0100', cs) == 2);
+ assert(lastIndexOf("\U00010143\u0100\U00010143hello"d, '\u0100', cs) == 1);
+ }
+ });
+}
+
+@safe pure unittest
+{
+ import std.conv : to;
+ import std.traits : EnumMembers;
+
+ 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', 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, 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)
+ {
+ assert(lastIndexOf("\U00010143\u0100\U00010143hello", '\u0100', cs) == 4);
+ assert(lastIndexOf("\U00010143\u0100\U00010143hello"w, '\u0100', cs) == 2);
+ assert(lastIndexOf("\U00010143\u0100\U00010143hello"d, '\u0100', cs) == 1);
+ }
+}
+
+/++
+ 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 = Yes.caseSensitive) @safe pure
+if (isSomeChar!Char1 && isSomeChar!Char2)
+{
+ import std.algorithm.searching : endsWith;
+ import std.conv : to;
+ import std.range.primitives : walkLength;
+ static import std.uni;
+ import std.utf : strideBack;
+ if (sub.empty)
+ return -1;
+
+ if (walkLength(sub) == 1)
+ return lastIndexOf(s, sub.front, cs);
+
+ if (cs == Yes.caseSensitive)
+ {
+ static if (is(Unqual!Char1 == Unqual!Char2))
+ {
+ import core.stdc.string : memcmp;
+
+ immutable c = sub[0];
+
+ for (ptrdiff_t i = s.length - sub.length; i >= 0; --i)
+ {
+ if (s[i] == c)
+ {
+ if (__ctfe)
+ {
+ foreach (j; 1 .. sub.length)
+ {
+ if (s[i + j] != sub[j])
+ continue;
+ }
+ return i;
+ }
+ else
+ {
+ auto trustedMemcmp(in void* s1, in void* s2, size_t n) @trusted
+ {
+ return memcmp(s1, s2, n);
+ }
+ if (trustedMemcmp(&s[i + 1], &sub[1],
+ (sub.length - 1) * Char1.sizeof) == 0)
+ return i;
+ }
+ }
+ }
+ }
+ else
+ {
+ for (size_t i = s.length; !s.empty;)
+ {
+ if (s.endsWith(sub))
+ return cast(ptrdiff_t) i - to!(const(Char1)[])(sub).length;
+
+ i -= strideBack(s, i);
+ s = s[0 .. i];
+ }
+ }
+ }
+ else
+ {
+ for (size_t i = s.length; !s.empty;)
+ {
+ if (endsWith!((a, b) => std.uni.toLower(a) == std.uni.toLower(b))
+ (s, sub))
+ {
+ return cast(ptrdiff_t) i - to!(const(Char1)[])(sub).length;
+ }
+
+ i -= strideBack(s, i);
+ s = s[0 .. i];
+ }
+ }
+
+ return -1;
+}
+
+/// 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)
+{
+ 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;
+
+ assertCTFEable!(
+ {
+ foreach (S; AliasSeq!(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(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);
+ assert(lastIndexOf(to!S("abcdefCdef"), to!T("c")) == 2, typeStr);
+ 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("")) == -1, typeStr);
+ assert(lastIndexOf(to!S("öabcdefcdef"), to!T("ö")) == 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"), 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("ö"), 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"), 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)
+ {
+ enum csString = to!string(cs);
+
+ assert(lastIndexOf("\U00010143\u0100\U00010143hello", to!S("\u0100"), cs) == 4, csString);
+ assert(lastIndexOf("\U00010143\u0100\U00010143hello"w, to!S("\u0100"), cs) == 2, csString);
+ assert(lastIndexOf("\U00010143\u0100\U00010143hello"d, to!S("\u0100"), cs) == 1, csString);
+ }
+ }
+ });
+}
+
+@safe pure unittest // issue13529
+{
+ import std.conv : to;
+ foreach (S; AliasSeq!(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("ö ö"));
+ assert(idx != -1, to!string(idx) ~ " " ~ typeStr);
+
+ idx = lastIndexOf(to!T("Hällö Wörldö ö"),to!S("ö öd"));
+ assert(idx == -1, to!string(idx) ~ " " ~ typeStr);
+ }
+ }
+}
+
+@safe pure unittest
+{
+ import std.conv : to;
+ import std.traits : EnumMembers;
+
+ foreach (S; AliasSeq!(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(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 ~
+ format(" %u", lastIndexOf(to!S("abcdefcdef"), to!T("ef"), 6)));
+ 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("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) == -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, 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)
+ {
+ enum csString = to!string(cs);
+
+ assert(lastIndexOf("\U00010143\u0100\U00010143hello", to!S("\u0100"), 6, cs) == 4, csString);
+ assert(lastIndexOf("\U00010143\u0100\U00010143hello"w, to!S("\u0100"), 6, cs) == 2, csString);
+ assert(lastIndexOf("\U00010143\u0100\U00010143hello"d, to!S("\u0100"), 3, cs) == 1, csString);
+ }
+ }
+}
+
+private ptrdiff_t indexOfAnyNeitherImpl(bool forward, bool any, Char, Char2)(
+ const(Char)[] haystack, const(Char2)[] needles,
+ in CaseSensitive cs = Yes.caseSensitive) @safe pure
+if (isSomeChar!Char && isSomeChar!Char2)
+{
+ import std.algorithm.searching : canFind, findAmong;
+ if (cs == Yes.caseSensitive)
+ {
+ static if (forward)
+ {
+ static if (any)
+ {
+ size_t n = haystack.findAmong(needles).length;
+ return n ? haystack.length - n : -1;
+ }
+ else
+ {
+ foreach (idx, dchar hay; haystack)
+ {
+ if (!canFind(needles, hay))
+ {
+ return idx;
+ }
+ }
+ }
+ }
+ else
+ {
+ static if (any)
+ {
+ import std.range : retro;
+ import std.utf : strideBack;
+ size_t n = haystack.retro.findAmong(needles).source.length;
+ if (n)
+ {
+ return n - haystack.strideBack(n);
+ }
+ }
+ else
+ {
+ foreach_reverse (idx, dchar hay; haystack)
+ {
+ if (!canFind(needles, hay))
+ {
+ return idx;
+ }
+ }
+ }
+ }
+ }
+ 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++] = toLower(c);
+ }
+
+ static if (forward)
+ {
+ foreach (i, dchar c; haystack)
+ {
+ if (canFind(scratch[0 .. si], toLower(c)) == any)
+ {
+ return i;
+ }
+ }
+ }
+ else
+ {
+ foreach_reverse (i, dchar c; haystack)
+ {
+ if (canFind(scratch[0 .. si], toLower(c)) == any)
+ {
+ return i;
+ }
+ }
+ }
+ }
+ else
+ {
+ static bool f(dchar a, dchar b)
+ {
+ return toLower(a) == b;
+ }
+
+ static if (forward)
+ {
+ foreach (i, dchar c; haystack)
+ {
+ if (canFind!f(needles, toLower(c)) == any)
+ {
+ return i;
+ }
+ }
+ }
+ else
+ {
+ foreach_reverse (i, dchar c; haystack)
+ {
+ if (canFind!f(needles, toLower(c)) == any)
+ {
+ return i;
+ }
+ }
+ }
+ }
+ }
+
+ return -1;
+}
+
+/**
+ 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. 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.
+ 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 = 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
+{
+ import std.conv : to;
+
+ ptrdiff_t i = "helloWorld".indexOfAny("Wr");
+ assert(i == 5);
+ i = "öällo world".indexOfAny("lo ");
+ 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;
+
+ assertCTFEable!(
+ {
+ foreach (S; AliasSeq!(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(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);
+ assert(indexOfAny(to!S("dfefffg"), to!T("fgh")) == 1);
+ assert(indexOfAny(to!S("dfeffgfff"), to!T("feg")) == 1);
+
+ assert(indexOfAny(to!S("zfeffgfff"), to!T("ACDC"),
+ No.caseSensitive) == -1);
+ assert(indexOfAny(to!S("def"), to!T("MI6"),
+ No.caseSensitive) == -1);
+ assert(indexOfAny(to!S("abba"), to!T("DEA"),
+ 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"),
+ No.caseSensitive) == 0);
+ assert(indexOfAny(to!S("dfeffgfff"), to!T("BNDabCHIJKQEPÖÖSYXÄ??ß"),
+ No.caseSensitive) == 0);
+
+ assert(indexOfAny("\u0100", to!T("\u0100"), No.caseSensitive) == 0);
+ }();
+ }
+ }
+ );
+}
+
+@safe pure unittest
+{
+ import std.conv : to;
+ import std.traits : EnumMembers;
+
+ foreach (S; AliasSeq!(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(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);
+ assert(indexOfAny(to!S("dfefffg"), to!T("foo"), 2) == 3);
+ assert(indexOfAny(to!S("dfeffgfff"), to!T("fsb"), 5) == 6);
+
+ assert(indexOfAny(to!S("dfeffgfff"), to!T("NDS"), 1,
+ No.caseSensitive) == -1);
+ assert(indexOfAny(to!S("def"), to!T("DRS"), 2,
+ No.caseSensitive) == -1);
+ assert(indexOfAny(to!S("abba"), to!T("SI"), 3,
+ No.caseSensitive) == -1);
+ assert(indexOfAny(to!S("deO"), to!T("ASIO"), 1,
+ No.caseSensitive) == 2);
+ assert(indexOfAny(to!S("dfefffg"), to!T("fbh"), 2,
+ No.caseSensitive) == 3);
+ assert(indexOfAny(to!S("dfeffgfff"), to!T("fEe"), 4,
+ No.caseSensitive) == 4);
+ assert(indexOfAny(to!S("dfeffgffföä"), to!T("föä"), 9,
+ No.caseSensitive) == 9);
+
+ assert(indexOfAny("\u0100", to!T("\u0100"), 0,
+ No.caseSensitive) == 0);
+ }();
+
+ foreach (cs; EnumMembers!CaseSensitive)
+ {
+ assert(indexOfAny("hello\U00010143\u0100\U00010143",
+ to!S("e\u0100"), 3, cs) == 9);
+ assert(indexOfAny("hello\U00010143\u0100\U00010143"w,
+ to!S("h\u0100"), 3, cs) == 7);
+ assert(indexOfAny("hello\U00010143\u0100\U00010143"d,
+ to!S("l\u0100"), 5, cs) == 6);
+ }
+ }
+}
+
+/**
+ 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. 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.
+ 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 = 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
+{
+ ptrdiff_t i = "helloWorld".lastIndexOfAny("Wlo");
+ assert(i == 8);
+
+ i = "Foo öäöllo world".lastIndexOfAny("öF");
+ 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;
+
+ assertCTFEable!(
+ {
+ foreach (S; AliasSeq!(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(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);
+ assert(lastIndexOfAny(to!S("dfefffg"), to!T("fgh")) == 6);
+
+ ptrdiff_t oeIdx = 9;
+ if (is(S == wstring) || is(S == dstring))
+ {
+ oeIdx = 8;
+ }
+
+ auto foundOeIdx = lastIndexOfAny(to!S("dfeffgföf"), to!T("feg"));
+ assert(foundOeIdx == oeIdx, to!string(foundOeIdx));
+
+ assert(lastIndexOfAny(to!S("zfeffgfff"), to!T("ACDC"),
+ No.caseSensitive) == -1);
+ assert(lastIndexOfAny(to!S("def"), to!T("MI6"),
+ No.caseSensitive) == -1);
+ assert(lastIndexOfAny(to!S("abba"), to!T("DEA"),
+ No.caseSensitive) == 3);
+ assert(lastIndexOfAny(to!S("def"), to!T("FBI"),
+ No.caseSensitive) == 2);
+ assert(lastIndexOfAny(to!S("dfefffg"), to!T("NSA"),
+ No.caseSensitive) == -1);
+
+ oeIdx = 2;
+ if (is(S == wstring) || is(S == dstring))
+ {
+ oeIdx = 1;
+ }
+ assert(lastIndexOfAny(to!S("ödfeffgfff"), to!T("BND"),
+ No.caseSensitive) == oeIdx);
+
+ assert(lastIndexOfAny("\u0100", to!T("\u0100"),
+ No.caseSensitive) == 0);
+ }();
+ }
+ }
+ );
+}
+
+@safe pure unittest
+{
+ import std.conv : to;
+ import std.exception : assertCTFEable;
+
+ assertCTFEable!(
+ {
+ foreach (S; AliasSeq!(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,
+ typeStr);
+ assert(lastIndexOfAny(to!S("abcdefcdef"), to!T("c"), 7) == 6,
+ typeStr);
+ assert(lastIndexOfAny(to!S("abcdefcdef"), to!T("cd"), 5) == 3,
+ typeStr);
+ assert(lastIndexOfAny(to!S("abcdefcdef"), to!T("ef"), 6) == 5,
+ typeStr);
+ assert(lastIndexOfAny(to!S("abcdefCdef"), to!T("c"), 8) == 2,
+ typeStr);
+ assert(lastIndexOfAny(to!S("abcdefcdef"), to!T("x"), 7) == -1,
+ typeStr);
+ assert(lastIndexOfAny(to!S("abcdefcdef"), to!T("xy"), 4) == -1,
+ typeStr);
+ assert(lastIndexOfAny(to!S("öabcdefcdef"), to!T("ö"), 2) == 0,
+ typeStr);
+
+ assert(lastIndexOfAny(cast(S) null, to!T("a"), 1337,
+ No.caseSensitive) == -1, typeStr);
+ assert(lastIndexOfAny(to!S("abcdefcdef"), to!T("C"), 7,
+ No.caseSensitive) == 6, typeStr);
+ assert(lastIndexOfAny(to!S("ABCDEFCDEF"), to!T("cd"), 5,
+ No.caseSensitive) == 3, typeStr);
+ assert(lastIndexOfAny(to!S("abcdefcdef"), to!T("EF"), 6,
+ No.caseSensitive) == 5, typeStr);
+ assert(lastIndexOfAny(to!S("ABCDEFcDEF"), to!T("C"), 8,
+ No.caseSensitive) == 6, typeStr);
+ assert(lastIndexOfAny(to!S("ABCDEFCDEF"), to!T("x"), 7,
+ No.caseSensitive) == -1, typeStr);
+ assert(lastIndexOfAny(to!S("abCdefcdef"), to!T("XY"), 4,
+ No.caseSensitive) == -1, typeStr);
+ assert(lastIndexOfAny(to!S("ÖABCDEFCDEF"), to!T("ö"), 2,
+ No.caseSensitive) == 0, typeStr);
+ }();
+ }
+ }
+ );
+}
+
+/**
+ 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.
+ 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 = Yes.caseSensitive)
+ @safe pure
+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
+{
+ assert(indexOfNeither("def", "a") == 0);
+ assert(indexOfNeither("def", "de") == 2);
+ assert(indexOfNeither("dfefffg", "dfe") == 6);
+}
+
+@safe pure unittest
+{
+ 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;
+
+ assertCTFEable!(
+ {
+ foreach (S; AliasSeq!(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("abba", "a") == 1);
+
+ assert(indexOfNeither(to!S("dfeffgfff"), to!T("a"),
+ No.caseSensitive) == 0);
+ assert(indexOfNeither(to!S("def"), to!T("D"),
+ No.caseSensitive) == 1);
+ assert(indexOfNeither(to!S("ABca"), to!T("a"),
+ No.caseSensitive) == 1);
+ assert(indexOfNeither(to!S("def"), to!T("f"),
+ No.caseSensitive) == 0);
+ assert(indexOfNeither(to!S("DfEfffg"), to!T("dFe"),
+ No.caseSensitive) == 6);
+ if (is(S == string))
+ {
+ assert(indexOfNeither(to!S("äDfEfffg"), to!T("ädFe"),
+ No.caseSensitive) == 8,
+ to!string(indexOfNeither(to!S("äDfEfffg"), to!T("ädFe"),
+ No.caseSensitive)));
+ }
+ else
+ {
+ assert(indexOfNeither(to!S("äDfEfffg"), to!T("ädFe"),
+ No.caseSensitive) == 7,
+ to!string(indexOfNeither(to!S("äDfEfffg"), to!T("ädFe"),
+ No.caseSensitive)));
+ }
+ }();
+ }
+ }
+ );
+}
+
+@safe pure unittest
+{
+ import std.conv : to;
+ import std.exception : assertCTFEable;
+
+ assertCTFEable!(
+ {
+ foreach (S; AliasSeq!(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(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,
+ No.caseSensitive) == 4);
+ assert(indexOfNeither(to!S("def"), to!T("D"), 2,
+ No.caseSensitive) == 2);
+ assert(indexOfNeither(to!S("ABca"), to!T("a"), 3,
+ No.caseSensitive) == -1);
+ assert(indexOfNeither(to!S("def"), to!T("tzf"), 2,
+ No.caseSensitive) == -1);
+ assert(indexOfNeither(to!S("DfEfffg"), to!T("dFe"), 5,
+ No.caseSensitive) == 6);
+ if (is(S == string))
+ {
+ assert(indexOfNeither(to!S("öDfEfffg"), to!T("äDi"), 2,
+ 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,
+ No.caseSensitive) == 2, to!string(indexOfNeither(
+ to!S("öDfEfffg"), to!T("äDi"), 2, 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 CaseSensitive cs = Yes.caseSensitive)
+ @safe pure
+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
+{
+ assert(lastIndexOfNeither("abba", "a") == 2);
+ 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;
+
+ assertCTFEable!(
+ {
+ foreach (S; AliasSeq!(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(to!S("def"), to!T("rsa")) == 2);
+ assert(lastIndexOfNeither(to!S("dfefffg"), to!T("fgh")) == 2);
+
+ ptrdiff_t oeIdx = 8;
+ if (is(S == string))
+ {
+ oeIdx = 9;
+ }
+
+ auto foundOeIdx = lastIndexOfNeither(to!S("ödfefegff"), to!T("zeg"));
+ assert(foundOeIdx == oeIdx, to!string(foundOeIdx));
+
+ assert(lastIndexOfNeither(to!S("zfeffgfsb"), to!T("FSB"),
+ No.caseSensitive) == 5);
+ assert(lastIndexOfNeither(to!S("def"), to!T("MI6"),
+ No.caseSensitive) == 2, to!string(lastIndexOfNeither(to!S("def"),
+ to!T("MI6"), No.caseSensitive)));
+ assert(lastIndexOfNeither(to!S("abbadeafsb"), to!T("fSb"),
+ No.caseSensitive) == 6, to!string(lastIndexOfNeither(
+ to!S("abbadeafsb"), to!T("fSb"), No.caseSensitive)));
+ assert(lastIndexOfNeither(to!S("defbi"), to!T("FBI"),
+ No.caseSensitive) == 1);
+ assert(lastIndexOfNeither(to!S("dfefffg"), to!T("NSA"),
+ No.caseSensitive) == 6);
+ assert(lastIndexOfNeither(to!S("dfeffgfffö"), to!T("BNDabCHIJKQEPÖÖSYXÄ??ß"),
+ No.caseSensitive) == 8, to!string(lastIndexOfNeither(to!S("dfeffgfffö"),
+ to!T("BNDabCHIJKQEPÖÖSYXÄ??ß"), No.caseSensitive)));
+ }();
+ }
+ }
+ );
+}
+
+@safe pure unittest
+{
+ import std.conv : to;
+ import std.exception : assertCTFEable;
+
+ assertCTFEable!(
+ {
+ foreach (S; AliasSeq!(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(to!S("def"), to!T("f")) == 1);
+ assert(lastIndexOfNeither(to!S("dfefffg"), to!T("fgh")) == 2);
+
+ ptrdiff_t oeIdx = 4;
+ if (is(S == string))
+ {
+ oeIdx = 5;
+ }
+
+ auto foundOeIdx = lastIndexOfNeither(to!S("ödfefegff"), to!T("zeg"),
+ 7);
+ assert(foundOeIdx == oeIdx, to!string(foundOeIdx));
+
+ assert(lastIndexOfNeither(to!S("zfeffgfsb"), to!T("FSB"), 6,
+ No.caseSensitive) == 5);
+ assert(lastIndexOfNeither(to!S("def"), to!T("MI6"), 2,
+ No.caseSensitive) == 1, to!string(lastIndexOfNeither(to!S("def"),
+ to!T("MI6"), 2, No.caseSensitive)));
+ assert(lastIndexOfNeither(to!S("abbadeafsb"), to!T("fSb"), 6,
+ No.caseSensitive) == 5, to!string(lastIndexOfNeither(
+ to!S("abbadeafsb"), to!T("fSb"), 6, No.caseSensitive)));
+ assert(lastIndexOfNeither(to!S("defbi"), to!T("FBI"), 3,
+ No.caseSensitive) == 1);
+ assert(lastIndexOfNeither(to!S("dfefffg"), to!T("NSA"), 2,
+ 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
+ * 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)
+{
+ import std.traits : ModifyTypePreservingTQ;
+ alias ToRepType(T) = AliasSeq!(ubyte, ushort, uint)[T.sizeof / 2];
+ return cast(ModifyTypePreservingTQ!(ToRepType, Char)[])s;
+}
+
+///
+@safe pure unittest
+{
+ string s = "hello";
+ static assert(is(typeof(representation(s)) == immutable(ubyte)[]));
+ assert(representation(s) is cast(immutable(ubyte)[]) s);
+ assert(representation(s) == [0x68, 0x65, 0x6c, 0x6c, 0x6f]);
+}
+
+@system pure unittest
+{
+ import std.exception : assertCTFEable;
+ import std.traits : Fields;
+ import std.typecons : Tuple;
+
+ assertCTFEable!(
+ {
+ void test(Char, T)(Char[] str)
+ {
+ static assert(is(typeof(representation(str)) == T[]));
+ assert(representation(str) is cast(T[]) str);
+ }
+
+ foreach (Type; AliasSeq!(Tuple!(char , ubyte ),
+ Tuple!(wchar, ushort),
+ Tuple!(dchar, uint )))
+ {
+ alias Char = Fields!Type[0];
+ alias Int = Fields!Type[1];
+ enum immutable(Char)[] hello = "hello";
+
+ test!( immutable Char, immutable Int)(hello);
+ test!( const Char, const Int)(hello);
+ test!( Char, Int)(hello.dup);
+ test!( shared Char, shared Int)(cast(shared) hello.dup);
+ test!(const shared Char, const shared Int)(hello);
+ }
+ });
+}
+
+
+/**
+ * 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.array : array;
+ import std.uni : asCapitalized;
+ import std.utf : byUTF;
+
+ return input.asCapitalized.byUTF!(ElementEncodingType!(S)).array;
+}
+
+///
+pure @safe unittest
+{
+ assert(capitalize("hello") == "Hello");
+ assert(capitalize("World") == "World");
+}
+
+auto capitalize(S)(auto ref S s)
+if (!isSomeString!S && is(StringTypeOf!S))
+{
+ return capitalize!(StringTypeOf!S)(s);
+}
+
+@safe pure unittest
+{
+ assert(testAliasedString!capitalize("hello"));
+}
+
+@safe pure unittest
+{
+ import std.algorithm.comparison : cmp;
+ import std.conv : to;
+ import std.exception : assertCTFEable;
+
+ assertCTFEable!(
+ {
+ foreach (S; AliasSeq!(string, wstring, dstring, char[], wchar[], dchar[]))
+ {
+ S s1 = to!S("FoL");
+ S s2;
+
+ s2 = capitalize(s1);
+ assert(cmp(s2, "Fol") == 0);
+ assert(s2 !is s1);
+
+ s2 = capitalize(s1[0 .. 2]);
+ assert(cmp(s2, "Fo") == 0);
+
+ s1 = to!S("fOl");
+ s2 = capitalize(s1);
+ assert(cmp(s2, "Fol") == 0);
+ assert(s2 !is s1);
+ s1 = to!S("\u0131 \u0130");
+ s2 = capitalize(s1);
+ assert(cmp(s2, "\u0049 i\u0307") == 0);
+ assert(s2 !is s1);
+
+ s1 = to!S("\u017F \u0049");
+ s2 = capitalize(s1);
+ assert(cmp(s2, "\u0053 \u0069") == 0);
+ assert(s2 !is s1);
+ }
+ });
+}
+
+/++
+ Split $(D s) into an array of lines according to the unicode standard using
+ $(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.
+
+ Does not throw on invalid UTF; such is simply passed unchanged
+ to the output.
+
+ Allocates memory; use $(LREF lineSplitter) for an alternative that
+ does not.
+
+ 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), 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)
+ $(REF splitter, std,algorithm)
+ $(REF splitter, std,regex)
+ +/
+alias KeepTerminator = Flag!"keepTerminator";
+
+/// ditto
+S[] splitLines(S)(S s, in KeepTerminator keepTerm = No.keepTerminator) @safe pure
+if (isSomeString!S)
+{
+ import std.array : appender;
+ import std.uni : lineSep, paraSep;
+
+ size_t iStart = 0;
+ auto retval = appender!(S[])();
+
+ for (size_t i; i < s.length; ++i)
+ {
+ switch (s[i])
+ {
+ case '\v', '\f', '\n':
+ 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 == Yes.keepTerminator) * 2]);
+ iStart = i + 2;
+ ++i;
+ }
+ else
+ {
+ goto case '\n';
+ }
+ break;
+
+ static if (s[i].sizeof == 1)
+ {
+ /* Manually decode:
+ * lineSep is E2 80 A8
+ * paraSep is E2 80 A9
+ */
+ case 0xE2:
+ if (i + 2 < s.length &&
+ s[i + 1] == 0x80 &&
+ (s[i + 2] == 0xA8 || s[i + 2] == 0xA9)
+ )
+ {
+ retval.put(s[iStart .. i + (keepTerm == Yes.keepTerminator) * 3]);
+ iStart = i + 3;
+ i += 2;
+ }
+ else
+ goto default;
+ break;
+ /* Manually decode:
+ * NEL is C2 85
+ */
+ case 0xC2:
+ if (i + 1 < s.length && s[i + 1] == 0x85)
+ {
+ retval.put(s[iStart .. i + (keepTerm == Yes.keepTerminator) * 2]);
+ iStart = i + 2;
+ i += 1;
+ }
+ else
+ goto default;
+ break;
+ }
+ else
+ {
+ case lineSep:
+ case paraSep:
+ case '\u0085':
+ goto case '\n';
+ }
+
+ default:
+ break;
+ }
+ }
+
+ if (iStart != s.length)
+ retval.put(s[iStart .. $]);
+
+ 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;
+
+ assertCTFEable!(
+ {
+ foreach (S; AliasSeq!(char[], wchar[], dchar[], string, wstring, dstring))
+ {
+ auto s = to!S(
+ "\rpeter\n\rpaul\r\njerry\u2028ice\u2029cream\n\nsunday\n" ~
+ "mon\u2030day\nschadenfreude\vkindergarten\f\vcookies\u0085"
+ );
+ auto lines = splitLines(s);
+ assert(lines.length == 14);
+ assert(lines[0] == "");
+ assert(lines[1] == "peter");
+ assert(lines[2] == "");
+ assert(lines[3] == "paul");
+ assert(lines[4] == "jerry");
+ assert(lines[5] == "ice");
+ assert(lines[6] == "cream");
+ assert(lines[7] == "");
+ assert(lines[8] == "sunday");
+ assert(lines[9] == "mon\u2030day");
+ assert(lines[10] == "schadenfreude");
+ assert(lines[11] == "kindergarten");
+ assert(lines[12] == "");
+ assert(lines[13] == "cookies");
+
+
+ ubyte[] u = ['a', 0xFF, 0x12, 'b']; // invalid UTF
+ auto ulines = splitLines(cast(char[]) u);
+ assert(cast(ubyte[])(ulines[0]) == u);
+
+ lines = splitLines(s, Yes.keepTerminator);
+ assert(lines.length == 14);
+ assert(lines[0] == "\r");
+ assert(lines[1] == "peter\n");
+ assert(lines[2] == "\r");
+ assert(lines[3] == "paul\r\n");
+ assert(lines[4] == "jerry\u2028");
+ assert(lines[5] == "ice\u2029");
+ assert(lines[6] == "cream\n");
+ assert(lines[7] == "\n");
+ assert(lines[8] == "sunday\n");
+ assert(lines[9] == "mon\u2030day\n");
+ assert(lines[10] == "schadenfreude\v");
+ assert(lines[11] == "kindergarten\f");
+ assert(lines[12] == "\v");
+ assert(lines[13] == "cookies\u0085");
+
+ s.popBack(); // Lop-off trailing \n
+ lines = splitLines(s);
+ assert(lines.length == 14);
+ assert(lines[9] == "mon\u2030day");
+
+ lines = splitLines(s, Yes.keepTerminator);
+ assert(lines.length == 14);
+ assert(lines[13] == "cookies");
+ }
+ });
+}
+
+private struct LineSplitter(KeepTerminator keepTerm = No.keepTerminator, Range)
+{
+ import std.conv : unsigned;
+ import std.uni : lineSep, paraSep;
+private:
+ Range _input;
+
+ 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;
+ }
+
+ static if (isInfinite!Range)
+ {
+ enum bool empty = false;
+ }
+ else
+ {
+ @property bool empty()
+ {
+ return iStart == _unComputed && iNext == _input.length;
+ }
+ }
+
+ @property typeof(_input) front()
+ {
+ if (iStart == _unComputed)
+ {
+ iStart = iNext;
+ Loop:
+ for (IndexType i = iNext; ; ++i)
+ {
+ if (i == _input.length)
+ {
+ iEnd = i;
+ iNext = i;
+ break Loop;
+ }
+ switch (_input[i])
+ {
+ 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 + (keepTerm == Yes.keepTerminator) * 2;
+ iNext = i + 2;
+ break Loop;
+ }
+ else
+ {
+ goto case '\n';
+ }
+
+ 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)
+ )
+ {
+ 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)
+ {
+ iEnd = i + (keepTerm == Yes.keepTerminator) * 2;
+ iNext = i + 2;
+ break Loop;
+ }
+ else
+ goto default;
+ }
+ else
+ {
+ case '\u0085':
+ case lineSep:
+ case paraSep:
+ goto case '\n';
+ }
+
+ default:
+ break;
+ }
+ }
+ }
+ return _input[iStart .. iEnd];
+ }
+
+ void popFront()
+ {
+ if (iStart == _unComputed)
+ {
+ assert(!empty);
+ front;
+ }
+ iStart = _unComputed;
+ }
+
+ static if (isForwardRange!Range)
+ {
+ @property typeof(this) save()
+ {
+ auto ret = this;
+ ret._input = _input.save;
+ return ret;
+ }
+ }
+}
+
+/***********************************
+ * 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.array : array;
+
+ string s = "Hello\nmy\rname\nis";
+
+ /* notice the call to 'array' to turn the lazy range created by
+ lineSplitter comparable to the 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;
+
+ 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");
+ assert(lines[2] == "");
+ assert(lines[3] == "paul");
+ assert(lines[4] == "jerry");
+ assert(lines[5] == "ice");
+ assert(lines[6] == "cream");
+ assert(lines[7] == "");
+ assert(lines[8] == "sunday");
+ assert(lines[9] == "mon\u2030day");
+ assert(lines[10] == "schadenfreude");
+ assert(lines[11] == "kindergarten");
+ assert(lines[12] == "");
+ assert(lines[13] == "cookies");
+
+
+ ubyte[] u = ['a', 0xFF, 0x12, 'b']; // invalid UTF
+ auto ulines = lineSplitter(cast(char[]) u).array;
+ assert(cast(ubyte[])(ulines[0]) == u);
+
+ lines = lineSplitter!(Yes.keepTerminator)(s).array;
+ assert(lines.length == 14);
+ assert(lines[0] == "\r");
+ assert(lines[1] == "peter\n");
+ assert(lines[2] == "\r");
+ assert(lines[3] == "paul\r\n");
+ assert(lines[4] == "jerry\u2028");
+ assert(lines[5] == "ice\u2029");
+ assert(lines[6] == "cream\n");
+ assert(lines[7] == "\n");
+ assert(lines[8] == "sunday\n");
+ assert(lines[9] == "mon\u2030day\n");
+ assert(lines[10] == "schadenfreude\v");
+ assert(lines[11] == "kindergarten\f");
+ assert(lines[12] == "\v");
+ assert(lines[13] == "cookies\u0085");
+
+ s.popBack(); // Lop-off trailing \n
+ lines = lineSplitter(s).array;
+ assert(lines.length == 14);
+ assert(lines[9] == "mon\u2030day");
+
+ lines = lineSplitter!(Yes.keepTerminator)(s).array;
+ assert(lines.length == 14);
+ assert(lines[13] == "cookies");
+ }
+ });
+}
+
+///
+@nogc @safe pure unittest
+{
+ auto s = "\rpeter\n\rpaul\r\njerry\u2028ice\u2029cream\n\nsunday\nmon\u2030day\n";
+ auto lines = s.lineSplitter();
+ static immutable witness = ["", "peter", "", "paul", "jerry", "ice", "cream", "", "sunday", "mon\u2030day"];
+ uint i;
+ foreach (line; lines)
+ {
+ assert(line == witness[i++]);
+ }
+ 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 $(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.
+
+ Postconditions: $(D input) and the returned value
+ will share the same tail (see $(REF sameTail, std,array)).
+
+ See_Also:
+ Generic stripping on ranges: $(REF _stripLeft, std, algorithm, mutation)
+ +/
+auto stripLeft(Range)(Range input)
+if (isForwardRange!Range && isSomeChar!(ElementEncodingType!Range) &&
+ !isInfinite!Range && !isConvertibleToString!Range)
+{
+ static import std.ascii;
+ static import std.uni;
+ import std.utf : decodeFront;
+
+ while (!input.empty)
+ {
+ 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 input;
+}
+
+///
+@safe pure unittest
+{
+ import std.uni : lineSep, paraSep;
+ assert(stripLeft(" hello world ") ==
+ "hello world ");
+ assert(stripLeft("\n\t\v\rhello world\n\t\v\r") ==
+ "hello world\n\t\v\r");
+ assert(stripLeft("hello world") ==
+ "hello world");
+ assert(stripLeft([lineSep] ~ "hello world" ~ lineSep) ==
+ "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 $(REF isWhite, std,uni)).
+
+ Params:
+ str = string or random access range of characters
+
+ Returns:
+ slice of $(D str) stripped of trailing whitespace.
+
+ See_Also:
+ Generic stripping on ranges: $(REF _stripRight, std, algorithm, mutation)
+ +/
+auto stripRight(Range)(Range str)
+if (isSomeString!Range ||
+ isRandomAccessRange!Range && hasLength!Range && hasSlicing!Range &&
+ !isConvertibleToString!Range &&
+ isSomeChar!(ElementEncodingType!Range))
+{
+ import std.uni : isWhite;
+ alias C = Unqual!(ElementEncodingType!(typeof(str)));
+
+ static if (isSomeString!(typeof(str)))
+ {
+ 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 .. i + 1];
+ }
+}
+
+///
+@safe pure
+unittest
+{
+ import std.uni : lineSep, paraSep;
+ assert(stripRight(" hello world ") ==
+ " hello world");
+ assert(stripRight("\n\t\v\rhello world\n\t\v\r") ==
+ "\n\t\v\rhello world");
+ assert(stripRight("hello world") ==
+ "hello world");
+ assert(stripRight([lineSep] ~ "hello world" ~ lineSep) ==
+ [lineSep] ~ "hello world");
+ assert(stripRight([paraSep] ~ "hello world" ~ paraSep) ==
+ [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
+ $(REF isWhite, std,uni)).
+
+ Params:
+ str = string or random access range of characters
+
+ Returns:
+ slice of $(D str) stripped of leading and trailing whitespace.
+
+ See_Also:
+ Generic stripping on ranges: $(REF _strip, std, algorithm, mutation)
+ +/
+auto strip(Range)(Range str)
+if (isSomeString!Range ||
+ isRandomAccessRange!Range && hasLength!Range && hasSlicing!Range &&
+ !isConvertibleToString!Range &&
+ isSomeChar!(ElementEncodingType!Range))
+{
+ return stripRight(stripLeft(str));
+}
+
+///
+@safe pure unittest
+{
+ import std.uni : lineSep, paraSep;
+ assert(strip(" hello world ") ==
+ "hello world");
+ assert(strip("\n\t\v\rhello world\n\t\v\r") ==
+ "hello world");
+ assert(strip("hello world") ==
+ "hello world");
+ assert(strip([lineSep] ~ "hello world" ~ [lineSep]) ==
+ "hello world");
+ assert(strip([paraSep] ~ "hello world" ~ [paraSep]) ==
+ "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.exception : assertCTFEable;
+
+ assertCTFEable!(
+ {
+ 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"));
+ assert(equal(stripLeft(to!S("\u0085 μ \u0085 \u00BB \r")), "μ \u0085 \u00BB \r"));
+ assert(equal(stripLeft(to!S("1")), "1"));
+ assert(equal(stripLeft(to!S("\U0010FFFE")), "\U0010FFFE"));
+ assert(equal(stripLeft(to!S("")), ""));
+
+ assert(equal(stripRight(to!S(" foo\t ")), " foo"));
+ assert(equal(stripRight(to!S("\u2008 foo\t \u2007")), "\u2008 foo"));
+ assert(equal(stripRight(to!S("\u0085 μ \u0085 \u00BB \r")), "\u0085 μ \u0085 \u00BB"));
+ assert(equal(stripRight(to!S("1")), "1"));
+ assert(equal(stripRight(to!S("\U0010FFFE")), "\U0010FFFE"));
+ assert(equal(stripRight(to!S("")), ""));
+
+ assert(equal(strip(to!S(" foo\t ")), "foo"));
+ assert(equal(strip(to!S("\u2008 foo\t \u2007")), "foo"));
+ assert(equal(strip(to!S("\u0085 μ \u0085 \u00BB \r")), "μ \u0085 \u00BB"));
+ assert(equal(strip(to!S("\U0010FFFE")), "\U0010FFFE"));
+ assert(equal(strip(to!S("")), ""));
+ }
+ });
+}
+
+@safe pure unittest
+{
+ import std.array : sameHead, sameTail;
+ import std.exception : assertCTFEable;
+ assertCTFEable!(
+ {
+ wstring s = " ";
+ assert(s.sameTail(s.stripLeft()));
+ assert(s.sameHead(s.stripRight()));
+ });
+}
+
+
+/++
+ If $(D str) ends with $(D delimiter), then $(D str) is returned without
+ $(D delimiter) on its end. If it $(D str) does $(I not) end with
+ $(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'), $(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.
+
+ Params:
+ str = string or indexable range of characters
+ delimiter = string of characters to be sliced off end of str[]
+
+ Returns:
+ slice of str
+ +/
+Range chomp(Range)(Range str)
+if ((isRandomAccessRange!Range && isSomeChar!(ElementEncodingType!Range) ||
+ isNarrowString!Range) &&
+ !isConvertibleToString!Range)
+{
+ import std.uni : lineSep, paraSep, nelSep;
+ if (str.empty)
+ return str;
+
+ alias C = ElementEncodingType!Range;
+
+ switch (str[$ - 1])
+ {
+ case '\n':
+ {
+ if (str.length > 1 && str[$ - 2] == '\r')
+ return str[0 .. $ - 2];
+ goto case;
+ }
+ case '\r', '\v', '\f':
+ return str[0 .. $ - 1];
+
+ // Pop off the last character if lineSep, paraSep, or nelSep
+ static if (is(C : const char))
+ {
+ /* Manually decode:
+ * lineSep is E2 80 A8
+ * paraSep is E2 80 A9
+ */
+ case 0xA8: // Last byte of lineSep
+ case 0xA9: // Last byte of paraSep
+ if (str.length > 2 && str[$ - 2] == 0x80 && str[$ - 3] == 0xE2)
+ return str [0 .. $ - 3];
+ goto default;
+
+ /* Manually decode:
+ * NEL is C2 85
+ */
+ case 0x85:
+ if (str.length > 1 && str[$ - 2] == 0xC2)
+ return str [0 .. $ - 2];
+ goto default;
+ }
+ else
+ {
+ case lineSep:
+ case paraSep:
+ case nelSep:
+ return str[0 .. $ - 1];
+ }
+ default:
+ return str;
+ }
+}
+
+/// Ditto
+Range chomp(Range, C2)(Range str, const(C2)[] delimiter)
+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) && (isSomeString!Range || (hasSlicing!Range && C2.sizeof == 4)))
+ {
+ import std.algorithm.searching : endsWith;
+ if (str.endsWith(delimiter))
+ return str[0 .. $ - delimiter.length];
+ return str;
+ }
+ else
+ {
+ auto orig = str.save;
+
+ static if (isSomeString!Range)
+ alias C = dchar; // because strings auto-decode
+ else
+ alias C = C1; // and ranges do not
+
+ foreach_reverse (C c; delimiter)
+ {
+ if (str.empty || str.back != c)
+ return orig;
+
+ str.popBack();
+ }
+
+ return str;
+ }
+}
+
+///
+@safe pure
+unittest
+{
+ 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 ");
+ assert(chomp(" hello world \v") == " hello world ");
+ assert(chomp(" hello world \n\n") == " hello world \n");
+ assert(chomp(" hello world \n\n ") == " hello world \n\n ");
+ assert(chomp(" hello world \n\n" ~ [lineSep]) == " hello world \n\n");
+ assert(chomp(" hello world \n\n" ~ [paraSep]) == " hello world \n\n");
+ assert(chomp(" hello world \n\n" ~ [ nelSep]) == " hello world \n\n");
+ assert(chomp(" hello world") == " hello world");
+ assert(chomp("") == "");
+
+ assert(chomp(" hello world", "orld") == " hello w");
+ assert(chomp(" hello world", " he") == " hello world");
+ assert(chomp("", "hello") == "");
+
+ // Don't decode pointlessly
+ assert(chomp("hello\xFE", "\r") == "hello\xFE");
+}
+
+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;
+
+ string s;
+
+ assertCTFEable!(
+ {
+ foreach (S; AliasSeq!(char[], wchar[], dchar[], string, wstring, dstring))
+ {
+ // @@@ BUG IN COMPILER, MUST INSERT CAST
+ 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");
+ assert(chomp(to!S("hello\r\n")) == "hello");
+ assert(chomp(to!S("hello\n\r")) == "hello\n");
+ assert(chomp(to!S("hello\n\n")) == "hello\n");
+ assert(chomp(to!S("hello\r\r")) == "hello\r");
+ assert(chomp(to!S("hello\nxxx\n")) == "hello\nxxx");
+ assert(chomp(to!S("hello\u2028")) == "hello");
+ assert(chomp(to!S("hello\u2029")) == "hello");
+ assert(chomp(to!S("hello\u0085")) == "hello");
+ assert(chomp(to!S("hello\u2028\u2028")) == "hello\u2028");
+ assert(chomp(to!S("hello\u2029\u2029")) == "hello\u2029");
+ assert(chomp(to!S("hello\u2029\u2129")) == "hello\u2029\u2129");
+ assert(chomp(to!S("hello\u2029\u0185")) == "hello\u2029\u0185");
+
+ 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(to!S("hello"), to!T("o")) == "hell");
+ assert(chomp(to!S("hello"), to!T("p")) == "hello");
+ // @@@ BUG IN COMPILER, MUST INSERT CAST
+ assert(chomp(to!S("hello"), cast(T) null) == "hello");
+ assert(chomp(to!S("hello"), to!T("llo")) == "he");
+ assert(chomp(to!S("\uFF28ello"), to!T("llo")) == "\uFF28e");
+ assert(chomp(to!S("\uFF28el\uFF4co"), to!T("l\uFF4co")) == "\uFF28e");
+ }();
+ }
+ });
+
+ // Ranges
+ import std.array : array;
+ import std.utf : byChar, byWchar, byDchar;
+ 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 $(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
+ +/
+Range chompPrefix(Range, C2)(Range str, const(C2)[] delimiter)
+if ((isForwardRange!Range && isSomeChar!(ElementEncodingType!Range) ||
+ isNarrowString!Range) &&
+ !isConvertibleToString!Range &&
+ isSomeChar!C2)
+{
+ alias C1 = ElementEncodingType!Range;
+
+ static if (is(Unqual!C1 == Unqual!C2) && (isSomeString!Range || (hasSlicing!Range && C2.sizeof == 4)))
+ {
+ import std.algorithm.searching : startsWith;
+ if (str.startsWith(delimiter))
+ return str[delimiter.length .. $];
+ return str;
+ }
+ else
+ {
+ auto orig = str.save;
+
+ static if (isSomeString!Range)
+ alias C = dchar; // because strings auto-decode
+ else
+ alias C = C1; // and ranges do not
+
+ foreach (C c; delimiter)
+ {
+ if (str.empty || str.front != c)
+ return orig;
+
+ str.popFront();
+ }
+
+ return str;
+ }
+}
+
+///
+@safe pure unittest
+{
+ assert(chompPrefix("hello world", "he") == "llo world");
+ assert(chompPrefix("hello world", "hello w") == "orld");
+ assert(chompPrefix("hello world", " world") == "hello world");
+ assert(chompPrefix("", "hello") == "");
+}
+
+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.exception : assertCTFEable;
+ assertCTFEable!(
+ {
+ foreach (S; AliasSeq!(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"));
+ assert(equal(chompPrefix(to!S("\uFF28el\uFF4co"), to!T("\uFF28el\uFF4co")), ""));
+ assert(equal(chompPrefix(to!S("\uFF28el\uFF4co"), to!T("\uFF28el")), "\uFF4co"));
+ assert(equal(chompPrefix(to!S("\uFF28el"), to!T("\uFF28el\uFF4co")), "\uFF28el"));
+ }();
+ }
+ });
+
+ // 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
+ +/
+
+Range chop(Range)(Range str)
+if ((isBidirectionalRange!Range && isSomeChar!(ElementEncodingType!Range) ||
+ isNarrowString!Range) &&
+ !isConvertibleToString!Range)
+{
+ if (str.empty)
+ 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;
+ }
+}
+
+///
+@safe pure unittest
+{
+ assert(chop("hello world") == "hello worl");
+ assert(chop("hello world\n") == "hello world");
+ assert(chop("hello world\r") == "hello world");
+ assert(chop("hello world\n\r") == "hello world\n");
+ assert(chop("hello world\r\n") == "hello world");
+ assert(chop("Walter Bright") == "Walter Brigh");
+ assert(chop("") == "");
+}
+
+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.exception : assertCTFEable;
+
+ assertCTFEable!(
+ {
+ foreach (S; AliasSeq!(char[], wchar[], dchar[], string, wstring, dstring))
+ {
+ assert(chop(cast(S) null) is null);
+ assert(equal(chop(to!S("hello")), "hell"));
+ assert(equal(chop(to!S("hello\r\n")), "hello"));
+ assert(equal(chop(to!S("hello\n\r")), "hello\n"));
+ assert(equal(chop(to!S("Verité")), "Verit"));
+ assert(equal(chop(to!S(`さいごの果実`)), "さいごの果"));
+ assert(equal(chop(to!S(`ミツバチと科学者`)), "ミツバチと科学"));
+ }
+ });
+}
+
+
+/++
+ 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.
+
+ Params:
+ s = string
+ width = minimum field width
+ fillChar = used to pad end up to $(D width) characters
+
+ Returns:
+ GC allocated string
+
+ 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");
+}
+
+/++
+ 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.
+
+ Params:
+ r = string or range of characters
+ width = minimum field width
+ fillChar = used to pad end up to $(D width) characters
+
+ 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);
+
+ static if (C.sizeof == 1)
+ {
+ import std.utf : byDchar, byChar;
+ return leftJustifier(r.byDchar, width, fillChar).byChar;
+ }
+ else static if (C.sizeof == 2)
+ {
+ import std.utf : byDchar, byWchar;
+ return leftJustifier(r.byDchar, width, fillChar).byWchar;
+ }
+ else static if (C.sizeof == 4)
+ {
+ 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;
+ }
+ }
+ }
+
+ 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));
+}
+
+/++
+ 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 rightJustify(S)(S s, size_t width, dchar fillChar = ' ')
+if (isSomeString!S)
+{
+ 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");
+}
+
+/++
+ 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:
+ 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);
+
+ static if (C.sizeof == 1)
+ {
+ import std.utf : byDchar, byChar;
+ return rightJustifier(r.byDchar, width, fillChar).byChar;
+ }
+ else static if (C.sizeof == 2)
+ {
+ 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;
+ }
+
+ @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));
+}
+
+@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;
+
+ assertCTFEable!(
+ {
+ foreach (S; AliasSeq!(char[], wchar[], dchar[], string, wstring, dstring))
+ {
+ S s = to!S("hello");
+
+ assert(leftJustify(s, 2) == "hello");
+ assert(rightJustify(s, 2) == "hello");
+ assert(center(s, 2) == "hello");
+
+ assert(leftJustify(s, 7) == "hello ");
+ assert(rightJustify(s, 7) == " hello");
+ assert(center(s, 7) == " hello ");
+
+ assert(leftJustify(s, 8) == "hello ");
+ assert(rightJustify(s, 8) == " hello");
+ assert(center(s, 8) == " hello ");
+
+ assert(leftJustify(s, 8, '\u0100') == "hello\u0100\u0100\u0100");
+ assert(rightJustify(s, 8, '\u0100') == "\u0100\u0100\u0100hello");
+ assert(center(s, 8, '\u0100') == "\u0100hello\u0100\u0100");
+
+ assert(leftJustify(s, 8, 'ö') == "helloööö");
+ assert(rightJustify(s, 8, 'ö') == "öööhello");
+ assert(center(s, 8, 'ö') == "öhelloöö");
+ }
+ });
+}
+
+/++
+ 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.
+
+ Params:
+ s = string
+ tabSize = distance between tab stops
+
+ Returns:
+ GC allocated string with tabs replaced with spaces
+ +/
+auto detab(Range)(auto ref Range s, size_t tabSize = 8) pure
+if ((isForwardRange!Range && isSomeChar!(ElementEncodingType!Range))
+ || __traits(compiles, StringTypeOf!Range))
+{
+ 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!(ElementEncodingType!(Range));
+
+ static struct Result
+ {
+ 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))
+ {
+ enum bool empty = false;
+ }
+ else
+ {
+ @property bool empty()
+ {
+ 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)[]))
+ {
+ dc = c;
+ 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;
+ break;
+
+ case '\t':
+ nspaces = _tabSize - (column % _tabSize);
+ column += nspaces;
+ c = ' ';
+ break;
+
+ default:
+ ++column;
+ break;
+ }
+ return c;
+ }
+
+ void popFront()
+ {
+ if (!index)
+ front;
+ if (nspaces)
+ --nspaces;
+ if (!nspaces)
+ {
+ static if (isSomeString!(Range))
+ _input = _input[1 .. $];
+ else
+ _input.popFront();
+ --index;
+ }
+ }
+
+ @property typeof(this) save()
+ {
+ auto ret = this;
+ ret._input = _input.save;
+ return ret;
+ }
+ }
+
+ 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));
+}
+
+@system pure unittest
+{
+ import std.algorithm.comparison : cmp;
+ import std.conv : to;
+ import std.exception : assertCTFEable;
+
+ assertCTFEable!(
+ {
+ 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("").empty);
+ assert(detab("a") == "a");
+ assert(detab("\t") == " ");
+ assert(detab("\t", 3) == " ");
+ 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.
+
+ 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)
+ +/
+auto entab(Range)(Range s, size_t tabSize = 8)
+if (isForwardRange!Range && isSomeChar!(ElementEncodingType!Range))
+{
+ import std.array : array;
+ return entabber(s, tabSize).array;
+}
+
+///
+@safe pure unittest
+{
+ assert(entab(" x \n") == "\tx\n");
+}
+
+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);
+}
+
+@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;
+
+ @property C getFront()
+ {
+ static if (isSomeString!Range)
+ return _input[0]; // avoid autodecode
+ else
+ return _input.front;
+ }
+
+ public:
+
+ this(Range input, size_t tabSize)
+ {
+ _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)
+ {
+ foreach (c; _input)
+ {
+ if (c != ' ' && c != '\t')
+ return false;
+ }
+ return true;
+ }
+ else
+ {
+ 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;
+ }
+ }
+
+ @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)[]))
+ {
+ 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
+ {
+ auto r = _input.save;
+ dc = decodeFront(r, index); // lookahead to decode
+ }
+ //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;
+ }
+
+ void popFront()
+ {
+ //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;
+ }
+ }
+
+ @property typeof(this) save()
+ {
+ auto ret = this;
+ ret._input = _input.save;
+ return ret;
+ }
+ }
+
+ 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;
+
+ assertCTFEable!(
+ {
+ assert(entab(cast(string) null) is null);
+ assert(entab("").empty);
+ assert(entab("a") == "a");
+ assert(entab(" ") == "");
+ assert(entab(" x") == "\tx");
+ assert(entab(" ab asdf ") == " ab\tasdf");
+ assert(entab(" ab asdf ") == " ab\t asdf");
+ assert(entab(" ab \t asdf ") == " ab\t asdf");
+ assert(entab("1234567 \ta") == "1234567\t\ta");
+ assert(entab("1234567 \ta") == "1234567\t\ta");
+ assert(entab("1234567 \ta") == "1234567\t\ta");
+ assert(entab("1234567 \ta") == "1234567\t\ta");
+ assert(entab("1234567 \ta") == "1234567\t\ta");
+ assert(entab("1234567 \ta") == "1234567\t\ta");
+ assert(entab("1234567 \ta") == "1234567\t\ta");
+ assert(entab("1234567 \ta") == "1234567\t\ta");
+ assert(entab("1234567 \ta") == "1234567\t\t\ta");
+
+ assert(entab("a ") == "a");
+ assert(entab("a\v") == "a\v");
+ assert(entab("a\f") == "a\f");
+ assert(entab("a\n") == "a\n");
+ assert(entab("a\n\r") == "a\n\r");
+ 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
+ their corresponding values in $(D transTable). $(D transTable) is an AA
+ where its keys are $(D dchar) and its values are either $(D dchar) or some
+ type of string. Also, if $(D toRemove) is given, the characters in it are
+ removed from $(D str) prior to translation. $(D str) itself is unaltered.
+ A copy with the changes is returned.
+
+ See_Also:
+ $(LREF tr)
+ $(REF replace, std,array)
+
+ Params:
+ str = The original string.
+ transTable = The AA indicating which characters to replace and what to
+ replace them with.
+ toRemove = The characters to remove from the string.
+ +/
+C1[] translate(C1, C2 = immutable char)(C1[] str,
+ in dchar[dchar] transTable,
+ const(C2)[] toRemove = null) @safe pure
+if (isSomeChar!C1 && isSomeChar!C2)
+{
+ import std.array : appender;
+ auto buffer = appender!(C1[])();
+ translateImpl(str, transTable, toRemove, buffer);
+ return buffer.data;
+}
+
+///
+@safe pure unittest
+{
+ dchar[dchar] transTable1 = ['e' : '5', 'o' : '7', '5': 'q'];
+ assert(translate("hello world", transTable1) == "h5ll7 w7rld");
+
+ assert(translate("hello world", transTable1, "low") == "h5 rd");
+
+ string[dchar] transTable2 = ['e' : "5", 'o' : "orange"];
+ assert(translate("hello world", transTable2) == "h5llorange worangerld");
+}
+
+@safe pure unittest // issue 13018
+{
+ immutable dchar[dchar] transTable1 = ['e' : '5', 'o' : '7', '5': 'q'];
+ assert(translate("hello world", transTable1) == "h5ll7 w7rld");
+
+ assert(translate("hello world", transTable1, "low") == "h5 rd");
+
+ immutable string[dchar] transTable2 = ['e' : "5", 'o' : "orange"];
+ assert(translate("hello world", transTable2) == "h5llorange worangerld");
+}
+
+@system pure unittest
+{
+ import std.conv : to;
+ import std.exception : assertCTFEable;
+
+ assertCTFEable!(
+ {
+ 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"));
+ assert(translate(to!S("hello world"), cast(dchar[dchar])['o' : 'l', 'l' : '\U00010143']) ==
+ to!S("he\U00010143\U00010143l wlr\U00010143d"));
+ assert(translate(to!S("hello \U00010143 world"), cast(dchar[dchar])['h' : 'q', 'l': '5']) ==
+ 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"));
+
+ 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; AliasSeq!(dchar[dchar], const dchar[dchar],
+ immutable dchar[dchar]))
+ {
+ R tt = ['h' : 'q', 'l' : '5'];
+ assert(translate(to!S("hello world"), tt, to!T("r"))
+ == to!S("qe55o wo5d"));
+ assert(translate(to!S("hello world"), tt, to!T("helo"))
+ == to!S(" wrd"));
+ assert(translate(to!S("hello world"), tt, to!T("q5"))
+ == to!S("qe55o wor5d"));
+ }
+ }();
+
+ auto s = to!S("hello world");
+ dchar[dchar] transTable = ['h' : 'q', 'l' : '5'];
+ static assert(is(typeof(s) == typeof(translate(s, transTable))));
+ }
+ });
+}
+
+/++ Ditto +/
+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)
+{
+ import std.array : appender;
+ auto buffer = appender!(C1[])();
+ translateImpl(str, transTable, toRemove, buffer);
+ return buffer.data;
+}
+
+@system pure unittest
+{
+ import std.conv : to;
+ import std.exception : assertCTFEable;
+
+ assertCTFEable!(
+ {
+ 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"));
+ assert(translate(to!S("hello world"), ['o' : "owl", 'l' : "\U00010143\U00010143"]) ==
+ to!S("he\U00010143\U00010143\U00010143\U00010143owl wowlr\U00010143\U00010143d"));
+ assert(translate(to!S("hello \U00010143 world"), ['h' : "yellow", 'l' : "42"]) ==
+ to!S("yellowe4242o \U00010143 wor42d"));
+ assert(translate(to!S("hello \U00010143 world"), ['o' : "owl", 'l' : "\U00010143\U00010143"]) ==
+ to!S("he\U00010143\U00010143\U00010143\U00010143owl \U00010143 wowlr\U00010143\U00010143d"));
+ assert(translate(to!S("hello \U00010143 world"), ['h' : ""]) ==
+ 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"));
+
+ 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; AliasSeq!(string[dchar], const string[dchar],
+ immutable string[dchar]))
+ {
+ R tt = ['h' : "yellow", 'l' : "42"];
+ assert(translate(to!S("hello world"), tt, to!T("r")) ==
+ to!S("yellowe4242o wo42d"));
+ assert(translate(to!S("hello world"), tt, to!T("helo")) ==
+ to!S(" wrd"));
+ assert(translate(to!S("hello world"), tt, to!T("y42")) ==
+ to!S("yellowe4242o wor42d"));
+ assert(translate(to!S("hello world"), tt, to!T("hello world")) ==
+ to!S(""));
+ assert(translate(to!S("hello world"), tt, to!T("42")) ==
+ to!S("yellowe4242o wor42d"));
+ }
+ }();
+
+ auto s = to!S("hello world");
+ string[dchar] transTable = ['h' : "silly", 'l' : "putty"];
+ static assert(is(typeof(s) == typeof(translate(s, transTable))));
+ }
+ });
+}
+
+/++
+ This is an overload of $(D translate) which takes an existing buffer to write the contents to.
+
+ Params:
+ str = The original string.
+ transTable = The AA indicating which characters to replace and what to
+ replace them with.
+ toRemove = The characters to remove from the string.
+ buffer = An output range to write the contents to.
+ +/
+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))
+{
+ translateImpl(str, transTable, toRemove, buffer);
+}
+
+///
+@safe pure unittest
+{
+ import std.array : appender;
+ dchar[dchar] transTable1 = ['e' : '5', 'o' : '7', '5': 'q'];
+ auto buffer = appender!(dchar[])();
+ translate("hello world", transTable1, null, buffer);
+ assert(buffer.data == "h5ll7 w7rld");
+
+ buffer.clear();
+ translate("hello world", transTable1, "low", buffer);
+ assert(buffer.data == "h5 rd");
+
+ buffer.clear();
+ string[dchar] transTable2 = ['e' : "5", 'o' : "orange"];
+ translate("hello world", transTable2, null, buffer);
+ assert(buffer.data == "h5llorange worangerld");
+}
+
+@safe pure unittest // issue 13018
+{
+ import std.array : appender;
+ immutable dchar[dchar] transTable1 = ['e' : '5', 'o' : '7', '5': 'q'];
+ auto buffer = appender!(dchar[])();
+ translate("hello world", transTable1, null, buffer);
+ assert(buffer.data == "h5ll7 w7rld");
+
+ buffer.clear();
+ translate("hello world", transTable1, "low", buffer);
+ assert(buffer.data == "h5 rd");
+
+ buffer.clear();
+ immutable string[dchar] transTable2 = ['e' : "5", 'o' : "orange"];
+ translate("hello world", transTable2, null, buffer);
+ assert(buffer.data == "h5llorange worangerld");
+}
+
+/++ Ditto +/
+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))
+{
+ translateImpl(str, transTable, toRemove, buffer);
+}
+
+private void translateImpl(C1, T, C2, Buffer)(C1[] str,
+ T transTable,
+ const(C2)[] toRemove,
+ Buffer buffer)
+{
+ bool[dchar] removeTable;
+
+ foreach (dchar c; toRemove)
+ removeTable[c] = true;
+
+ foreach (dchar c; str)
+ {
+ if (c in removeTable)
+ continue;
+
+ auto newC = c in transTable;
+
+ if (newC)
+ put(buffer, *newC);
+ else
+ put(buffer, c);
+ }
+}
+
+/++
+ This is an $(I $(RED ASCII-only)) overload of $(LREF _translate). It
+ will $(I not) work with Unicode. It exists as an optimization for the
+ 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 makeTransTable).
+
+ 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
+ ASCII characters if you want to (ASCII only actually uses $(D 128)
+ characters), but be warned that Extended ASCII characters are not valid
+ Unicode and therefore will result in a $(D UTFException) being thrown from
+ most other Phobos functions.
+
+ Also, because no decoding occurs, it is possible to use this overload to
+ translate ASCII characters within a proper UTF-8 string without altering the
+ other, non-ASCII characters. It's replacing any code unit greater than
+ $(D 127) with another code unit or replacing any code unit with another code
+ unit greater than $(D 127) which will cause UTF validation issues.
+
+ See_Also:
+ $(LREF tr)
+ $(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 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))
+in
+{
+ assert(transTable.length == 256);
+}
+body
+{
+ bool[256] remTable = false;
+
+ foreach (char c; toRemove)
+ remTable[c] = true;
+
+ size_t count = 0;
+ foreach (char c; str)
+ {
+ if (!remTable[c])
+ ++count;
+ }
+
+ auto buffer = new char[count];
+
+ size_t i = 0;
+ foreach (char c; str)
+ {
+ if (!remTable[c])
+ buffer[i++] = transTable[c];
+ }
+
+ return cast(C[])(buffer);
+}
+
+
+/**
+ * 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(isASCII(c));
+ foreach (char c; to)
+ assert(isASCII(c));
+}
+body
+{
+ char[256] result = void;
+
+ 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;
+
+ assertCTFEable!(
+ {
+ foreach (C; AliasSeq!(char, const char, immutable char))
+ {
+ assert(translate!C("hello world", makeTransTable("hl", "q5")) == to!(C[])("qe55o wor5d"));
+
+ auto s = to!(C[])("hello world");
+ auto transTable = makeTransTable("hl", "q5");
+ static assert(is(typeof(s) == typeof(translate!C(s, transTable))));
+ }
+
+ foreach (S; AliasSeq!(char[], const(char)[], immutable(char)[]))
+ {
+ 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"), 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; AliasSeq!(char[], const(char)[], immutable(char)[]))
+ (){ // avoid slow optimizations for large functions @@@BUG@@@ 2396
+ assert(translate(to!S("hello world"), makeTransTable("hl", "q5"), to!T("r")) ==
+ to!S("qe55o wo5d"));
+ assert(translate(to!S("hello \U00010143 world"), makeTransTable("hl", "q5"), to!T("r")) ==
+ to!S("qe55o \U00010143 wo5d"));
+ assert(translate(to!S("hello world"), makeTransTable("hl", "q5"), to!T("helo")) ==
+ to!S(" wrd"));
+ assert(translate(to!S("hello world"), makeTransTable("hl", "q5"), to!T("q5")) ==
+ to!S("qe55o wor5d"));
+ }();
+ }
+ });
+}
+
+/++
+ This is an $(I $(RED ASCII-only)) overload of $(D translate) which takes an existing buffer to write the contents to.
+
+ Params:
+ str = The original string.
+ transTable = The string indicating which characters to replace and what
+ 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))
+in
+{
+ assert(transTable.length == 256);
+}
+body
+{
+ bool[256] remTable = false;
+
+ foreach (char c; toRemove)
+ remTable[c] = true;
+
+ foreach (char c; str)
+ {
+ if (!remTable[c])
+ put(buffer, transTable[c]);
+ }
+}
+
+///
+@safe pure unittest
+{
+ import std.array : appender;
+ auto buffer = appender!(char[])();
+ auto transTable1 = makeTransTable("eo5", "57q");
+ translate("hello world", transTable1, null, buffer);
+ assert(buffer.data == "h5ll7 w7rld");
+
+ buffer.clear();
+ translate("hello world", transTable1, "low", buffer);
+ assert(buffer.data == "h5 rd");
+}
+
+//@@@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) 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 removeschars),
+ * and $(B squeeze) use patterns.
+ *
+ * Note: In the future, the pattern syntax may be improved
+ * to be more like regular expression character classes.
+ */
+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;
+ dchar lastc;
+
+ foreach (size_t i, dchar p; pattern)
+ {
+ if (p == '^' && i == 0)
+ {
+ result = true;
+ if (i + 1 == pattern.length)
+ return (c == p); // or should this be an error?
+ }
+ else if (range)
+ {
+ range = 0;
+ if (lastc <= c && c <= p || c == p)
+ return !result;
+ }
+ else if (p == '-' && i > result && i + 1 < pattern.length)
+ {
+ range = 1;
+ continue;
+ }
+ else if (c == p)
+ return !result;
+ lastc = p;
+ }
+ return result;
+}
+
+
+deprecated
+@safe pure @nogc unittest
+{
+ import std.conv : to;
+ import std.exception : assertCTFEable;
+
+ assertCTFEable!(
+ {
+ assert(inPattern('x', "x") == 1);
+ assert(inPattern('x', "y") == 0);
+ assert(inPattern('x', string.init) == 0);
+ assert(inPattern('x', "^y") == 1);
+ assert(inPattern('x', "yxxy") == 1);
+ assert(inPattern('x', "^yxxy") == 0);
+ assert(inPattern('x', "^abcd") == 1);
+ assert(inPattern('^', "^^") == 0);
+ assert(inPattern('^', "^") == 1);
+ assert(inPattern('^', "a^") == 1);
+ assert(inPattern('x', "a-z") == 1);
+ assert(inPattern('x', "A-Z") == 0);
+ assert(inPattern('x', "^a-z") == 0);
+ assert(inPattern('x', "^A-Z") == 1);
+ assert(inPattern('-', "a-") == 1);
+ assert(inPattern('-', "^A-") == 0);
+ assert(inPattern('a', "z-a") == 1);
+ assert(inPattern('z', "z-a") == 1);
+ assert(inPattern('x', "z-a") == 0);
+ });
+}
+
+//@@@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.
+ */
+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)
+ {
+ if (!inPattern(c, pattern))
+ {
+ return false;
+ }
+ }
+ 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.
+ */
+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)
+ {
+ count += inPattern(c, pattern);
+ }
+ return count;
+}
+
+deprecated
+@safe pure @nogc unittest
+{
+ import std.conv : to;
+ import std.exception : assertCTFEable;
+
+ assertCTFEable!(
+ {
+ assert(countchars("abc", "a-c") == 3);
+ assert(countchars("hello world", "or") == 3);
+ });
+}
+
+//@@@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.
+ */
+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;
+
+ Unqual!(typeof(s[0]))[] r;
+ bool changed = false;
+
+ foreach (size_t i, dchar c; s)
+ {
+ if (inPattern(c, pattern))
+ {
+ if (!changed)
+ {
+ changed = true;
+ r = s[0 .. i].dup;
+ }
+ continue;
+ }
+ if (changed)
+ {
+ encode(r, c);
+ }
+ }
+ if (changed)
+ return r;
+ else
+ return s;
+}
+
+deprecated
+@safe pure unittest
+{
+ import std.conv : to;
+ import std.exception : assertCTFEable;
+
+ assertCTFEable!(
+ {
+ assert(removechars("abc", "a-c").length == 0);
+ assert(removechars("hello world", "or") == "hell wld");
+ assert(removechars("hello world", "d") == "hello worl");
+ assert(removechars("hah", "h") == "a");
+ });
+}
+
+deprecated
+@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, stride;
+
+ Unqual!(typeof(s[0]))[] r;
+ dchar lastc;
+ size_t lasti;
+ int run;
+ bool changed;
+
+ foreach (size_t i, dchar c; s)
+ {
+ if (run && lastc == c)
+ {
+ changed = true;
+ }
+ else if (pattern is null || inPattern(c, pattern))
+ {
+ run = 1;
+ if (changed)
+ {
+ if (r is null)
+ r = s[0 .. lasti].dup;
+ encode(r, c);
+ }
+ else
+ lasti = i + stride(s, i);
+ lastc = c;
+ }
+ else
+ {
+ run = 0;
+ if (changed)
+ {
+ if (r is null)
+ r = s[0 .. lasti].dup;
+ encode(r, c);
+ }
+ }
+ }
+ return changed ? ((r is null) ? s[0 .. lasti] : cast(S) r) : s;
+}
+
+deprecated
+@system pure unittest
+{
+ import std.conv : to;
+ import std.exception : assertCTFEable;
+
+ assertCTFEable!(
+ {
+ string s;
+
+ assert(squeeze("hello") == "helo");
+
+ s = "abcd";
+ assert(squeeze(s) is s);
+ s = "xyzz";
+ assert(squeeze(s).ptr == s.ptr); // should just be a slice
+
+ assert(squeeze("hello goodbyee", "oe") == "hello godbye");
+ });
+}
+
+//@@@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
+ $(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).
+
+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;
+ foreach (i, dchar c; s)
+ {
+ if (!inPattern(c, pattern))
+ {
+ j = i;
+ break;
+ }
+ }
+ scope(exit) s = s[j .. $];
+ return s[0 .. j];
+}
+
+///
+deprecated
+@safe pure @nogc unittest
+{
+ string s = "123abc";
+ string t = munch(s, "0123456789");
+ assert(t == "123" && s == "abc");
+ t = munch(s, "0123456789");
+ assert(t == "" && s == "abc");
+}
+
+deprecated
+@safe pure @nogc unittest
+{
+ string s = "123€abc";
+ string t = munch(s, "0123456789");
+ assert(t == "123" && s == "€abc");
+ t = munch(s, "0123456789");
+ assert(t == "" && s == "€abc");
+ t = munch(s, "£$€¥");
+ assert(t == "€" && s == "abc");
+}
+
+
+/**********************************************
+ * Return string that is the 'successor' to s[].
+ * If the rightmost character is a-zA-Z0-9, it is incremented within
+ * its case or digits. If it generates a carry, the process is
+ * repeated with the one to its immediate left.
+ */
+
+S succ(S)(S s) @safe pure
+if (isSomeString!S)
+{
+ import std.ascii : isAlphaNum;
+
+ if (s.length && isAlphaNum(s[$ - 1]))
+ {
+ auto r = s.dup;
+ size_t i = r.length - 1;
+
+ while (1)
+ {
+ dchar c = s[i];
+ dchar carry;
+
+ switch (c)
+ {
+ case '9':
+ c = '0';
+ carry = '1';
+ goto Lcarry;
+ case 'z':
+ case 'Z':
+ c -= 'Z' - 'A';
+ carry = c;
+ Lcarry:
+ r[i] = cast(char) c;
+ if (i == 0)
+ {
+ auto t = new typeof(r[0])[r.length + 1];
+ t[0] = cast(char) carry;
+ t[1 .. $] = r[];
+ return t;
+ }
+ i--;
+ break;
+
+ default:
+ if (isAlphaNum(c))
+ r[i]++;
+ return r;
+ }
+ }
+ }
+ return s;
+}
+
+///
+@safe pure unittest
+{
+ assert(succ("1") == "2");
+ assert(succ("9") == "10");
+ assert(succ("999") == "1000");
+ assert(succ("zz99") == "aaa00");
+}
+
+@safe pure unittest
+{
+ import std.conv : to;
+ import std.exception : assertCTFEable;
+
+ assertCTFEable!(
+ {
+ assert(succ(string.init) is null);
+ assert(succ("!@#$%") == "!@#$%");
+ assert(succ("1") == "2");
+ assert(succ("9") == "10");
+ assert(succ("999") == "1000");
+ assert(succ("zz99") == "aaa00");
+ });
+}
+
+
+/++
+ Replaces the characters in $(D str) which are in $(D from) with the
+ the corresponding characters in $(D to) and returns the resulting string.
+
+ $(D tr) is based on
+ $(HTTP pubs.opengroup.org/onlinepubs/9699919799/utilities/_tr.html, Posix's tr),
+ though it doesn't do everything that the Posix utility does.
+
+ Params:
+ str = The original string.
+ from = The characters to replace.
+ to = The characters to replace with.
+ modifiers = String containing modifiers.
+
+ Modifiers:
+ $(BOOKTABLE,
+ $(TR $(TD Modifier) $(TD Description))
+ $(TR $(TD $(D 'c')) $(TD Complement the list of characters in $(D from)))
+ $(TR $(TD $(D 'd')) $(TD Removes matching characters with no corresponding
+ replacement in $(D to)))
+ $(TR $(TD $(D 's')) $(TD Removes adjacent duplicates in the replaced
+ characters))
+ )
+
+ If the modifier $(D 'd') is present, then the number of characters in
+ $(D to) may be only $(D 0) or $(D 1).
+
+ If the modifier $(D 'd') is $(I not) present, and $(D to) is empty, then
+ $(D to) is taken to be the same as $(D from).
+
+ If the modifier $(D 'd') is $(I not) present, and $(D to) is shorter than
+ $(D from), then $(D to) is extended by replicating the last character in
+ $(D to).
+
+ Both $(D from) and $(D to) may contain ranges using the $(D '-') character
+ (e.g. $(D "a-d") is synonymous with $(D "abcd").) Neither accept a leading
+ $(D '^') as meaning the complement of the string (use the $(D 'c') modifier
+ for that).
+ +/
+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;
+
+ bool mod_c;
+ bool mod_d;
+ bool mod_s;
+
+ foreach (char c; modifiers)
+ {
+ switch (c)
+ {
+ case 'c': mod_c = 1; break; // complement
+ case 'd': mod_d = 1; break; // delete unreplaced chars
+ case 's': mod_s = 1; break; // squeeze duplicated replaced chars
+ default: assert(0);
+ }
+ }
+
+ if (to.empty && !mod_d)
+ to = conv_to!(typeof(to))(from);
+
+ auto result = appender!(C1[])();
+ bool modified;
+ dchar lastc;
+
+ foreach (dchar c; str)
+ {
+ dchar lastf;
+ dchar lastt;
+ dchar newc;
+ int n = 0;
+
+ for (size_t i = 0; i < from.length; )
+ {
+ immutable f = decode(from, i);
+ if (f == '-' && lastf != dchar.init && i < from.length)
+ {
+ immutable nextf = decode(from, i);
+ if (lastf <= c && c <= nextf)
+ {
+ n += c - lastf - 1;
+ if (mod_c)
+ goto Lnotfound;
+ goto Lfound;
+ }
+ n += nextf - lastf;
+ lastf = lastf.init;
+ continue;
+ }
+
+ if (c == f)
+ { if (mod_c)
+ goto Lnotfound;
+ goto Lfound;
+ }
+ lastf = f;
+ n++;
+ }
+ if (!mod_c)
+ goto Lnotfound;
+ n = 0; // consider it 'found' at position 0
+
+ Lfound:
+
+ // Find the nth character in to[]
+ dchar nextt;
+ for (size_t i = 0; i < to.length; )
+ {
+ immutable t = decode(to, i);
+ if (t == '-' && lastt != dchar.init && i < to.length)
+ {
+ nextt = decode(to, i);
+ n -= nextt - lastt;
+ if (n < 0)
+ {
+ newc = nextt + n + 1;
+ goto Lnewc;
+ }
+ lastt = dchar.init;
+ continue;
+ }
+ if (n == 0)
+ { newc = t;
+ goto Lnewc;
+ }
+ lastt = t;
+ nextt = t;
+ n--;
+ }
+ if (mod_d)
+ continue;
+ newc = nextt;
+
+ Lnewc:
+ if (mod_s && modified && newc == lastc)
+ continue;
+ result.put(newc);
+ assert(newc != dchar.init);
+ modified = true;
+ lastc = newc;
+ continue;
+
+ Lnotfound:
+ result.put(c);
+ lastc = c;
+ modified = false;
+ }
+
+ return result.data;
+}
+
+@safe pure unittest
+{
+ import std.algorithm.comparison : equal;
+ import std.conv : to;
+ import std.exception : assertCTFEable;
+
+ // Complete list of test types; too slow to test'em all
+ // 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 = AliasSeq!(char[], const(wchar)[], immutable(dchar)[]);
+
+ assertCTFEable!(
+ {
+ foreach (S; TestTypes)
+ {
+ foreach (T; TestTypes)
+ {
+ foreach (U; TestTypes)
+ {
+ assert(equal(tr(to!S("abcdef"), to!T("cd"), to!U("CD")), "abCDef"));
+ assert(equal(tr(to!S("abcdef"), to!T("b-d"), to!U("B-D")), "aBCDef"));
+ assert(equal(tr(to!S("abcdefgh"), to!T("b-dh"), to!U("B-Dx")), "aBCDefgx"));
+ assert(equal(tr(to!S("abcdefgh"), to!T("b-dh"), to!U("B-CDx")), "aBCDefgx"));
+ assert(equal(tr(to!S("abcdefgh"), to!T("b-dh"), to!U("B-BCDx")), "aBCDefgx"));
+ assert(equal(tr(to!S("abcdef"), to!T("ef"), to!U("*"), to!S("c")), "****ef"));
+ assert(equal(tr(to!S("abcdef"), to!T("ef"), to!U(""), to!T("d")), "abcd"));
+ assert(equal(tr(to!S("hello goodbye"), to!T("lo"), to!U(""), to!U("s")), "helo godbye"));
+ assert(equal(tr(to!S("hello goodbye"), to!T("lo"), to!U("x"), "s"), "hex gxdbye"));
+ assert(equal(tr(to!S("14-Jul-87"), to!T("a-zA-Z"), to!U(" "), "cs"), " Jul "));
+ assert(equal(tr(to!S("Abc"), to!T("AAA"), to!U("XYZ")), "Xbc"));
+ }
+ }
+
+ auto s = to!S("hello world");
+ static assert(is(typeof(s) == typeof(tr(s, "he", "if"))));
+ }
+ });
+}
+
+@system pure unittest
+{
+ import core.exception : AssertError;
+ import std.exception : assertThrown;
+ assertThrown!AssertError(tr("abcdef", "cd", "CD", "X"));
+}
+
+/**
+ * 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 $(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;
+
+ // 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;
+ }
+
+ if (codeUnits.empty)
+ return false;
+
+ // Check for NaN (Not a Number) and for Infinity
+ if (codeUnits.among!((a, b) => asciiCmp(a.save, b))
+ ("nan", "nani", "nan+nani", "inf", "-inf"))
+ return true;
+
+ immutable frontResult = codeUnits.front;
+ if (frontResult == '-' || frontResult == '+')
+ codeUnits.popFront;
+
+ immutable iLen = codeUnits.length;
+ bool bDecimalPoint, bExponent, bComplex, sawDigits;
+
+ for (size_t i = 0; i < iLen; i++)
+ {
+ immutable c = codeUnits[i];
+
+ if (!c.isASCII)
+ return false;
+
+ // Digits are good, skip to the next character
+ if (c >= '0' && c <= '9')
+ {
+ sawDigits = true;
+ continue;
+ }
+
+ // Check for the complex type, and if found
+ // reset the flags for checking the 2nd number.
+ if (c == '+')
+ {
+ if (!i)
+ return false;
+ bDecimalPoint = false;
+ bExponent = false;
+ bComplex = true;
+ sawDigits = false;
+ continue;
+ }
+
+ // Allow only one exponent per number
+ 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 (codeUnits[i + 1] != '-' && codeUnits[i + 1] != '+')
+ return false;
+ bExponent = true;
+ i++;
+ continue;
+ }
+ // Allow only one decimal point per number to be used
+ if (c == '.')
+ {
+ // A 2nd decimal point found, return not a number
+ if (bDecimalPoint)
+ return false;
+ bDecimalPoint = true;
+ continue;
+ }
+ // Check for ending literal characters: "f,u,l,i,ul,fi,li",
+ // and whether they're being used with the correct datatype.
+ if (i == iLen - 2)
+ {
+ if (!sawDigits)
+ return false;
+ // Integer Whole Number
+ if (asciiCmp(codeUnits[i .. iLen], "ul") &&
+ (!bDecimalPoint && !bExponent && !bComplex))
+ return true;
+ // Floating-Point Number
+ if (codeUnits[i .. iLen].among!((a, b) => asciiCmp(a, b))("fi", "li") &&
+ (bDecimalPoint || bExponent || bComplex))
+ return true;
+ 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 codeUnits[i .. iLen].among!((a, b) => asciiCmp(a, b))
+ ("ul", "fi", "li") != 0;
+ }
+ if (i == iLen - 1)
+ {
+ if (!sawDigits)
+ return false;
+ // Integer Whole Number
+ if (c.among!('u', 'l', 'U', 'L')() &&
+ (!bDecimalPoint && !bExponent && !bComplex))
+ return true;
+ // Check to see if the last character in the string
+ // is the required 'i' character
+ if (bComplex)
+ return c.among!('i', 'I')() != 0;
+ // Floating-Point Number
+ return c.among!('l', 'L', 'f', 'F', 'i', 'I')() != 0;
+ }
+ // Check if separators are allowed to be in the numeric string
+ if (!bAllowSep || !c.among!('_', ',')())
+ return false;
+ }
+
+ return sawDigits;
+}
+
+/**
+ * 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"));
+ assert(!isNumeric("U"));
+ assert(!isNumeric("i"));
+ assert(!isNumeric("fi"));
+ assert(!isNumeric("ul"));
+ assert(!isNumeric("li"));
+ assert(!isNumeric("."));
+ assert(!isNumeric("-"));
+ assert(!isNumeric("+"));
+ assert(!isNumeric("e-"));
+ 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);
+}
+
+@system unittest
+{
+ import std.conv : to;
+ import std.exception : assertCTFEable;
+
+ assertCTFEable!(
+ {
+ // Test the isNumeric(in string) function
+ assert(isNumeric("1") == true );
+ assert(isNumeric("1.0") == true );
+ assert(isNumeric("1e-1") == true );
+ assert(isNumeric("12345xxxx890") == false );
+ assert(isNumeric("567L") == true );
+ assert(isNumeric("23UL") == true );
+ assert(isNumeric("-123..56f") == false );
+ assert(isNumeric("12.3.5.6") == false );
+ assert(isNumeric(" 12.356") == false );
+ assert(isNumeric("123 5.6") == false );
+ assert(isNumeric("1233E-1+1.0e-1i") == true );
+
+ assert(isNumeric("123.00E-5+1234.45E-12Li") == true);
+ assert(isNumeric("123.00e-5+1234.45E-12iL") == false);
+ assert(isNumeric("123.00e-5+1234.45e-12uL") == false);
+ assert(isNumeric("123.00E-5+1234.45e-12lu") == false);
+
+ assert(isNumeric("123fi") == true);
+ assert(isNumeric("123li") == true);
+ assert(isNumeric("--123L") == false);
+ assert(isNumeric("+123.5UL") == false);
+ assert(isNumeric("123f") == true);
+ assert(isNumeric("123.u") == false);
+
+ // @@@BUG@@ to!string(float) is not CTFEable.
+ // Related: formatValue(T) if (is(FloatingPointTypeOf!T))
+ if (!__ctfe)
+ {
+ assert(isNumeric(to!string(real.nan)) == true);
+ assert(isNumeric(to!string(-real.infinity)) == true);
+ assert(isNumeric(to!string(123e+2+1234.78Li)) == true);
+ }
+
+ string s = "$250.99-";
+ assert(isNumeric(s[1 .. s.length - 2]) == true);
+ assert(isNumeric(s) == false);
+ assert(isNumeric(s[0 .. s.length - 1]) == false);
+ });
+
+ assert(!isNumeric("-"));
+ assert(!isNumeric("+"));
+}
+
+/*****************************
+ * Soundex algorithm.
+ *
+ * The Soundex algorithm converts a word into 4 characters
+ * based on how the word sounds phonetically. The idea is that
+ * two spellings that sound alike will have the same Soundex
+ * value, which means that Soundex can be used for fuzzy matching
+ * of names.
+ *
+ * Params:
+ * str = String or InputRange to convert to Soundex representation.
+ *
+ * Returns:
+ * The four character array with the Soundex result in it.
+ * 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[4] soundexer(Range)(Range str)
+if (isInputRange!Range && isSomeChar!(ElementEncodingType!Range) &&
+ !isConvertibleToString!Range)
+{
+ alias C = Unqual!(ElementEncodingType!Range);
+
+ static immutable dex =
+ // ABCDEFGHIJKLMNOPQRSTUVWXYZ
+ "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')
+ {
+ }
+ else
+ {
+ lastc = lastc.init;
+ continue;
+ }
+ if (b == 0)
+ {
+ result[0] = cast(char) c;
+ b++;
+ lastc = dex[c - 'A'];
+ }
+ else
+ {
+ if (c == 'H' || c == 'W')
+ continue;
+ if (c == 'A' || c == 'E' || c == 'I' || c == 'O' || c == 'U')
+ lastc = lastc.init;
+ c = dex[c - 'A'];
+ if (c != '0' && c != lastc)
+ {
+ result[b] = cast(char) c;
+ b++;
+ lastc = c;
+ }
+ if (b == 4)
+ goto Lret;
+ }
+ }
+ if (b == 0)
+ result[] = 0;
+ else
+ 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 : assertCTFEable;
+ assertCTFEable!(
+ {
+ char[4] buffer;
+
+ assert(soundex(null) == null);
+ assert(soundex("") == null);
+ assert(soundex("0123^&^^**&^") == null);
+ assert(soundex("Euler") == "E460");
+ assert(soundex(" Ellery ") == "E460");
+ assert(soundex("Gauss") == "G200");
+ assert(soundex("Ghosh") == "G200");
+ assert(soundex("Hilbert") == "H416");
+ assert(soundex("Heilbronn") == "H416");
+ assert(soundex("Knuth") == "K530");
+ assert(soundex("Kant", buffer) == "K530");
+ assert(soundex("Lloyd") == "L300");
+ assert(soundex("Ladd") == "L300");
+ assert(soundex("Lukasiewicz", buffer) == "L222");
+ assert(soundex("Lissajous") == "L222");
+ assert(soundex("Robert") == "R163");
+ assert(soundex("Rupert") == "R163");
+ assert(soundex("Rubin") == "R150");
+ assert(soundex("Washington") == "W252");
+ assert(soundex("Lee") == "L000");
+ assert(soundex("Gutierrez") == "G362");
+ assert(soundex("Pfister") == "P236");
+ assert(soundex("Jackson") == "J250");
+ assert(soundex("Tymczak") == "T522");
+ assert(soundex("Ashcraft") == "A261");
+
+ assert(soundex("Woo") == "W000");
+ assert(soundex("Pilgrim") == "P426");
+ assert(soundex("Flingjingwaller") == "F452");
+ assert(soundex("PEARSE") == "P620");
+ assert(soundex("PIERCE") == "P620");
+ assert(soundex("Price") == "P620");
+ assert(soundex("CATHY") == "C300");
+ assert(soundex("KATHY") == "K300");
+ assert(soundex("Jones") == "J520");
+ 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
+ * abbreviations that uniquely map to the strings in values.
+ *
+ * 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
+ * auto-complete the string once sufficient characters have been
+ * entered that uniquely identify it.
+ */
+
+string[string] abbrev(string[] values) @safe pure
+{
+ import std.algorithm.sorting : sort;
+
+ string[string] result;
+
+ // Make a copy when sorting so we follow COW principles.
+ values = values.dup;
+ sort(values);
+
+ size_t values_length = values.length;
+ size_t lasti = values_length;
+ size_t nexti;
+
+ string nv;
+ string lv;
+
+ for (size_t i = 0; i < values_length; i = nexti)
+ {
+ string value = values[i];
+
+ // Skip dups
+ for (nexti = i + 1; nexti < values_length; nexti++)
+ {
+ nv = values[nexti];
+ if (value != values[nexti])
+ break;
+ }
+
+ import std.utf : stride;
+
+ for (size_t j = 0; j < value.length; j += stride(value, j))
+ {
+ string v = value[0 .. j];
+
+ if ((nexti == values_length || j > nv.length || v != nv[0 .. j]) &&
+ (lasti == values_length || j > lv.length || v != lv[0 .. j]))
+ {
+ result[v] = value;
+ }
+ }
+ result[value] = value;
+ lasti = i;
+ lv = value;
+ }
+
+ return result;
+}
+
+///
+@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.exception : assertCTFEable;
+
+ assertCTFEable!(
+ {
+ string[] values;
+ values ~= "hello";
+ values ~= "hello";
+ values ~= "he";
+
+ string[string] r;
+
+ r = abbrev(values);
+ auto keys = r.keys.dup;
+ sort(keys);
+
+ assert(keys.length == 4);
+ assert(keys[0] == "he");
+ assert(keys[1] == "hel");
+ assert(keys[2] == "hell");
+ assert(keys[3] == "hello");
+
+ assert(r[keys[0]] == "he");
+ assert(r[keys[1]] == "hello");
+ assert(r[keys[2]] == "hello");
+ assert(r[keys[3]] == "hello");
+ });
+}
+
+
+/******************************************
+ * Compute _column number at the end of the printed form of the string,
+ * assuming the string starts in the leftmost _column, which is numbered
+ * starting from 0.
+ *
+ * Tab characters are expanded into enough spaces to bring the _column number
+ * to the next multiple of tabsize.
+ * If there are multiple lines in the string, the _column number of the last
+ * line is returned.
+ *
+ * Params:
+ * str = string or InputRange to be analyzed
+ * tabsize = number of columns a tab character represents
+ *
+ * Returns:
+ * column number
+ */
+
+size_t column(Range)(Range str, in size_t tabsize = 8)
+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;
+
+ return str.byDchar.column(tabsize);
+ }
+ else
+ {
+ // decoding not needed for wchars and dchars
+ import std.uni : lineSep, paraSep, nelSep;
+
+ size_t column;
+
+ foreach (const c; str)
+ {
+ switch (c)
+ {
+ case '\t':
+ column = (column + tabsize) / tabsize * tabsize;
+ break;
+
+ case '\r':
+ case '\n':
+ case paraSep:
+ case lineSep:
+ case nelSep:
+ column = 0;
+ break;
+
+ default:
+ column++;
+ break;
+ }
+ }
+ return column;
+ }
+}
+
+///
+@safe pure unittest
+{
+ import std.utf : byChar, byWchar, byDchar;
+
+ assert(column("1234 ") == 5);
+ assert(column("1234 "w) == 5);
+ assert(column("1234 "d) == 5);
+
+ assert(column("1234 ".byChar()) == 5);
+ assert(column("1234 "w.byWchar()) == 5);
+ assert(column("1234 "d.byDchar()) == 5);
+
+ // Tab stops are set at 8 spaces by default; tab characters insert enough
+ // spaces to bring the column position to the next multiple of 8.
+ assert(column("\t") == 8);
+ assert(column("1\t") == 8);
+ assert(column("\t1") == 9);
+ assert(column("123\t") == 8);
+
+ // Other tab widths are possible by specifying it explicitly:
+ assert(column("\t", 4) == 4);
+ assert(column("1\t", 4) == 4);
+ assert(column("\t1", 4) == 5);
+ assert(column("123\t", 4) == 4);
+
+ // New lines reset the column number.
+ assert(column("abc\n") == 0);
+ assert(column("abc\n1") == 1);
+ assert(column("abcdefg\r1234") == 4);
+ assert(column("abc\u20281") == 1);
+ assert(column("abc\u20291") == 1);
+ assert(column("abc\u00851") == 1);
+ 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;
+
+ assertCTFEable!(
+ {
+ assert(column(string.init) == 0);
+ assert(column("") == 0);
+ assert(column("\t") == 8);
+ assert(column("abc\t") == 8);
+ assert(column("12345678\t") == 16);
+ });
+}
+
+/******************************************
+ * Wrap text into a paragraph.
+ *
+ * The input text string s is formed into a paragraph
+ * by breaking it up into a sequence of lines, delineated
+ * by \n, such that the number of columns is not exceeded
+ * on each line.
+ * The last line is terminated with a \n.
+ * Params:
+ * s = text string to be wrapped
+ * columns = maximum number of _columns in the paragraph
+ * firstindent = string used to _indent first line of the paragraph
+ * indent = string to use to _indent following lines of the paragraph
+ * tabsize = column spacing of tabs in firstindent[] and indent[]
+ * Returns:
+ * resulting paragraph as an allocated string
+ */
+
+S wrap(S)(S s, in size_t columns = 80, S firstindent = null,
+S indent = null, in size_t tabsize = 8)
+if (isSomeString!S)
+{
+ import std.uni : isWhite;
+ typeof(s.dup) result;
+ bool inword;
+ bool first = true;
+ size_t wordstart;
+
+ const indentcol = column(indent, tabsize);
+
+ result.length = firstindent.length + s.length;
+ result.length = firstindent.length;
+ result[] = firstindent[];
+ auto col = column(firstindent, tabsize);
+ foreach (size_t i, dchar c; s)
+ {
+ if (isWhite(c))
+ {
+ if (inword)
+ {
+ if (first)
+ {
+ }
+ else if (col + 1 + (i - wordstart) > columns)
+ {
+ result ~= '\n';
+ result ~= indent;
+ col = indentcol;
+ }
+ else
+ {
+ result ~= ' ';
+ col += 1;
+ }
+ result ~= s[wordstart .. i];
+ col += i - wordstart;
+ inword = false;
+ first = false;
+ }
+ }
+ else
+ {
+ if (!inword)
+ {
+ wordstart = i;
+ inword = true;
+ }
+ }
+ }
+
+ if (inword)
+ {
+ if (col + 1 + (s.length - wordstart) >= columns)
+ {
+ result ~= '\n';
+ result ~= indent;
+ }
+ else if (result.length != firstindent.length)
+ result ~= ' ';
+ result ~= s[wordstart .. s.length];
+ }
+ result ~= '\n';
+
+ 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;
+
+ assertCTFEable!(
+ {
+ assert(wrap(string.init) == "\n");
+ assert(wrap(" a b df ") == "a b df\n");
+ assert(wrap(" a b df ", 3) == "a b\ndf\n");
+ assert(wrap(" a bc df ", 3) == "a\nbc\ndf\n");
+ assert(wrap(" abcd df ", 3) == "abcd\ndf\n");
+ assert(wrap("x") == "x\n");
+ assert(wrap("u u") == "u u\n");
+ assert(wrap("abcd", 3) == "\nabcd\n");
+ assert(wrap("a de", 10, "\t", " ", 8) == "\ta\n de\n");
+ });
+}
+
+/******************************************
+ * Removes one level of indentation from a multi-line string.
+ *
+ * This uniformly outdents the text as much as possible.
+ * Whitespace-only lines are always converted to blank lines.
+ *
+ * Does not allocate memory if it does not throw.
+ *
+ * Params:
+ * str = multi-line string
+ *
+ * Returns:
+ * outdented string
+ *
+ * Throws:
+ * StringException if indentation is done with different sequences
+ * of whitespace characters.
+ */
+S outdent(S)(S str) @safe pure
+if (isSomeString!S)
+{
+ return str.splitLines(Yes.keepTerminator).outdent().join();
+}
+
+///
+@safe pure unittest
+{
+ enum pretty = q{
+ import std.stdio;
+ void main() {
+ writeln("Hello");
+ }
+ }.outdent();
+
+ enum ugly = q{
+import std.stdio;
+void main() {
+ writeln("Hello");
+}
+};
+
+ assert(pretty == ugly);
+}
+
+
+/******************************************
+ * Removes one level of indentation from an array of single-line strings.
+ *
+ * This uniformly outdents the text as much as possible.
+ * Whitespace-only lines are always converted to blank lines.
+ *
+ * Params:
+ * lines = array of single-line strings
+ *
+ * Returns:
+ * lines[] is rewritten in place with outdented lines
+ *
+ * Throws:
+ * StringException if indentation is done with different sequences
+ * of whitespace characters.
+ */
+S[] outdent(S)(S[] lines) @safe pure
+if (isSomeString!S)
+{
+ import std.algorithm.searching : startsWith;
+
+ if (lines.empty)
+ {
+ return null;
+ }
+
+ static S leadingWhiteOf(S str)
+ {
+ return str[ 0 .. $ - stripLeft(str).length ];
+ }
+
+ S shortestIndent;
+ foreach (ref line; lines)
+ {
+ const stripped = line.stripLeft();
+
+ if (stripped.empty)
+ {
+ line = line[line.chomp().length .. $];
+ }
+ else
+ {
+ const indent = leadingWhiteOf(line);
+
+ // Comparing number of code units instead of code points is OK here
+ // because this function throws upon inconsistent indentation.
+ if (shortestIndent is null || indent.length < shortestIndent.length)
+ {
+ if (indent.empty)
+ return lines;
+ shortestIndent = indent;
+ }
+ }
+ }
+
+ foreach (ref line; lines)
+ {
+ const stripped = line.stripLeft();
+
+ if (stripped.empty)
+ {
+ // Do nothing
+ }
+ else if (line.startsWith(shortestIndent))
+ {
+ line = line[shortestIndent.length .. $];
+ }
+ else
+ {
+ throw new StringException("outdent: Inconsistent indentation");
+ }
+ }
+
+ return lines;
+}
+
+@safe pure unittest
+{
+ import std.conv : to;
+ import std.exception : assertCTFEable;
+
+ template outdent_testStr(S)
+ {
+ enum S outdent_testStr =
+"
+ \t\tX
+ \t\U00010143X
+ \t\t
+
+ \t\t\tX
+\t ";
+ }
+
+ template outdent_expected(S)
+ {
+ enum S outdent_expected =
+"
+\tX
+\U00010143X
+
+
+\t\tX
+";
+ }
+
+ assertCTFEable!(
+ {
+
+ foreach (S; AliasSeq!(string, wstring, dstring))
+ {
+ enum S blank = "";
+ assert(blank.outdent() == blank);
+ static assert(blank.outdent() == blank);
+
+ enum S testStr1 = " \n \t\n ";
+ enum S expected1 = "\n\n";
+ assert(testStr1.outdent() == expected1);
+ static assert(testStr1.outdent() == expected1);
+
+ assert(testStr1[0..$-1].outdent() == expected1);
+ static assert(testStr1[0..$-1].outdent() == expected1);
+
+ enum S testStr2 = "a\n \t\nb";
+ assert(testStr2.outdent() == testStr2);
+ static assert(testStr2.outdent() == testStr2);
+
+ enum S testStr3 =
+"
+ \t\tX
+ \t\U00010143X
+ \t\t
+
+ \t\t\tX
+\t ";
+
+ enum S expected3 =
+"
+\tX
+\U00010143X
+
+
+\t\tX
+";
+ assert(testStr3.outdent() == expected3);
+ static assert(testStr3.outdent() == expected3);
+
+ enum testStr4 = " X\r X\n X\r\n X\u2028 X\u2029 X";
+ enum expected4 = "X\rX\nX\r\nX\u2028X\u2029X";
+ assert(testStr4.outdent() == expected4);
+ static assert(testStr4.outdent() == expected4);
+
+ enum testStr5 = testStr4[0..$-1];
+ enum expected5 = expected4[0..$-1];
+ assert(testStr5.outdent() == expected5);
+ static assert(testStr5.outdent() == expected5);
+
+ enum testStr6 = " \r \n \r\n \u2028 \u2029";
+ enum expected6 = "\r\n\r\n\u2028\u2029";
+ assert(testStr6.outdent() == expected6);
+ static assert(testStr6.outdent() == expected6);
+
+ enum testStr7 = " a \n b ";
+ enum expected7 = "a \nb ";
+ assert(testStr7.outdent() == expected7);
+ static assert(testStr7.outdent() == expected7);
+ }
+ });
+}
+
+@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
+
+Returns:
+ arr retyped as an array of chars, wchars, or dchars
+
+See_Also: $(LREF representation)
+*/
+auto assumeUTF(T)(T[] arr) pure
+if (staticIndexOf!(Unqual!T, ubyte, ushort, uint) != -1)
+{
+ import std.traits : ModifyTypePreservingTQ;
+ import std.utf : validate;
+ alias ToUTFType(U) = AliasSeq!(char, wchar, dchar)[U.sizeof / 2];
+ auto asUTF = cast(ModifyTypePreservingTQ!(ToUTFType, T)[])arr;
+ debug validate(asUTF);
+ return asUTF;
+}
+
+///
+@safe pure unittest
+{
+ string a = "Hölo World";
+ immutable(ubyte)[] b = a.representation;
+ string c = b.assumeUTF;
+
+ assert(a == c);
+}
+
+pure @system unittest
+{
+ 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[]))
+ {
+ auto gt = cast(ubyte[]) jt;
+ auto gtc = cast(const(ubyte)[])jt;
+ auto gti = cast(immutable(ubyte)[])jt;
+ }
+ else static if (is(T == wchar[]))
+ {
+ auto gt = cast(ushort[]) jt;
+ auto gtc = cast(const(ushort)[])jt;
+ auto gti = cast(immutable(ushort)[])jt;
+ }
+ else static if (is(T == dchar[]))
+ {
+ auto gt = cast(uint[]) jt;
+ auto gtc = cast(const(uint)[])jt;
+ auto gti = cast(immutable(uint)[])jt;
+ }
+
+ auto ht = assumeUTF(gt);
+ auto htc = assumeUTF(gtc);
+ auto hti = assumeUTF(gti);
+ assert(equal(jt, ht));
+ assert(equal(jt, htc));
+ assert(equal(jt, hti));
+ }
+}
diff --git a/libphobos/src/std/system.d b/libphobos/src/std/system.d
new file mode 100644
index 0000000..f8c23b7
--- /dev/null
+++ b/libphobos/src/std/system.d
@@ -0,0 +1,74 @@
+// Written in the D programming language.
+
+/**
+ * Information about the target operating system, environment, and CPU.
+ *
+ * Copyright: Copyright Digital Mars 2000 - 2011
+ * 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;
+
+immutable
+{
+ /++
+ Operating system.
+
+ Note:
+ This is for cases where you need a value representing the OS at
+ runtime. If you're doing something which should compile differently
+ on different OSes, then please use $(D version (Windows)),
+ $(D version (linux)), etc.
+
+ See_Also:
+ $(DDSUBLINK spec/version,PredefinedVersions, Predefined Versions)
+ +/
+ enum OS
+ {
+ win32 = 1, /// Microsoft 32 bit Windows systems
+ win64, /// Microsoft 64 bit Windows systems
+ linux, /// All Linux Systems
+ osx, /// Mac OS X
+ freeBSD, /// FreeBSD
+ netBSD, /// NetBSD
+ solaris, /// Solaris
+ android, /// Android
+ otherPosix /// Other Posix Systems
+ }
+
+ /// 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 (NetBSD) OS os = OS.netBSD;
+ else version (Posix) OS os = OS.otherPosix;
+ else static assert(0, "Unknown OS.");
+
+ /++
+ Byte order endianness.
+
+ Note:
+ This is intended for cases where you need to deal with endianness at
+ runtime. If you're doing something which should compile differently
+ depending on whether you're compiling on a big endian or little
+ endian machine, then please use $(D version (BigEndian)) and
+ $(D version (LittleEndian)).
+
+ See_Also:
+ $(DDSUBLINK spec/version,PredefinedVersions, Predefined Versions)
+ +/
+ enum Endian
+ {
+ bigEndian, /// Big endian byte order
+ littleEndian /// Little endian byte order
+ }
+
+ /// The endianness that the program was compiled for.
+ version (LittleEndian) Endian endian = Endian.littleEndian;
+ else Endian endian = Endian.bigEndian;
+}
+
diff --git a/libphobos/src/std/traits.d b/libphobos/src/std/traits.d
new file mode 100644
index 0000000..4359dfb
--- /dev/null
+++ b/libphobos/src/std/traits.d
@@ -0,0 +1,8058 @@
+// Written in the D programming language.
+
+/**
+ * Templates which extract information about types and symbols at compile time.
+ *
+ * $(SCRIPT inhibitQuickIndex = 1;)
+ *
+ * $(DIVC quickindex,
+ * $(BOOKTABLE ,
+ * $(TR $(TH Category) $(TH Templates))
+ * $(TR $(TD Symbol Name _traits) $(TD
+ * $(LREF fullyQualifiedName)
+ * $(LREF moduleName)
+ * $(LREF packageName)
+ * ))
+ * $(TR $(TD Function _traits) $(TD
+ * $(LREF isFunction)
+ * $(LREF arity)
+ * $(LREF functionAttributes)
+ * $(LREF hasFunctionAttributes)
+ * $(LREF functionLinkage)
+ * $(LREF FunctionTypeOf)
+ * $(LREF isSafe)
+ * $(LREF isUnsafe)
+ * $(LREF isFinal)
+ * $(LREF ParameterDefaults)
+ * $(LREF ParameterIdentifierTuple)
+ * $(LREF ParameterStorageClassTuple)
+ * $(LREF Parameters)
+ * $(LREF ReturnType)
+ * $(LREF SetFunctionAttributes)
+ * $(LREF variadicFunctionStyle)
+ * ))
+ * $(TR $(TD Aggregate Type _traits) $(TD
+ * $(LREF BaseClassesTuple)
+ * $(LREF BaseTypeTuple)
+ * $(LREF classInstanceAlignment)
+ * $(LREF EnumMembers)
+ * $(LREF FieldNameTuple)
+ * $(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)
+ * $(LREF TemplateArgsOf)
+ * $(LREF TemplateOf)
+ * $(LREF TransitiveBaseTypeTuple)
+ * ))
+ * $(TR $(TD Type Conversion) $(TD
+ * $(LREF CommonType)
+ * $(LREF ImplicitConversionTargets)
+ * $(LREF CopyTypeQualifiers)
+ * $(LREF CopyConstness)
+ * $(LREF isAssignable)
+ * $(LREF isCovariantWith)
+ * $(LREF isImplicitlyConvertible)
+ * ))
+ * $(TR $(TD SomethingTypeOf) $(TD
+ * $(LREF rvalueOf)
+ * $(LREF lvalueOf)
+ * $(LREF InoutOf)
+ * $(LREF ConstOf)
+ * $(LREF SharedOf)
+ * $(LREF SharedInoutOf)
+ * $(LREF SharedConstOf)
+ * $(LREF ImmutableOf)
+ * $(LREF QualifierOf)
+ * ))
+ * $(TR $(TD Categories of types) $(TD
+ * $(LREF allSameType)
+ * $(LREF ifTestable)
+ * $(LREF isType)
+ * $(LREF isAggregateType)
+ * $(LREF isArray)
+ * $(LREF isAssociativeArray)
+ * $(LREF isAutodecodableString)
+ * $(LREF isBasicType)
+ * $(LREF isBoolean)
+ * $(LREF isBuiltinType)
+ * $(LREF isCopyable)
+ * $(LREF isDynamicArray)
+ * $(LREF isEqualityComparable)
+ * $(LREF isFloatingPoint)
+ * $(LREF isIntegral)
+ * $(LREF isNarrowString)
+ * $(LREF isConvertibleToString)
+ * $(LREF isNumeric)
+ * $(LREF isOrderingComparable)
+ * $(LREF isPointer)
+ * $(LREF isScalarType)
+ * $(LREF isSigned)
+ * $(LREF isSIMDVector)
+ * $(LREF isSomeChar)
+ * $(LREF isSomeString)
+ * $(LREF isStaticArray)
+ * $(LREF isUnsigned)
+ * ))
+ * $(TR $(TD Type behaviours) $(TD
+ * $(LREF isAbstractClass)
+ * $(LREF isAbstractFunction)
+ * $(LREF isCallable)
+ * $(LREF isDelegate)
+ * $(LREF isExpressions)
+ * $(LREF isFinalClass)
+ * $(LREF isFinalFunction)
+ * $(LREF isFunctionPointer)
+ * $(LREF isInstanceOf)
+ * $(LREF isIterable)
+ * $(LREF isMutable)
+ * $(LREF isSomeFunction)
+ * $(LREF isTypeTuple)
+ * ))
+ * $(TR $(TD General Types) $(TD
+ * $(LREF ForeachType)
+ * $(LREF KeyType)
+ * $(LREF Largest)
+ * $(LREF mostNegative)
+ * $(LREF OriginalType)
+ * $(LREF PointerTarget)
+ * $(LREF Signed)
+ * $(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)
+ * ))
+ * )
+ * )
+ *
+ * Copyright: Copyright Digital Mars 2005 - 2009.
+ * 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,
+ * $(HTTP octarineparrot.com, Robert Clipsham),
+ * $(HTTP klickverbot.at, David Nadlinger),
+ * Kenji Hara,
+ * Shoichi Kato
+ * Source: $(PHOBOSSRC std/_traits.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)
+ */
+module std.traits;
+
+import std.meta : AliasSeq, allSatisfy;
+import std.functional : unaryFun;
+
+// Legacy inheritance from std.typetuple
+// See also: https://github.com/dlang/phobos/pull/5484#discussion_r122602797
+import std.meta : staticMapMeta = staticMap;
+// TODO: find a way to trigger deprecation warnings
+//deprecated("staticMap is part of std.meta: Please import std.meta")
+alias staticMap = staticMapMeta;
+
+///////////////////////////////////////////////////////////////////////////////
+// Functions
+///////////////////////////////////////////////////////////////////////////////
+
+// Petit demangler
+// (this or similar thing will eventually go to std.demangle if necessary
+// ctfe stuffs are available)
+private
+{
+ struct Demangle(T)
+ {
+ T value; // extracted information
+ string rest;
+ }
+
+ /* Demangles mstr as the storage class part of Argument. */
+ Demangle!uint demangleParameterStorageClass(string mstr)
+ {
+ uint pstc = 0; // parameter storage class
+
+ // Argument --> Argument2 | M Argument2
+ if (mstr.length > 0 && mstr[0] == 'M')
+ {
+ pstc |= ParameterStorageClass.scope_;
+ mstr = mstr[1 .. $];
+ }
+
+ // Argument2 --> Type | J Type | K Type | L Type
+ ParameterStorageClass stc2;
+
+ switch (mstr.length ? mstr[0] : char.init)
+ {
+ case 'J': stc2 = ParameterStorageClass.out_; break;
+ case 'K': stc2 = ParameterStorageClass.ref_; break;
+ case 'L': stc2 = ParameterStorageClass.lazy_; break;
+ case 'N': if (mstr.length >= 2 && mstr[1] == 'k')
+ stc2 = ParameterStorageClass.return_;
+ break;
+ default : break;
+ }
+ if (stc2 != ParameterStorageClass.init)
+ {
+ pstc |= stc2;
+ mstr = mstr[1 .. $];
+ if (stc2 & ParameterStorageClass.return_)
+ mstr = mstr[1 .. $];
+ }
+
+ return Demangle!uint(pstc, mstr);
+ }
+
+ /* Demangles mstr as FuncAttrs. */
+ Demangle!uint demangleFunctionAttributes(string mstr)
+ {
+ immutable LOOKUP_ATTRIBUTE =
+ [
+ 'a': FunctionAttribute.pure_,
+ 'b': FunctionAttribute.nothrow_,
+ 'c': FunctionAttribute.ref_,
+ 'd': FunctionAttribute.property,
+ 'e': FunctionAttribute.trusted,
+ 'f': FunctionAttribute.safe,
+ 'i': FunctionAttribute.nogc,
+ 'j': FunctionAttribute.return_,
+ 'l': FunctionAttribute.scope_
+ ];
+ uint atts = 0;
+
+ // FuncAttrs --> FuncAttr | FuncAttr FuncAttrs
+ // FuncAttr --> empty | Na | Nb | Nc | Nd | Ne | Nf | Ni | Nj
+ // except 'Ng' == inout, because it is a qualifier of function type
+ while (mstr.length >= 2 && mstr[0] == 'N' && mstr[1] != 'g' && mstr[1] != 'k')
+ {
+ if (FunctionAttribute att = LOOKUP_ATTRIBUTE[ mstr[1] ])
+ {
+ atts |= att;
+ mstr = mstr[2 .. $];
+ }
+ else assert(0);
+ }
+ return Demangle!uint(atts, mstr);
+ }
+
+ static if (is(ucent))
+ {
+ alias CentTypeList = AliasSeq!(cent, ucent);
+ alias SignedCentTypeList = AliasSeq!(cent);
+ alias UnsignedCentTypeList = AliasSeq!(ucent);
+ }
+ else
+ {
+ alias CentTypeList = AliasSeq!();
+ alias SignedCentTypeList = AliasSeq!();
+ alias UnsignedCentTypeList = AliasSeq!();
+ }
+
+ alias IntegralTypeList = AliasSeq!(byte, ubyte, short, ushort, int, uint, long, ulong, CentTypeList);
+ alias SignedIntTypeList = AliasSeq!(byte, short, int, long, SignedCentTypeList);
+ alias UnsignedIntTypeList = AliasSeq!(ubyte, ushort, uint, ulong, UnsignedCentTypeList);
+ alias FloatingPointTypeList = AliasSeq!(float, double, real);
+ alias ImaginaryTypeList = AliasSeq!(ifloat, idouble, ireal);
+ alias ComplexTypeList = AliasSeq!(cfloat, cdouble, creal);
+ alias NumericTypeList = AliasSeq!(IntegralTypeList, FloatingPointTypeList);
+ alias CharTypeList = AliasSeq!(char, wchar, dchar);
+}
+
+package
+{
+ // Add the mutable qualifier to the given type T.
+ template MutableOf(T) { alias MutableOf = T ; }
+}
+
+/// Add the inout qualifier to the given type T.
+template InoutOf(T) { alias InoutOf = inout(T) ; }
+/// Add the const qualifier to the given type T.
+template ConstOf(T) { alias ConstOf = const(T) ; }
+/// Add the shared qualifier to the given type T.
+template SharedOf(T) { alias SharedOf = shared(T) ; }
+/// Add the shared and inout qualifiers to the given type T.
+template SharedInoutOf(T) { alias SharedInoutOf = shared(inout(T)); }
+/// Add the shared and const qualifiers to the given type T.
+template SharedConstOf(T) { alias SharedConstOf = shared(const(T)); }
+/// Add the immutable qualifier to the given type T.
+template ImmutableOf(T) { alias ImmutableOf = immutable(T) ; }
+
+@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));
+}
+
+/// 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)
+{
+ alias TypeQualifierList = AliasSeq!(MutableOf, ConstOf, SharedOf, SharedConstOf, ImmutableOf);
+
+ struct SubTypeOf(T)
+ {
+ T val;
+ alias val this;
+ }
+}
+
+private alias parentOf(alias sym) = Identity!(__traits(parent, sym));
+private alias parentOf(alias sym : T!Args, alias T, Args...) = Identity!(__traits(parent, T));
+
+/**
+ * Get the full package name for the given symbol.
+ */
+template packageName(alias T)
+{
+ import std.algorithm.searching : startsWith;
+
+ static if (__traits(compiles, parentOf!T))
+ enum parent = packageName!(parentOf!T);
+ else
+ enum string parent = null;
+
+ static if (T.stringof.startsWith("package "))
+ enum packageName = (parent.length ? parent ~ '.' : "") ~ T.stringof[8 .. $];
+ else static if (parent)
+ enum packageName = parent;
+ else
+ static assert(false, T.stringof ~ " has no parent");
+}
+
+///
+@safe unittest
+{
+ import std.traits;
+ static assert(packageName!packageName == "std");
+}
+
+@safe unittest
+{
+ import std.array;
+
+ // Commented out because of dmd @@@BUG8922@@@
+ // static assert(packageName!std == "std"); // this package (currently: "std.std")
+ static assert(packageName!(std.traits) == "std"); // this module
+ static assert(packageName!packageName == "std"); // symbol in this module
+ static assert(packageName!(std.array) == "std"); // other module from same package
+
+ import core.sync.barrier; // local import
+ static assert(packageName!core == "core");
+ static assert(packageName!(core.sync) == "core.sync");
+ static assert(packageName!Barrier == "core.sync");
+
+ struct X12287(T) { T i; }
+ static assert(packageName!(X12287!int.i) == "std");
+}
+
+version (none) version (unittest) //Please uncomment me when changing packageName to test global imports
+{
+ import core.sync.barrier; // global import
+ static assert(packageName!core == "core");
+ static assert(packageName!(core.sync) == "core.sync");
+ static assert(packageName!Barrier == "core.sync");
+}
+
+/**
+ * Get the module name (including package) for the given symbol.
+ */
+template moduleName(alias T)
+{
+ import std.algorithm.searching : startsWith;
+
+ static assert(!T.stringof.startsWith("package "), "cannot get the module name for a package");
+
+ static if (T.stringof.startsWith("module "))
+ {
+ static if (__traits(compiles, packageName!T))
+ enum packagePrefix = packageName!T ~ '.';
+ else
+ enum packagePrefix = "";
+
+ enum moduleName = packagePrefix ~ T.stringof[7..$];
+ }
+ else
+ alias moduleName = moduleName!(parentOf!T); // If you use enum, it will cause compiler ICE
+}
+
+///
+@safe unittest
+{
+ import std.traits;
+ static assert(moduleName!moduleName == "std.traits");
+}
+
+@safe unittest
+{
+ import std.array;
+
+ static assert(!__traits(compiles, moduleName!std));
+ static assert(moduleName!(std.traits) == "std.traits"); // this module
+ static assert(moduleName!moduleName == "std.traits"); // symbol in this module
+ static assert(moduleName!(std.array) == "std.array"); // other module
+ static assert(moduleName!(std.array.array) == "std.array"); // symbol in other module
+
+ import core.sync.barrier; // local import
+ static assert(!__traits(compiles, moduleName!(core.sync)));
+ static assert(moduleName!(core.sync.barrier) == "core.sync.barrier");
+ static assert(moduleName!Barrier == "core.sync.barrier");
+
+ struct X12287(T) { T i; }
+ static assert(moduleName!(X12287!int.i) == "std.traits");
+}
+
+version (none) version (unittest) //Please uncomment me when changing moduleName to test global imports
+{
+ import core.sync.barrier; // global import
+ static assert(!__traits(compiles, moduleName!(core.sync)));
+ static assert(moduleName!(core.sync.barrier) == "core.sync.barrier");
+ static assert(moduleName!Barrier == "core.sync.barrier");
+}
+
+/***
+ * Get the fully qualified name of a type or a symbol. Can act as an intelligent type/symbol to string converter.
+
+Example:
+-----------------
+module myModule;
+struct MyStruct {}
+static assert(fullyQualifiedName!(const MyStruct[]) == "const(myModule.MyStruct[])");
+-----------------
+*/
+template fullyQualifiedName(T...)
+ if (T.length == 1)
+{
+
+ static if (is(T))
+ enum fullyQualifiedName = fqnType!(T[0], false, false, false, false);
+ else
+ enum fullyQualifiedName = fqnSym!(T[0]);
+}
+
+///
+@safe unittest
+{
+ static assert(fullyQualifiedName!fullyQualifiedName == "std.traits.fullyQualifiedName");
+}
+
+version (unittest)
+{
+ // Used for both fqnType and fqnSym unittests
+ private struct QualifiedNameTests
+ {
+ struct Inner
+ {
+ }
+
+ ref const(Inner[string]) func( ref Inner var1, lazy scope string var2 );
+ ref const(Inner[string]) retfunc( return ref Inner var1 );
+ Inner inoutFunc(inout Inner) inout;
+ shared(const(Inner[string])[]) data;
+ const Inner delegate(double, string) @safe nothrow deleg;
+ inout(int) delegate(inout int) inout inoutDeleg;
+ Inner function(out double, string) funcPtr;
+ extern(C) Inner function(double, string) cFuncPtr;
+
+ extern(C) void cVarArg(int, ...);
+ void dVarArg(...);
+ void dVarArg2(int, ...);
+ void typesafeVarArg(int[] ...);
+
+ Inner[] array;
+ Inner[16] sarray;
+ Inner[Inner] aarray;
+ const(Inner[const(Inner)]) qualAarray;
+
+ shared(immutable(Inner) delegate(ref double, scope string) const shared @trusted nothrow) attrDeleg;
+
+ struct Data(T) { int x; }
+ void tfunc(T...)(T args) {}
+
+ template Inst(alias A) { int x; }
+
+ class Test12309(T, int x, string s) {}
+ }
+
+ private enum QualifiedEnum
+ {
+ a = 42
+ }
+}
+
+private template fqnSym(alias T : X!A, alias X, A...)
+{
+ template fqnTuple(T...)
+ {
+ static if (T.length == 0)
+ enum fqnTuple = "";
+ else static if (T.length == 1)
+ {
+ static if (isExpressionTuple!T)
+ enum fqnTuple = T[0].stringof;
+ else
+ enum fqnTuple = fullyQualifiedName!(T[0]);
+ }
+ else
+ enum fqnTuple = fqnTuple!(T[0]) ~ ", " ~ fqnTuple!(T[1 .. $]);
+ }
+
+ enum fqnSym =
+ fqnSym!(__traits(parent, X)) ~
+ '.' ~ __traits(identifier, X) ~ "!(" ~ fqnTuple!A ~ ")";
+}
+
+private template fqnSym(alias T)
+{
+ static if (__traits(compiles, __traits(parent, T)) && !__traits(isSame, T, __traits(parent, T)))
+ enum parentPrefix = fqnSym!(__traits(parent, T)) ~ ".";
+ else
+ enum parentPrefix = null;
+
+ static string adjustIdent(string s)
+ {
+ import std.algorithm.searching : findSplit, skipOver;
+
+ if (s.skipOver("package ") || s.skipOver("module "))
+ return s;
+ return s.findSplit("(")[0];
+ }
+ enum fqnSym = parentPrefix ~ adjustIdent(__traits(identifier, T));
+}
+
+@safe unittest
+{
+ alias fqn = fullyQualifiedName;
+
+ // Make sure those 2 are the same
+ static assert(fqnSym!fqn == fqn!fqn);
+
+ static assert(fqn!fqn == "std.traits.fullyQualifiedName");
+
+ alias qnTests = QualifiedNameTests;
+ enum prefix = "std.traits.QualifiedNameTests.";
+ static assert(fqn!(qnTests.Inner) == prefix ~ "Inner");
+ static assert(fqn!(qnTests.func) == prefix ~ "func");
+ static assert(fqn!(qnTests.Data!int) == prefix ~ "Data!(int)");
+ static assert(fqn!(qnTests.Data!int.x) == prefix ~ "Data!(int).x");
+ static assert(fqn!(qnTests.tfunc!(int[])) == prefix ~ "tfunc!(int[])");
+ static assert(fqn!(qnTests.Inst!(Object)) == prefix ~ "Inst!(object.Object)");
+ static assert(fqn!(qnTests.Inst!(Object).x) == prefix ~ "Inst!(object.Object).x");
+
+ static assert(fqn!(qnTests.Test12309!(int, 10, "str"))
+ == prefix ~ "Test12309!(int, 10, \"str\")");
+
+ import core.sync.barrier;
+ static assert(fqn!Barrier == "core.sync.barrier.Barrier");
+}
+
+@safe unittest
+{
+ struct TemplatedStruct()
+ {
+ enum foo = 0;
+ }
+ alias TemplatedStructAlias = TemplatedStruct;
+ assert("TemplatedStruct.foo" == fullyQualifiedName!(TemplatedStructAlias!().foo));
+}
+
+private template fqnType(T,
+ bool alreadyConst, bool alreadyImmutable, bool alreadyShared, bool alreadyInout)
+{
+ import std.format : format;
+
+ // Convenience tags
+ enum {
+ _const = 0,
+ _immutable = 1,
+ _shared = 2,
+ _inout = 3
+ }
+
+ alias qualifiers = AliasSeq!(is(T == const), is(T == immutable), is(T == shared), is(T == inout));
+ alias noQualifiers = AliasSeq!(false, false, false, false);
+
+ string storageClassesString(uint psc)() @property
+ {
+ alias PSC = ParameterStorageClass;
+
+ return format("%s%s%s%s%s",
+ psc & PSC.scope_ ? "scope " : "",
+ psc & PSC.return_ ? "return " : "",
+ psc & PSC.out_ ? "out " : "",
+ psc & PSC.ref_ ? "ref " : "",
+ psc & PSC.lazy_ ? "lazy " : ""
+ );
+ }
+
+ string parametersTypeString(T)() @property
+ {
+ alias parameters = Parameters!(T);
+ alias parameterStC = ParameterStorageClassTuple!(T);
+
+ enum variadic = variadicFunctionStyle!T;
+ static if (variadic == Variadic.no)
+ enum variadicStr = "";
+ else static if (variadic == Variadic.c)
+ enum variadicStr = ", ...";
+ else static if (variadic == Variadic.d)
+ enum variadicStr = parameters.length ? ", ..." : "...";
+ else static if (variadic == Variadic.typesafe)
+ enum variadicStr = " ...";
+ else
+ static assert(0, "New variadic style has been added, please update fullyQualifiedName implementation");
+
+ static if (parameters.length)
+ {
+ 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]))(
+ zip([staticMap!(storageClassesString, parameterStC)],
+ [staticMap!(fullyQualifiedName, parameters)])
+ ),
+ ", "
+ );
+
+ return result ~= variadicStr;
+ }
+ else
+ return variadicStr;
+ }
+
+ string linkageString(T)() @property
+ {
+ enum linkage = functionLinkage!T;
+
+ if (linkage != "D")
+ return format("extern(%s) ", linkage);
+ else
+ return "";
+ }
+
+ string functionAttributeString(T)() @property
+ {
+ alias FA = FunctionAttribute;
+ enum attrs = functionAttributes!T;
+
+ static if (attrs == FA.none)
+ return "";
+ else
+ return format("%s%s%s%s%s%s%s%s",
+ attrs & FA.pure_ ? " pure" : "",
+ attrs & FA.nothrow_ ? " nothrow" : "",
+ attrs & FA.ref_ ? " ref" : "",
+ attrs & FA.property ? " @property" : "",
+ attrs & FA.trusted ? " @trusted" : "",
+ attrs & FA.safe ? " @safe" : "",
+ attrs & FA.nogc ? " @nogc" : "",
+ attrs & FA.return_ ? " return" : ""
+ );
+ }
+
+ string addQualifiers(string typeString,
+ bool addConst, bool addImmutable, bool addShared, bool addInout)
+ {
+ auto result = typeString;
+ if (addShared)
+ {
+ result = format("shared(%s)", result);
+ }
+ if (addConst || addImmutable || addInout)
+ {
+ result = format("%s(%s)",
+ addConst ? "const" :
+ addImmutable ? "immutable" : "inout",
+ result
+ );
+ }
+ return result;
+ }
+
+ // Convenience template to avoid copy-paste
+ template chain(string current)
+ {
+ enum chain = addQualifiers(current,
+ qualifiers[_const] && !alreadyConst,
+ qualifiers[_immutable] && !alreadyImmutable,
+ qualifiers[_shared] && !alreadyShared,
+ qualifiers[_inout] && !alreadyInout);
+ }
+
+ static if (is(T == string))
+ {
+ enum fqnType = "string";
+ }
+ else static if (is(T == wstring))
+ {
+ enum fqnType = "wstring";
+ }
+ else static if (is(T == dstring))
+ {
+ enum fqnType = "dstring";
+ }
+ else static if (isBasicType!T && !is(T == enum))
+ {
+ enum fqnType = chain!((Unqual!T).stringof);
+ }
+ else static if (isAggregateType!T || is(T == enum))
+ {
+ enum fqnType = chain!(fqnSym!T);
+ }
+ else static if (isStaticArray!T)
+ {
+ enum fqnType = chain!(
+ format("%s[%s]", fqnType!(typeof(T.init[0]), qualifiers), T.length)
+ );
+ }
+ else static if (isArray!T)
+ {
+ enum fqnType = chain!(
+ format("%s[]", fqnType!(typeof(T.init[0]), qualifiers))
+ );
+ }
+ else static if (isAssociativeArray!T)
+ {
+ enum fqnType = chain!(
+ format("%s[%s]", fqnType!(ValueType!T, qualifiers), fqnType!(KeyType!T, noQualifiers))
+ );
+ }
+ else static if (isSomeFunction!T)
+ {
+ static if (is(T F == delegate))
+ {
+ enum qualifierString = format("%s%s",
+ is(F == shared) ? " shared" : "",
+ is(F == inout) ? " inout" :
+ is(F == immutable) ? " immutable" :
+ is(F == const) ? " const" : ""
+ );
+ enum formatStr = "%s%s delegate(%s)%s%s";
+ enum fqnType = chain!(
+ format(formatStr, linkageString!T, fqnType!(ReturnType!T, noQualifiers),
+ parametersTypeString!(T), functionAttributeString!T, qualifierString)
+ );
+ }
+ else
+ {
+ static if (isFunctionPointer!T)
+ enum formatStr = "%s%s function(%s)%s";
+ else
+ enum formatStr = "%s%s(%s)%s";
+
+ enum fqnType = chain!(
+ format(formatStr, linkageString!T, fqnType!(ReturnType!T, noQualifiers),
+ parametersTypeString!(T), functionAttributeString!T)
+ );
+ }
+ }
+ else static if (isPointer!T)
+ {
+ enum fqnType = chain!(
+ format("%s*", fqnType!(PointerTarget!T, qualifiers))
+ );
+ }
+ else static if (is(T : __vector(V[N]), V, size_t N))
+ {
+ 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");
+}
+
+@safe unittest
+{
+ import std.format : format;
+ alias fqn = fullyQualifiedName;
+
+ // Verify those 2 are the same for simple case
+ alias Ambiguous = const(QualifiedNameTests.Inner);
+ static assert(fqn!Ambiguous == fqnType!(Ambiguous, false, false, false, false));
+
+ // Main tests
+ enum inner_name = "std.traits.QualifiedNameTests.Inner";
+ with (QualifiedNameTests)
+ {
+ // Special cases
+ 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);
+ static assert(fqn!(QualifiedEnum) == "std.traits.QualifiedEnum"); // type
+ static assert(fqn!(QualifiedEnum.a) == "std.traits.QualifiedEnum.a"); // symbol
+
+ // Array types
+ static assert(fqn!(typeof(array)) == format("%s[]", inner_name));
+ static assert(fqn!(typeof(sarray)) == format("%s[16]", inner_name));
+ static assert(fqn!(typeof(aarray)) == format("%s[%s]", inner_name, inner_name));
+
+ // qualified key for AA
+ static assert(fqn!(typeof(qualAarray)) == format("const(%s[const(%s)])", inner_name, inner_name));
+
+ // Qualified composed data types
+ static assert(fqn!(typeof(data)) == format("shared(const(%s[string])[])", inner_name));
+
+ // Function types + function attributes
+ static assert(fqn!(typeof(func)) == format("const(%s[string])(ref %s, scope lazy string) ref",
+ inner_name, inner_name));
+ static assert(fqn!(typeof(retfunc)) == format("const(%s[string])(return %s) ref", inner_name, inner_name));
+ static assert(fqn!(typeof(inoutFunc)) == format("inout(%s(inout(%s)))", inner_name, inner_name));
+ static assert(fqn!(typeof(deleg)) == format("const(%s delegate(double, string) nothrow @safe)", inner_name));
+ static assert(fqn!(typeof(inoutDeleg)) == "inout(int) delegate(inout(int)) inout");
+ static assert(fqn!(typeof(funcPtr)) == format("%s function(out double, string)", inner_name));
+ static assert(fqn!(typeof(cFuncPtr)) == format("extern(C) %s function(double, string)", inner_name));
+
+ // Delegate type with qualified function type
+ static assert(fqn!(typeof(attrDeleg)) == format("shared(immutable(%s) "~
+ "delegate(ref double, scope string) nothrow @trusted shared const)", inner_name));
+
+ // Variable argument function types
+ static assert(fqn!(typeof(cVarArg)) == "extern(C) void(int, ...)");
+ static assert(fqn!(typeof(dVarArg)) == "void(...)");
+ static assert(fqn!(typeof(dVarArg2)) == "void(int, ...)");
+ static assert(fqn!(typeof(typesafeVarArg)) == "void(int[] ...)");
+
+ // SIMD vector
+ static if (is(__vector(float[4])))
+ {
+ static assert(fqn!(__vector(float[4])) == "__vector(float[4])");
+ }
+ }
+}
+
+/***
+ * Get the type of the return value from a function,
+ * a pointer to function, a delegate, a struct
+ * with an opCall, a pointer to a struct with an opCall,
+ * or a class with an $(D opCall). Please note that $(D_KEYWORD ref)
+ * is not part of a type, but the attribute of the function
+ * (see template $(LREF functionAttributes)).
+ */
+template ReturnType(func...)
+ if (func.length == 1 && isCallable!func)
+{
+ static if (is(FunctionTypeOf!func R == return))
+ alias ReturnType = R;
+ else
+ static assert(0, "argument has no return type");
+}
+
+///
+@safe unittest
+{
+ int foo();
+ ReturnType!foo x; // x is declared as int
+}
+
+@safe unittest
+{
+ struct G
+ {
+ int opCall (int i) { return 1;}
+ }
+
+ alias ShouldBeInt = ReturnType!G;
+ static assert(is(ShouldBeInt == int));
+
+ G g;
+ static assert(is(ReturnType!g == int));
+
+ G* p;
+ alias pg = ReturnType!p;
+ static assert(is(pg == int));
+
+ class C
+ {
+ int opCall (int i) { return 1;}
+ }
+
+ static assert(is(ReturnType!C == int));
+
+ C c;
+ static assert(is(ReturnType!c == int));
+
+ class Test
+ {
+ int prop() @property { return 0; }
+ }
+ alias R_Test_prop = ReturnType!(Test.prop);
+ static assert(is(R_Test_prop == int));
+
+ alias R_dglit = ReturnType!((int a) { return a; });
+ static assert(is(R_dglit == int));
+}
+
+/***
+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 Parameters(func...)
+ if (func.length == 1 && isCallable!func)
+{
+ static if (is(FunctionTypeOf!func P == function))
+ alias Parameters = P;
+ else
+ static assert(0, "argument has no parameters");
+}
+
+///
+@safe unittest
+{
+ int foo(int, long);
+ void bar(Parameters!foo); // declares void bar(int, long);
+ void abc(Parameters!foo[1]); // declares void abc(long);
+}
+
+/**
+ * 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 == AliasSeq!(int, bool)));
+ static assert(is(ParameterTypeTuple!(typeof(&foo)) == AliasSeq!(int, bool)));
+
+ struct S { real opCall(real r, int i) { return 0.0; } }
+ S s;
+ static assert(is(ParameterTypeTuple!S == AliasSeq!(real, int)));
+ static assert(is(ParameterTypeTuple!(S*) == AliasSeq!(real, int)));
+ static assert(is(ParameterTypeTuple!s == AliasSeq!(real, int)));
+
+ class Test
+ {
+ int prop() @property { return 0; }
+ }
+ alias P_Test_prop = ParameterTypeTuple!(Test.prop);
+ static assert(P_Test_prop.length == 0);
+
+ alias P_dglit = ParameterTypeTuple!((int a){});
+ static assert(P_dglit.length == 1);
+ static assert(is(P_dglit[0] == int));
+}
+
+/**
+Returns the number of arguments of function $(D func).
+arity is undefined for variadic functions.
+*/
+template arity(alias func)
+ if ( isCallable!func && variadicFunctionStyle!func == Variadic.no )
+{
+ enum size_t arity = Parameters!func.length;
+}
+
+///
+@safe unittest
+{
+ void foo(){}
+ static assert(arity!foo == 0);
+ void bar(uint){}
+ static assert(arity!bar == 1);
+ void variadicFoo(uint...){}
+ static assert(!__traits(compiles, arity!variadicFoo));
+}
+
+/**
+Get tuple, one per function parameter, of the storage classes of the parameters.
+Params:
+ func = function symbol or type of function, delegate, or pointer to function
+Returns:
+ A tuple of ParameterStorageClass bits
+ */
+enum ParameterStorageClass : uint
+{
+ /**
+ * These flags can be bitwise OR-ed together to represent complex storage
+ * class.
+ */
+ none = 0,
+ scope_ = 1, /// ditto
+ out_ = 2, /// ditto
+ ref_ = 4, /// ditto
+ lazy_ = 8, /// ditto
+ return_ = 0x10, /// ditto
+}
+
+/// ditto
+template ParameterStorageClassTuple(func...)
+ if (func.length == 1 && isCallable!func)
+{
+ alias Func = FunctionTypeOf!func;
+
+ static if (is(Func PT == __parameters))
+ {
+ template StorageClass(size_t i)
+ {
+ static if (i < PT.length)
+ {
+ alias StorageClass = AliasSeq!(
+ extractParameterStorageClassFlags!(__traits(getParameterStorageClasses, Func, i)),
+ StorageClass!(i + 1));
+ }
+ else
+ alias StorageClass = AliasSeq!();
+ }
+ alias ParameterStorageClassTuple = StorageClass!0;
+ }
+ else
+ {
+ static assert(0, func[0].stringof ~ " is not a function");
+ alias ParameterStorageClassTuple = AliasSeq!();
+ }
+}
+
+///
+@safe unittest
+{
+ alias STC = ParameterStorageClass; // shorten the enum name
+
+ void func(ref int ctx, out real result, real param)
+ {
+ }
+ alias pstc = ParameterStorageClassTuple!func;
+ static assert(pstc.length == 3); // three parameters
+ static assert(pstc[0] == STC.ref_);
+ static assert(pstc[1] == STC.out_);
+ static assert(pstc[2] == STC.none);
+}
+
+/*****************
+ * Convert string tuple Attribs to ParameterStorageClass bits
+ * Params:
+ * Attribs = string tuple
+ * Returns:
+ * ParameterStorageClass bits
+ */
+template extractParameterStorageClassFlags(Attribs...)
+{
+ enum ParameterStorageClass extractParameterStorageClassFlags = ()
+ {
+ auto result = ParameterStorageClass.none;
+ static if (Attribs.length > 0)
+ {
+ foreach (attrib; [Attribs])
+ {
+ final switch (attrib) with (ParameterStorageClass)
+ {
+ case "scope": result |= scope_; break;
+ case "out": result |= out_; break;
+ case "ref": result |= ref_; break;
+ case "lazy": result |= lazy_; break;
+ case "return": result |= return_; break;
+ }
+ }
+ /* Mimic behavor of original version of ParameterStorageClassTuple()
+ * to avoid breaking existing code.
+ */
+ if (result == (ParameterStorageClass.ref_ | ParameterStorageClass.return_))
+ result = ParameterStorageClass.return_;
+ }
+ return result;
+ }();
+}
+
+@safe unittest
+{
+ alias STC = ParameterStorageClass;
+
+ void noparam() {}
+ static assert(ParameterStorageClassTuple!noparam.length == 0);
+
+ 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_);
+ static assert(test_pstc[1] == STC.ref_);
+ static assert(test_pstc[2] == STC.out_);
+ static assert(test_pstc[3] == STC.lazy_);
+ static assert(test_pstc[4] == STC.none);
+ static assert(test_pstc[5] == STC.return_);
+
+ interface Test
+ {
+ void test_const(int) const;
+ void test_sharedconst(int) shared const;
+ }
+ Test testi;
+
+ alias test_const_pstc = ParameterStorageClassTuple!(Test.test_const);
+ static assert(test_const_pstc.length == 1);
+ static assert(test_const_pstc[0] == STC.none);
+
+ alias test_sharedconst_pstc = ParameterStorageClassTuple!(testi.test_sharedconst);
+ static assert(test_sharedconst_pstc.length == 1);
+ static assert(test_sharedconst_pstc[0] == STC.none);
+
+ alias dglit_pstc = ParameterStorageClassTuple!((ref int a) {});
+ static assert(dglit_pstc.length == 1);
+ static assert(dglit_pstc[0] == STC.ref_);
+
+ // Bugzilla 9317
+ static inout(int) func(inout int param) { return param; }
+ static assert(ParameterStorageClassTuple!(typeof(func))[0] == STC.none);
+}
+
+@safe unittest
+{
+ // Bugzilla 14253
+ static struct Foo {
+ ref Foo opAssign(ref Foo rhs) return { return this; }
+ }
+
+ alias tup = ParameterStorageClassTuple!(__traits(getOverloads, Foo, "opAssign")[0]);
+}
+
+
+/**
+Get, as a tuple, the identifiers of the parameters to a function symbol.
+ */
+template ParameterIdentifierTuple(func...)
+ if (func.length == 1 && isCallable!func)
+{
+ static if (is(FunctionTypeOf!func PT == __parameters))
+ {
+ template Get(size_t i)
+ {
+ static if (!isFunctionPointer!func && !isDelegate!func
+ // Unnamed parameters yield CT error.
+ && is(typeof(__traits(identifier, PT[i .. i+1]))))
+ {
+ enum Get = __traits(identifier, PT[i .. i+1]);
+ }
+ else
+ {
+ enum Get = "";
+ }
+ }
+ }
+ else
+ {
+ static assert(0, func[0].stringof ~ "is not a function");
+
+ // Define dummy entities to avoid pointless errors
+ template Get(size_t i) { enum Get = ""; }
+ alias PT = AliasSeq!();
+ }
+
+ template Impl(size_t i = 0)
+ {
+ static if (i == PT.length)
+ alias Impl = AliasSeq!();
+ else
+ alias Impl = AliasSeq!(Get!i, Impl!(i+1));
+ }
+
+ alias ParameterIdentifierTuple = Impl!();
+}
+
+///
+@safe unittest
+{
+ int foo(int num, string name, int);
+ static assert([ParameterIdentifierTuple!foo] == ["num", "name", ""]);
+}
+
+@safe unittest
+{
+ alias PIT = ParameterIdentifierTuple;
+
+ void bar(int num, string name, int[] array){}
+ static assert([PIT!bar] == ["num", "name", "array"]);
+
+ // might be changed in the future?
+ void function(int num, string name) fp;
+ static assert([PIT!fp] == ["", ""]);
+
+ // might be changed in the future?
+ void delegate(int num, string name, int[long] aa) dg;
+ static assert([PIT!dg] == ["", "", ""]);
+
+ interface Test
+ {
+ @property string getter();
+ @property void setter(int a);
+ Test method(int a, long b, string c);
+ }
+ static assert([PIT!(Test.getter)] == []);
+ static assert([PIT!(Test.setter)] == ["a"]);
+ static assert([PIT!(Test.method)] == ["a", "b", "c"]);
+
+/+
+ // depends on internal
+ void baw(int, string, int[]){}
+ static assert([PIT!baw] == ["_param_0", "_param_1", "_param_2"]);
+
+ // depends on internal
+ void baz(AliasSeq!(int, string, int[]) args){}
+ static assert([PIT!baz] == ["_param_0", "_param_1", "_param_2"]);
++/
+}
+
+
+/**
+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 ParameterDefaults(func...)
+ if (func.length == 1 && isCallable!func)
+{
+ alias param_names = ParameterIdentifierTuple!func;
+ static if (is(FunctionTypeOf!(func[0]) PT == __parameters))
+ {
+ template Get(size_t i)
+ {
+ // `PT[i .. i+1]` declares a parameter with an arbitrary name.
+ // To avoid a name clash, generate local names that are distinct
+ // from the parameter name, and mix them in.
+ enum name = param_names[i];
+ enum args = "args" ~ (name == "args" ? "_" : "");
+ enum val = "val" ~ (name == "val" ? "_" : "");
+ enum ptr = "ptr" ~ (name == "ptr" ? "_" : "");
+ mixin("
+ // 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 the parameter is lazy, we force it to be evaluated
+ // like this.
+ auto " ~ val ~ " = " ~ args ~ "[0];
+ auto " ~ ptr ~ " = &" ~ val ~ ";
+ // workaround Bugzilla 16582
+ return *" ~ ptr ~ ";
+ };
+ ");
+ static if (is(typeof(get())))
+ enum Get = get();
+ else
+ alias Get = void;
+ // If default arg doesn't exist, returns void instead.
+ }
+ }
+ else
+ {
+ static assert(0, func[0].stringof ~ "is not a function");
+
+ // Define dummy entities to avoid pointless errors
+ template Get(size_t i) { enum Get = ""; }
+ alias PT = AliasSeq!();
+ }
+
+ template Impl(size_t i = 0)
+ {
+ static if (i == PT.length)
+ alias Impl = AliasSeq!();
+ else
+ alias Impl = AliasSeq!(Get!i, Impl!(i+1));
+ }
+
+ alias ParameterDefaults = Impl!();
+}
+
+///
+@safe unittest
+{
+ 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);
+}
+
+@safe unittest // issue 17192
+{
+ static void func(int i, int PT, int __pd_value, int __pd_val, int __args,
+ int name, int args, int val, int ptr, int args_, int val_, int ptr_)
+ {
+ }
+ alias Voids = ParameterDefaults!func;
+ static assert(Voids.length == 12);
+ foreach (V; Voids) static assert(is(V == void));
+}
+
+/**
+ * Alternate name for $(LREF ParameterDefaults), kept for legacy compatibility.
+ */
+alias ParameterDefaultValueTuple = ParameterDefaults;
+
+@safe unittest
+{
+ alias PDVT = ParameterDefaultValueTuple;
+
+ void bar(int n = 1, string s = "hello"){}
+ static assert(PDVT!bar.length == 2);
+ static assert(PDVT!bar[0] == 1);
+ static assert(PDVT!bar[1] == "hello");
+ static assert(is(typeof(PDVT!bar) == typeof(AliasSeq!(1, "hello"))));
+
+ void baz(int x, int n = 1, string s = "hello"){}
+ static assert(PDVT!baz.length == 3);
+ static assert(is(PDVT!baz[0] == void));
+ static assert( PDVT!baz[1] == 1);
+ static assert( PDVT!baz[2] == "hello");
+ static assert(is(typeof(PDVT!baz) == typeof(AliasSeq!(void, 1, "hello"))));
+
+ // bug 10800 - property functions return empty string
+ @property void foo(int x = 3) { }
+ static assert(PDVT!foo.length == 1);
+ static assert(PDVT!foo[0] == 3);
+ static assert(is(typeof(PDVT!foo) == typeof(AliasSeq!(3))));
+
+ struct Colour
+ {
+ ubyte a,r,g,b;
+
+ static immutable Colour white = Colour(255,255,255,255);
+ }
+ 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 FunctionAttribute mask for function $(D func).
+
+See_Also:
+ $(LREF hasFunctionAttributes)
+ */
+enum FunctionAttribute : uint
+{
+ /**
+ * These flags can be bitwise OR-ed together to represent a complex attribute.
+ */
+ none = 0,
+ pure_ = 1 << 0, /// ditto
+ nothrow_ = 1 << 1, /// ditto
+ ref_ = 1 << 2, /// ditto
+ property = 1 << 3, /// ditto
+ trusted = 1 << 4, /// ditto
+ safe = 1 << 5, /// ditto
+ nogc = 1 << 6, /// ditto
+ system = 1 << 7, /// ditto
+ const_ = 1 << 8, /// ditto
+ immutable_ = 1 << 9, /// ditto
+ inout_ = 1 << 10, /// ditto
+ shared_ = 1 << 11, /// ditto
+ return_ = 1 << 12, /// ditto
+ scope_ = 1 << 13, /// ditto
+}
+
+/// ditto
+template functionAttributes(func...)
+ if (func.length == 1 && isCallable!func)
+{
+ // @bug: workaround for opCall
+ alias FuncSym = Select!(is(typeof(__traits(getFunctionAttributes, func))),
+ func, Unqual!(FunctionTypeOf!func));
+
+ enum FunctionAttribute functionAttributes =
+ extractAttribFlags!(__traits(getFunctionAttributes, FuncSym))();
+}
+
+///
+@safe unittest
+{
+ import std.traits : functionAttributes, FunctionAttribute;
+
+ alias FA = FunctionAttribute; // shorten the enum name
+
+ real func(real x) pure nothrow @safe
+ {
+ return x;
+ }
+ static assert(functionAttributes!func & FA.pure_);
+ static assert(functionAttributes!func & FA.safe);
+ static assert(!(functionAttributes!func & FA.trusted)); // not @trusted
+}
+
+@system unittest
+{
+ alias FA = FunctionAttribute;
+
+ struct S
+ {
+ int noF() { return 0; }
+ int constF() const { return 0; }
+ int immutableF() immutable { return 0; }
+ int inoutF() inout { return 0; }
+ int sharedF() shared { return 0; }
+
+ int x;
+ ref int refF() return { return x; }
+ int propertyF() @property { return 0; }
+ int nothrowF() nothrow { return 0; }
+ int nogcF() @nogc { return 0; }
+
+ int systemF() @system { return 0; }
+ int trustedF() @trusted { return 0; }
+ int safeF() @safe { return 0; }
+
+ int pureF() pure { return 0; }
+ }
+
+ static assert(functionAttributes!(S.noF) == FA.system);
+ static assert(functionAttributes!(typeof(S.noF)) == FA.system);
+
+ static assert(functionAttributes!(S.constF) == (FA.const_ | FA.system));
+ static assert(functionAttributes!(typeof(S.constF)) == (FA.const_ | FA.system));
+
+ static assert(functionAttributes!(S.immutableF) == (FA.immutable_ | FA.system));
+ static assert(functionAttributes!(typeof(S.immutableF)) == (FA.immutable_ | FA.system));
+
+ static assert(functionAttributes!(S.inoutF) == (FA.inout_ | FA.system));
+ static assert(functionAttributes!(typeof(S.inoutF)) == (FA.inout_ | FA.system));
+
+ 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 | 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));
+
+ static assert(functionAttributes!(S.nothrowF) == (FA.nothrow_ | FA.system));
+ static assert(functionAttributes!(typeof(S.nothrowF)) == (FA.nothrow_ | FA.system));
+
+ static assert(functionAttributes!(S.nogcF) == (FA.nogc | FA.system));
+ static assert(functionAttributes!(typeof(S.nogcF)) == (FA.nogc | FA.system));
+
+ static assert(functionAttributes!(S.systemF) == FA.system);
+ static assert(functionAttributes!(typeof(S.systemF)) == FA.system);
+
+ static assert(functionAttributes!(S.trustedF) == FA.trusted);
+ static assert(functionAttributes!(typeof(S.trustedF)) == FA.trusted);
+
+ static assert(functionAttributes!(S.safeF) == FA.safe);
+ static assert(functionAttributes!(typeof(S.safeF)) == FA.safe);
+
+ static assert(functionAttributes!(S.pureF) == (FA.pure_ | FA.system));
+ static assert(functionAttributes!(typeof(S.pureF)) == (FA.pure_ | FA.system));
+
+ 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));
+
+ static assert(functionAttributes!(safe_nothrow) == (FA.safe | FA.nothrow_));
+ static assert(functionAttributes!(typeof(safe_nothrow)) == (FA.safe | FA.nothrow_));
+
+ static assert(functionAttributes!(static_ref_property) == (FA.property | FA.ref_ | FA.system));
+ static assert(functionAttributes!(typeof(&static_ref_property)) == (FA.property | FA.ref_ | FA.system));
+
+ static assert(functionAttributes!(ref_property) == (FA.property | FA.ref_ | FA.system));
+ static assert(functionAttributes!(typeof(&ref_property)) == (FA.property | FA.ref_ | FA.system));
+
+ struct S2
+ {
+ int pure_const() const pure { return 0; }
+ int pure_sharedconst() const shared pure { return 0; }
+ }
+
+ static assert(functionAttributes!(S2.pure_const) == (FA.const_ | FA.pure_ | FA.system));
+ static assert(functionAttributes!(typeof(S2.pure_const)) == (FA.const_ | FA.pure_ | FA.system));
+
+ static assert(functionAttributes!(S2.pure_sharedconst) == (FA.const_ | FA.shared_ | FA.pure_ | FA.system));
+ static assert(functionAttributes!(typeof(S2.pure_sharedconst)) == (FA.const_ | FA.shared_ | FA.pure_ | FA.system));
+
+ static assert(functionAttributes!((int a) { }) == (FA.pure_ | FA.nothrow_ | FA.nogc | FA.safe));
+ static assert(functionAttributes!(typeof((int a) { })) == (FA.pure_ | FA.nothrow_ | FA.nogc | FA.safe));
+
+ auto safeDel = delegate() @safe { };
+ static assert(functionAttributes!(safeDel) == (FA.pure_ | FA.nothrow_ | FA.nogc | FA.safe));
+ static assert(functionAttributes!(typeof(safeDel)) == (FA.pure_ | FA.nothrow_ | FA.nogc | FA.safe));
+
+ auto trustedDel = delegate() @trusted { };
+ static assert(functionAttributes!(trustedDel) == (FA.pure_ | FA.nothrow_ | FA.nogc | FA.trusted));
+ static assert(functionAttributes!(typeof(trustedDel)) == (FA.pure_ | FA.nothrow_ | FA.nogc | FA.trusted));
+
+ auto systemDel = delegate() @system { };
+ static assert(functionAttributes!(systemDel) == (FA.pure_ | FA.nothrow_ | FA.nogc | FA.system));
+ static assert(functionAttributes!(typeof(systemDel)) == (FA.pure_ | FA.nothrow_ | FA.nogc | FA.system));
+}
+
+private FunctionAttribute extractAttribFlags(Attribs...)()
+{
+ auto res = FunctionAttribute.none;
+
+ foreach (attrib; Attribs)
+ {
+ switch (attrib) with (FunctionAttribute)
+ {
+ case "pure": res |= pure_; break;
+ case "nothrow": res |= nothrow_; break;
+ case "ref": res |= ref_; break;
+ case "@property": res |= property; break;
+ case "@trusted": res |= trusted; break;
+ case "@safe": res |= safe; break;
+ case "@nogc": res |= nogc; break;
+ case "@system": res |= system; break;
+ case "const": res |= const_; break;
+ case "immutable": res |= immutable_; break;
+ case "inout": res |= inout_; break;
+ case "shared": res |= shared_; break;
+ case "return": res |= return_; break;
+ case "scope": res |= scope_; break;
+ default: assert(0, attrib);
+ }
+ }
+
+ 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;
+ }();
+}
+
+///
+@safe 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"));
+}
+
+@system 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)
+{
+ enum isSafe = (functionAttributes!func & FunctionAttribute.safe) != 0 ||
+ (functionAttributes!func & FunctionAttribute.trusted) != 0;
+}
+
+///
+@safe 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( isSafe!add);
+ static assert( isSafe!sub);
+ static assert(!isSafe!mul);
+}
+
+
+@safe unittest
+{
+ //Member functions
+ interface Set
+ {
+ int systemF() @system;
+ int trustedF() @trusted;
+ int safeF() @safe;
+ }
+ static assert( isSafe!(Set.safeF));
+ static assert( isSafe!(Set.trustedF));
+ static assert(!isSafe!(Set.systemF));
+
+ //Functions
+ @safe static safeFunc() {}
+ @trusted static trustedFunc() {}
+ @system static systemFunc() {}
+
+ static assert( isSafe!safeFunc);
+ static assert( isSafe!trustedFunc);
+ static assert(!isSafe!systemFunc);
+
+ //Delegates
+ auto safeDel = delegate() @safe {};
+ auto trustedDel = delegate() @trusted {};
+ auto systemDel = delegate() @system {};
+
+ static assert( isSafe!safeDel);
+ static assert( isSafe!trustedDel);
+ static assert(!isSafe!systemDel);
+
+ //Lambdas
+ static assert( isSafe!({safeDel();}));
+ static assert( isSafe!({trustedDel();}));
+ static assert(!isSafe!({systemDel();}));
+
+ //Static opCall
+ struct SafeStatic { @safe static SafeStatic opCall() { return SafeStatic.init; } }
+ struct TrustedStatic { @trusted static TrustedStatic opCall() { return TrustedStatic.init; } }
+ struct SystemStatic { @system static SystemStatic opCall() { return SystemStatic.init; } }
+
+ static assert( isSafe!(SafeStatic()));
+ static assert( isSafe!(TrustedStatic()));
+ static assert(!isSafe!(SystemStatic()));
+
+ //Non-static opCall
+ struct Safe { @safe Safe opCall() { return Safe.init; } }
+ struct Trusted { @trusted Trusted opCall() { return Trusted.init; } }
+ struct System { @system System opCall() { return System.init; } }
+
+ static assert( isSafe!(Safe.init()));
+ static assert( isSafe!(Trusted.init()));
+ static assert(!isSafe!(System.init()));
+}
+
+
+/**
+$(D true) if $(D func) is $(D @system).
+*/
+template isUnsafe(alias func)
+{
+ enum isUnsafe = !isSafe!func;
+}
+
+///
+@safe 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(!isUnsafe!add);
+ static assert(!isUnsafe!sub);
+ static assert( isUnsafe!mul);
+}
+
+@safe unittest
+{
+ //Member functions
+ interface Set
+ {
+ int systemF() @system;
+ int trustedF() @trusted;
+ int safeF() @safe;
+ }
+ static assert(!isUnsafe!(Set.safeF));
+ static assert(!isUnsafe!(Set.trustedF));
+ static assert( isUnsafe!(Set.systemF));
+
+ //Functions
+ @safe static safeFunc() {}
+ @trusted static trustedFunc() {}
+ @system static systemFunc() {}
+
+ static assert(!isUnsafe!safeFunc);
+ static assert(!isUnsafe!trustedFunc);
+ static assert( isUnsafe!systemFunc);
+
+ //Delegates
+ auto safeDel = delegate() @safe {};
+ auto trustedDel = delegate() @trusted {};
+ auto systemDel = delegate() @system {};
+
+ static assert(!isUnsafe!safeDel);
+ static assert(!isUnsafe!trustedDel);
+ static assert( isUnsafe!systemDel);
+
+ //Lambdas
+ static assert(!isUnsafe!({safeDel();}));
+ static assert(!isUnsafe!({trustedDel();}));
+ static assert( isUnsafe!({systemDel();}));
+
+ //Static opCall
+ struct SafeStatic { @safe static SafeStatic opCall() { return SafeStatic.init; } }
+ struct TrustedStatic { @trusted static TrustedStatic opCall() { return TrustedStatic.init; } }
+ struct SystemStatic { @system static SystemStatic opCall() { return SystemStatic.init; } }
+
+ static assert(!isUnsafe!(SafeStatic()));
+ static assert(!isUnsafe!(TrustedStatic()));
+ static assert( isUnsafe!(SystemStatic()));
+
+ //Non-static opCall
+ struct Safe { @safe Safe opCall() { return Safe.init; } }
+ struct Trusted { @trusted Trusted opCall() { return Trusted.init; } }
+ struct System { @system System opCall() { return System.init; } }
+
+ static assert(!isUnsafe!(Safe.init()));
+ static assert(!isUnsafe!(Trusted.init()));
+ static assert( isUnsafe!(System.init()));
+}
+
+
+/**
+Determine the linkage attribute of the function.
+Params:
+ func = the function symbol, or the type of a function, delegate, or pointer to function
+Returns:
+ one of the strings "D", "C", "Windows", "Pascal", or "Objective-C"
+*/
+template functionLinkage(func...)
+ if (func.length == 1 && isCallable!func)
+{
+ enum string functionLinkage = __traits(getLinkage, FunctionTypeOf!func);
+}
+
+///
+@safe unittest
+{
+ extern(D) void Dfunc() {}
+ extern(C) void Cfunc() {}
+ static assert(functionLinkage!Dfunc == "D");
+ static assert(functionLinkage!Cfunc == "C");
+
+ string a = functionLinkage!Dfunc;
+ assert(a == "D");
+
+ auto fp = &Cfunc;
+ string b = functionLinkage!fp;
+ assert(b == "C");
+}
+
+@safe unittest
+{
+ interface Test
+ {
+ void const_func() const;
+ void sharedconst_func() shared const;
+ }
+ static assert(functionLinkage!(Test.const_func) == "D");
+ static assert(functionLinkage!(Test.sharedconst_func) == "D");
+
+ static assert(functionLinkage!((int a){}) == "D");
+}
+
+
+/**
+Determines what kind of variadic parameters function has.
+Params:
+ func = function symbol or type of function, delegate, or pointer to function
+Returns:
+ enum Variadic
+ */
+enum Variadic
+{
+ no, /// Function is not variadic.
+ c, /// Function is a _C-style variadic function, which uses
+ /// core.stdc.stdarg
+ /// Function is a _D-style variadic function, which uses
+ d, /// __argptr and __arguments.
+ typesafe, /// Function is a typesafe variadic function.
+}
+
+/// ditto
+template variadicFunctionStyle(func...)
+ if (func.length == 1 && isCallable!func)
+{
+ enum string varargs = __traits(getFunctionVariadicStyle, FunctionTypeOf!func);
+ enum Variadic variadicFunctionStyle =
+ (varargs == "stdarg") ? Variadic.c :
+ (varargs == "argptr") ? Variadic.d :
+ (varargs == "typesafe") ? Variadic.typesafe :
+ (varargs == "none") ? Variadic.no : Variadic.no;
+}
+
+///
+@safe unittest
+{
+ void func() {}
+ static assert(variadicFunctionStyle!func == Variadic.no);
+
+ extern(C) int printf(in char*, ...);
+ static assert(variadicFunctionStyle!printf == Variadic.c);
+}
+
+@safe unittest
+{
+ import core.vararg;
+
+ extern(D) void novar() {}
+ extern(C) void cstyle(int, ...) {}
+ extern(D) void dstyle(...) {}
+ extern(D) void typesafe(int[]...) {}
+
+ static assert(variadicFunctionStyle!novar == Variadic.no);
+ static assert(variadicFunctionStyle!cstyle == Variadic.c);
+ static assert(variadicFunctionStyle!dstyle == Variadic.d);
+ static assert(variadicFunctionStyle!typesafe == Variadic.typesafe);
+
+ static assert(variadicFunctionStyle!((int[] a...) {}) == Variadic.typesafe);
+}
+
+
+/**
+Get the function type from a callable object $(D func).
+
+Using builtin $(D typeof) on a property function yields the types of the
+property value, not of the property function itself. Still,
+$(D FunctionTypeOf) is able to obtain function types of properties.
+
+Note:
+Do not confuse function types with function pointer types; function types are
+usually used for compile-time reflection purposes.
+ */
+template FunctionTypeOf(func...)
+ if (func.length == 1 && isCallable!func)
+{
+ static if (is(typeof(& func[0]) Fsym : Fsym*) && is(Fsym == function) || is(typeof(& func[0]) Fsym == delegate))
+ {
+ alias FunctionTypeOf = Fsym; // HIT: (nested) function symbol
+ }
+ else static if (is(typeof(& func[0].opCall) Fobj == delegate))
+ {
+ alias FunctionTypeOf = Fobj; // HIT: callable object
+ }
+ else static if (is(typeof(& func[0].opCall) Ftyp : Ftyp*) && is(Ftyp == function))
+ {
+ alias FunctionTypeOf = Ftyp; // HIT: callable type
+ }
+ else static if (is(func[0] T) || is(typeof(func[0]) T))
+ {
+ static if (is(T == function))
+ alias FunctionTypeOf = T; // HIT: function
+ else static if (is(T Fptr : Fptr*) && is(Fptr == function))
+ alias FunctionTypeOf = Fptr; // HIT: function pointer
+ else static if (is(T Fdlg == delegate))
+ alias FunctionTypeOf = Fdlg; // HIT: delegate
+ else
+ static assert(0);
+ }
+ else
+ static assert(0);
+}
+
+///
+@safe unittest
+{
+ class C
+ {
+ int value() @property { return 0; }
+ }
+ static assert(is( typeof(C.value) == int ));
+ static assert(is( FunctionTypeOf!(C.value) == function ));
+}
+
+@system unittest
+{
+ 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)) ));
+ static assert(is( typeof(test) == FunctionTypeOf!test ));
+ static assert(is( typeof(test) == FunctionTypeOf!test_fp ));
+ static assert(is( typeof(test) == FunctionTypeOf!test_dg ));
+ alias int GetterType() @property;
+ alias int SetterType(int) @property;
+ static assert(is( FunctionTypeOf!propGet == GetterType ));
+ static assert(is( FunctionTypeOf!propSet == SetterType ));
+
+ interface Prop { int prop() @property; }
+ Prop prop;
+ static assert(is( FunctionTypeOf!(Prop.prop) == GetterType ));
+ static assert(is( FunctionTypeOf!(prop.prop) == GetterType ));
+
+ class Callable { int opCall(int) { return 0; } }
+ auto call = new Callable;
+ static assert(is( FunctionTypeOf!call == typeof(test) ));
+
+ struct StaticCallable { static int opCall(int) { return 0; } }
+ StaticCallable stcall_val;
+ StaticCallable* stcall_ptr;
+ static assert(is( FunctionTypeOf!stcall_val == typeof(test) ));
+ static assert(is( FunctionTypeOf!stcall_ptr == typeof(test) ));
+
+ interface Overloads
+ {
+ void test(string);
+ real test(real);
+ int test(int);
+ int test() @property;
+ }
+ alias ov = AliasSeq!(__traits(getVirtualFunctions, Overloads, "test"));
+ alias F_ov0 = FunctionTypeOf!(ov[0]);
+ alias F_ov1 = FunctionTypeOf!(ov[1]);
+ alias F_ov2 = FunctionTypeOf!(ov[2]);
+ alias F_ov3 = FunctionTypeOf!(ov[3]);
+ static assert(is(F_ov0* == void function(string)));
+ static assert(is(F_ov1* == real function(real)));
+ static assert(is(F_ov2* == int function(int)));
+ static assert(is(F_ov3* == int function() @property));
+
+ alias F_dglit = FunctionTypeOf!((int a){ return a; });
+ static assert(is(F_dglit* : int function(int)));
+}
+
+/**
+ * Constructs a new function or delegate type with the same basic signature
+ * as the given one, but different attributes (including linkage).
+ *
+ * This is especially useful for adding/removing attributes to/from types in
+ * generic code, where the actual type name cannot be spelt out.
+ *
+ * Params:
+ * T = The base type.
+ * linkage = The desired linkage of the result type.
+ * attrs = The desired $(LREF FunctionAttribute)s of the result type.
+ */
+template SetFunctionAttributes(T, string linkage, uint attrs)
+ if (isFunctionPointer!T || isDelegate!T)
+{
+ mixin({
+ import std.algorithm.searching : canFind;
+
+ static assert(!(attrs & FunctionAttribute.trusted) ||
+ !(attrs & FunctionAttribute.safe),
+ "Cannot have a function/delegate that is both trusted and safe.");
+
+ static immutable linkages = ["D", "C", "Windows", "Pascal", "C++", "System"];
+ static assert(canFind(linkages, linkage), "Invalid linkage '" ~
+ linkage ~ "', must be one of " ~ linkages.stringof ~ ".");
+
+ string result = "alias ";
+
+ static if (linkage != "D")
+ result ~= "extern(" ~ linkage ~ ") ";
+
+ static if (attrs & FunctionAttribute.ref_)
+ result ~= "ref ";
+
+ result ~= "ReturnType!T";
+
+ static if (isDelegate!T)
+ result ~= " delegate";
+ else
+ result ~= " function";
+
+ result ~= "(";
+
+ static if (Parameters!T.length > 0)
+ result ~= "Parameters!T";
+
+ enum varStyle = variadicFunctionStyle!T;
+ static if (varStyle == Variadic.c)
+ result ~= ", ...";
+ else static if (varStyle == Variadic.d)
+ result ~= "...";
+ else static if (varStyle == Variadic.typesafe)
+ result ~= "...";
+
+ result ~= ")";
+
+ static if (attrs & FunctionAttribute.pure_)
+ result ~= " pure";
+ static if (attrs & FunctionAttribute.nothrow_)
+ result ~= " nothrow";
+ static if (attrs & FunctionAttribute.property)
+ result ~= " @property";
+ static if (attrs & FunctionAttribute.trusted)
+ result ~= " @trusted";
+ static if (attrs & FunctionAttribute.safe)
+ result ~= " @safe";
+ static if (attrs & FunctionAttribute.nogc)
+ result ~= " @nogc";
+ static if (attrs & FunctionAttribute.system)
+ result ~= " @system";
+ static if (attrs & FunctionAttribute.const_)
+ result ~= " const";
+ static if (attrs & FunctionAttribute.immutable_)
+ result ~= " immutable";
+ static if (attrs & FunctionAttribute.inout_)
+ result ~= " inout";
+ static if (attrs & FunctionAttribute.shared_)
+ result ~= " shared";
+ static if (attrs & FunctionAttribute.return_)
+ result ~= " return";
+
+ result ~= " SetFunctionAttributes;";
+ return result;
+ }());
+}
+
+/// Ditto
+template SetFunctionAttributes(T, string linkage, uint attrs)
+ if (is(T == function))
+{
+ // To avoid a lot of syntactic headaches, we just use the above version to
+ // operate on the corresponding function pointer type and then remove the
+ // indirection again.
+ alias SetFunctionAttributes = FunctionTypeOf!(SetFunctionAttributes!(T*, linkage, attrs));
+}
+
+///
+@safe unittest
+{
+ alias ExternC(T) = SetFunctionAttributes!(T, "C", functionAttributes!T);
+
+ auto assumePure(T)(T t)
+ if (isFunctionPointer!T || isDelegate!T)
+ {
+ enum attrs = functionAttributes!T | FunctionAttribute.pure_;
+ return cast(SetFunctionAttributes!(T, functionLinkage!T, attrs)) t;
+ }
+}
+
+version (unittest)
+{
+ // Some function types to test.
+ int sc(scope int, ref int, out int, lazy int, int);
+ extern(System) int novar();
+ extern(C) int cstyle(int, ...);
+ extern(D) int dstyle(...);
+ extern(D) int typesafe(int[]...);
+}
+@safe unittest
+{
+ import std.algorithm.iteration : reduce;
+
+ alias FA = FunctionAttribute;
+ foreach (BaseT; AliasSeq!(typeof(&sc), typeof(&novar), typeof(&cstyle),
+ typeof(&dstyle), typeof(&typesafe)))
+ {
+ foreach (T; AliasSeq!(BaseT, FunctionTypeOf!BaseT))
+ (){ // avoid slow optimizations for large functions @@@BUG@@@ 2396
+ enum linkage = functionLinkage!T;
+ enum attrs = functionAttributes!T;
+
+ static assert(is(SetFunctionAttributes!(T, linkage, attrs) == T),
+ "Identity check failed for: " ~ T.stringof);
+
+ // Check that all linkage types work (D-style variadics require D linkage).
+ static if (variadicFunctionStyle!T != Variadic.d)
+ {
+ foreach (newLinkage; AliasSeq!("D", "C", "Windows", "Pascal", "C++"))
+ {
+ alias New = SetFunctionAttributes!(T, newLinkage, attrs);
+ static assert(functionLinkage!New == newLinkage,
+ "Linkage test failed for: " ~ T.stringof ~ ", " ~ newLinkage ~
+ " (got " ~ New.stringof ~ ")");
+ }
+ }
+
+ // Add @safe.
+ alias T1 = SetFunctionAttributes!(T, functionLinkage!T, FA.safe);
+ static assert(functionAttributes!T1 == FA.safe);
+
+ // 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.scope_;
+
+ alias T2 = SetFunctionAttributes!(T1, functionLinkage!T, allAttrs);
+ static assert(functionAttributes!T2 == allAttrs);
+
+ // Strip all attributes again.
+ alias T3 = SetFunctionAttributes!(T2, functionLinkage!T, FA.none);
+ static assert(is(T3 == T));
+ }();
+ }
+}
+
+
+//::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::://
+// 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))
+{
+ enum isNested = __traits(isNested, T);
+}
+
+///
+@safe unittest
+{
+ static struct S { }
+ static assert(!isNested!S);
+
+ int i;
+ struct NestedStruct { void f() { ++i; } }
+ static assert(isNested!NestedStruct);
+}
+
+/**
+Determines whether $(D T) or any of its representation types
+have a context pointer.
+*/
+template hasNested(T)
+{
+ import std.meta : anySatisfy, Filter;
+
+ 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))
+ {
+ // prevent infinite recursion for class with member of same type
+ enum notSame(U) = !is(Unqual!T == Unqual!U);
+ enum hasNested = isNested!T ||
+ anySatisfy!(.hasNested, Filter!(notSame, Fields!T));
+ }
+ else
+ enum hasNested = false;
+}
+
+///
+@safe unittest
+{
+ static struct S { }
+
+ int i;
+ struct NS { void f() { ++i; } }
+
+ static assert(!hasNested!(S[2]));
+ static assert(hasNested!(NS[2]));
+}
+
+@safe unittest
+{
+ static assert(!__traits(compiles, isNested!int));
+ static assert(!hasNested!int);
+
+ static struct StaticStruct { }
+ static assert(!isNested!StaticStruct);
+ static assert(!hasNested!StaticStruct);
+
+ int i;
+ struct NestedStruct { void f() { ++i; } }
+ static assert( isNested!NestedStruct);
+ static assert( hasNested!NestedStruct);
+ static assert( isNested!(immutable NestedStruct));
+ static assert( hasNested!(immutable NestedStruct));
+
+ static assert(!__traits(compiles, isNested!(NestedStruct[1])));
+ static assert( hasNested!(NestedStruct[1]));
+ static assert(!hasNested!(NestedStruct[0]));
+
+ struct S1 { NestedStruct nested; }
+ static assert(!isNested!S1);
+ static assert( hasNested!S1);
+
+ static struct S2 { NestedStruct nested; }
+ static assert(!isNested!S2);
+ static assert( hasNested!S2);
+
+ static struct S3 { NestedStruct[0] nested; }
+ static assert(!isNested!S3);
+ static assert(!hasNested!S3);
+
+ static union U { NestedStruct nested; }
+ static assert(!isNested!U);
+ static assert( hasNested!U);
+
+ static class StaticClass { }
+ static assert(!isNested!StaticClass);
+ static assert(!hasNested!StaticClass);
+
+ class NestedClass { void f() { ++i; } }
+ static assert( isNested!NestedClass);
+ static assert( hasNested!NestedClass);
+ static assert( isNested!(immutable NestedClass));
+ static assert( hasNested!(immutable NestedClass));
+
+ static assert(!__traits(compiles, isNested!(NestedClass[1])));
+ static assert( hasNested!(NestedClass[1]));
+ static assert(!hasNested!(NestedClass[0]));
+
+ static class A
+ {
+ A a;
+ }
+ static assert(!hasNested!A);
+}
+
+
+/***
+ * 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 a tuple
+ * with one element $(D T).
+ */
+template Fields(T)
+{
+ static if (is(T == struct) || is(T == union))
+ alias Fields = typeof(T.tupleof[0 .. $ - isNested!T]);
+ else static if (is(T == class))
+ alias Fields = typeof(T.tupleof);
+ else
+ alias Fields = AliasSeq!T;
+}
+
+///
+@safe unittest
+{
+ struct S { int x; float y; }
+ static assert(is(Fields!S == AliasSeq!(int, float)));
+}
+
+/**
+ * Alternate name for $(LREF Fields), kept for legacy compatibility.
+ */
+alias FieldTypeTuple = Fields;
+
+@safe unittest
+{
+ static assert(is(FieldTypeTuple!int == AliasSeq!int));
+
+ static struct StaticStruct1 { }
+ static assert(is(FieldTypeTuple!StaticStruct1 == AliasSeq!()));
+
+ static struct StaticStruct2 { int a, b; }
+ static assert(is(FieldTypeTuple!StaticStruct2 == AliasSeq!(int, int)));
+
+ int i;
+
+ struct NestedStruct1 { void f() { ++i; } }
+ static assert(is(FieldTypeTuple!NestedStruct1 == AliasSeq!()));
+
+ struct NestedStruct2 { int a; void f() { ++i; } }
+ static assert(is(FieldTypeTuple!NestedStruct2 == AliasSeq!int));
+
+ class NestedClass { int a; void f() { ++i; } }
+ static assert(is(FieldTypeTuple!NestedClass == AliasSeq!int));
+}
+
+
+//Required for FieldNameTuple
+private enum NameOf(alias T) = T.stringof;
+
+/**
+ * Get as an expression tuple the names 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 an
+ * expression tuple with an empty string.
+ */
+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))
+ alias FieldNameTuple = staticMap!(NameOf, T.tupleof);
+ else
+ alias FieldNameTuple = AliasSeq!"";
+}
+
+///
+@safe unittest
+{
+ struct S { int x; float y; }
+ static assert(FieldNameTuple!S == AliasSeq!("x", "y"));
+ static assert(FieldNameTuple!int == AliasSeq!"");
+}
+
+@safe unittest
+{
+ static assert(FieldNameTuple!int == AliasSeq!"");
+
+ static struct StaticStruct1 { }
+ static assert(is(FieldNameTuple!StaticStruct1 == AliasSeq!()));
+
+ static struct StaticStruct2 { int a, b; }
+ static assert(FieldNameTuple!StaticStruct2 == AliasSeq!("a", "b"));
+
+ int i;
+
+ struct NestedStruct1 { void f() { ++i; } }
+ static assert(is(FieldNameTuple!NestedStruct1 == AliasSeq!()));
+
+ struct NestedStruct2 { int a; void f() { ++i; } }
+ static assert(FieldNameTuple!NestedStruct2 == AliasSeq!"a");
+
+ class NestedClass { int a; void f() { ++i; } }
+ static assert(FieldNameTuple!NestedClass == AliasSeq!"a");
+}
+
+
+/***
+Get the primitive types of the fields of a struct or class, in
+topological order.
+*/
+template RepresentationTypeTuple(T)
+{
+ template Impl(T...)
+ {
+ static if (T.length == 0)
+ {
+ alias Impl = AliasSeq!();
+ }
+ else
+ {
+ import std.typecons : Rebindable;
+
+ static if (is(T[0] R: Rebindable!R))
+ {
+ alias Impl = Impl!(Impl!R, T[1 .. $]);
+ }
+ else static if (is(T[0] == struct) || is(T[0] == union))
+ {
+ // @@@BUG@@@ this should work
+ //alias .RepresentationTypes!(T[0].tupleof)
+ // RepresentationTypes;
+ alias Impl = Impl!(FieldTypeTuple!(T[0]), T[1 .. $]);
+ }
+ else
+ {
+ alias Impl = AliasSeq!(T[0], Impl!(T[1 .. $]));
+ }
+ }
+ }
+
+ static if (is(T == struct) || is(T == union) || is(T == class))
+ {
+ alias RepresentationTypeTuple = Impl!(FieldTypeTuple!T);
+ }
+ else
+ {
+ alias RepresentationTypeTuple = Impl!T;
+ }
+}
+
+///
+@safe unittest
+{
+ struct S1 { int a; float b; }
+ struct S2 { char[] a; union { S1 b; S1 * c; } }
+ alias R = RepresentationTypeTuple!S2;
+ assert(R.length == 4
+ && is(R[0] == char[]) && is(R[1] == int)
+ && is(R[2] == float) && is(R[3] == S1*));
+}
+
+@safe unittest
+{
+ alias S1 = RepresentationTypeTuple!int;
+ static assert(is(S1 == AliasSeq!int));
+
+ struct S2 { int a; }
+ struct S3 { int a; char b; }
+ struct S4 { S1 a; int b; S3 c; }
+ static assert(is(RepresentationTypeTuple!S2 == AliasSeq!int));
+ static assert(is(RepresentationTypeTuple!S3 == AliasSeq!(int, char)));
+ static assert(is(RepresentationTypeTuple!S4 == AliasSeq!(int, int, int, char)));
+
+ struct S11 { int a; float b; }
+ struct S21 { char[] a; union { S11 b; S11 * c; } }
+ alias R = RepresentationTypeTuple!S21;
+ assert(R.length == 4
+ && is(R[0] == char[]) && is(R[1] == int)
+ && is(R[2] == float) && is(R[3] == S11*));
+
+ class C { int a; float b; }
+ alias R1 = RepresentationTypeTuple!C;
+ static assert(R1.length == 2 && is(R1[0] == int) && is(R1[1] == float));
+
+ /* Issue 6642 */
+ import std.typecons : Rebindable;
+
+ struct S5 { int a; Rebindable!(immutable Object) b; }
+ alias R2 = RepresentationTypeTuple!S5;
+ static assert(R2.length == 2 && is(R2[0] == int) && is(R2[1] == immutable(Object)));
+}
+
+/*
+Statically evaluates to $(D true) if and only if $(D T)'s
+representation contains at least one field of pointer or array type.
+Members of class types are not considered raw pointers. Pointers to
+immutable objects are not considered raw aliasing.
+*/
+private template hasRawAliasing(T...)
+{
+ template Impl(T...)
+ {
+ static if (T.length == 0)
+ {
+ enum Impl = false;
+ }
+ else
+ {
+ static if (is(T[0] foo : U*, U) && !isFunctionPointer!(T[0]))
+ enum has = !is(U == immutable);
+ else static if (is(T[0] foo : U[], U) && !isStaticArray!(T[0]))
+ enum has = !is(U == immutable);
+ else static if (isAssociativeArray!(T[0]))
+ enum has = !is(T[0] == immutable);
+ else
+ enum has = false;
+
+ enum Impl = has || Impl!(T[1 .. $]);
+ }
+ }
+
+ enum hasRawAliasing = Impl!(RepresentationTypeTuple!T);
+}
+
+///
+@safe unittest
+{
+ // simple types
+ static assert(!hasRawAliasing!int);
+ static assert( hasRawAliasing!(char*));
+ // references aren't raw pointers
+ static assert(!hasRawAliasing!Object);
+ // built-in arrays do contain raw pointers
+ static assert( hasRawAliasing!(int[]));
+ // aggregate of simple types
+ struct S1 { int a; double b; }
+ static assert(!hasRawAliasing!S1);
+ // indirect aggregation
+ struct S2 { S1 a; double b; }
+ static assert(!hasRawAliasing!S2);
+}
+
+@safe unittest
+{
+ // struct with a pointer member
+ struct S3 { int a; double * b; }
+ static assert( hasRawAliasing!S3);
+ // struct with an indirect pointer member
+ struct S4 { S3 a; double b; }
+ static assert( hasRawAliasing!S4);
+ struct S5 { int a; Object z; int c; }
+ static assert( hasRawAliasing!S3);
+ static assert( hasRawAliasing!S4);
+ static assert(!hasRawAliasing!S5);
+
+ union S6 { int a; int b; }
+ union S7 { int a; int * b; }
+ static assert(!hasRawAliasing!S6);
+ static assert( hasRawAliasing!S7);
+
+ static assert(!hasRawAliasing!(void delegate()));
+ static assert(!hasRawAliasing!(void delegate() const));
+ static assert(!hasRawAliasing!(void delegate() immutable));
+ static assert(!hasRawAliasing!(void delegate() shared));
+ static assert(!hasRawAliasing!(void delegate() shared const));
+ static assert(!hasRawAliasing!(const(void delegate())));
+ static assert(!hasRawAliasing!(immutable(void delegate())));
+
+ struct S8 { void delegate() a; int b; Object c; }
+ class S12 { typeof(S8.tupleof) a; }
+ class S13 { typeof(S8.tupleof) a; int* b; }
+ static assert(!hasRawAliasing!S8);
+ static assert(!hasRawAliasing!S12);
+ static assert( hasRawAliasing!S13);
+
+ enum S9 { a }
+ static assert(!hasRawAliasing!S9);
+
+ // indirect members
+ struct S10 { S7 a; int b; }
+ struct S11 { S6 a; int b; }
+ static assert( hasRawAliasing!S10);
+ static assert(!hasRawAliasing!S11);
+
+ static assert( hasRawAliasing!(int[string]));
+ static assert(!hasRawAliasing!(immutable(int[string])));
+}
+
+/*
+Statically evaluates to $(D true) if and only if $(D T)'s
+representation contains at least one non-shared field of pointer or
+array type. Members of class types are not considered raw pointers.
+Pointers to immutable objects are not considered raw aliasing.
+*/
+private template hasRawUnsharedAliasing(T...)
+{
+ template Impl(T...)
+ {
+ static if (T.length == 0)
+ {
+ enum Impl = false;
+ }
+ else
+ {
+ static if (is(T[0] foo : U*, U) && !isFunctionPointer!(T[0]))
+ enum has = !is(U == immutable) && !is(U == shared);
+ else static if (is(T[0] foo : U[], U) && !isStaticArray!(T[0]))
+ enum has = !is(U == immutable) && !is(U == shared);
+ else static if (isAssociativeArray!(T[0]))
+ enum has = !is(T[0] == immutable) && !is(T[0] == shared);
+ else
+ enum has = false;
+
+ enum Impl = has || Impl!(T[1 .. $]);
+ }
+ }
+
+ enum hasRawUnsharedAliasing = Impl!(RepresentationTypeTuple!T);
+}
+
+///
+@safe unittest
+{
+ // simple types
+ static assert(!hasRawUnsharedAliasing!int);
+ static assert( hasRawUnsharedAliasing!(char*));
+ static assert(!hasRawUnsharedAliasing!(shared char*));
+ // references aren't raw pointers
+ static assert(!hasRawUnsharedAliasing!Object);
+ // built-in arrays do contain raw pointers
+ static assert( hasRawUnsharedAliasing!(int[]));
+ static assert(!hasRawUnsharedAliasing!(shared int[]));
+ // aggregate of simple types
+ struct S1 { int a; double b; }
+ static assert(!hasRawUnsharedAliasing!S1);
+ // indirect aggregation
+ struct S2 { S1 a; double b; }
+ static assert(!hasRawUnsharedAliasing!S2);
+ // struct with a pointer member
+ struct S3 { int a; double * b; }
+ static assert( hasRawUnsharedAliasing!S3);
+ struct S4 { int a; shared double * b; }
+ static assert(!hasRawUnsharedAliasing!S4);
+}
+
+@safe unittest
+{
+ // struct with a pointer member
+ struct S3 { int a; double * b; }
+ static assert( hasRawUnsharedAliasing!S3);
+ struct S4 { int a; shared double * b; }
+ static assert(!hasRawUnsharedAliasing!S4);
+ // struct with an indirect pointer member
+ struct S5 { S3 a; double b; }
+ static assert( hasRawUnsharedAliasing!S5);
+ struct S6 { S4 a; double b; }
+ static assert(!hasRawUnsharedAliasing!S6);
+ struct S7 { int a; Object z; int c; }
+ static assert( hasRawUnsharedAliasing!S5);
+ static assert(!hasRawUnsharedAliasing!S6);
+ static assert(!hasRawUnsharedAliasing!S7);
+
+ union S8 { int a; int b; }
+ union S9 { int a; int* b; }
+ union S10 { int a; shared int* b; }
+ static assert(!hasRawUnsharedAliasing!S8);
+ static assert( hasRawUnsharedAliasing!S9);
+ static assert(!hasRawUnsharedAliasing!S10);
+
+ static assert(!hasRawUnsharedAliasing!(void delegate()));
+ static assert(!hasRawUnsharedAliasing!(void delegate() const));
+ static assert(!hasRawUnsharedAliasing!(void delegate() immutable));
+ static assert(!hasRawUnsharedAliasing!(void delegate() shared));
+ static assert(!hasRawUnsharedAliasing!(void delegate() shared const));
+ static assert(!hasRawUnsharedAliasing!(const(void delegate())));
+ static assert(!hasRawUnsharedAliasing!(const(void delegate() const)));
+ static assert(!hasRawUnsharedAliasing!(const(void delegate() immutable)));
+ static assert(!hasRawUnsharedAliasing!(const(void delegate() shared)));
+ static assert(!hasRawUnsharedAliasing!(const(void delegate() shared const)));
+ static assert(!hasRawUnsharedAliasing!(immutable(void delegate())));
+ static assert(!hasRawUnsharedAliasing!(immutable(void delegate() const)));
+ static assert(!hasRawUnsharedAliasing!(immutable(void delegate() immutable)));
+ static assert(!hasRawUnsharedAliasing!(immutable(void delegate() shared)));
+ static assert(!hasRawUnsharedAliasing!(immutable(void delegate() shared const)));
+ static assert(!hasRawUnsharedAliasing!(shared(void delegate())));
+ static assert(!hasRawUnsharedAliasing!(shared(void delegate() const)));
+ static assert(!hasRawUnsharedAliasing!(shared(void delegate() immutable)));
+ static assert(!hasRawUnsharedAliasing!(shared(void delegate() shared)));
+ static assert(!hasRawUnsharedAliasing!(shared(void delegate() shared const)));
+ static assert(!hasRawUnsharedAliasing!(shared(const(void delegate()))));
+ static assert(!hasRawUnsharedAliasing!(shared(const(void delegate() const))));
+ static assert(!hasRawUnsharedAliasing!(shared(const(void delegate() immutable))));
+ static assert(!hasRawUnsharedAliasing!(shared(const(void delegate() shared))));
+ static assert(!hasRawUnsharedAliasing!(shared(const(void delegate() shared const))));
+ static assert(!hasRawUnsharedAliasing!(void function()));
+
+ enum S13 { a }
+ static assert(!hasRawUnsharedAliasing!S13);
+
+ // indirect members
+ struct S14 { S9 a; int b; }
+ struct S15 { S10 a; int b; }
+ struct S16 { S6 a; int b; }
+ static assert( hasRawUnsharedAliasing!S14);
+ static assert(!hasRawUnsharedAliasing!S15);
+ static assert(!hasRawUnsharedAliasing!S16);
+
+ static assert( hasRawUnsharedAliasing!(int[string]));
+ static assert(!hasRawUnsharedAliasing!(shared(int[string])));
+ static assert(!hasRawUnsharedAliasing!(immutable(int[string])));
+
+ struct S17
+ {
+ void delegate() shared a;
+ void delegate() immutable b;
+ void delegate() shared const c;
+ shared(void delegate()) d;
+ shared(void delegate() shared) e;
+ shared(void delegate() immutable) f;
+ shared(void delegate() shared const) g;
+ immutable(void delegate()) h;
+ immutable(void delegate() shared) i;
+ immutable(void delegate() immutable) j;
+ immutable(void delegate() shared const) k;
+ shared(const(void delegate())) l;
+ shared(const(void delegate() shared)) m;
+ shared(const(void delegate() immutable)) n;
+ shared(const(void delegate() shared const)) o;
+ }
+ struct S18 { typeof(S17.tupleof) a; void delegate() p; }
+ struct S19 { typeof(S17.tupleof) a; Object p; }
+ struct S20 { typeof(S17.tupleof) a; int* p; }
+ class S21 { typeof(S17.tupleof) a; }
+ class S22 { typeof(S17.tupleof) a; void delegate() p; }
+ class S23 { typeof(S17.tupleof) a; Object p; }
+ class S24 { typeof(S17.tupleof) a; int* p; }
+ static assert(!hasRawUnsharedAliasing!S17);
+ static assert(!hasRawUnsharedAliasing!(immutable(S17)));
+ static assert(!hasRawUnsharedAliasing!(shared(S17)));
+ static assert(!hasRawUnsharedAliasing!S18);
+ static assert(!hasRawUnsharedAliasing!(immutable(S18)));
+ static assert(!hasRawUnsharedAliasing!(shared(S18)));
+ static assert(!hasRawUnsharedAliasing!S19);
+ static assert(!hasRawUnsharedAliasing!(immutable(S19)));
+ static assert(!hasRawUnsharedAliasing!(shared(S19)));
+ static assert( hasRawUnsharedAliasing!S20);
+ static assert(!hasRawUnsharedAliasing!(immutable(S20)));
+ static assert(!hasRawUnsharedAliasing!(shared(S20)));
+ static assert(!hasRawUnsharedAliasing!S21);
+ static assert(!hasRawUnsharedAliasing!(immutable(S21)));
+ static assert(!hasRawUnsharedAliasing!(shared(S21)));
+ static assert(!hasRawUnsharedAliasing!S22);
+ static assert(!hasRawUnsharedAliasing!(immutable(S22)));
+ static assert(!hasRawUnsharedAliasing!(shared(S22)));
+ static assert(!hasRawUnsharedAliasing!S23);
+ static assert(!hasRawUnsharedAliasing!(immutable(S23)));
+ static assert(!hasRawUnsharedAliasing!(shared(S23)));
+ static assert( hasRawUnsharedAliasing!S24);
+ static assert(!hasRawUnsharedAliasing!(immutable(S24)));
+ static assert(!hasRawUnsharedAliasing!(shared(S24)));
+ struct S25 {}
+ class S26 {}
+ interface S27 {}
+ union S28 {}
+ static assert(!hasRawUnsharedAliasing!S25);
+ static assert(!hasRawUnsharedAliasing!S26);
+ static assert(!hasRawUnsharedAliasing!S27);
+ static assert(!hasRawUnsharedAliasing!S28);
+}
+
+/*
+Statically evaluates to $(D true) if and only if $(D T)'s
+representation includes at least one non-immutable object reference.
+*/
+
+private template hasObjects(T...)
+{
+ static if (T.length == 0)
+ {
+ enum hasObjects = false;
+ }
+ else static if (is(T[0] == struct))
+ {
+ enum hasObjects = hasObjects!(
+ RepresentationTypeTuple!(T[0]), T[1 .. $]);
+ }
+ else
+ {
+ enum hasObjects = ((is(T[0] == class) || is(T[0] == interface))
+ && !is(T[0] == immutable)) || hasObjects!(T[1 .. $]);
+ }
+}
+
+/*
+Statically evaluates to $(D true) if and only if $(D T)'s
+representation includes at least one non-immutable non-shared object
+reference.
+*/
+private template hasUnsharedObjects(T...)
+{
+ static if (T.length == 0)
+ {
+ enum hasUnsharedObjects = false;
+ }
+ else static if (is(T[0] == struct))
+ {
+ enum hasUnsharedObjects = hasUnsharedObjects!(
+ RepresentationTypeTuple!(T[0]), T[1 .. $]);
+ }
+ else
+ {
+ enum hasUnsharedObjects = ((is(T[0] == class) || is(T[0] == interface)) &&
+ !is(T[0] == immutable) && !is(T[0] == shared)) ||
+ hasUnsharedObjects!(T[1 .. $]);
+ }
+}
+
+/**
+Returns $(D true) if and only if $(D T)'s representation includes at
+least one of the following: $(OL $(LI a raw pointer $(D U*) and $(D U)
+is not immutable;) $(LI an array $(D U[]) and $(D U) is not
+immutable;) $(LI a reference to a class or interface type $(D C) and $(D C) is
+not immutable.) $(LI an associative array that is not immutable.)
+$(LI a delegate.))
+*/
+template hasAliasing(T...)
+{
+ import std.meta : anySatisfy;
+ import std.typecons : Rebindable;
+
+ static if (T.length && is(T[0] : Rebindable!R, R))
+ {
+ enum hasAliasing = hasAliasing!(R, T[1 .. $]);
+ }
+ else
+ {
+ template isAliasingDelegate(T)
+ {
+ enum isAliasingDelegate = isDelegate!T
+ && !is(T == immutable)
+ && !is(FunctionTypeOf!T == immutable);
+ }
+ enum hasAliasing = hasRawAliasing!T || hasObjects!T ||
+ anySatisfy!(isAliasingDelegate, T, RepresentationTypeTuple!T);
+ }
+}
+
+///
+@safe unittest
+{
+ struct S1 { int a; Object b; }
+ struct S2 { string a; }
+ struct S3 { int a; immutable Object b; }
+ struct S4 { float[3] vals; }
+ static assert( hasAliasing!S1);
+ static assert(!hasAliasing!S2);
+ static assert(!hasAliasing!S3);
+ static assert(!hasAliasing!S4);
+}
+
+@safe unittest
+{
+ static assert( hasAliasing!(uint[uint]));
+ static assert(!hasAliasing!(immutable(uint[uint])));
+ static assert( hasAliasing!(void delegate()));
+ static assert( hasAliasing!(void delegate() const));
+ static assert(!hasAliasing!(void delegate() immutable));
+ static assert( hasAliasing!(void delegate() shared));
+ static assert( hasAliasing!(void delegate() shared const));
+ static assert( hasAliasing!(const(void delegate())));
+ static assert( hasAliasing!(const(void delegate() const)));
+ static assert(!hasAliasing!(const(void delegate() immutable)));
+ static assert( hasAliasing!(const(void delegate() shared)));
+ static assert( hasAliasing!(const(void delegate() shared const)));
+ static assert(!hasAliasing!(immutable(void delegate())));
+ static assert(!hasAliasing!(immutable(void delegate() const)));
+ static assert(!hasAliasing!(immutable(void delegate() immutable)));
+ static assert(!hasAliasing!(immutable(void delegate() shared)));
+ static assert(!hasAliasing!(immutable(void delegate() shared const)));
+ static assert( hasAliasing!(shared(const(void delegate()))));
+ static assert( hasAliasing!(shared(const(void delegate() const))));
+ static assert(!hasAliasing!(shared(const(void delegate() immutable))));
+ static assert( hasAliasing!(shared(const(void delegate() shared))));
+ static assert( hasAliasing!(shared(const(void delegate() shared const))));
+ static assert(!hasAliasing!(void function()));
+
+ interface I;
+ static assert( hasAliasing!I);
+
+ import std.typecons : Rebindable;
+ static assert( hasAliasing!(Rebindable!(const Object)));
+ static assert(!hasAliasing!(Rebindable!(immutable Object)));
+ static assert( hasAliasing!(Rebindable!(shared Object)));
+ static assert( hasAliasing!(Rebindable!Object));
+
+ struct S5
+ {
+ void delegate() immutable b;
+ shared(void delegate() immutable) f;
+ immutable(void delegate() immutable) j;
+ shared(const(void delegate() immutable)) n;
+ }
+ struct S6 { typeof(S5.tupleof) a; void delegate() p; }
+ static assert(!hasAliasing!S5);
+ static assert( hasAliasing!S6);
+
+ struct S7 { void delegate() a; int b; Object c; }
+ class S8 { int a; int b; }
+ class S9 { typeof(S8.tupleof) a; }
+ class S10 { typeof(S8.tupleof) a; int* b; }
+ static assert( hasAliasing!S7);
+ static assert( hasAliasing!S8);
+ static assert( hasAliasing!S9);
+ static assert( hasAliasing!S10);
+ struct S11 {}
+ class S12 {}
+ interface S13 {}
+ union S14 {}
+ static assert(!hasAliasing!S11);
+ static assert( hasAliasing!S12);
+ static assert( hasAliasing!S13);
+ static assert(!hasAliasing!S14);
+}
+/**
+Returns $(D true) if and only if $(D T)'s representation includes at
+least one of the following: $(OL $(LI a raw pointer $(D U*);) $(LI an
+array $(D U[]);) $(LI a reference to a class type $(D C).)
+$(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))
+ enum hasIndirections = is(E == void) ? true : hasIndirections!E;
+ else static if (isFunctionPointer!T)
+ enum hasIndirections = false;
+ else
+ enum hasIndirections = isPointer!T || isDelegate!T || isDynamicArray!T ||
+ isAssociativeArray!T || is (T == class) || is(T == interface);
+}
+
+///
+@safe unittest
+{
+ static assert( hasIndirections!(int[string]));
+ static assert( hasIndirections!(void delegate()));
+ static assert( hasIndirections!(void delegate() immutable));
+ static assert( hasIndirections!(immutable(void delegate())));
+ static assert( hasIndirections!(immutable(void delegate() immutable)));
+
+ static assert(!hasIndirections!(void function()));
+ static assert( hasIndirections!(void*[1]));
+ static assert(!hasIndirections!(byte[1]));
+}
+
+@safe unittest
+{
+ // void static array hides actual type of bits, so "may have indirections".
+ static assert( hasIndirections!(void[1]));
+ interface I {}
+ struct S1 {}
+ struct S2 { int a; }
+ struct S3 { int a; int b; }
+ struct S4 { int a; int* b; }
+ struct S5 { int a; Object b; }
+ struct S6 { int a; string b; }
+ struct S7 { int a; immutable Object b; }
+ struct S8 { int a; immutable I b; }
+ struct S9 { int a; void delegate() b; }
+ struct S10 { int a; immutable(void delegate()) b; }
+ struct S11 { int a; void delegate() immutable b; }
+ struct S12 { int a; immutable(void delegate() immutable) b; }
+ class S13 {}
+ class S14 { int a; }
+ class S15 { int a; int b; }
+ class S16 { int a; Object b; }
+ class S17 { string a; }
+ class S18 { int a; immutable Object b; }
+ class S19 { int a; immutable(void delegate() immutable) b; }
+ union S20 {}
+ union S21 { int a; }
+ union S22 { int a; int b; }
+ union S23 { int a; Object b; }
+ union S24 { string a; }
+ union S25 { int a; immutable Object b; }
+ union S26 { int a; immutable(void delegate() immutable) b; }
+ static assert( hasIndirections!I);
+ static assert(!hasIndirections!S1);
+ static assert(!hasIndirections!S2);
+ static assert(!hasIndirections!S3);
+ static assert( hasIndirections!S4);
+ static assert( hasIndirections!S5);
+ static assert( hasIndirections!S6);
+ static assert( hasIndirections!S7);
+ static assert( hasIndirections!S8);
+ static assert( hasIndirections!S9);
+ static assert( hasIndirections!S10);
+ static assert( hasIndirections!S12);
+ static assert( hasIndirections!S13);
+ static assert( hasIndirections!S14);
+ static assert( hasIndirections!S15);
+ static assert( hasIndirections!S16);
+ static assert( hasIndirections!S17);
+ static assert( hasIndirections!S18);
+ static assert( hasIndirections!S19);
+ static assert(!hasIndirections!S20);
+ static assert(!hasIndirections!S21);
+ static assert(!hasIndirections!S22);
+ static assert( hasIndirections!S23);
+ static assert( hasIndirections!S24);
+ static assert( hasIndirections!S25);
+ static assert( hasIndirections!S26);
+}
+
+@safe unittest //12000
+{
+ static struct S(T)
+ {
+ static assert(hasIndirections!T);
+ }
+
+ static class A(T)
+ {
+ S!A a;
+ }
+
+ A!int dummy;
+}
+
+/**
+Returns $(D true) if and only if $(D T)'s representation includes at
+least one of the following: $(OL $(LI a raw pointer $(D U*) and $(D U)
+is not immutable or shared;) $(LI an array $(D U[]) and $(D U) is not
+immutable or shared;) $(LI a reference to a class type $(D C) and
+$(D C) is not immutable or shared.) $(LI an associative array that is not
+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)
+ {
+ enum hasUnsharedAliasing = false;
+ }
+ else static if (is(T[0] R: Rebindable!R))
+ {
+ enum hasUnsharedAliasing = hasUnsharedAliasing!R;
+ }
+ else
+ {
+ template unsharedDelegate(T)
+ {
+ enum bool unsharedDelegate = isDelegate!T
+ && !is(T == shared)
+ && !is(T == shared)
+ && !is(T == immutable)
+ && !is(FunctionTypeOf!T == shared)
+ && !is(FunctionTypeOf!T == immutable);
+ }
+
+ enum hasUnsharedAliasing =
+ hasRawUnsharedAliasing!(T[0]) ||
+ anySatisfy!(unsharedDelegate, RepresentationTypeTuple!(T[0])) ||
+ hasUnsharedObjects!(T[0]) ||
+ hasUnsharedAliasing!(T[1..$]);
+ }
+}
+
+///
+@safe unittest
+{
+ struct S1 { int a; Object b; }
+ struct S2 { string a; }
+ struct S3 { int a; immutable Object b; }
+ static assert( hasUnsharedAliasing!S1);
+ static assert(!hasUnsharedAliasing!S2);
+ static assert(!hasUnsharedAliasing!S3);
+
+ struct S4 { int a; shared Object b; }
+ struct S5 { char[] a; }
+ struct S6 { shared char[] b; }
+ struct S7 { float[3] vals; }
+ static assert(!hasUnsharedAliasing!S4);
+ static assert( hasUnsharedAliasing!S5);
+ static assert(!hasUnsharedAliasing!S6);
+ static assert(!hasUnsharedAliasing!S7);
+}
+
+@safe unittest
+{
+ /* Issue 6642 */
+ import std.typecons : Rebindable;
+ struct S8 { int a; Rebindable!(immutable Object) b; }
+ static assert(!hasUnsharedAliasing!S8);
+
+ static assert( hasUnsharedAliasing!(uint[uint]));
+
+ static assert( hasUnsharedAliasing!(void delegate()));
+ static assert( hasUnsharedAliasing!(void delegate() const));
+ static assert(!hasUnsharedAliasing!(void delegate() immutable));
+ static assert(!hasUnsharedAliasing!(void delegate() shared));
+ static assert(!hasUnsharedAliasing!(void delegate() shared const));
+}
+
+@safe unittest
+{
+ import std.typecons : Rebindable;
+ static assert( hasUnsharedAliasing!(const(void delegate())));
+ static assert( hasUnsharedAliasing!(const(void delegate() const)));
+ static assert(!hasUnsharedAliasing!(const(void delegate() immutable)));
+ static assert(!hasUnsharedAliasing!(const(void delegate() shared)));
+ static assert(!hasUnsharedAliasing!(const(void delegate() shared const)));
+ static assert(!hasUnsharedAliasing!(immutable(void delegate())));
+ static assert(!hasUnsharedAliasing!(immutable(void delegate() const)));
+ static assert(!hasUnsharedAliasing!(immutable(void delegate() immutable)));
+ static assert(!hasUnsharedAliasing!(immutable(void delegate() shared)));
+ static assert(!hasUnsharedAliasing!(immutable(void delegate() shared const)));
+ static assert(!hasUnsharedAliasing!(shared(void delegate())));
+ static assert(!hasUnsharedAliasing!(shared(void delegate() const)));
+ static assert(!hasUnsharedAliasing!(shared(void delegate() immutable)));
+ static assert(!hasUnsharedAliasing!(shared(void delegate() shared)));
+ static assert(!hasUnsharedAliasing!(shared(void delegate() shared const)));
+ static assert(!hasUnsharedAliasing!(shared(const(void delegate()))));
+ static assert(!hasUnsharedAliasing!(shared(const(void delegate() const))));
+ static assert(!hasUnsharedAliasing!(shared(const(void delegate() immutable))));
+ static assert(!hasUnsharedAliasing!(shared(const(void delegate() shared))));
+ static assert(!hasUnsharedAliasing!(shared(const(void delegate() shared const))));
+ static assert(!hasUnsharedAliasing!(void function()));
+
+ interface I {}
+ static assert(hasUnsharedAliasing!I);
+
+ static assert( hasUnsharedAliasing!(Rebindable!(const Object)));
+ static assert(!hasUnsharedAliasing!(Rebindable!(immutable Object)));
+ static assert(!hasUnsharedAliasing!(Rebindable!(shared Object)));
+ static assert( hasUnsharedAliasing!(Rebindable!Object));
+
+ /* Issue 6979 */
+ static assert(!hasUnsharedAliasing!(int, shared(int)*));
+ static assert( hasUnsharedAliasing!(int, int*));
+ static assert( hasUnsharedAliasing!(int, const(int)[]));
+ static assert( hasUnsharedAliasing!(int, shared(int)*, Rebindable!Object));
+ static assert(!hasUnsharedAliasing!(shared(int)*, Rebindable!(shared Object)));
+ static assert(!hasUnsharedAliasing!());
+
+ struct S9
+ {
+ void delegate() shared a;
+ void delegate() immutable b;
+ void delegate() shared const c;
+ shared(void delegate()) d;
+ shared(void delegate() shared) e;
+ shared(void delegate() immutable) f;
+ shared(void delegate() shared const) g;
+ immutable(void delegate()) h;
+ immutable(void delegate() shared) i;
+ immutable(void delegate() immutable) j;
+ immutable(void delegate() shared const) k;
+ shared(const(void delegate())) l;
+ shared(const(void delegate() shared)) m;
+ shared(const(void delegate() immutable)) n;
+ shared(const(void delegate() shared const)) o;
+ }
+ struct S10 { typeof(S9.tupleof) a; void delegate() p; }
+ struct S11 { typeof(S9.tupleof) a; Object p; }
+ struct S12 { typeof(S9.tupleof) a; int* p; }
+ class S13 { typeof(S9.tupleof) a; }
+ class S14 { typeof(S9.tupleof) a; void delegate() p; }
+ class S15 { typeof(S9.tupleof) a; Object p; }
+ class S16 { typeof(S9.tupleof) a; int* p; }
+ static assert(!hasUnsharedAliasing!S9);
+ static assert(!hasUnsharedAliasing!(immutable(S9)));
+ static assert(!hasUnsharedAliasing!(shared(S9)));
+ static assert( hasUnsharedAliasing!S10);
+ static assert(!hasUnsharedAliasing!(immutable(S10)));
+ static assert(!hasUnsharedAliasing!(shared(S10)));
+ static assert( hasUnsharedAliasing!S11);
+ static assert(!hasUnsharedAliasing!(immutable(S11)));
+ static assert(!hasUnsharedAliasing!(shared(S11)));
+ static assert( hasUnsharedAliasing!S12);
+ static assert(!hasUnsharedAliasing!(immutable(S12)));
+ static assert(!hasUnsharedAliasing!(shared(S12)));
+ static assert( hasUnsharedAliasing!S13);
+ static assert(!hasUnsharedAliasing!(immutable(S13)));
+ static assert(!hasUnsharedAliasing!(shared(S13)));
+ static assert( hasUnsharedAliasing!S14);
+ static assert(!hasUnsharedAliasing!(immutable(S14)));
+ static assert(!hasUnsharedAliasing!(shared(S14)));
+ static assert( hasUnsharedAliasing!S15);
+ static assert(!hasUnsharedAliasing!(immutable(S15)));
+ static assert(!hasUnsharedAliasing!(shared(S15)));
+ static assert( hasUnsharedAliasing!S16);
+ static assert(!hasUnsharedAliasing!(immutable(S16)));
+ static assert(!hasUnsharedAliasing!(shared(S16)));
+ struct S17 {}
+ class S18 {}
+ interface S19 {}
+ union S20 {}
+ static assert(!hasUnsharedAliasing!S17);
+ static assert( hasUnsharedAliasing!S18);
+ static assert( hasUnsharedAliasing!S19);
+ static assert(!hasUnsharedAliasing!S20);
+}
+
+/**
+ True if $(D S) or any type embedded directly in the representation of $(D S)
+ defines an elaborate copy constructor. Elaborate copy constructors are
+ introduced by defining $(D this(this)) for a $(D struct).
+
+ Classes and unions never have elaborate copy constructors.
+ */
+template hasElaborateCopyConstructor(S)
+{
+ import std.meta : anySatisfy;
+ static if (isStaticArray!S && S.length)
+ {
+ enum bool hasElaborateCopyConstructor = hasElaborateCopyConstructor!(typeof(S.init[0]));
+ }
+ else static if (is(S == struct))
+ {
+ enum hasElaborateCopyConstructor = hasMember!(S, "__postblit")
+ || anySatisfy!(.hasElaborateCopyConstructor, FieldTypeTuple!S);
+ }
+ else
+ {
+ enum bool hasElaborateCopyConstructor = false;
+ }
+}
+
+///
+@safe unittest
+{
+ static assert(!hasElaborateCopyConstructor!int);
+
+ static struct S1 { }
+ static struct S2 { this(this) {} }
+ static struct S3 { S2 field; }
+ static struct S4 { S3[1] field; }
+ static struct S5 { S3[] field; }
+ static struct S6 { S3[0] field; }
+ static struct S7 { @disable this(); S3 field; }
+ static assert(!hasElaborateCopyConstructor!S1);
+ static assert( hasElaborateCopyConstructor!S2);
+ static assert( hasElaborateCopyConstructor!(immutable S2));
+ static assert( hasElaborateCopyConstructor!S3);
+ static assert( hasElaborateCopyConstructor!(S3[1]));
+ static assert(!hasElaborateCopyConstructor!(S3[0]));
+ static assert( hasElaborateCopyConstructor!S4);
+ static assert(!hasElaborateCopyConstructor!S5);
+ static assert(!hasElaborateCopyConstructor!S6);
+ static assert( hasElaborateCopyConstructor!S7);
+}
+
+/**
+ True if $(D S) or any type directly embedded in the representation of $(D S)
+ defines an elaborate assignment. Elaborate assignments are introduced by
+ defining $(D opAssign(typeof(this))) or $(D opAssign(ref typeof(this)))
+ for a $(D struct) or when there is a compiler-generated $(D opAssign).
+
+ A type $(D S) gets compiler-generated $(D opAssign) in case it has
+ an elaborate copy constructor or elaborate destructor.
+
+ Classes and unions never have elaborate assignments.
+
+ Note: Structs with (possibly nested) postblit operator(s) will have a
+ hidden yet elaborate compiler generated assignment operator (unless
+ explicitly disabled).
+ */
+template hasElaborateAssign(S)
+{
+ import std.meta : anySatisfy;
+ static if (isStaticArray!S && S.length)
+ {
+ enum bool hasElaborateAssign = hasElaborateAssign!(typeof(S.init[0]));
+ }
+ else static if (is(S == struct))
+ {
+ enum hasElaborateAssign = is(typeof(S.init.opAssign(rvalueOf!S))) ||
+ is(typeof(S.init.opAssign(lvalueOf!S))) ||
+ anySatisfy!(.hasElaborateAssign, FieldTypeTuple!S);
+ }
+ else
+ {
+ enum bool hasElaborateAssign = false;
+ }
+}
+
+///
+@safe unittest
+{
+ static assert(!hasElaborateAssign!int);
+
+ static struct S { void opAssign(S) {} }
+ static assert( hasElaborateAssign!S);
+ static assert(!hasElaborateAssign!(const(S)));
+
+ static struct S1 { void opAssign(ref S1) {} }
+ static struct S2 { void opAssign(int) {} }
+ static struct S3 { S s; }
+ static assert( hasElaborateAssign!S1);
+ static assert(!hasElaborateAssign!S2);
+ static assert( hasElaborateAssign!S3);
+ static assert( hasElaborateAssign!(S3[1]));
+ static assert(!hasElaborateAssign!(S3[0]));
+}
+
+@safe unittest
+{
+ static struct S { void opAssign(S) {} }
+ static struct S4
+ {
+ void opAssign(U)(U u) {}
+ @disable void opAssign(U)(ref U u);
+ }
+ static assert( hasElaborateAssign!S4);
+
+ static struct S41
+ {
+ void opAssign(U)(ref U u) {}
+ @disable void opAssign(U)(U u);
+ }
+ static assert( hasElaborateAssign!S41);
+
+ static struct S5 { @disable this(); this(int n){ s = S(); } S s; }
+ static assert( hasElaborateAssign!S5);
+
+ static struct S6 { this(this) {} }
+ static struct S7 { this(this) {} @disable void opAssign(S7); }
+ static struct S8 { this(this) {} @disable void opAssign(S8); void opAssign(int) {} }
+ static struct S9 { this(this) {} void opAssign(int) {} }
+ static struct S10 { ~this() { } }
+ static assert( hasElaborateAssign!S6);
+ static assert(!hasElaborateAssign!S7);
+ static assert(!hasElaborateAssign!S8);
+ static assert( hasElaborateAssign!S9);
+ static assert( hasElaborateAssign!S10);
+ static struct SS6 { S6 s; }
+ static struct SS7 { S7 s; }
+ 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!SS9);
+}
+
+/**
+ True if $(D S) or any type directly embedded in the representation
+ of $(D S) defines an elaborate destructor. Elaborate destructors
+ are introduced by defining $(D ~this()) for a $(D
+ struct).
+
+ Classes and unions never have elaborate destructors, even
+ though classes may define $(D ~this()).
+ */
+template hasElaborateDestructor(S)
+{
+ import std.meta : anySatisfy;
+ static if (isStaticArray!S && S.length)
+ {
+ enum bool hasElaborateDestructor = hasElaborateDestructor!(typeof(S.init[0]));
+ }
+ else static if (is(S == struct))
+ {
+ enum hasElaborateDestructor = hasMember!(S, "__dtor")
+ || anySatisfy!(.hasElaborateDestructor, FieldTypeTuple!S);
+ }
+ else
+ {
+ enum bool hasElaborateDestructor = false;
+ }
+}
+
+///
+@safe unittest
+{
+ static assert(!hasElaborateDestructor!int);
+
+ static struct S1 { }
+ static struct S2 { ~this() {} }
+ static struct S3 { S2 field; }
+ static struct S4 { S3[1] field; }
+ static struct S5 { S3[] field; }
+ static struct S6 { S3[0] field; }
+ static struct S7 { @disable this(); S3 field; }
+ static assert(!hasElaborateDestructor!S1);
+ static assert( hasElaborateDestructor!S2);
+ static assert( hasElaborateDestructor!(immutable S2));
+ static assert( hasElaborateDestructor!S3);
+ static assert( hasElaborateDestructor!(S3[1]));
+ static assert(!hasElaborateDestructor!(S3[0]));
+ static assert( hasElaborateDestructor!S4);
+ static assert(!hasElaborateDestructor!S5);
+ static assert(!hasElaborateDestructor!S6);
+ static assert( hasElaborateDestructor!S7);
+}
+
+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).
+ */
+enum hasMember(T, string name) = __traits(hasMember, T, name);
+
+///
+@safe unittest
+{
+ static assert(!hasMember!(int, "blah"));
+ struct S1 { int blah; }
+ struct S2 { int blah(){ return 0; } }
+ class C1 { int blah; }
+ class C2 { int blah(){ return 0; } }
+ static assert(hasMember!(S1, "blah"));
+ static assert(hasMember!(S2, "blah"));
+ static assert(hasMember!(C1, "blah"));
+ static assert(hasMember!(C2, "blah"));
+}
+
+@safe unittest
+{
+ // 8321
+ struct S {
+ int x;
+ void f(){}
+ void t()(){}
+ template T(){}
+ }
+ struct R1(T) {
+ T t;
+ alias t this;
+ }
+ struct R2(T) {
+ T t;
+ @property ref inout(T) payload() inout { return t; }
+ alias t this;
+ }
+ static assert(hasMember!(S, "x"));
+ static assert(hasMember!(S, "f"));
+ static assert(hasMember!(S, "t"));
+ static assert(hasMember!(S, "T"));
+ static assert(hasMember!(R1!S, "x"));
+ static assert(hasMember!(R1!S, "f"));
+ static assert(hasMember!(R1!S, "t"));
+ static assert(hasMember!(R1!S, "T"));
+ static assert(hasMember!(R2!S, "x"));
+ static assert(hasMember!(R2!S, "f"));
+ static assert(hasMember!(R2!S, "t"));
+ static assert(hasMember!(R2!S, "T"));
+}
+
+@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();
+ 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).
+ The members are arranged in the same order as declared in $(D E).
+
+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 $(REF NoDuplicates, std,meta) template to avoid
+ generating duplicate switch cases.
+
+Note:
+ Returned values are strictly typed with $(D E). Thus, the following code
+ does not work without the explicit cast:
+--------------------
+enum E : int { a, b, c }
+int[] abc = cast(int[]) [ EnumMembers!E ];
+--------------------
+ Cast is not necessary if the type of the variable is inferred. See the
+ example below.
+
+Example:
+ Creating an array of enumerated values:
+--------------------
+enum Sqrts : real
+{
+ one = 1,
+ two = 1.41421,
+ three = 1.73205,
+}
+auto sqrts = [ EnumMembers!Sqrts ];
+assert(sqrts == [ Sqrts.one, Sqrts.two, Sqrts.three ]);
+--------------------
+
+ A generic function $(D rank(v)) in the following example uses this
+ template for finding a member $(D e) in an enumerated type $(D E).
+--------------------
+// Returns i if e is the i-th enumerator of E.
+size_t rank(E)(E e)
+ if (is(E == enum))
+{
+ foreach (i, member; EnumMembers!E)
+ {
+ if (e == member)
+ return i;
+ }
+ assert(0, "Not an enum member");
+}
+
+enum Mode
+{
+ read = 1,
+ write = 2,
+ map = 4,
+}
+assert(rank(Mode.read ) == 0);
+assert(rank(Mode.write) == 1);
+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)
+ {
+ static if (ident == "Symbolize")
+ {
+ template Symbolize(alias value)
+ {
+ enum Symbolize = value;
+ }
+ }
+ else
+ {
+ mixin("template Symbolize(alias "~ ident ~")"
+ ~"{"
+ ~"alias Symbolize = "~ ident ~";"
+ ~"}");
+ }
+ }
+
+ template EnumSpecificMembers(names...)
+ {
+ 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 =
+ AliasSeq!(
+ WithIdentifier!(names[0])
+ .Symbolize!(__traits(getMember, E, names[0])),
+ EnumSpecificMembers!(names[1 .. $/2]),
+ EnumSpecificMembers!(names[$/2..$])
+ );
+ }
+ else
+ {
+ alias EnumSpecificMembers = AliasSeq!();
+ }
+ }
+
+ alias EnumMembers = EnumSpecificMembers!(__traits(allMembers, E));
+}
+
+@safe unittest
+{
+ enum A { a }
+ static assert([ EnumMembers!A ] == [ A.a ]);
+ enum B { a, b, c, d, e }
+ static assert([ EnumMembers!B ] == [ B.a, B.b, B.c, B.d, B.e ]);
+}
+
+@safe unittest // typed enums
+{
+ enum A : string { a = "alpha", b = "beta" }
+ static assert([ EnumMembers!A ] == [ A.a, A.b ]);
+
+ static struct S
+ {
+ int value;
+ int opCmp(S rhs) const nothrow { return value - rhs.value; }
+ }
+ enum B : S { a = S(1), b = S(2), c = S(3) }
+ static assert([ EnumMembers!B ] == [ B.a, B.b, B.c ]);
+}
+
+@safe unittest // duplicated values
+{
+ enum A
+ {
+ a = 0, b = 0,
+ c = 1, d = 1, e
+ }
+ static assert([ EnumMembers!A ] == [ A.a, A.b, A.c, A.d, A.e ]);
+}
+
+@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");
+ static assert(__traits(identifier, EnumMembers!E[1]) == "a");
+ static assert(__traits(identifier, EnumMembers!E[2]) == "b");
+}
+
+
+//::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::://
+// Classes and Interfaces
+//::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::://
+
+/***
+ * Get a $(D_PARAM AliasSeq) of the base class and base interfaces of
+ * this class or interface. $(D_PARAM BaseTypeTuple!Object) returns
+ * the empty type tuple.
+ */
+template BaseTypeTuple(A)
+{
+ static if (is(A P == super))
+ alias BaseTypeTuple = P;
+ else
+ static assert(0, "argument is not a class or interface");
+}
+
+///
+@safe unittest
+{
+ interface I1 { }
+ interface I2 { }
+ interface I12 : I1, I2 { }
+ static assert(is(BaseTypeTuple!I12 == AliasSeq!(I1, I2)));
+
+ interface I3 : I1 { }
+ interface I123 : I1, I2, I3 { }
+ static assert(is(BaseTypeTuple!I123 == AliasSeq!(I1, I2, I3)));
+}
+
+@safe unittest
+{
+ interface I1 { }
+ interface I2 { }
+ class A { }
+ class C : A, I1, I2 { }
+
+ alias TL = BaseTypeTuple!C;
+ assert(TL.length == 3);
+ assert(is (TL[0] == A));
+ assert(is (TL[1] == I1));
+ assert(is (TL[2] == I2));
+
+ assert(BaseTypeTuple!Object.length == 0);
+}
+
+/**
+ * Get a $(D_PARAM AliasSeq) of $(I all) base classes of this class,
+ * in decreasing order. Interfaces are not included. $(D_PARAM
+ * BaseClassesTuple!Object) yields the empty type tuple.
+ */
+template BaseClassesTuple(T)
+ if (is(T == class))
+{
+ static if (is(T == Object))
+ {
+ alias BaseClassesTuple = AliasSeq!();
+ }
+ else static if (is(BaseTypeTuple!T[0] == Object))
+ {
+ alias BaseClassesTuple = AliasSeq!Object;
+ }
+ else
+ {
+ alias BaseClassesTuple =
+ AliasSeq!(BaseTypeTuple!T[0],
+ BaseClassesTuple!(BaseTypeTuple!T[0]));
+ }
+}
+
+///
+@safe unittest
+{
+ class C1 { }
+ class C2 : C1 { }
+ class C3 : C2 { }
+ static assert(!BaseClassesTuple!Object.length);
+ static assert(is(BaseClassesTuple!C1 == AliasSeq!(Object)));
+ static assert(is(BaseClassesTuple!C2 == AliasSeq!(C1, Object)));
+ static assert(is(BaseClassesTuple!C3 == AliasSeq!(C2, C1, Object)));
+}
+
+@safe unittest
+{
+ struct S { }
+ static assert(!__traits(compiles, BaseClassesTuple!S));
+ interface I { }
+ static assert(!__traits(compiles, BaseClassesTuple!I));
+ class C4 : I { }
+ class C5 : C4, I { }
+ static assert(is(BaseClassesTuple!C5 == AliasSeq!(C4, Object)));
+}
+
+/**
+ * Get a $(D_PARAM AliasSeq) of $(I all) interfaces directly or
+ * indirectly inherited by this class or interface. Interfaces do not
+ * repeat if multiply implemented. $(D_PARAM InterfacesTuple!Object)
+ * yields the empty type tuple.
+ */
+template InterfacesTuple(T)
+{
+ import std.meta : NoDuplicates;
+ template Flatten(H, T...)
+ {
+ static if (T.length)
+ {
+ alias Flatten = AliasSeq!(Flatten!H, Flatten!T);
+ }
+ else
+ {
+ static if (is(H == interface))
+ alias Flatten = AliasSeq!(H, InterfacesTuple!H);
+ else
+ alias Flatten = InterfacesTuple!H;
+ }
+ }
+
+ static if (is(T S == super) && S.length)
+ alias InterfacesTuple = NoDuplicates!(Flatten!S);
+ else
+ alias InterfacesTuple = AliasSeq!();
+}
+
+@safe unittest
+{
+ // doc example
+ interface I1 {}
+ interface I2 {}
+ class A : I1, I2 { }
+ class B : A, I1 { }
+ class C : B { }
+ alias TL = InterfacesTuple!C;
+ static assert(is(TL[0] == I1) && is(TL[1] == I2));
+}
+
+@safe unittest
+{
+ interface Iaa {}
+ interface Iab {}
+ interface Iba {}
+ interface Ibb {}
+ interface Ia : Iaa, Iab {}
+ interface Ib : Iba, Ibb {}
+ interface I : Ia, Ib {}
+ interface J {}
+ class B2 : J {}
+ class C2 : B2, Ia, Ib {}
+ static assert(is(InterfacesTuple!I ==
+ AliasSeq!(Ia, Iaa, Iab, Ib, Iba, Ibb)));
+ static assert(is(InterfacesTuple!C2 ==
+ AliasSeq!(J, Ia, Iaa, Iab, Ib, Iba, Ibb)));
+
+}
+
+/**
+ * Get a $(D_PARAM AliasSeq) of $(I all) base classes of $(D_PARAM
+ * T), in decreasing order, followed by $(D_PARAM T)'s
+ * interfaces. $(D_PARAM TransitiveBaseTypeTuple!Object) yields the
+ * empty type tuple.
+ */
+template TransitiveBaseTypeTuple(T)
+{
+ static if (is(T == Object))
+ alias TransitiveBaseTypeTuple = AliasSeq!();
+ else
+ alias TransitiveBaseTypeTuple =
+ AliasSeq!(BaseClassesTuple!T, InterfacesTuple!T);
+}
+
+///
+@safe unittest
+{
+ interface J1 {}
+ interface J2 {}
+ class B1 {}
+ class B2 : B1, J1, J2 {}
+ class B3 : B2, J1 {}
+ alias TL = TransitiveBaseTypeTuple!B3;
+ assert(TL.length == 5);
+ assert(is (TL[0] == B2));
+ assert(is (TL[1] == B1));
+ assert(is (TL[2] == Object));
+ assert(is (TL[3] == J1));
+ assert(is (TL[4] == J2));
+
+ assert(TransitiveBaseTypeTuple!Object.length == 0);
+}
+
+
+/**
+Returns a tuple of non-static functions with the name $(D name) declared in the
+class or interface $(D C). Covariant duplicates are shrunk into the most
+derived one.
+ */
+template MemberFunctionsTuple(C, string name)
+ if (is(C == class) || is(C == interface))
+{
+ static if (__traits(hasMember, C, name))
+ {
+ /*
+ * First, collect all overloads in the class hierarchy.
+ */
+ template CollectOverloads(Node)
+ {
+ static if (__traits(hasMember, Node, name) && __traits(compiles, __traits(getMember, Node, name)))
+ {
+ // Get all overloads in sight (not hidden).
+ alias inSight = AliasSeq!(__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 walkThru = AliasSeq!(
+ CollectOverloads!(Parents[0]),
+ walkThru!(Parents[1 .. $])
+ );
+ else
+ alias walkThru = AliasSeq!();
+ }
+
+ static if (is(Node Parents == super))
+ alias CollectOverloads = AliasSeq!(inSight, walkThru!Parents);
+ else
+ alias CollectOverloads = AliasSeq!inSight;
+ }
+ else
+ alias CollectOverloads = AliasSeq!(); // no overloads in this hierarchy
+ }
+
+ // duplicates in this tuple will be removed by shrink()
+ 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...)
+ {
+ 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 Target = FunctionTypeOf!target;
+ alias Rest0 = FunctionTypeOf!(rest[0]);
+
+ 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 = shrinkOne!(target, rest[1 .. $]);
+ else static if (isCovariantWith!(Rest0, Target))
+ // rest[0] overrides target -- erase target.
+ alias shrinkOne = shrinkOne!(rest[0], rest[1 .. $]);
+ else
+ // target and rest[0] are distinct.
+ alias shrinkOne = AliasSeq!(
+ shrinkOne!(target, rest[1 .. $]),
+ rest[0] // keep
+ );
+ }
+ else
+ alias shrinkOne = AliasSeq!target; // done
+ }
+
+ /*
+ * Now shrink covariant overloads into one.
+ */
+ template shrink(overloads...)
+ {
+ static if (overloads.length > 0)
+ {
+ alias temp = shrinkOne!overloads;
+ alias shrink = AliasSeq!(temp[0], shrink!(temp[1 .. $]));
+ }
+ else
+ alias shrink = AliasSeq!(); // done
+ }
+
+ // done.
+ alias MemberFunctionsTuple = shrink!overloads;
+ }
+ else
+ alias MemberFunctionsTuple = AliasSeq!();
+}
+
+///
+@safe unittest
+{
+ interface I { I foo(); }
+ class B
+ {
+ real foo(real v) { return v; }
+ }
+ class C : B, I
+ {
+ override C foo() { return this; } // covariant overriding of I.foo()
+ }
+ 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));
+}
+
+@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(); }
+ interface K { K test(int); }
+ class B : I, K
+ {
+ K test(int) { return this; }
+ B test() { return this; }
+ static void test(string) { }
+ }
+ class C : B, J
+ {
+ override C test() { return this; }
+ }
+ alias test =MemberFunctionsTuple!(C, "test");
+ static assert(test.length == 2);
+ static assert(is(FunctionTypeOf!(test[0]) == FunctionTypeOf!(C.test)));
+ static assert(is(FunctionTypeOf!(test[1]) == FunctionTypeOf!(K.test)));
+ alias noexist = MemberFunctionsTuple!(C, "noexist");
+ static assert(noexist.length == 0);
+
+ interface L { int prop() @property; }
+ alias prop = MemberFunctionsTuple!(L, "prop");
+ static assert(prop.length == 1);
+
+ interface Test_I
+ {
+ void foo();
+ void foo(int);
+ void foo(int, int);
+ }
+ interface Test : Test_I {}
+ alias Test_foo = MemberFunctionsTuple!(Test, "foo");
+ static assert(Test_foo.length == 3);
+ static assert(is(typeof(&Test_foo[0]) == void function()));
+ static assert(is(typeof(&Test_foo[2]) == void function(int)));
+ static assert(is(typeof(&Test_foo[1]) == void function(int, int)));
+}
+
+
+/**
+Returns an alias to the template that $(D T) is an instance of.
+ */
+template TemplateOf(alias T : Base!Args, alias Base, Args...)
+{
+ alias TemplateOf = Base;
+}
+
+/// ditto
+template TemplateOf(T : Base!Args, alias Base, Args...)
+{
+ alias TemplateOf = Base;
+}
+
+///
+@safe unittest
+{
+ struct Foo(T, U) {}
+ static assert(__traits(isSame, TemplateOf!(Foo!(int, real)), Foo));
+}
+
+@safe unittest
+{
+ template Foo1(A) {}
+ template Foo2(A, B) {}
+ template Foo3(alias A) {}
+ template Foo4(string A) {}
+ struct Foo5(A) {}
+ struct Foo6(A, B) {}
+ struct Foo7(alias A) {}
+ template Foo8(A) { template Foo9(B) {} }
+ template Foo10() {}
+
+ static assert(__traits(isSame, TemplateOf!(Foo1!(int)), Foo1));
+ static assert(__traits(isSame, TemplateOf!(Foo2!(int, int)), Foo2));
+ static assert(__traits(isSame, TemplateOf!(Foo3!(123)), Foo3));
+ static assert(__traits(isSame, TemplateOf!(Foo4!("123")), Foo4));
+ static assert(__traits(isSame, TemplateOf!(Foo5!(int)), Foo5));
+ static assert(__traits(isSame, TemplateOf!(Foo6!(int, int)), Foo6));
+ static assert(__traits(isSame, TemplateOf!(Foo7!(123)), Foo7));
+ static assert(__traits(isSame, TemplateOf!(Foo8!(int).Foo9!(real)), Foo8!(int).Foo9));
+ static assert(__traits(isSame, TemplateOf!(Foo10!()), Foo10));
+}
+
+
+/**
+Returns a $(D AliasSeq) of the template arguments used to instantiate $(D T).
+ */
+template TemplateArgsOf(alias T : Base!Args, alias Base, Args...)
+{
+ alias TemplateArgsOf = Args;
+}
+
+/// ditto
+template TemplateArgsOf(T : Base!Args, alias Base, Args...)
+{
+ alias TemplateArgsOf = Args;
+}
+
+///
+@safe unittest
+{
+ struct Foo(T, U) {}
+ static assert(is(TemplateArgsOf!(Foo!(int, real)) == AliasSeq!(int, real)));
+}
+
+@safe unittest
+{
+ template Foo1(A) {}
+ template Foo2(A, B) {}
+ template Foo3(alias A) {}
+ template Foo4(string A) {}
+ struct Foo5(A) {}
+ struct Foo6(A, B) {}
+ struct Foo7(alias A) {}
+ template Foo8(A) { template Foo9(B) {} }
+ template Foo10() {}
+
+ enum x = 123;
+ enum y = "123";
+ static assert(is(TemplateArgsOf!(Foo1!(int)) == AliasSeq!(int)));
+ static assert(is(TemplateArgsOf!(Foo2!(int, int)) == AliasSeq!(int, int)));
+ static assert(__traits(isSame, TemplateArgsOf!(Foo3!(x)), AliasSeq!(x)));
+ static assert(TemplateArgsOf!(Foo4!(y)) == AliasSeq!(y));
+ static assert(is(TemplateArgsOf!(Foo5!(int)) == AliasSeq!(int)));
+ static assert(is(TemplateArgsOf!(Foo6!(int, int)) == AliasSeq!(int, int)));
+ static assert(__traits(isSame, TemplateArgsOf!(Foo7!(x)), AliasSeq!(x)));
+ static assert(is(TemplateArgsOf!(Foo8!(int).Foo9!(real)) == AliasSeq!(real)));
+ static assert(is(TemplateArgsOf!(Foo10!()) == AliasSeq!()));
+}
+
+
+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.comparison : max;
+ enum maxAlignment = max(staticMap!(.maxAlignment, U));
+ }
+}
+
+
+/**
+Returns class instance alignment.
+ */
+template classInstanceAlignment(T) if (is(T == class))
+{
+ alias classInstanceAlignment = maxAlignment!(void*, typeof(T.tupleof));
+}
+
+///
+@safe unittest
+{
+ class A { byte b; }
+ class B { long l; }
+
+ // As class instance always has a hidden pointer
+ static assert(classInstanceAlignment!A == (void*).alignof);
+ static assert(classInstanceAlignment!B == long.alignof);
+}
+
+
+//::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::://
+// Type Conversion
+//::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::://
+
+/**
+Get the type that all types can be implicitly converted to. Useful
+e.g. in figuring out an array type from a bunch of initializing
+values. Returns $(D_PARAM void) if passed an empty list, or if the
+types have no common type.
+ */
+template CommonType(T...)
+{
+ static if (!T.length)
+ {
+ alias CommonType = void;
+ }
+ else static if (T.length == 1)
+ {
+ static if (is(typeof(T[0])))
+ {
+ alias CommonType = typeof(T[0]);
+ }
+ else
+ {
+ alias CommonType = T[0];
+ }
+ }
+ else static if (is(typeof(true ? T[0].init : T[1].init) U))
+ {
+ alias CommonType = CommonType!(U, T[2 .. $]);
+ }
+ else
+ alias CommonType = void;
+}
+
+///
+@safe unittest
+{
+ alias X = CommonType!(int, long, short);
+ assert(is(X == long));
+ alias Y = CommonType!(int, char[], short);
+ assert(is(Y == void));
+}
+@safe unittest
+{
+ static assert(is(CommonType!(3) == int));
+ static assert(is(CommonType!(double, 4, float) == double));
+ static assert(is(CommonType!(string, char[]) == const(char)[]));
+ static assert(is(CommonType!(3, 3U) == uint));
+}
+
+
+/**
+ * Returns a tuple with all possible target types of an implicit
+ * conversion of a value of type $(D_PARAM T).
+ *
+ * Important note:
+ *
+ * The possible targets are computed more conservatively than the D
+ * 2.005 compiler does, eliminating all dangerous conversions. For
+ * example, $(D_PARAM ImplicitConversionTargets!double) does not
+ * include $(D_PARAM float).
+ */
+template ImplicitConversionTargets(T)
+{
+ static if (is(T == bool))
+ alias ImplicitConversionTargets =
+ AliasSeq!(byte, ubyte, short, ushort, int, uint, long, ulong, CentTypeList,
+ float, double, real, char, wchar, dchar);
+ else static if (is(T == byte))
+ alias ImplicitConversionTargets =
+ AliasSeq!(short, ushort, int, uint, long, ulong, CentTypeList,
+ float, double, real, char, wchar, dchar);
+ else static if (is(T == ubyte))
+ alias ImplicitConversionTargets =
+ AliasSeq!(short, ushort, int, uint, long, ulong, CentTypeList,
+ float, double, real, char, wchar, dchar);
+ else static if (is(T == short))
+ alias ImplicitConversionTargets =
+ AliasSeq!(int, uint, long, ulong, CentTypeList, float, double, real);
+ else static if (is(T == ushort))
+ alias ImplicitConversionTargets =
+ AliasSeq!(int, uint, long, ulong, CentTypeList, float, double, real);
+ else static if (is(T == int))
+ alias ImplicitConversionTargets =
+ AliasSeq!(long, ulong, CentTypeList, float, double, real);
+ else static if (is(T == uint))
+ alias ImplicitConversionTargets =
+ AliasSeq!(long, ulong, CentTypeList, float, double, real);
+ else static if (is(T == long))
+ alias ImplicitConversionTargets = AliasSeq!(float, double, real);
+ else static if (is(T == ulong))
+ alias ImplicitConversionTargets = AliasSeq!(float, double, real);
+ else static if (is(cent) && is(T == cent))
+ alias ImplicitConversionTargets = AliasSeq!(float, double, real);
+ else static if (is(ucent) && is(T == ucent))
+ alias ImplicitConversionTargets = AliasSeq!(float, double, real);
+ else static if (is(T == float))
+ alias ImplicitConversionTargets = AliasSeq!(double, real);
+ else static if (is(T == double))
+ alias ImplicitConversionTargets = AliasSeq!real;
+ else static if (is(T == char))
+ alias ImplicitConversionTargets =
+ AliasSeq!(wchar, dchar, byte, ubyte, short, ushort,
+ int, uint, long, ulong, CentTypeList, float, double, real);
+ else static if (is(T == wchar))
+ alias ImplicitConversionTargets =
+ AliasSeq!(dchar, short, ushort, int, uint, long, ulong, CentTypeList,
+ float, double, real);
+ else static if (is(T == dchar))
+ alias ImplicitConversionTargets =
+ AliasSeq!(int, uint, long, ulong, CentTypeList, float, double, real);
+ else static if (is(T : typeof(null)))
+ alias ImplicitConversionTargets = AliasSeq!(typeof(null));
+ else static if (is(T : Object))
+ alias ImplicitConversionTargets = TransitiveBaseTypeTuple!(T);
+ else static if (isDynamicArray!T && !is(typeof(T.init[0]) == const))
+ alias ImplicitConversionTargets =
+ AliasSeq!(const(Unqual!(typeof(T.init[0])))[]);
+ else static if (is(T : void*))
+ alias ImplicitConversionTargets = AliasSeq!(void*);
+ else
+ alias ImplicitConversionTargets = AliasSeq!();
+}
+
+@safe unittest
+{
+ static assert(is(ImplicitConversionTargets!(double)[0] == real));
+ static assert(is(ImplicitConversionTargets!(string)[0] == const(char)[]));
+}
+
+/**
+Is $(D From) implicitly convertible to $(D To)?
+ */
+template isImplicitlyConvertible(From, To)
+{
+ enum bool isImplicitlyConvertible = is(typeof({
+ void fun(ref From v)
+ {
+ void gun(To) {}
+ gun(v);
+ }
+ }));
+}
+
+///
+@safe unittest
+{
+ static assert( isImplicitlyConvertible!(immutable(char), char));
+ static assert( isImplicitlyConvertible!(const(char), char));
+ static assert( isImplicitlyConvertible!(char, wchar));
+ static assert(!isImplicitlyConvertible!(wchar, char));
+
+ static assert(!isImplicitlyConvertible!(const(ushort), ubyte));
+ static assert(!isImplicitlyConvertible!(const(uint), ubyte));
+ static assert(!isImplicitlyConvertible!(const(ulong), ubyte));
+
+ static assert(!isImplicitlyConvertible!(const(char)[], string));
+ static assert( isImplicitlyConvertible!(string, const(char)[]));
+}
+
+/**
+Returns $(D true) iff a value of type $(D Rhs) can be assigned to a variable of
+type $(D Lhs).
+
+$(D isAssignable) returns whether both an lvalue and rvalue can be assigned.
+
+If you omit $(D Rhs), $(D isAssignable) will check identity assignable of $(D Lhs).
+*/
+enum isAssignable(Lhs, Rhs = Lhs) = isRvalueAssignable!(Lhs, Rhs) && isLvalueAssignable!(Lhs, Rhs);
+
+///
+@safe unittest
+{
+ static assert( isAssignable!(long, int));
+ static assert(!isAssignable!(int, long));
+ static assert( isAssignable!(const(char)[], string));
+ static assert(!isAssignable!(string, char[]));
+
+ // int is assignable to int
+ static assert( isAssignable!int);
+
+ // immutable int is not assignable to immutable int
+ static assert(!isAssignable!(immutable int));
+}
+
+// ditto
+private enum isRvalueAssignable(Lhs, Rhs = Lhs) = __traits(compiles, lvalueOf!Lhs = rvalueOf!Rhs);
+
+// ditto
+private enum isLvalueAssignable(Lhs, Rhs = Lhs) = __traits(compiles, lvalueOf!Lhs = lvalueOf!Rhs);
+
+@safe unittest
+{
+ static assert(!isAssignable!(immutable int, int));
+ static assert( isAssignable!(int, immutable int));
+
+ static assert(!isAssignable!(inout int, int));
+ static assert( isAssignable!(int, inout int));
+ static assert(!isAssignable!(inout int));
+
+ static assert( isAssignable!(shared int, int));
+ static assert( isAssignable!(int, shared int));
+ static assert( isAssignable!(shared int));
+
+ static assert( isAssignable!(void[1], void[1]));
+
+ struct S { @disable this(); this(int n){} }
+ static assert( isAssignable!(S, S));
+
+ struct S2 { this(int n){} }
+ static assert( isAssignable!(S2, S2));
+ static assert(!isAssignable!(S2, int));
+
+ struct S3 { @disable void opAssign(); }
+ static assert( isAssignable!(S3, S3));
+
+ struct S3X { @disable void opAssign(S3X); }
+ static assert(!isAssignable!(S3X, S3X));
+
+ struct S4 { void opAssign(int); }
+ static assert( isAssignable!(S4, S4));
+ static assert( isAssignable!(S4, int));
+ static assert( isAssignable!(S4, immutable int));
+
+ struct S5 { @disable this(); @disable this(this); }
+ struct S6 { void opAssign(in ref S5); }
+ static assert(!isAssignable!(S6, S5));
+ static assert(!isRvalueAssignable!(S6, S5));
+ static assert( isLvalueAssignable!(S6, S5));
+ static assert( isLvalueAssignable!(S6, immutable S5));
+}
+
+
+// Equivalent with TypeStruct::isAssignable in compiler code.
+package template isBlitAssignable(T)
+{
+ static if (is(OriginalType!T U) && !is(T == U))
+ {
+ enum isBlitAssignable = isBlitAssignable!U;
+ }
+ else static if (isStaticArray!T && is(T == E[n], E, size_t n))
+ // Workaround for issue 11499 : isStaticArray!T should not be necessary.
+ {
+ enum isBlitAssignable = isBlitAssignable!E;
+ }
+ else static if (is(T == struct) || is(T == union))
+ {
+ enum isBlitAssignable = isMutable!T &&
+ {
+ size_t offset = 0;
+ bool assignable = true;
+ foreach (i, F; FieldTypeTuple!T)
+ {
+ static if (i == 0)
+ {
+ }
+ else
+ {
+ 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;
+ }
+ return assignable;
+ }();
+ }
+ else
+ enum isBlitAssignable = isMutable!T;
+}
+
+@safe unittest
+{
+ static assert( isBlitAssignable!int);
+ static assert(!isBlitAssignable!(const int));
+
+ class C{ const int i; }
+ static assert( isBlitAssignable!C);
+
+ struct S1{ int i; }
+ struct S2{ const int i; }
+ static assert( isBlitAssignable!S1);
+ static assert(!isBlitAssignable!S2);
+
+ struct S3X { union { int x; int y; } }
+ struct S3Y { union { int x; const int y; } }
+ struct S3Z { union { const int x; const int y; } }
+ static assert( isBlitAssignable!(S3X));
+ static assert( isBlitAssignable!(S3Y));
+ static assert(!isBlitAssignable!(S3Z));
+ static assert(!isBlitAssignable!(const S3X));
+ static assert(!isBlitAssignable!(inout S3Y));
+ static assert(!isBlitAssignable!(immutable S3Z));
+ static assert( isBlitAssignable!(S3X[3]));
+ static assert( isBlitAssignable!(S3Y[3]));
+ static assert(!isBlitAssignable!(S3Z[3]));
+ enum ES3X : S3X { a = S3X() }
+ enum ES3Y : S3Y { a = S3Y() }
+ enum ES3Z : S3Z { a = S3Z() }
+ static assert( isBlitAssignable!(ES3X));
+ static assert( isBlitAssignable!(ES3Y));
+ static assert(!isBlitAssignable!(ES3Z));
+ static assert(!isBlitAssignable!(const ES3X));
+ static assert(!isBlitAssignable!(inout ES3Y));
+ static assert(!isBlitAssignable!(immutable ES3Z));
+ static assert( isBlitAssignable!(ES3X[3]));
+ static assert( isBlitAssignable!(ES3Y[3]));
+ static assert(!isBlitAssignable!(ES3Z[3]));
+
+ union U1X { int x; int y; }
+ union U1Y { int x; const int y; }
+ union U1Z { const int x; const int y; }
+ static assert( isBlitAssignable!(U1X));
+ static assert( isBlitAssignable!(U1Y));
+ static assert(!isBlitAssignable!(U1Z));
+ static assert(!isBlitAssignable!(const U1X));
+ static assert(!isBlitAssignable!(inout U1Y));
+ static assert(!isBlitAssignable!(immutable U1Z));
+ static assert( isBlitAssignable!(U1X[3]));
+ static assert( isBlitAssignable!(U1Y[3]));
+ static assert(!isBlitAssignable!(U1Z[3]));
+ enum EU1X : U1X { a = U1X() }
+ enum EU1Y : U1Y { a = U1Y() }
+ enum EU1Z : U1Z { a = U1Z() }
+ static assert( isBlitAssignable!(EU1X));
+ static assert( isBlitAssignable!(EU1Y));
+ static assert(!isBlitAssignable!(EU1Z));
+ static assert(!isBlitAssignable!(const EU1X));
+ static assert(!isBlitAssignable!(inout EU1Y));
+ static assert(!isBlitAssignable!(immutable EU1Z));
+ static assert( isBlitAssignable!(EU1X[3]));
+ static assert( isBlitAssignable!(EU1Y[3]));
+ static assert(!isBlitAssignable!(EU1Z[3]));
+
+ struct SA
+ {
+ @property int[3] foo() { return [1,2,3]; }
+ alias foo this;
+ const int x; // SA is not blit assignable
+ }
+ static assert(!isStaticArray!SA);
+ static assert(!isBlitAssignable!(SA[3]));
+}
+
+
+/*
+Works like $(D isImplicitlyConvertible), except this cares only about storage
+classes of the arguments.
+ */
+private template isStorageClassImplicitlyConvertible(From, To)
+{
+ alias Pointify(T) = void*;
+
+ enum isStorageClassImplicitlyConvertible = isImplicitlyConvertible!(
+ ModifyTypePreservingTQ!(Pointify, From),
+ ModifyTypePreservingTQ!(Pointify, To) );
+}
+
+@safe unittest
+{
+ static assert( isStorageClassImplicitlyConvertible!( int, const int));
+ static assert( isStorageClassImplicitlyConvertible!(immutable int, const int));
+
+ static assert(!isStorageClassImplicitlyConvertible!(const int, int));
+ static assert(!isStorageClassImplicitlyConvertible!(const int, immutable int));
+ static assert(!isStorageClassImplicitlyConvertible!(int, shared int));
+ static assert(!isStorageClassImplicitlyConvertible!(shared int, int));
+}
+
+
+/**
+Determines whether the function type $(D F) is covariant with $(D G), i.e.,
+functions of the type $(D F) can override ones of the type $(D G).
+ */
+template isCovariantWith(F, G)
+ if (is(F == function) && is(G == function))
+{
+ static if (is(F : G))
+ enum isCovariantWith = true;
+ else
+ {
+ alias Upr = F;
+ alias Lwr = G;
+
+ /*
+ * Check for calling convention: require exact match.
+ */
+ template checkLinkage()
+ {
+ enum ok = functionLinkage!Upr == functionLinkage!Lwr;
+ }
+ /*
+ * Check for variadic parameter: require exact match.
+ */
+ template checkVariadicity()
+ {
+ enum ok = variadicFunctionStyle!Upr == variadicFunctionStyle!Lwr;
+ }
+ /*
+ * Check for function storage class:
+ * - overrider can have narrower storage class than base
+ */
+ template checkSTC()
+ {
+ // Note the order of arguments. The convertion order Lwr -> Upr is
+ // correct since Upr should be semantically 'narrower' than Lwr.
+ enum ok = isStorageClassImplicitlyConvertible!(Lwr, Upr);
+ }
+ /*
+ * Check for function attributes:
+ * - require exact match for ref and @property
+ * - overrider can add pure and nothrow, but can't remove them
+ * - @safe and @trusted are covariant with each other, unremovable
+ */
+ template checkAttributes()
+ {
+ alias FA = FunctionAttribute;
+ enum uprAtts = functionAttributes!Upr;
+ enum lwrAtts = functionAttributes!Lwr;
+ //
+ enum wantExact = FA.ref_ | FA.property;
+ enum safety = FA.safe | FA.trusted;
+ enum ok =
+ ( (uprAtts & wantExact) == (lwrAtts & wantExact)) &&
+ ( (uprAtts & FA.pure_ ) >= (lwrAtts & FA.pure_ )) &&
+ ( (uprAtts & FA.nothrow_) >= (lwrAtts & FA.nothrow_)) &&
+ (!!(uprAtts & safety ) >= !!(lwrAtts & safety )) ;
+ }
+ /*
+ * Check for return type: usual implicit convertion.
+ */
+ template checkReturnType()
+ {
+ enum ok = is(ReturnType!Upr : ReturnType!Lwr);
+ }
+ /*
+ * Check for parameters:
+ * - require exact match for types (cf. bugzilla 3075)
+ * - require exact match for in, out, ref and lazy
+ * - overrider can add scope, but can't remove
+ */
+ template checkParameters()
+ {
+ alias STC = ParameterStorageClass;
+ alias UprParams = Parameters!Upr;
+ alias LwrParams = Parameters!Lwr;
+ alias UprPSTCs = ParameterStorageClassTuple!Upr;
+ alias LwrPSTCs = ParameterStorageClassTuple!Lwr;
+ //
+ template checkNext(size_t i)
+ {
+ static if (i < UprParams.length)
+ {
+ enum uprStc = UprPSTCs[i];
+ enum lwrStc = LwrPSTCs[i];
+ //
+ enum wantExact = STC.out_ | STC.ref_ | STC.lazy_ | STC.return_;
+ enum ok =
+ ((uprStc & wantExact ) == (lwrStc & wantExact )) &&
+ ((uprStc & STC.scope_) >= (lwrStc & STC.scope_)) &&
+ checkNext!(i + 1).ok;
+ }
+ else
+ enum ok = true; // done
+ }
+ static if (UprParams.length == LwrParams.length)
+ enum ok = is(UprParams == LwrParams) && checkNext!(0).ok;
+ else
+ enum ok = false;
+ }
+
+ /* run all the checks */
+ enum isCovariantWith =
+ checkLinkage !().ok &&
+ checkVariadicity!().ok &&
+ checkSTC !().ok &&
+ checkAttributes !().ok &&
+ checkReturnType !().ok &&
+ checkParameters !().ok ;
+ }
+}
+
+///
+@safe unittest
+{
+ interface I { I clone(); }
+ interface J { J clone(); }
+ class C : I
+ {
+ override C clone() // covariant overriding of I.clone()
+ {
+ return new C;
+ }
+ }
+
+ // C.clone() can override I.clone(), indeed.
+ static assert(isCovariantWith!(typeof(C.clone), typeof(I.clone)));
+
+ // C.clone() can't override J.clone(); the return type C is not implicitly
+ // convertible to J.
+ static assert(!isCovariantWith!(typeof(C.clone), typeof(J.clone)));
+}
+
+@safe unittest
+{
+ enum bool isCovariantWith(alias f, alias g) = .isCovariantWith!(typeof(f), typeof(g));
+
+ // covariant return type
+ interface I {}
+ interface J : I {}
+ interface BaseA { const(I) test(int); }
+ interface DerivA_1 : BaseA { override const(J) test(int); }
+ interface DerivA_2 : BaseA { override J test(int); }
+ static assert( isCovariantWith!(DerivA_1.test, BaseA.test));
+ static assert( isCovariantWith!(DerivA_2.test, BaseA.test));
+ static assert(!isCovariantWith!(BaseA.test, DerivA_1.test));
+ static assert(!isCovariantWith!(BaseA.test, DerivA_2.test));
+ static assert( isCovariantWith!(BaseA.test, BaseA.test));
+ static assert( isCovariantWith!(DerivA_1.test, DerivA_1.test));
+ 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*); }
+ static assert( isCovariantWith!(DerivB_1.test, BaseB.test));
+ static assert( isCovariantWith!(DerivB_2.test, BaseB.test));
+ static assert( isCovariantWith!(DerivB_3.test, BaseB.test));
+ static assert(!isCovariantWith!(BaseB.test, DerivB_1.test));
+ static assert(!isCovariantWith!(BaseB.test, DerivB_2.test));
+ static assert(!isCovariantWith!(BaseB.test, DerivB_3.test));
+
+ // function storage class
+ interface BaseC { void test() ; }
+ interface DerivC_1 : BaseC { override void test() const; }
+ static assert( isCovariantWith!(DerivC_1.test, BaseC.test));
+ static assert(!isCovariantWith!(BaseC.test, DerivC_1.test));
+
+ // increasing safety
+ interface BaseE { void test() ; }
+ interface DerivE_1 : BaseE { override void test() @safe ; }
+ interface DerivE_2 : BaseE { override void test() @trusted; }
+ static assert( isCovariantWith!(DerivE_1.test, BaseE.test));
+ static assert( isCovariantWith!(DerivE_2.test, BaseE.test));
+ static assert(!isCovariantWith!(BaseE.test, DerivE_1.test));
+ static assert(!isCovariantWith!(BaseE.test, DerivE_2.test));
+
+ // @safe and @trusted
+ interface BaseF
+ {
+ void test1() @safe;
+ void test2() @trusted;
+ }
+ interface DerivF : BaseF
+ {
+ override void test1() @trusted;
+ override void test2() @safe;
+ }
+ static assert( isCovariantWith!(DerivF.test1, BaseF.test1));
+ static assert( isCovariantWith!(DerivF.test2, BaseF.test2));
+}
+
+
+// Needed for rvalueOf/lvalueOf because "inout on return means
+// inout must be on a parameter as well"
+private struct __InoutWorkaroundStruct{}
+
+/**
+Creates an lvalue or rvalue of type $(D T) for $(D typeof(...)) and
+$(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.
+
+Example:
+---
+// Note that `f` doesn't have to be implemented
+// as is isn't called.
+int f(int);
+bool f(ref int);
+static assert(is(typeof(f(rvalueOf!int)) == int));
+static assert(is(typeof(f(lvalueOf!int)) == bool));
+
+int i = rvalueOf!int; // error, no actual value is returned
+---
+*/
+@property T rvalueOf(T)(inout __InoutWorkaroundStruct = __InoutWorkaroundStruct.init);
+
+/// ditto
+@property ref T lvalueOf(T)(inout __InoutWorkaroundStruct = __InoutWorkaroundStruct.init);
+
+// Note: unittest can't be used as an example here as function overloads
+// aren't allowed inside functions.
+
+@system unittest
+{
+ void needLvalue(T)(ref T);
+ static struct S { }
+ int i;
+ struct Nested { void f() { ++i; } }
+ foreach (T; AliasSeq!(int, immutable int, inout int, string, S, Nested, Object))
+ {
+ static assert(!__traits(compiles, needLvalue(rvalueOf!T)));
+ static assert( __traits(compiles, needLvalue(lvalueOf!T)));
+ static assert(is(typeof(rvalueOf!T) == T));
+ static assert(is(typeof(lvalueOf!T) == T));
+ }
+
+ static assert(!__traits(compiles, rvalueOf!int = 1));
+ static assert( __traits(compiles, lvalueOf!byte = 127));
+ static assert(!__traits(compiles, lvalueOf!byte = 128));
+}
+
+
+//::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::://
+// SomethingTypeOf
+//::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::://
+
+private template AliasThisTypeOf(T) if (isAggregateType!T)
+{
+ alias members = AliasSeq!(__traits(getAliasThis, T));
+
+ static if (members.length == 1)
+ {
+ alias AliasThisTypeOf = typeof(__traits(getMember, T.init, members[0]));
+ }
+ else
+ static assert(0, T.stringof~" does not have alias this type");
+}
+
+/*
+ */
+template BooleanTypeOf(T)
+{
+ static if (is(AliasThisTypeOf!T AT) && !is(AT[] == AT))
+ alias X = BooleanTypeOf!AT;
+ else
+ alias X = OriginalType!T;
+
+ static if (is(Unqual!X == bool))
+ {
+ alias BooleanTypeOf = X;
+ }
+ else
+ static assert(0, T.stringof~" is not boolean type");
+}
+
+@safe unittest
+{
+ // unexpected failure, maybe dmd type-merging bug
+ foreach (T; AliasSeq!bool)
+ foreach (Q; TypeQualifierList)
+ {
+ static assert( is(Q!T == BooleanTypeOf!( Q!T )));
+ static assert( is(Q!T == BooleanTypeOf!( SubTypeOf!(Q!T) )));
+ }
+
+ foreach (T; AliasSeq!(void, NumericTypeList, ImaginaryTypeList, ComplexTypeList, CharTypeList))
+ foreach (Q; TypeQualifierList)
+ {
+ static assert(!is(BooleanTypeOf!( Q!T )), Q!T.stringof);
+ static assert(!is(BooleanTypeOf!( SubTypeOf!(Q!T) )));
+ }
+}
+
+@safe unittest
+{
+ struct B
+ {
+ bool val;
+ alias val this;
+ }
+ struct S
+ {
+ B b;
+ alias b this;
+ }
+ static assert(is(BooleanTypeOf!B == bool));
+ static assert(is(BooleanTypeOf!S == bool));
+}
+
+/*
+ */
+template IntegralTypeOf(T)
+{
+ import std.meta : staticIndexOf;
+ static if (is(AliasThisTypeOf!T AT) && !is(AT[] == AT))
+ alias X = IntegralTypeOf!AT;
+ else
+ alias X = OriginalType!T;
+
+ static if (staticIndexOf!(Unqual!X, IntegralTypeList) >= 0)
+ {
+ alias IntegralTypeOf = X;
+ }
+ else
+ static assert(0, T.stringof~" is not an integral type");
+}
+
+@safe unittest
+{
+ foreach (T; IntegralTypeList)
+ foreach (Q; TypeQualifierList)
+ {
+ static assert( is(Q!T == IntegralTypeOf!( Q!T )));
+ static assert( is(Q!T == IntegralTypeOf!( SubTypeOf!(Q!T) )));
+ }
+
+ foreach (T; AliasSeq!(void, bool, FloatingPointTypeList, ImaginaryTypeList, ComplexTypeList, CharTypeList))
+ foreach (Q; TypeQualifierList)
+ {
+ static assert(!is(IntegralTypeOf!( Q!T )));
+ static assert(!is(IntegralTypeOf!( SubTypeOf!(Q!T) )));
+ }
+}
+
+/*
+ */
+template FloatingPointTypeOf(T)
+{
+ import std.meta : staticIndexOf;
+ static if (is(AliasThisTypeOf!T AT) && !is(AT[] == AT))
+ alias X = FloatingPointTypeOf!AT;
+ else
+ alias X = OriginalType!T;
+
+ static if (staticIndexOf!(Unqual!X, FloatingPointTypeList) >= 0)
+ {
+ alias FloatingPointTypeOf = X;
+ }
+ else
+ static assert(0, T.stringof~" is not a floating point type");
+}
+
+@safe unittest
+{
+ foreach (T; FloatingPointTypeList)
+ foreach (Q; TypeQualifierList)
+ {
+ static assert( is(Q!T == FloatingPointTypeOf!( Q!T )));
+ static assert( is(Q!T == FloatingPointTypeOf!( SubTypeOf!(Q!T) )));
+ }
+
+ foreach (T; AliasSeq!(void, bool, IntegralTypeList, ImaginaryTypeList, ComplexTypeList, CharTypeList))
+ foreach (Q; TypeQualifierList)
+ {
+ static assert(!is(FloatingPointTypeOf!( Q!T )));
+ static assert(!is(FloatingPointTypeOf!( SubTypeOf!(Q!T) )));
+ }
+}
+
+/*
+ */
+template NumericTypeOf(T)
+{
+ static if (is(IntegralTypeOf!T X) || is(FloatingPointTypeOf!T X))
+ {
+ alias NumericTypeOf = X;
+ }
+ else
+ static assert(0, T.stringof~" is not a numeric type");
+}
+
+@safe unittest
+{
+ foreach (T; NumericTypeList)
+ foreach (Q; TypeQualifierList)
+ {
+ static assert( is(Q!T == NumericTypeOf!( Q!T )));
+ static assert( is(Q!T == NumericTypeOf!( SubTypeOf!(Q!T) )));
+ }
+
+ foreach (T; AliasSeq!(void, bool, CharTypeList, ImaginaryTypeList, ComplexTypeList))
+ foreach (Q; TypeQualifierList)
+ {
+ static assert(!is(NumericTypeOf!( Q!T )));
+ static assert(!is(NumericTypeOf!( SubTypeOf!(Q!T) )));
+ }
+}
+
+/*
+ */
+template UnsignedTypeOf(T)
+{
+ import std.meta : staticIndexOf;
+ static if (is(IntegralTypeOf!T X) &&
+ staticIndexOf!(Unqual!X, UnsignedIntTypeList) >= 0)
+ alias UnsignedTypeOf = X;
+ else
+ static assert(0, T.stringof~" is not an unsigned type.");
+}
+
+/*
+ */
+template SignedTypeOf(T)
+{
+ import std.meta : staticIndexOf;
+ static if (is(IntegralTypeOf!T X) &&
+ staticIndexOf!(Unqual!X, SignedIntTypeList) >= 0)
+ alias SignedTypeOf = X;
+ else static if (is(FloatingPointTypeOf!T X))
+ alias SignedTypeOf = X;
+ else
+ static assert(0, T.stringof~" is not an signed type.");
+}
+
+/*
+ */
+template CharTypeOf(T)
+{
+ import std.meta : staticIndexOf;
+ static if (is(AliasThisTypeOf!T AT) && !is(AT[] == AT))
+ alias X = CharTypeOf!AT;
+ else
+ alias X = OriginalType!T;
+
+ static if (staticIndexOf!(Unqual!X, CharTypeList) >= 0)
+ {
+ alias CharTypeOf = X;
+ }
+ else
+ static assert(0, T.stringof~" is not a character type");
+}
+
+@safe unittest
+{
+ foreach (T; CharTypeList)
+ foreach (Q; TypeQualifierList)
+ {
+ static assert( is(CharTypeOf!( Q!T )));
+ static assert( is(CharTypeOf!( SubTypeOf!(Q!T) )));
+ }
+
+ foreach (T; AliasSeq!(void, bool, NumericTypeList, ImaginaryTypeList, ComplexTypeList))
+ foreach (Q; TypeQualifierList)
+ {
+ static assert(!is(CharTypeOf!( Q!T )));
+ static assert(!is(CharTypeOf!( SubTypeOf!(Q!T) )));
+ }
+
+ foreach (T; AliasSeq!(string, wstring, dstring, char[4]))
+ foreach (Q; TypeQualifierList)
+ {
+ static assert(!is(CharTypeOf!( Q!T )));
+ static assert(!is(CharTypeOf!( SubTypeOf!(Q!T) )));
+ }
+}
+
+/*
+ */
+template StaticArrayTypeOf(T)
+{
+ static if (is(AliasThisTypeOf!T AT) && !is(AT[] == AT))
+ alias X = StaticArrayTypeOf!AT;
+ else
+ alias X = OriginalType!T;
+
+ static if (is(X : E[n], E, size_t n))
+ alias StaticArrayTypeOf = X;
+ else
+ static assert(0, T.stringof~" is not a static array type");
+}
+
+@safe unittest
+{
+ foreach (T; AliasSeq!(bool, NumericTypeList, ImaginaryTypeList, ComplexTypeList))
+ foreach (Q; AliasSeq!(TypeQualifierList, InoutOf, SharedInoutOf))
+ {
+ static assert(is( Q!( T[1] ) == StaticArrayTypeOf!( Q!( T[1] ) ) ));
+
+ foreach (P; TypeQualifierList)
+ { // SubTypeOf cannot have inout type
+ static assert(is( Q!(P!(T[1])) == StaticArrayTypeOf!( Q!(SubTypeOf!(P!(T[1]))) ) ));
+ }
+ }
+
+ foreach (T; AliasSeq!void)
+ foreach (Q; AliasSeq!TypeQualifierList)
+ {
+ static assert(is( StaticArrayTypeOf!( Q!(void[1]) ) == Q!(void[1]) ));
+ }
+}
+
+/*
+ */
+template DynamicArrayTypeOf(T)
+{
+ static if (is(AliasThisTypeOf!T AT) && !is(AT[] == AT))
+ alias X = DynamicArrayTypeOf!AT;
+ else
+ alias X = OriginalType!T;
+
+ static if (is(Unqual!X : E[], E) && !is(typeof({ enum n = X.length; })))
+ {
+ alias DynamicArrayTypeOf = X;
+ }
+ else
+ static assert(0, T.stringof~" is not a dynamic array");
+}
+
+@safe unittest
+{
+ foreach (T; AliasSeq!(/*void, */bool, NumericTypeList, ImaginaryTypeList, ComplexTypeList))
+ foreach (Q; AliasSeq!(TypeQualifierList, InoutOf, SharedInoutOf))
+ {
+ static assert(is( Q!T[] == DynamicArrayTypeOf!( Q!T[] ) ));
+ static assert(is( Q!(T[]) == DynamicArrayTypeOf!( Q!(T[]) ) ));
+
+ foreach (P; AliasSeq!(MutableOf, ConstOf, ImmutableOf))
+ {
+ static assert(is( Q!(P!T[]) == DynamicArrayTypeOf!( Q!(SubTypeOf!(P!T[])) ) ));
+ static assert(is( Q!(P!(T[])) == DynamicArrayTypeOf!( Q!(SubTypeOf!(P!(T[]))) ) ));
+ }
+ }
+
+ static assert(!is(DynamicArrayTypeOf!(int[3])));
+ static assert(!is(DynamicArrayTypeOf!(void[3])));
+ static assert(!is(DynamicArrayTypeOf!(typeof(null))));
+}
+
+/*
+ */
+template ArrayTypeOf(T)
+{
+ static if (is(StaticArrayTypeOf!T X) || is(DynamicArrayTypeOf!T X))
+ {
+ alias ArrayTypeOf = X;
+ }
+ else
+ static assert(0, T.stringof~" is not an array type");
+}
+
+/*
+Always returns the Dynamic Array version.
+ */
+template StringTypeOf(T)
+{
+ static if (is(T == typeof(null)))
+ {
+ // It is impossible to determine exact string type from typeof(null) -
+ // it means that StringTypeOf!(typeof(null)) is undefined.
+ // Then this behavior is convenient for template constraint.
+ static assert(0, T.stringof~" is not a string type");
+ }
+ else static if (is(T : const char[]) || is(T : const wchar[]) || is(T : const dchar[]))
+ {
+ static if (is(T : U[], U))
+ alias StringTypeOf = U[];
+ else
+ static assert(0);
+ }
+ else
+ static assert(0, T.stringof~" is not a string type");
+}
+
+@safe unittest
+{
+ foreach (T; CharTypeList)
+ foreach (Q; AliasSeq!(MutableOf, ConstOf, ImmutableOf, InoutOf))
+ {
+ static assert(is(Q!T[] == StringTypeOf!( Q!T[] )));
+
+ static if (!__traits(isSame, Q, InoutOf))
+ {
+ static assert(is(Q!T[] == StringTypeOf!( SubTypeOf!(Q!T[]) )));
+
+ alias Str = Q!T[];
+ class C(S) { S val; alias val this; }
+ static assert(is(StringTypeOf!(C!Str) == Str));
+ }
+ }
+
+ foreach (T; CharTypeList)
+ foreach (Q; AliasSeq!(SharedOf, SharedConstOf, SharedInoutOf))
+ {
+ static assert(!is(StringTypeOf!( Q!T[] )));
+ }
+}
+
+@safe unittest
+{
+ static assert(is(StringTypeOf!(char[4]) == char[]));
+}
+
+/*
+ */
+template AssocArrayTypeOf(T)
+{
+ static if (is(AliasThisTypeOf!T AT) && !is(AT[] == AT))
+ alias X = AssocArrayTypeOf!AT;
+ else
+ alias X = OriginalType!T;
+
+ static if (is(Unqual!X : V[K], K, V))
+ {
+ alias AssocArrayTypeOf = X;
+ }
+ else
+ static assert(0, T.stringof~" is not an associative array type");
+}
+
+@safe unittest
+{
+ foreach (T; AliasSeq!(int/*bool, CharTypeList, NumericTypeList, ImaginaryTypeList, ComplexTypeList*/))
+ foreach (P; AliasSeq!(TypeQualifierList, InoutOf, SharedInoutOf))
+ foreach (Q; AliasSeq!(TypeQualifierList, InoutOf, SharedInoutOf))
+ foreach (R; AliasSeq!(TypeQualifierList, InoutOf, SharedInoutOf))
+ {
+ static assert(is( P!(Q!T[R!T]) == AssocArrayTypeOf!( P!(Q!T[R!T]) ) ));
+ }
+
+ foreach (T; AliasSeq!(int/*bool, CharTypeList, NumericTypeList, ImaginaryTypeList, ComplexTypeList*/))
+ foreach (O; AliasSeq!(TypeQualifierList, InoutOf, SharedInoutOf))
+ foreach (P; AliasSeq!TypeQualifierList)
+ foreach (Q; AliasSeq!TypeQualifierList)
+ foreach (R; AliasSeq!TypeQualifierList)
+ {
+ static assert(is( O!(P!(Q!T[R!T])) == AssocArrayTypeOf!( O!(SubTypeOf!(P!(Q!T[R!T]))) ) ));
+ }
+}
+
+/*
+ */
+template BuiltinTypeOf(T)
+{
+ static if (is(T : void)) alias BuiltinTypeOf = void;
+ else static if (is(BooleanTypeOf!T X)) alias BuiltinTypeOf = X;
+ else static if (is(IntegralTypeOf!T X)) alias BuiltinTypeOf = X;
+ else static if (is(FloatingPointTypeOf!T X))alias BuiltinTypeOf = X;
+ else static if (is(T : const(ireal))) alias BuiltinTypeOf = ireal; //TODO
+ else static if (is(T : const(creal))) alias BuiltinTypeOf = creal; //TODO
+ else static if (is(CharTypeOf!T X)) alias BuiltinTypeOf = X;
+ else static if (is(ArrayTypeOf!T X)) alias BuiltinTypeOf = X;
+ else static if (is(AssocArrayTypeOf!T X)) alias BuiltinTypeOf = X;
+ else static assert(0);
+}
+
+//::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::://
+// isSomething
+//::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::://
+
+/**
+ * Detect whether $(D T) is a built-in boolean type.
+ */
+enum bool isBoolean(T) = is(BooleanTypeOf!T) && !isAggregateType!T;
+
+///
+@safe unittest
+{
+ static assert( isBoolean!bool);
+ enum EB : bool { a = true }
+ static assert( isBoolean!EB);
+ 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;
+
+///
+@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)
+ {
+ foreach (Q; TypeQualifierList)
+ {
+ static assert( isIntegral!(Q!T));
+ static assert(!isIntegral!(SubTypeOf!(Q!T)));
+ }
+ }
+
+ static assert(!isIntegral!float);
+
+ enum EU : uint { a = 0, b = 1, c = 2 } // base type is unsigned
+ enum EI : int { a = -1, b = 0, c = 1 } // base type is signed (bug 7909)
+ static assert(isIntegral!EU && isUnsigned!EU && !isSigned!EU);
+ static assert(isIntegral!EI && !isUnsigned!EI && isSigned!EI);
+}
+
+/**
+ * Detect whether $(D T) is a built-in floating point type.
+ */
+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));
+
+///
+@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 }
+
+ foreach (T; AliasSeq!(FloatingPointTypeList, EF))
+ {
+ foreach (Q; TypeQualifierList)
+ {
+ static assert( isFloatingPoint!(Q!T));
+ static assert(!isFloatingPoint!(SubTypeOf!(Q!T)));
+ }
+ }
+ foreach (T; IntegralTypeList)
+ {
+ foreach (Q; TypeQualifierList)
+ {
+ static assert(!isFloatingPoint!(Q!T));
+ }
+ }
+}
+
+// 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).
+ */
+enum bool isNumeric(T) = __traits(isArithmetic, T) && !(is(Unqual!T == bool) ||
+ is(Unqual!T == char) ||
+ is(Unqual!T == wchar) ||
+ is(Unqual!T == dchar));
+
+///
+@safe unittest
+{
+ static assert(
+ 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!bool &&
+ !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; AliasSeq!(NumericTypeList))
+ {
+ foreach (Q; TypeQualifierList)
+ {
+ static assert( isNumeric!(Q!T));
+ 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).
+ */
+enum bool isScalarType(T) = is(T : real) && !isAggregateType!T;
+
+///
+@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).
+ */
+enum bool isBasicType(T) = isScalarType!T || is(Unqual!T == void);
+
+///
+@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)));
+ static assert(isBasicType!(const(dchar)));
+}
+
+/**
+ * Detect whether $(D T) is a built-in unsigned numeric type.
+ */
+enum bool isUnsigned(T) = __traits(isUnsigned, T) && !(is(Unqual!T == char) ||
+ is(Unqual!T == wchar) ||
+ is(Unqual!T == dchar) ||
+ is(Unqual!T == bool));
+
+///
+@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; AliasSeq!(UnsignedIntTypeList))
+ {
+ foreach (Q; TypeQualifierList)
+ {
+ static assert( isUnsigned!(Q!T));
+ 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.
+ */
+enum bool isSigned(T) = __traits(isArithmetic, T) && !__traits(isUnsigned, T);
+
+///
+@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; AliasSeq!(SignedIntTypeList))
+ {
+ foreach (Q; TypeQualifierList)
+ {
+ static assert( isSigned!(Q!T));
+ 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.
+ *
+ * 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;
+
+///
+@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);
+ static assert(!isSomeChar!wstring);
+ static assert(!isSomeChar!dstring);
+ static assert(!isSomeChar!(char[4]));
+}
+
+@safe unittest
+{
+ enum EC : char { a = 'x', b = 'y' }
+
+ foreach (T; AliasSeq!(CharTypeList, EC))
+ {
+ foreach (Q; TypeQualifierList)
+ {
+ static assert( isSomeChar!( Q!T ));
+ 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));
+}
+
+/**
+Detect whether $(D T) is one of the built-in string types.
+
+The built-in string types are $(D Char[]), where $(D Char) is any of $(D char),
+$(D wchar) or $(D dchar), with or without qualifiers.
+
+Static arrays of characters (like $(D char[80])) are not considered
+built-in string types.
+ */
+enum bool isSomeString(T) = is(StringTypeOf!T) && !isAggregateType!T && !isStaticArray!T;
+
+///
+@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]));
+}
+
+@safe unittest
+{
+ foreach (T; AliasSeq!(char[], dchar[], string, wstring, dstring))
+ {
+ static assert( isSomeString!( T ));
+ static assert(!isSomeString!(SubTypeOf!(T)));
+ }
+}
+
+/**
+ * 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;
+
+///
+@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; AliasSeq!(char[], string, wstring))
+ {
+ foreach (Q; AliasSeq!(MutableOf, ConstOf, ImmutableOf)/*TypeQualifierList*/)
+ {
+ static assert( isNarrowString!( Q!T ));
+ static assert(!isNarrowString!( SubTypeOf!(Q!T) ));
+ }
+ }
+
+ foreach (T; AliasSeq!(int, int[], byte[], dchar[], dstring, char[4]))
+ {
+ foreach (Q; TypeQualifierList)
+ {
+ static assert(!isNarrowString!( Q!T ));
+ static assert(!isNarrowString!( SubTypeOf!(Q!T) ));
+ }
+ }
+}
+
+/**
+ * Detects whether `T` is a comparable type. Basic types and structs and
+ * classes that implement opCmp are ordering comparable.
+ */
+enum bool isOrderingComparable(T) = ifTestable!(T, unaryFun!"a < a");
+
+///
+@safe unittest
+{
+ static assert(isOrderingComparable!int);
+ static assert(isOrderingComparable!string);
+ static assert(!isOrderingComparable!creal);
+
+ static struct Foo {}
+ static assert(!isOrderingComparable!Foo);
+
+ static struct Bar
+ {
+ int a;
+ auto opCmp(Bar b1) const { return a - b1.a; }
+ }
+
+ Bar b1 = Bar(5);
+ Bar b2 = Bar(7);
+ assert(isOrderingComparable!Bar && b2 > b1);
+}
+
+/// ditto
+enum bool isEqualityComparable(T) = ifTestable!(T, unaryFun!"a == a");
+
+@safe unittest
+{
+ static assert(isEqualityComparable!int);
+ static assert(isEqualityComparable!string);
+ static assert(isEqualityComparable!creal);
+ static assert(!isEqualityComparable!void);
+
+ struct Foo {}
+ static assert(isEqualityComparable!Foo);
+
+ struct Bar
+ {
+ int a;
+ auto opEquals(Bar b1) const { return a == b1.a; }
+ }
+
+ Bar b1 = Bar(5);
+ Bar b2 = Bar(5);
+ Bar b3 = Bar(7);
+ static assert(isEqualityComparable!Bar);
+ assert(b1 == b2);
+ assert(b1 != b3);
+}
+
+/**
+ * Detect whether $(D T) is a struct, static array, or enum that is implicitly
+ * convertible to a string.
+ */
+template isConvertibleToString(T)
+{
+ enum isConvertibleToString =
+ (isAggregateType!T || isStaticArray!T || is(T == enum))
+ && is(StringTypeOf!T);
+}
+
+///
+@safe unittest
+{
+ 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!(int[3]));
+ static assert( isStaticArray!(const(int)[5]));
+ static assert( isStaticArray!(const(int)[][5]));
+
+ static assert(!isStaticArray!(const(int)[]));
+ static assert(!isStaticArray!(immutable(int)[]));
+ static assert(!isStaticArray!(const(int)[4][]));
+ static assert(!isStaticArray!(int[]));
+ static assert(!isStaticArray!(int[char]));
+ static assert(!isStaticArray!(int[1][]));
+ static assert(!isStaticArray!(int[int]));
+ static assert(!isStaticArray!int);
+}
+
+@safe unittest
+{
+ foreach (T; AliasSeq!(int[51], int[][2],
+ char[][int][11], immutable char[13u],
+ const(real)[1], const(real)[1][1], void[0]))
+ {
+ foreach (Q; TypeQualifierList)
+ {
+ static assert( isStaticArray!( Q!T ));
+ static assert(!isStaticArray!( SubTypeOf!(Q!T) ));
+ }
+ }
+
+ //enum ESA : int[1] { a = [1], b = [2] }
+ //static assert( isStaticArray!ESA);
+}
+
+/**
+ * Detect whether type $(D T) is a dynamic array.
+ */
+enum bool isDynamicArray(T) = is(DynamicArrayTypeOf!T) && !isAggregateType!T;
+
+///
+@safe unittest
+{
+ 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)
+ {
+ static assert( isDynamicArray!( Q!T ));
+ static assert(!isDynamicArray!( SubTypeOf!(Q!T) ));
+ }
+ }
+}
+
+/**
+ * Detect whether type $(D T) is an array (static or dynamic; for associative
+ * arrays see $(LREF isAssociativeArray)).
+ */
+enum bool isArray(T) = isStaticArray!T || isDynamicArray!T;
+
+///
+@safe unittest
+{
+ 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)
+ {
+ static assert( isArray!(Q!T));
+ static assert(!isArray!(SubTypeOf!(Q!T)));
+ }
+ }
+}
+
+/**
+ * Detect whether $(D T) is an associative array type
+ */
+enum bool isAssociativeArray(T) = __traits(isAssociativeArray, T);
+
+@safe unittest
+{
+ struct Foo
+ {
+ @property uint[] keys() { return null; }
+ @property uint[] values() { return null; }
+ }
+
+ foreach (T; AliasSeq!(int[int], int[string], immutable(char[5])[int]))
+ {
+ foreach (Q; TypeQualifierList)
+ {
+ static assert( isAssociativeArray!(Q!T));
+ static assert(!isAssociativeArray!(SubTypeOf!(Q!T)));
+ }
+ }
+
+ static assert(!isAssociativeArray!Foo);
+ static assert(!isAssociativeArray!int);
+ static assert(!isAssociativeArray!(int[]));
+ static assert(!isAssociativeArray!(typeof(null)));
+
+ //enum EAA : int[int] { a = [1:1], b = [2:2] }
+ //static assert( isAssociativeArray!EAA);
+}
+
+/**
+ * Detect whether type $(D T) is a builtin type.
+ */
+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);
+
+@safe unittest
+{
+ static if (is(__vector(float[4])))
+ {
+ alias SimdVec = __vector(float[4]);
+ static assert(isSIMDVector!(__vector(float[4])));
+ static assert(isSIMDVector!SimdVec);
+ }
+ static assert(!isSIMDVector!uint);
+ static assert(!isSIMDVector!(float[4]));
+}
+
+/**
+ * Detect whether type $(D T) is a pointer.
+ */
+enum bool isPointer(T) = is(T == U*, U) && !isAggregateType!T;
+
+@safe unittest
+{
+ foreach (T; AliasSeq!(int*, void*, char[]*))
+ {
+ foreach (Q; TypeQualifierList)
+ {
+ static assert( isPointer!(Q!T));
+ static assert(!isPointer!(SubTypeOf!(Q!T)));
+ }
+ }
+
+ static assert(!isPointer!uint);
+ static assert(!isPointer!(uint[uint]));
+ static assert(!isPointer!(char[]));
+ static assert(!isPointer!(typeof(null)));
+}
+
+/**
+Returns the target type of a pointer.
+*/
+alias PointerTarget(T : T*) = T;
+
+///
+@safe unittest
+{
+ static assert(is(PointerTarget!(int*) == int));
+ static assert(is(PointerTarget!(void*) == void));
+}
+
+/**
+ * Detect whether type $(D T) is an aggregate type.
+ */
+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
+ * the $(D foreach) loop is implemented. This includes ranges, structs/classes
+ * 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) {} }));
+
+///
+@safe unittest
+{
+ struct OpApply
+ {
+ int opApply(scope int delegate(ref uint) dg) { assert(0); }
+ }
+
+ struct Range
+ {
+ @property uint front() { assert(0); }
+ void popFront() { assert(0); }
+ enum bool empty = false;
+ }
+
+ static assert( isIterable!(uint[]));
+ static assert( isIterable!OpApply);
+ static assert( isIterable!(uint[string]));
+ static assert( isIterable!Range);
+
+ static assert(!isIterable!uint);
+}
+
+/**
+ * Returns true if T is not const or immutable. Note that isMutable is true for
+ * string, or immutable(char)[], because the 'head' is mutable.
+ */
+enum bool isMutable(T) = !is(T == const) && !is(T == immutable) && !is(T == inout);
+
+///
+@safe unittest
+{
+ static assert( isMutable!int);
+ static assert( isMutable!string);
+ static assert( isMutable!(shared int));
+ static assert( isMutable!(shared const(int)[]));
+
+ static assert(!isMutable!(const int));
+ static assert(!isMutable!(inout int));
+ static assert(!isMutable!(shared(const int)));
+ static assert(!isMutable!(shared(inout int)));
+ static assert(!isMutable!(immutable string));
+}
+
+/**
+ * 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;
+}
+
+///
+@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(!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));
+}
+
+/**
+ * Check whether the tuple T is an expression tuple.
+ * An expression tuple only contains expressions.
+ *
+ * See_Also: $(LREF isTypeTuple).
+ */
+template isExpressions(T ...)
+{
+ static if (T.length >= 2)
+ enum bool isExpressions =
+ isExpressions!(T[0 .. $/2]) &&
+ isExpressions!(T[$/2 .. $]);
+ else static if (T.length == 1)
+ enum bool isExpressions =
+ !is(T[0]) && __traits(compiles, { auto ex = T[0]; });
+ else
+ enum bool isExpressions = true; // default
+}
+
+///
+@safe unittest
+{
+ static assert(isExpressions!(1, 2.0, "a"));
+ static assert(!isExpressions!(int, double, string));
+ static assert(!isExpressions!(int, 2.0, "a"));
+}
+
+/**
+ * Alternate name for $(LREF isExpressions), kept for legacy compatibility.
+ */
+
+alias isExpressionTuple = isExpressions;
+
+@safe unittest
+{
+ void foo();
+ static int bar() { return 42; }
+ immutable aa = [ 1: -1 ];
+ alias myint = int;
+
+ static assert( isExpressionTuple!(42));
+ static assert( isExpressionTuple!aa);
+ static assert( isExpressionTuple!("cattywampus", 2.7, aa));
+ static assert( isExpressionTuple!(bar()));
+
+ static assert(!isExpressionTuple!isExpressionTuple);
+ static assert(!isExpressionTuple!foo);
+ static assert(!isExpressionTuple!( (a) { } ));
+ static assert(!isExpressionTuple!int);
+ static assert(!isExpressionTuple!myint);
+}
+
+
+/**
+ * Check whether the tuple $(D T) is a type tuple.
+ * A type tuple only contains types.
+ *
+ * See_Also: $(LREF isExpressions).
+ */
+template isTypeTuple(T...)
+{
+ static if (T.length >= 2)
+ enum bool isTypeTuple = isTypeTuple!(T[0 .. $/2]) && isTypeTuple!(T[$/2 .. $]);
+ else static if (T.length == 1)
+ enum bool isTypeTuple = is(T[0]);
+ else
+ enum bool isTypeTuple = true; // default
+}
+
+///
+@safe unittest
+{
+ static assert(isTypeTuple!(int, float, string));
+ static assert(!isTypeTuple!(1, 2.0, "a"));
+ static assert(!isTypeTuple!(1, double, string));
+}
+
+@safe unittest
+{
+ class C {}
+ void func(int) {}
+ auto c = new C;
+ enum CONST = 42;
+
+ static assert( isTypeTuple!int);
+ static assert( isTypeTuple!string);
+ static assert( isTypeTuple!C);
+ static assert( isTypeTuple!(typeof(func)));
+ static assert( isTypeTuple!(int, char, double));
+
+ static assert(!isTypeTuple!c);
+ static assert(!isTypeTuple!isTypeTuple);
+ static assert(!isTypeTuple!CONST);
+}
+
+
+/**
+Detect whether symbol or type $(D T) is a function pointer.
+ */
+template isFunctionPointer(T...)
+ if (T.length == 1)
+{
+ static if (is(T[0] U) || is(typeof(T[0]) U))
+ {
+ static if (is(U F : F*) && is(F == function))
+ enum bool isFunctionPointer = true;
+ else
+ enum bool isFunctionPointer = false;
+ }
+ else
+ enum bool isFunctionPointer = false;
+}
+
+///
+@safe unittest
+{
+ static void foo() {}
+ void bar() {}
+
+ auto fpfoo = &foo;
+ static assert( isFunctionPointer!fpfoo);
+ static assert( isFunctionPointer!(void function()));
+
+ auto dgbar = &bar;
+ static assert(!isFunctionPointer!dgbar);
+ static assert(!isFunctionPointer!(void delegate()));
+ static assert(!isFunctionPointer!foo);
+ static assert(!isFunctionPointer!bar);
+
+ static assert( isFunctionPointer!((int a) {}));
+}
+
+/**
+Detect whether symbol or type $(D T) is a delegate.
+*/
+template isDelegate(T...)
+ if (T.length == 1)
+{
+ static if (is(typeof(& T[0]) U : U*) && is(typeof(& T[0]) U == delegate))
+ {
+ // T is a (nested) function symbol.
+ enum bool isDelegate = true;
+ }
+ else static if (is(T[0] W) || is(typeof(T[0]) W))
+ {
+ // T is an expression or a type. Take the type of it and examine.
+ enum bool isDelegate = is(W == delegate);
+ }
+ else
+ enum bool isDelegate = false;
+}
+
+///
+@safe unittest
+{
+ static void sfunc() { }
+ int x;
+ void func() { x++; }
+
+ int delegate() dg;
+ assert(isDelegate!dg);
+ assert(isDelegate!(int delegate()));
+ assert(isDelegate!(typeof(&func)));
+
+ int function() fp;
+ assert(!isDelegate!fp);
+ assert(!isDelegate!(int function()));
+ assert(!isDelegate!(typeof(&sfunc)));
+}
+
+/**
+Detect whether symbol or type $(D T) is a function, a function pointer or a delegate.
+ */
+template isSomeFunction(T...)
+ if (T.length == 1)
+{
+ static if (is(typeof(& T[0]) U : U*) && is(U == function) || is(typeof(& T[0]) U == delegate))
+ {
+ // T is a (nested) function symbol.
+ enum bool isSomeFunction = true;
+ }
+ else static if (is(T[0] W) || is(typeof(T[0]) W))
+ {
+ // T is an expression or a type. Take the type of it and examine.
+ static if (is(W F : F*) && is(F == function))
+ enum bool isSomeFunction = true; // function pointer
+ else
+ enum bool isSomeFunction = is(W == function) || is(W == delegate);
+ }
+ else
+ enum bool isSomeFunction = false;
+}
+
+@safe unittest
+{
+ static real func(ref int) { return 0; }
+ static void prop() @property { }
+ void nestedFunc() { }
+ void nestedProp() @property { }
+ class C
+ {
+ real method(ref int) { return 0; }
+ real prop() @property { return 0; }
+ }
+ auto c = new C;
+ auto fp = &func;
+ auto dg = &c.method;
+ real val;
+
+ static assert( isSomeFunction!func);
+ static assert( isSomeFunction!prop);
+ static assert( isSomeFunction!nestedFunc);
+ static assert( isSomeFunction!nestedProp);
+ static assert( isSomeFunction!(C.method));
+ static assert( isSomeFunction!(C.prop));
+ static assert( isSomeFunction!(c.prop));
+ static assert( isSomeFunction!(c.prop));
+ static assert( isSomeFunction!fp);
+ static assert( isSomeFunction!dg);
+ static assert( isSomeFunction!(typeof(func)));
+ static assert( isSomeFunction!(real function(ref int)));
+ static assert( isSomeFunction!(real delegate(ref int)));
+ static assert( isSomeFunction!((int a) { return a; }));
+
+ static assert(!isSomeFunction!int);
+ static assert(!isSomeFunction!val);
+ static assert(!isSomeFunction!isSomeFunction);
+}
+
+
+/**
+Detect whether $(D T) is a callable object, which can be called with the
+function call operator $(D $(LPAREN)...$(RPAREN)).
+ */
+template isCallable(T...)
+ if (T.length == 1)
+{
+ static if (is(typeof(& T[0].opCall) == delegate))
+ // T is a object which has a member function opCall().
+ enum bool isCallable = true;
+ else static if (is(typeof(& T[0].opCall) V : V*) && is(V == function))
+ // T is a type which has a static member function opCall().
+ enum bool isCallable = true;
+ else
+ enum bool isCallable = isSomeFunction!T;
+}
+
+///
+@safe unittest
+{
+ interface I { real value() @property; }
+ struct S { static int opCall(int) { return 0; } }
+ class C { int opCall(int) { return 0; } }
+ auto c = new C;
+
+ static assert( isCallable!c);
+ static assert( isCallable!S);
+ static assert( isCallable!(c.opCall));
+ static assert( isCallable!(I.value));
+ static assert( isCallable!((int a) { return a; }));
+
+ static assert(!isCallable!I);
+}
+
+
+/**
+ * Detect whether $(D T) is an abstract function.
+ */
+template isAbstractFunction(T...)
+ if (T.length == 1)
+{
+ enum bool isAbstractFunction = __traits(isAbstractFunction, T[0]);
+}
+
+@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 final function.
+ */
+template isFinalFunction(T...)
+ if (T.length == 1)
+{
+ enum bool isFinalFunction = __traits(isFinalFunction, T[0]);
+}
+
+///
+@safe unittest
+{
+ struct S { void bar() { } }
+ final class FC { void foo(); }
+ class C
+ {
+ void bar() { }
+ final void foo();
+ }
+ static assert(!isFinalFunction!(int));
+ static assert(!isFinalFunction!(S.bar));
+ static assert( isFinalFunction!(FC.foo));
+ static assert(!isFinalFunction!(C.bar));
+ static assert( isFinalFunction!(C.foo));
+}
+
+/**
+Determines whether function $(D f) requires a context pointer.
+*/
+template isNestedFunction(alias f)
+{
+ enum isNestedFunction = __traits(isNested, f);
+}
+
+@safe unittest
+{
+ static void f() { }
+ void g() { }
+ static assert(!isNestedFunction!f);
+ static assert( isNestedFunction!g);
+}
+
+/**
+ * Detect whether $(D T) is an abstract class.
+ */
+template isAbstractClass(T...)
+ if (T.length == 1)
+{
+ enum bool isAbstractClass = __traits(isAbstractClass, T[0]);
+}
+
+///
+@safe unittest
+{
+ struct S { }
+ class C { }
+ abstract class AC { }
+ 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 final class.
+ */
+template isFinalClass(T...)
+ if (T.length == 1)
+{
+ enum bool isFinalClass = __traits(isFinalClass, T[0]);
+}
+
+///
+@safe unittest
+{
+ class C { }
+ abstract class AC { }
+ final class FC1 : C { }
+ final class FC2 { }
+ static assert(!isFinalClass!C);
+ static assert(!isFinalClass!AC);
+ static assert( isFinalClass!FC1);
+ static assert( isFinalClass!FC2);
+ C c;
+ static assert(!isFinalClass!c);
+ FC1 fc1;
+ static assert( isFinalClass!fc1);
+}
+
+//::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::://
+// General Types
+//::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::://
+
+/**
+Removes all qualifiers, if any, from type $(D T).
+ */
+template Unqual(T)
+{
+ version (none) // Error: recursive alias declaration @@@BUG1308@@@
+ {
+ static if (is(T U == const U)) alias Unqual = Unqual!U;
+ else static if (is(T U == immutable U)) alias Unqual = Unqual!U;
+ else static if (is(T U == inout U)) alias Unqual = Unqual!U;
+ else static if (is(T U == shared U)) alias Unqual = Unqual!U;
+ else alias Unqual = T;
+ }
+ else // workaround
+ {
+ static if (is(T U == immutable U)) alias Unqual = U;
+ else static if (is(T U == shared inout const U)) alias Unqual = U;
+ else static if (is(T U == shared inout U)) alias Unqual = U;
+ else static if (is(T U == shared const U)) alias Unqual = U;
+ else static if (is(T U == shared U)) alias Unqual = U;
+ else static if (is(T U == inout const U)) alias Unqual = U;
+ else static if (is(T U == inout U)) alias Unqual = U;
+ else static if (is(T U == const U)) alias Unqual = U;
+ else alias Unqual = T;
+ }
+}
+
+///
+@safe unittest
+{
+ static assert(is(Unqual!int == int));
+ static assert(is(Unqual!(const int) == int));
+ static assert(is(Unqual!(immutable int) == int));
+ static assert(is(Unqual!(shared int) == int));
+ static assert(is(Unqual!(shared(const int)) == int));
+}
+
+@safe unittest
+{
+ static assert(is(Unqual!( int) == int));
+ static assert(is(Unqual!( const int) == int));
+ static assert(is(Unqual!( inout int) == int));
+ static assert(is(Unqual!( inout const int) == int));
+ static assert(is(Unqual!(shared int) == int));
+ static assert(is(Unqual!(shared const int) == int));
+ static assert(is(Unqual!(shared inout int) == int));
+ static assert(is(Unqual!(shared inout const int) == int));
+ static assert(is(Unqual!( immutable int) == int));
+
+ alias ImmIntArr = immutable(int[]);
+ static assert(is(Unqual!ImmIntArr == immutable(int)[]));
+}
+
+// [For internal use]
+package template ModifyTypePreservingTQ(alias 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;
+}
+
+@safe unittest
+{
+ alias Intify(T) = 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 $(B 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));
+}
+
+/**
+Returns the inferred type of the loop variable when a variable of type T
+is iterated over using a $(D foreach) loop with a single loop variable and
+automatically inferred return type. Note that this may not be the same as
+$(D std.range.ElementType!Range) in the case of narrow strings, or if T
+has both opApply and a range interface.
+*/
+template ForeachType(T)
+{
+ alias ForeachType = ReturnType!(typeof(
+ (inout int x = 0)
+ {
+ foreach (elem; T.init)
+ {
+ return elem;
+ }
+ assert(0);
+ }));
+}
+
+///
+@safe unittest
+{
+ static assert(is(ForeachType!(uint[]) == uint));
+ static assert(is(ForeachType!string == immutable(char)));
+ static assert(is(ForeachType!(string[string]) == string));
+ static assert(is(ForeachType!(inout(int)[]) == inout(int)));
+}
+
+
+/**
+ * Strips off all $(D enum)s from type $(D T).
+ */
+template OriginalType(T)
+{
+ template Impl(T)
+ {
+ static if (is(T U == enum)) alias Impl = OriginalType!U;
+ else alias Impl = T;
+ }
+
+ alias OriginalType = ModifyTypePreservingTQ!(Impl, T);
+}
+
+///
+@safe unittest
+{
+ enum E : real { a }
+ enum F : E { a = E.a }
+ alias G = const(F);
+ static assert(is(OriginalType!E == real));
+ static assert(is(OriginalType!F == real));
+ static assert(is(OriginalType!G == const real));
+}
+
+/**
+ * Get the Key type of an Associative Array.
+ */
+alias KeyType(V : V[K], K) = K;
+
+///
+@safe unittest
+{
+ import std.traits;
+ alias Hash = int[string];
+ static assert(is(KeyType!Hash == string));
+ static assert(is(ValueType!Hash == int));
+ KeyType!Hash str = "a"; // str is declared as string
+ ValueType!Hash num = 1; // num is declared as int
+}
+
+/**
+ * Get the Value type of an Associative Array.
+ */
+alias ValueType(V : V[K], K) = V;
+
+///
+@safe unittest
+{
+ import std.traits;
+ alias Hash = int[string];
+ static assert(is(KeyType!Hash == string));
+ static assert(is(ValueType!Hash == int));
+ KeyType!Hash str = "a"; // str is declared as string
+ ValueType!Hash num = 1; // num is declared as int
+}
+
+/**
+ * Returns the corresponding unsigned type for T. T must be a numeric
+ * integral type, otherwise a compile-time error occurs.
+ */
+template Unsigned(T)
+{
+ template Impl(T)
+ {
+ static if (is(T : __vector(V[N]), V, size_t N))
+ alias Impl = __vector(Impl!V[N]);
+ else static if (isUnsigned!T)
+ alias Impl = T;
+ else static if (isSigned!T && !isFloatingPoint!T)
+ {
+ static if (is(T == byte )) alias Impl = ubyte;
+ 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 = ModifyTypePreservingTQ!(Impl, OriginalType!T);
+}
+
+@safe unittest
+{
+ alias U1 = Unsigned!int;
+ alias U2 = Unsigned!(const(int));
+ alias U3 = Unsigned!(immutable(int));
+ static assert(is(U1 == uint));
+ static assert(is(U2 == const(uint)));
+ static assert(is(U3 == immutable(uint)));
+ static if (is(__vector(int[4])) && is(__vector(uint[4])))
+ {
+ 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)));
+ }
+}
+
+/**
+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)
+{
+ static if (T.length == 1)
+ {
+ alias Largest = T[0];
+ }
+ else static if (T.length == 2)
+ {
+ static if (T[0].sizeof >= T[1].sizeof)
+ {
+ alias Largest = T[0];
+ }
+ else
+ {
+ alias Largest = T[1];
+ }
+ }
+ else
+ {
+ alias Largest = Largest!(Largest!(T[0 .. $/2]), Largest!(T[$/2 .. $]));
+ }
+}
+
+///
+@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));
+}
+
+/**
+Returns the corresponding signed type for T. T must be a numeric integral type,
+otherwise a compile-time error occurs.
+ */
+template Signed(T)
+{
+ template Impl(T)
+ {
+ static if (is(T : __vector(V[N]), V, size_t N))
+ alias Impl = __vector(Impl!V[N]);
+ else static if (isSigned!T)
+ alias Impl = T;
+ else static if (isUnsigned!T)
+ {
+ static if (is(T == ubyte )) alias Impl = byte;
+ 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 = ModifyTypePreservingTQ!(Impl, OriginalType!T);
+}
+
+///
+@safe unittest
+{
+ alias S1 = Signed!uint;
+ static assert(is(S1 == int));
+ alias S2 = Signed!(const(uint));
+ 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));
+ }
+}
+
+@safe unittest
+{
+ static assert(is(Signed!float == float));
+ static if (is(__vector(int[4])) && is(__vector(uint[4])))
+ {
+ 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]))));
+ }
+}
+
+
+/**
+Returns the most negative value of the numeric type T.
+*/
+template mostNegative(T)
+ if (isNumeric!T || isSomeChar!T || isBoolean!T)
+{
+ static if (is(typeof(T.min_normal)))
+ enum mostNegative = -T.max;
+ else static if (T.min == 0)
+ enum byte mostNegative = 0;
+ else
+ enum mostNegative = T.min;
+}
+
+///
+@safe unittest
+{
+ static assert(mostNegative!float == -float.max);
+ static assert(mostNegative!double == -double.max);
+ static assert(mostNegative!real == -real.max);
+ static assert(mostNegative!bool == false);
+}
+
+///
+@safe unittest
+{
+ foreach (T; AliasSeq!(bool, byte, short, int, long))
+ static assert(mostNegative!T == T.min);
+
+ foreach (T; AliasSeq!(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));
+}
+
+///
+@safe 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));
+}
+
+@safe unittest
+{
+ // promote to int:
+ foreach (T; AliasSeq!(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; AliasSeq!(int, uint, long, ulong, float, double, real))
+ {
+ static assert(is(Promoted!T == T));
+ static assert(is(Promoted!(immutable(T)) == immutable(T)));
+ }
+}
+
+//:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::://
+// Misc.
+//:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::://
+
+/**
+Returns the mangled name of symbol or type $(D sth).
+
+$(D mangledName) is the same as builtin $(D .mangleof) property, but
+might be more convenient in generic code, e.g. as a template argument
+when invoking staticMap.
+ */
+template mangledName(sth...)
+ if (sth.length == 1)
+{
+ enum string mangledName = sth[0].mangleof;
+}
+
+///
+@safe unittest
+{
+ alias TL = staticMap!(mangledName, int, const int, immutable int);
+ static assert(TL == AliasSeq!("i", "xi", "yi"));
+}
+
+version (unittest) void freeFunc(string);
+
+@safe unittest
+{
+ class C { int value() @property { return 0; } }
+ static assert(mangledName!int == int.mangleof);
+ static assert(mangledName!C == C.mangleof);
+ static assert(mangledName!(C.value) == C.value.mangleof);
+ static assert(mangledName!(C.value)[$ - 12 .. $] == "5valueMFNdZi");
+ static assert(mangledName!mangledName == "3std6traits11mangledName");
+ static assert(mangledName!freeFunc == "_D3std6traits8freeFuncFAyaZv");
+ int x;
+ static if (is(typeof({ return x; }) : int delegate() pure)) // issue 9148
+ static assert(mangledName!((int a) { return a+x; }) == "DFNaNbNiNfiZi"); // pure nothrow @safe @nogc
+ else
+ static assert(mangledName!((int a) { return a+x; }) == "DFNbNiNfiZi"); // nothrow @safe @nnogc
+}
+
+@system unittest
+{
+ // @system due to demangle
+ // Test for bug 5718
+ import std.demangle : demangle;
+ int foo;
+ auto foo_demangled = demangle(mangledName!foo);
+ assert(foo_demangled[0 .. 4] == "int " && foo_demangled[$-3 .. $] == "foo",
+ foo_demangled);
+
+ void bar();
+ auto bar_demangled = demangle(mangledName!bar);
+ assert(bar_demangled[0 .. 5] == "void " && bar_demangled[$-5 .. $] == "bar()");
+}
+
+
+
+// XXX Select & select should go to another module. (functional or algorithm?)
+
+/**
+Aliases itself to $(D T[0]) if the boolean $(D condition) is $(D true)
+and to $(D T[1]) otherwise.
+ */
+template Select(bool condition, T...) if (T.length == 2)
+{
+ import std.meta : Alias;
+ alias Select = Alias!(T[!condition]);
+}
+
+///
+@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;
+ int b = 2;
+ alias selA = Select!(true, a, b);
+ 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);
+}
+
+/**
+If $(D cond) is $(D true), returns $(D a) without evaluating $(D
+b). Otherwise, returns $(D b) without evaluating $(D a).
+ */
+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; }
+
+@safe unittest
+{
+ real pleasecallme() { return 0; }
+ int dontcallme() { assert(0); }
+ auto a = select!true(pleasecallme(), dontcallme());
+ auto b = select!false(dontcallme(), pleasecallme());
+ 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 inaccessible members
+ enum isAccessibleMember(string name) = __traits(compiles, __traits(getMember, symbol, name));
+ alias accessibleMembers = Filter!(isAccessibleMember, __traits(allMembers, symbol));
+
+ // filtering not compiled members such as alias of basic types
+ enum hasSpecificUDA(string name) = mixin("hasUDA!(symbol." ~ name ~ ", attribute)");
+ enum isCorrectMember(string name) = __traits(compiles, hasSpecificUDA!(name));
+
+ alias correctMembers = Filter!(isCorrectMember, accessibleMembers);
+ alias membersWithUDA = toSymbols!(Filter!(hasSpecificUDA, correctMembers));
+
+ // 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;
+ // Trying access to private member from another file therefore we do not have access
+ // for this otherwise we get deprecation warning - not visible from module
+ static assert(getSymbolsByUDA!(HasPrivateMembers, Attr).length == 1);
+ static assert(hasUDA!(getSymbolsByUDA!(HasPrivateMembers, Attr)[0], Attr));
+}
+
+///
+@safe unittest
+{
+ enum Attr;
+ struct A
+ {
+ alias int INT;
+ alias void function(INT) SomeFunction;
+ @Attr int a;
+ int b;
+ @Attr private int c;
+ private int d;
+ }
+
+ // Here everything is fine, we have access to private member c
+ static assert(getSymbolsByUDA!(A, Attr).length == 2);
+ static assert(hasUDA!(getSymbolsByUDA!(A, Attr)[0], Attr));
+ static assert(hasUDA!(getSymbolsByUDA!(A, Attr)[1], Attr));
+}
+
+// #16387: getSymbolsByUDA works with structs but fails with classes
+@safe 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);
+ static assert(isCopyable!(int[]));
+}
diff --git a/libphobos/src/std/typecons.d b/libphobos/src/std/typecons.d
new file mode 100644
index 0000000..a63227c
--- /dev/null
+++ b/libphobos/src/std/typecons.d
@@ -0,0 +1,8029 @@
+// Written in the D programming language.
+
+/**
+This module implements a variety of type constructors, i.e., templates
+that allow construction of new, useful general-purpose types.
+
+$(SCRIPT inhibitQuickIndex = 1;)
+$(BOOKTABLE,
+$(TR $(TH Category) $(TH Functions))
+$(TR $(TD Tuple) $(TD
+ $(LREF isTuple)
+ $(LREF Tuple)
+ $(LREF tuple)
+ $(LREF reverse)
+))
+$(TR $(TD Flags) $(TD
+ $(LREF BitFlags)
+ $(LREF isBitFlagEnum)
+ $(LREF Flag)
+ $(LREF No)
+ $(LREF Yes)
+))
+$(TR $(TD Memory allocation) $(TD
+ $(LREF RefCounted)
+ $(LREF refCounted)
+ $(LREF RefCountedAutoInitialize)
+ $(LREF scoped)
+ $(LREF Unique)
+))
+$(TR $(TD Code generation) $(TD
+ $(LREF AutoImplement)
+ $(LREF BlackHole)
+ $(LREF generateAssertTrap)
+ $(LREF generateEmptyFunction)
+ $(LREF WhiteHole)
+))
+$(TR $(TD Nullable) $(TD
+ $(LREF Nullable)
+ $(LREF nullable)
+ $(LREF NullableRef)
+ $(LREF nullableRef)
+))
+$(TR $(TD Proxies) $(TD
+ $(LREF Proxy)
+ $(LREF rebindable)
+ $(LREF Rebindable)
+ $(LREF ReplaceType)
+ $(LREF unwrap)
+ $(LREF wrap)
+))
+$(TR $(TD Types) $(TD
+ $(LREF alignForSize)
+ $(LREF Ternary)
+ $(LREF Typedef)
+ $(LREF TypedefType)
+ $(LREF UnqualRef)
+))
+)
+
+Copyright: Copyright the respective authors, 2008-
+License: $(HTTP boost.org/LICENSE_1_0.txt, Boost License 1.0).
+Source: $(PHOBOSSRC std/_typecons.d)
+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;
+
+///
+@safe unittest
+{
+ // value tuples
+ alias Coord = Tuple!(int, "x", int, "y", int, "z");
+ Coord c;
+ c[1] = 1; // access by index
+ c.z = 1; // access by given name
+ assert(c == Coord(0, 1, 1));
+
+ // names can be omitted
+ alias DicEntry = Tuple!(string, string);
+
+ // tuples can also be constructed on instantiation
+ assert(tuple(2, 3, 4)[1] == 3);
+ // construction on instantiation works with names too
+ assert(tuple!("x", "y", "z")(2, 3, 4).y == 3);
+
+ // Rebindable references to const and immutable objects
+ {
+ class Widget { void foo() const @safe {} }
+ const w1 = new Widget, w2 = new Widget;
+ w1.foo();
+ // w1 = w2 would not work; can't rebind const object
+ auto r = Rebindable!(const Widget)(w1);
+ // invoke method as if r were a Widget object
+ r.foo();
+ // rebind r to refer to another object
+ r = w2;
+ }
+}
+
+debug(Unique) import std.stdio;
+
+/**
+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 or
+instance of an interface, 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)
+{
+/** Represents a reference to $(D T). Resolves to $(D T*) if $(D T) is a value type. */
+static if (is(T == class) || is(T == interface))
+ alias RefT = T;
+else
+ alias RefT = T*;
+
+public:
+ // Deferred in case we get some language support for checking uniqueness.
+ version (None)
+ /**
+ Allows safe construction of $(D Unique). It creates the resource and
+ guarantees unique ownership of it (unless $(D T) publishes aliases of
+ $(D this)).
+ Note: Nested structs/classes cannot be created.
+ Params:
+ args = Arguments to pass to $(D T)'s constructor.
+ ---
+ static class C {}
+ auto u = Unique!(C).create();
+ ---
+ */
+ static Unique!T create(A...)(auto ref A args)
+ if (__traits(compiles, new T(args)))
+ {
+ debug(Unique) writeln("Unique.create for ", T.stringof);
+ Unique!T u;
+ u._p = new T(args);
+ return u;
+ }
+
+ /**
+ Constructor that takes an rvalue.
+ It will ensure uniqueness, as long as the rvalue
+ isn't just a view on an lvalue (e.g., a cast).
+ Typical usage:
+ ----
+ Unique!Foo f = new Foo;
+ ----
+ */
+ this(RefT p)
+ {
+ debug(Unique) writeln("Unique constructor with rvalue");
+ _p = p;
+ }
+ /**
+ Constructor that takes an lvalue. It nulls its source.
+ The nulling will ensure uniqueness as long as there
+ are no previous aliases to the source.
+ */
+ this(ref RefT p)
+ {
+ _p = p;
+ debug(Unique) writeln("Unique constructor nulling source");
+ p = null;
+ assert(p is null);
+ }
+ /**
+ Constructor that takes a $(D Unique) of a type that is convertible to our type.
+
+ Typically used to transfer a $(D Unique) rvalue of derived type to
+ a $(D Unique) of base type.
+ Example:
+ ---
+ class C : Object {}
+
+ Unique!C uc = new C;
+ Unique!Object uo = uc.release;
+ ---
+ */
+ this(U)(Unique!U u)
+ if (is(u.RefT:RefT))
+ {
+ debug(Unique) writeln("Unique constructor converting from ", U.stringof);
+ _p = u._p;
+ u._p = null;
+ }
+
+ /// Transfer ownership from a $(D Unique) of a type that is convertible to our type.
+ void opAssign(U)(Unique!U u)
+ if (is(u.RefT:RefT))
+ {
+ debug(Unique) writeln("Unique opAssign converting from ", U.stringof);
+ // first delete any resource we own
+ destroy(this);
+ _p = u._p;
+ u._p = null;
+ }
+
+ ~this()
+ {
+ debug(Unique) writeln("Unique destructor of ", (_p is null)? null: _p);
+ 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.
+ Same as calling std.algorithm.move on it.
+ */
+ Unique release()
+ {
+ debug(Unique) writeln("Unique Release");
+ import std.algorithm.mutation : move;
+ return this.move;
+ }
+
+ /** Forwards member access to contents. */
+ mixin Proxy!_p;
+
+ /**
+ Postblit operator is undefined to prevent the cloning of $(D Unique) objects.
+ */
+ @disable this(this);
+
+private:
+ RefT _p;
+}
+
+///
+@system unittest
+{
+ static struct S
+ {
+ int i;
+ this(int i){this.i = i;}
+ }
+ Unique!S produce()
+ {
+ // Construct a unique instance of S on the heap
+ Unique!S ut = new S(5);
+ // Implicit transfer of ownership
+ return ut;
+ }
+ // Borrow a unique resource by ref
+ void increment(ref Unique!S ur)
+ {
+ ur.i++;
+ }
+ void consume(Unique!S u2)
+ {
+ assert(u2.i == 6);
+ // Resource automatically deleted here
+ }
+ Unique!S u1;
+ assert(u1.isEmpty);
+ u1 = produce();
+ increment(u1);
+ assert(u1.i == 6);
+ //consume(u1); // Error: u1 is not copyable
+ // Transfer ownership of the resource
+ consume(u1.release);
+ assert(u1.isEmpty);
+}
+
+@system unittest
+{
+ // test conversion to base ref
+ int deleted = 0;
+ class C
+ {
+ ~this(){deleted++;}
+ }
+ // constructor conversion
+ Unique!Object u = Unique!C(new C);
+ static assert(!__traits(compiles, {u = new C;}));
+ assert(!u.isEmpty);
+ destroy(u);
+ assert(deleted == 1);
+
+ Unique!C uc = new C;
+ static assert(!__traits(compiles, {Unique!Object uo = uc;}));
+ Unique!Object uo = new C;
+ // opAssign conversion, deleting uo resource first
+ uo = uc.release;
+ assert(uc.isEmpty);
+ assert(!uo.isEmpty);
+ assert(deleted == 2);
+}
+
+@system unittest
+{
+ debug(Unique) writeln("Unique class");
+ class Bar
+ {
+ ~this() { debug(Unique) writeln(" Bar destructor"); }
+ int val() const { return 4; }
+ }
+ alias UBar = Unique!(Bar);
+ UBar g(UBar u)
+ {
+ debug(Unique) writeln("inside g");
+ return u.release;
+ }
+ auto ub = UBar(new Bar);
+ assert(!ub.isEmpty);
+ assert(ub.val == 4);
+ static assert(!__traits(compiles, {auto ub3 = g(ub);}));
+ debug(Unique) writeln("Calling g");
+ auto ub2 = g(ub.release);
+ debug(Unique) writeln("Returned from g");
+ assert(ub.isEmpty);
+ assert(!ub2.isEmpty);
+}
+
+@system unittest
+{
+ debug(Unique) writeln("Unique interface");
+ interface Bar
+ {
+ int val() const;
+ }
+ class BarImpl : Bar
+ {
+ static int count;
+ this()
+ {
+ count++;
+ }
+ ~this()
+ {
+ count--;
+ }
+ int val() const { return 4; }
+ }
+ alias UBar = Unique!Bar;
+ UBar g(UBar u)
+ {
+ debug(Unique) writeln("inside g");
+ return u.release;
+ }
+ void consume(UBar u)
+ {
+ assert(u.val() == 4);
+ // Resource automatically deleted here
+ }
+ auto ub = UBar(new BarImpl);
+ assert(BarImpl.count == 1);
+ assert(!ub.isEmpty);
+ assert(ub.val == 4);
+ static assert(!__traits(compiles, {auto ub3 = g(ub);}));
+ debug(Unique) writeln("Calling g");
+ auto ub2 = g(ub.release);
+ debug(Unique) writeln("Returned from g");
+ assert(ub.isEmpty);
+ assert(!ub2.isEmpty);
+ consume(ub2.release);
+ assert(BarImpl.count == 0);
+}
+
+@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);
+
+ UFoo f(UFoo u)
+ {
+ debug(Unique) writeln("inside f");
+ return u.release;
+ }
+
+ auto uf = UFoo(new Foo);
+ assert(!uf.isEmpty);
+ assert(uf.val == 3);
+ static assert(!__traits(compiles, {auto uf3 = f(uf);}));
+ debug(Unique) writeln("Unique struct: calling f");
+ auto uf2 = f(uf.release);
+ debug(Unique) writeln("Unique struct: returned from f");
+ assert(uf.isEmpty);
+ 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
+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
+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 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.meta : staticMap;
+
+ // Parse (type,name) pairs (FieldSpecs) out of the specified
+ // arguments. Some fields would have name, others not.
+ template parseSpecs(Specs...)
+ {
+ static if (Specs.length == 0)
+ {
+ alias parseSpecs = AliasSeq!();
+ }
+ else static if (is(Specs[0]))
+ {
+ static if (is(typeof(Specs[1]) : string))
+ {
+ alias parseSpecs =
+ AliasSeq!(FieldSpec!(Specs[0 .. 2]),
+ parseSpecs!(Specs[2 .. $]));
+ }
+ else
+ {
+ alias parseSpecs =
+ AliasSeq!(FieldSpec!(Specs[0]),
+ parseSpecs!(Specs[1 .. $]));
+ }
+ }
+ else
+ {
+ static assert(0, "Attempted to instantiate Tuple with an "
+ ~"invalid argument: "~ Specs[0].stringof);
+ }
+ }
+
+ template FieldSpec(T, string s = "")
+ {
+ alias Type = T;
+ alias name = s;
+ }
+
+ alias fieldSpecs = parseSpecs!Specs;
+
+ // Used with staticMap.
+ alias extractType(alias spec) = spec.Type;
+ alias extractName(alias spec) = spec.name;
+
+ // Generates named fields as follows:
+ // alias name_0 = Identity!(field[0]);
+ // alias name_1 = Identity!(field[1]);
+ // :
+ // NOTE: field[k] is an expression (which yields a symbol of a
+ // variable) and can't be aliased directly.
+ string injectNamedFields()
+ {
+ string decl = "";
+ foreach (i, name; staticMap!(extractName, fieldSpecs))
+ {
+ import std.format : format;
+
+ decl ~= format("alias _%s = Identity!(field[%s]);", i, i);
+ if (name.length != 0)
+ {
+ decl ~= format("alias %s = _%s;", name, i);
+ }
+ }
+ return decl;
+ }
+
+ // Returns Specs for a subtuple this[from .. to] preserving field
+ // names if any.
+ alias sliceSpecs(size_t from, size_t to) =
+ staticMap!(expandSpec, fieldSpecs[from .. to]);
+
+ template expandSpec(alias spec)
+ {
+ static if (spec.name.length == 0)
+ {
+ alias expandSpec = AliasSeq!(spec.Type);
+ }
+ else
+ {
+ alias expandSpec = AliasSeq!(spec.Type, spec.name);
+ }
+ }
+
+ enum areCompatibleTuples(Tup1, Tup2, string op) = isTuple!Tup2 && is(typeof(
+ (ref Tup1 tup1, ref Tup2 tup2)
+ {
+ static assert(tup1.field.length == tup2.field.length);
+ foreach (i, _; Tup1.Types)
+ {
+ auto lhs = typeof(tup1.field[i]).init;
+ auto rhs = typeof(tup2.field[i]).init;
+ static if (op == "=")
+ lhs = rhs;
+ else
+ auto result = mixin("lhs "~op~" rhs");
+ }
+ }));
+
+ enum areBuildCompatibleTuples(Tup1, Tup2) = isTuple!Tup2 && is(typeof(
+ {
+ static assert(Tup1.Types.length == Tup2.Types.length);
+ foreach (i, _; Tup1.Types)
+ static assert(isBuildable!(Tup1.Types[i], Tup2.Types[i]));
+ }));
+
+ /+ Returns $(D true) iff a $(D T) can be initialized from a $(D U). +/
+ enum isBuildable(T, U) = is(typeof(
+ {
+ U u = U.init;
+ T t = u;
+ }));
+ /+ Helper for partial instanciation +/
+ template isBuildableFrom(U)
+ {
+ enum isBuildableFrom(T) = isBuildable!(T, U);
+ }
+
+ struct Tuple
+ {
+ /**
+ * The types of the `Tuple`'s components.
+ */
+ alias Types = staticMap!(extractType, fieldSpecs);
+
+ ///
+ static if (Specs.length == 0) @safe unittest
+ {
+ alias Fields = Tuple!(int, "id", 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);
+
+ ///
+ static if (Specs.length == 0) @safe unittest
+ {
+ alias Fields = Tuple!(int, "id", string, float);
+ static assert(Fields.fieldNames == AliasSeq!("id", "", ""));
+ }
+
+ /**
+ * Use $(D t.expand) for a `Tuple` $(D t) to expand it into its
+ * components. The result of $(D expand) acts as if the `Tuple`'s components
+ * were listed as a list of values. (Ordinarily, a $(D Tuple) acts as a
+ * single value.)
+ */
+ Types expand;
+ mixin(injectNamedFields());
+
+ ///
+ 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)
+ {
+ assert(n == 4 && s == "test" && b == false);
+ }
+
+ auto t2 = tuple(4, "test", false);
+ //t.expand acting as a list of values
+ takeSeveralTypes(t2.expand);
+ }
+
+ static if (is(Specs))
+ {
+ // This is mostly to make t[n] work.
+ alias expand this;
+ }
+ else
+ {
+ @property
+ ref inout(Tuple!Types) _Tuple_super() inout @trusted
+ {
+ foreach (i, _; Types) // Rely on the field layout
+ {
+ static assert(typeof(return).init.tupleof[i].offsetof ==
+ expand[i].offsetof);
+ }
+ return *cast(typeof(return)*) &(field[0]);
+ }
+ // This is mostly to make t[n] work.
+ alias _Tuple_super this;
+ }
+
+ // backwards compatibility
+ alias field = expand;
+
+ /**
+ * Constructor taking one value for each field.
+ *
+ * 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
+ * order as they appear in `Types`.
+ */
+ static if (Types.length > 0)
+ {
+ this(Types values)
+ {
+ field[] = values[];
+ }
+ }
+
+ ///
+ static if (Specs.length == 0) @safe unittest
+ {
+ alias ISD = Tuple!(int, string, double);
+ auto tup = ISD(1, "test", 3.2);
+ assert(tup.toString() == `Tuple!(int, string, double)(1, "test", 3.2)`);
+ }
+
+ /**
+ * Constructor taking a compatible array.
+ *
+ * Params:
+ * values = A compatible static array to build the `Tuple` from.
+ * Array slices are not supported.
+ */
+ this(U, size_t n)(U[n] values)
+ if (n == Types.length && allSatisfy!(isBuildableFrom!U, Types))
+ {
+ foreach (i, _; Types)
+ {
+ field[i] = values[i];
+ }
+ }
+
+ ///
+ static if (Specs.length == 0) @safe unittest
+ {
+ int[2] ints;
+ Tuple!(int, int) t = ints;
+ }
+
+ /**
+ * Constructor taking a compatible `Tuple`. Two `Tuple`s are compatible
+ * $(B iff) they are both of the same length, and, for each type `T` on the
+ * left-hand side, the corresponding type `U` on the right-hand side can
+ * implicitly convert to `T`.
+ *
+ * Params:
+ * another = A compatible `Tuple` to build from. Its type must be
+ * compatible with the target `Tuple`'s type.
+ */
+ this(U)(U another)
+ if (areBuildCompatibleTuples!(typeof(this), U))
+ {
+ field[] = another.field[];
+ }
+
+ ///
+ 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
+ //IntVec iv2 = dv;
+ }
+
+ /**
+ * Comparison for equality. Two `Tuple`s are considered equal
+ * $(B iff) they fulfill the following criteria:
+ *
+ * $(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
+ * 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
+ * true.))
+ *
+ * Params:
+ * rhs = The `Tuple` to compare against. It must meeting the criteria
+ * for comparison between `Tuple`s.
+ *
+ * Returns:
+ * true if both `Tuple`s are equal, otherwise false.
+ */
+ bool opEquals(R)(R rhs)
+ if (areCompatibleTuples!(typeof(this), R, "=="))
+ {
+ return field[] == rhs.field[];
+ }
+
+ /// ditto
+ bool opEquals(R)(R rhs) const
+ if (areCompatibleTuples!(typeof(this), R, "=="))
+ {
+ return field[] == rhs.field[];
+ }
+
+ ///
+ static if (Specs.length == 0) @safe unittest
+ {
+ Tuple!(int, string) t1 = tuple(1, "test");
+ Tuple!(double, string) t2 = tuple(1.0, "test");
+ //Ok, int can be compared with double and
+ //both have a value of 1
+ assert(t1 == t2);
+ }
+
+ /**
+ * Comparison for ordering.
+ *
+ * Params:
+ * rhs = The `Tuple` to compare against. It must meet the criteria
+ * for comparison between `Tuple`s.
+ *
+ * Returns:
+ * For any values `v1` on the right-hand side and `v2` on the
+ * left-hand side:
+ *
+ * $(UL
+ * $(LI A negative integer if the expression `v1 < v2` is true.)
+ * $(LI A positive integer if the expression `v1 > v2` is true.)
+ * $(LI 0 if the expression `v1 == v2` is true.))
+ */
+ int opCmp(R)(R rhs)
+ if (areCompatibleTuples!(typeof(this), R, "<"))
+ {
+ foreach (i, Unused; Types)
+ {
+ if (field[i] != rhs.field[i])
+ {
+ return field[i] < rhs.field[i] ? -1 : 1;
+ }
+ }
+ return 0;
+ }
+
+ /// ditto
+ int opCmp(R)(R rhs) const
+ if (areCompatibleTuples!(typeof(this), R, "<"))
+ {
+ foreach (i, Unused; Types)
+ {
+ if (field[i] != rhs.field[i])
+ {
+ return field[i] < rhs.field[i] ? -1 : 1;
+ }
+ }
+ return 0;
+ }
+
+ /**
+ The first `v1` for which `v1 > v2` is true determines
+ the result. This could lead to unexpected behaviour.
+ */
+ 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);
+ }
+
+ /**
+ * Assignment from another `Tuple`.
+ *
+ * Params:
+ * 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.mutation : swap;
+
+ static if (is(R : Tuple!Types) && !__traits(isRef, rhs))
+ {
+ if (__ctfe)
+ {
+ // Cannot use swap at compile time
+ field[] = rhs.field[];
+ }
+ else
+ {
+ // Use swap-and-destroy to optimize rvalue assignment
+ swap!(Tuple!Types)(this, rhs);
+ }
+ }
+ else
+ {
+ // Do not swap; opAssign should be called on the fields.
+ field[] = rhs.field[];
+ }
+ }
+
+ /**
+ * 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
+ * the original.
+ */
+ @property
+ 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]);
+ }
+
+ ///
+ static if (Specs.length == 0) @safe unittest
+ {
+ Tuple!(int, string, float, double) a;
+ a[1] = "abc";
+ a[2] = 4.5;
+ 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`.
+ */
+ size_t toHash() const nothrow @trusted
+ {
+ size_t h = 0;
+ foreach (i, T; Types)
+ h += typeid(T).getHash(cast(const void*)&field[i]);
+ return h;
+ }
+
+ ///
+ template toString()
+ {
+ /**
+ * Converts to string.
+ *
+ * Returns:
+ * The string representation of this `Tuple`.
+ */
+ string toString()() const
+ {
+ 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)
+ {
+ 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));
+ }
+ }
+ else if (fmt.spec == 's')
+ {
+ 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
+ {
+ throw new FormatException(
+ "Expected '%s' or '%(...%)' or '%(...%|...%)' format specifier for type '" ~
+ Unqual!(typeof(this)).stringof ~ "', not '%" ~ fmt.spec ~ "'.");
+ }
+ }
+ }
+ }
+}
+
+///
+@safe unittest
+{
+ Tuple!(int, int) point;
+ // assign coordinates
+ point[0] = 5;
+ point[1] = 6;
+ // read coordinates
+ auto x = point[0];
+ 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.
+ */
+@safe unittest
+{
+ alias Entry = Tuple!(int, "index", string, "value");
+ Entry e;
+ e.index = 4;
+ e.value = "Hello";
+ assert(e[1] == "Hello");
+ assert(e[0] == 4);
+}
+
+/**
+ A `Tuple` with named fields is a distinct type from a `Tuple` with unnamed
+ fields, i.e. each naming imparts a separate type for the `Tuple`. Two
+ `Tuple`s differing in naming only are still distinct, even though they
+ might have the same structure.
+ */
+@safe unittest
+{
+ Tuple!(int, "x", int, "y") point1;
+ Tuple!(int, int) point2;
+ assert(!is(typeof(point1) == typeof(point2)));
+}
+
+/**
+ Creates a copy of a $(LREF Tuple) with its fields in _reverse order.
+
+ Params:
+ t = The `Tuple` to copy.
+
+ Returns:
+ A new `Tuple`.
+ */
+auto reverse(T)(T t)
+ if (isTuple!T)
+{
+ 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));
+
+ ReverseTupleType!T result;
+ auto tup = t.expand;
+ result.expand = Reverse!tup;
+ return result;
+}
+
+///
+@safe unittest
+{
+ auto tup = tuple(1, "2");
+ assert(tup.reverse == tuple("2", 1));
+}
+
+/* Get a Tuple type with the reverse specification of Tuple T. */
+private template ReverseTupleType(T)
+ if (isTuple!T)
+{
+ static if (is(T : Tuple!A, A...))
+ alias ReverseTupleType = Tuple!(ReverseTupleSpecs!A);
+}
+
+/* Reverse the Specs of a Tuple. */
+private template ReverseTupleSpecs(T...)
+{
+ static if (T.length > 1)
+ {
+ static if (is(typeof(T[$-1]) : string))
+ {
+ alias ReverseTupleSpecs = AliasSeq!(T[$-2], T[$-1], ReverseTupleSpecs!(T[0 .. $-2]));
+ }
+ else
+ {
+ alias ReverseTupleSpecs = AliasSeq!(T[$-1], ReverseTupleSpecs!(T[0 .. $-1]));
+ }
+ }
+ else
+ {
+ alias ReverseTupleSpecs = T;
+ }
+}
+
+// ensure that internal Tuple unittests are compiled
+@safe unittest
+{
+ Tuple!() t;
+}
+
+@safe unittest
+{
+ import std.conv;
+ {
+ Tuple!(int, "a", int, "b") nosh;
+ static assert(nosh.length == 2);
+ nosh.a = 5;
+ nosh.b = 6;
+ assert(nosh.a == 5);
+ assert(nosh.b == 6);
+ }
+ {
+ Tuple!(short, double) b;
+ static assert(b.length == 2);
+ b[1] = 5;
+ auto a = Tuple!(int, real)(b);
+ assert(a[0] == 0 && a[1] == 5);
+ a = Tuple!(int, real)(1, 2);
+ assert(a[0] == 1 && a[1] == 2);
+ auto c = Tuple!(int, "a", double, "b")(a);
+ assert(c[0] == 1 && c[1] == 2);
+ }
+ {
+ Tuple!(int, real) nosh;
+ nosh[0] = 5;
+ nosh[1] = 0;
+ assert(nosh[0] == 5 && nosh[1] == 0);
+ assert(nosh.to!string == "Tuple!(int, real)(5, 0)", nosh.to!string);
+ Tuple!(int, int) yessh;
+ nosh = yessh;
+ }
+ {
+ class A {}
+ Tuple!(int, shared A) nosh;
+ nosh[0] = 5;
+ assert(nosh[0] == 5 && nosh[1] is null);
+ assert(nosh.to!string == "Tuple!(int, shared(A))(5, shared(A))");
+ }
+ {
+ Tuple!(int, string) t;
+ t[0] = 10;
+ t[1] = "str";
+ assert(t[0] == 10 && t[1] == "str");
+ assert(t.to!string == `Tuple!(int, string)(10, "str")`, t.to!string);
+ }
+ {
+ Tuple!(int, "a", double, "b") x;
+ static assert(x.a.offsetof == x[0].offsetof);
+ static assert(x.b.offsetof == x[1].offsetof);
+ x.b = 4.5;
+ x.a = 5;
+ assert(x[0] == 5 && x[1] == 4.5);
+ assert(x.a == 5 && x.b == 4.5);
+ }
+ // indexing
+ {
+ Tuple!(int, real) t;
+ static assert(is(typeof(t[0]) == int));
+ static assert(is(typeof(t[1]) == real));
+ int* p0 = &t[0];
+ real* p1 = &t[1];
+ t[0] = 10;
+ t[1] = -200.0L;
+ assert(*p0 == t[0]);
+ assert(*p1 == t[1]);
+ }
+ // slicing
+ {
+ Tuple!(int, "x", real, "y", double, "z", string) t;
+ t[0] = 10;
+ t[1] = 11;
+ t[2] = 12;
+ t[3] = "abc";
+ auto a = t.slice!(0, 3);
+ assert(a.length == 3);
+ assert(a.x == t.x);
+ assert(a.y == t.y);
+ assert(a.z == t.z);
+ auto b = t.slice!(2, 4);
+ assert(b.length == 2);
+ assert(b.z == t.z);
+ assert(b[1] == t[3]);
+ }
+ // nesting
+ {
+ Tuple!(Tuple!(int, real), Tuple!(string, "s")) t;
+ static assert(is(typeof(t[0]) == Tuple!(int, real)));
+ static assert(is(typeof(t[1]) == Tuple!(string, "s")));
+ static assert(is(typeof(t[0][0]) == int));
+ static assert(is(typeof(t[0][1]) == real));
+ static assert(is(typeof(t[1].s) == string));
+ t[0] = tuple(10, 20.0L);
+ t[1].s = "abc";
+ assert(t[0][0] == 10);
+ assert(t[0][1] == 20.0L);
+ assert(t[1].s == "abc");
+ }
+ // non-POD
+ {
+ static struct S
+ {
+ int count;
+ this(this) { ++count; }
+ ~this() { --count; }
+ void opAssign(S rhs) { count = rhs.count; }
+ }
+ Tuple!(S, S) ss;
+ Tuple!(S, S) ssCopy = ss;
+ assert(ssCopy[0].count == 1);
+ assert(ssCopy[1].count == 1);
+ ssCopy[1] = ssCopy[0];
+ assert(ssCopy[1].count == 2);
+ }
+ // bug 2800
+ {
+ static struct R
+ {
+ Tuple!(int, int) _front;
+ @property ref Tuple!(int, int) front() return { return _front; }
+ @property bool empty() { return _front[0] >= 10; }
+ void popFront() { ++_front[0]; }
+ }
+ foreach (a; R())
+ {
+ static assert(is(typeof(a) == Tuple!(int, int)));
+ assert(0 <= a[0] && a[0] < 10);
+ assert(a[1] == 0);
+ }
+ }
+ // Construction with compatible elements
+ {
+ auto t1 = Tuple!(int, double)(1, 1);
+
+ // 8702
+ auto t8702a = tuple(tuple(1));
+ auto t8702b = Tuple!(Tuple!(int))(Tuple!(int)(1));
+ }
+ // Construction with compatible tuple
+ {
+ Tuple!(int, int) x;
+ x[0] = 10;
+ x[1] = 20;
+ Tuple!(int, "a", double, "b") y = x;
+ assert(y.a == 10);
+ assert(y.b == 20);
+ // incompatible
+ static assert(!__traits(compiles, Tuple!(int, int)(y)));
+ }
+ // 6275
+ {
+ const int x = 1;
+ auto t1 = tuple(x);
+ alias T = Tuple!(const(int));
+ auto t2 = T(1);
+ }
+ // 9431
+ {
+ alias T = Tuple!(int[1][]);
+ auto t = T([[10]]);
+ }
+ // 7666
+ {
+ auto tup = tuple(1, "2");
+ assert(tup.reverse == tuple("2", 1));
+ }
+ {
+ Tuple!(int, "x", string, "y") tup = tuple(1, "2");
+ auto rev = tup.reverse;
+ assert(rev == tuple("2", 1));
+ assert(rev.x == 1 && rev.y == "2");
+ }
+ {
+ Tuple!(wchar, dchar, int, "x", string, "y", char, byte, float) tup;
+ 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.x == 3 && rev.y == "4");
+ }
+}
+@safe unittest
+{
+ // opEquals
+ {
+ struct Equ1 { bool opEquals(Equ1) { return true; } }
+ auto tm1 = tuple(Equ1.init);
+ const tc1 = tuple(Equ1.init);
+ static assert( is(typeof(tm1 == tm1)));
+ static assert(!is(typeof(tm1 == tc1)));
+ static assert(!is(typeof(tc1 == tm1)));
+ static assert(!is(typeof(tc1 == tc1)));
+
+ struct Equ2 { bool opEquals(const Equ2) const { return true; } }
+ auto tm2 = tuple(Equ2.init);
+ const tc2 = tuple(Equ2.init);
+ static assert( is(typeof(tm2 == tm2)));
+ static assert( is(typeof(tm2 == tc2)));
+ static assert( is(typeof(tc2 == tm2)));
+ static assert( is(typeof(tc2 == tc2)));
+
+ struct Equ3 { bool opEquals(T)(T) { return true; } }
+ auto tm3 = tuple(Equ3.init); // bugzilla 8686
+ const tc3 = tuple(Equ3.init);
+ static assert( is(typeof(tm3 == tm3)));
+ static assert( is(typeof(tm3 == tc3)));
+ static assert(!is(typeof(tc3 == tm3)));
+ static assert(!is(typeof(tc3 == tc3)));
+
+ struct Equ4 { bool opEquals(T)(T) const { return true; } }
+ auto tm4 = tuple(Equ4.init);
+ const tc4 = tuple(Equ4.init);
+ static assert( is(typeof(tm4 == tm4)));
+ static assert( is(typeof(tm4 == tc4)));
+ static assert( is(typeof(tc4 == tm4)));
+ static assert( is(typeof(tc4 == tc4)));
+ }
+ // opCmp
+ {
+ struct Cmp1 { int opCmp(Cmp1) { return 0; } }
+ auto tm1 = tuple(Cmp1.init);
+ const tc1 = tuple(Cmp1.init);
+ static assert( is(typeof(tm1 < tm1)));
+ static assert(!is(typeof(tm1 < tc1)));
+ static assert(!is(typeof(tc1 < tm1)));
+ static assert(!is(typeof(tc1 < tc1)));
+
+ struct Cmp2 { int opCmp(const Cmp2) const { return 0; } }
+ auto tm2 = tuple(Cmp2.init);
+ const tc2 = tuple(Cmp2.init);
+ static assert( is(typeof(tm2 < tm2)));
+ static assert( is(typeof(tm2 < tc2)));
+ static assert( is(typeof(tc2 < tm2)));
+ static assert( is(typeof(tc2 < tc2)));
+
+ struct Cmp3 { int opCmp(T)(T) { return 0; } }
+ auto tm3 = tuple(Cmp3.init);
+ const tc3 = tuple(Cmp3.init);
+ static assert( is(typeof(tm3 < tm3)));
+ static assert( is(typeof(tm3 < tc3)));
+ static assert(!is(typeof(tc3 < tm3)));
+ static assert(!is(typeof(tc3 < tc3)));
+
+ struct Cmp4 { int opCmp(T)(T) const { return 0; } }
+ auto tm4 = tuple(Cmp4.init);
+ const tc4 = tuple(Cmp4.init);
+ static assert( is(typeof(tm4 < tm4)));
+ static assert( is(typeof(tm4 < tc4)));
+ 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;
+ assert(t[0] == 1 && t[1] == 2);
+ Tuple!(long, uint) t2 = ints;
+ assert(t2[0] == 1 && t2[1] == 2);
+ }
+}
+@safe unittest
+{
+ auto t1 = Tuple!(int, "x", string, "y")(1, "a");
+ assert(t1.x == 1);
+ assert(t1.y == "a");
+ void foo(Tuple!(int, string) t2) {}
+ foo(t1);
+
+ Tuple!(int, int)[] arr;
+ arr ~= tuple(10, 20); // OK
+ arr ~= Tuple!(int, "x", int, "y")(10, 20); // NG -> OK
+
+ static assert(is(typeof(Tuple!(int, "x", string, "y").tupleof) ==
+ typeof(Tuple!(int, string ).tupleof)));
+}
+@safe unittest
+{
+ // Bugzilla 10686
+ immutable Tuple!(int) t1;
+ auto r1 = t1[0]; // OK
+ immutable Tuple!(int, "x") t2;
+ auto r2 = t2[0]; // error
+}
+@safe unittest
+{
+ import std.exception : assertCTFEable;
+
+ // Bugzilla 10218
+ assertCTFEable!(
+ {
+ auto t = tuple(1);
+ t = tuple(2); // assignment
+ });
+}
+@safe unittest
+{
+ class Foo{}
+ Tuple!(immutable(Foo)[]) a;
+}
+
+@safe unittest
+{
+ //Test non-assignable
+ static struct S
+ {
+ int* p;
+ }
+ alias IS = immutable S;
+ static assert(!isAssignable!IS);
+
+ auto s = IS.init;
+
+ alias TIS = Tuple!IS;
+ TIS a = tuple(s);
+ TIS b = a;
+
+ alias TISIS = Tuple!(IS, IS);
+ TISIS d = tuple(s, s);
+ IS[2] ss;
+ TISIS e = TISIS(ss);
+}
+
+// Bugzilla #9819
+@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 == AliasSeq!("id", "", ""));
+}
+
+// Bugzilla 13837
+@safe unittest
+{
+ // New behaviour, named arguments.
+ static assert(is(
+ typeof(tuple!("x")(1)) == Tuple!(int, "x")));
+ static assert(is(
+ typeof(tuple!("x")(1.0)) == Tuple!(double, "x")));
+ static assert(is(
+ typeof(tuple!("x")("foo")) == Tuple!(string, "x")));
+ static assert(is(
+ typeof(tuple!("x", "y")(1, 2.0)) == Tuple!(int, "x", double, "y")));
+
+ auto a = tuple!("a", "b", "c")("1", 2, 3.0f);
+ static assert(is(typeof(a.a) == string));
+ static assert(is(typeof(a.b) == int));
+ static assert(is(typeof(a.c) == float));
+
+ // Old behaviour, but with explicit type parameters.
+ static assert(is(
+ typeof(tuple!(int, double)(1, 2.0)) == Tuple!(int, double)));
+ static assert(is(
+ typeof(tuple!(const int)(1)) == Tuple!(const int)));
+ static assert(is(
+ typeof(tuple()) == Tuple!()));
+
+ // Nonsensical behaviour
+ static assert(!__traits(compiles, tuple!(1)(2)));
+ static assert(!__traits(compiles, tuple!("x")(1, 2)));
+ static assert(!__traits(compiles, tuple!("x", "y")(1)));
+ static assert(!__traits(compiles, tuple!("x")()));
+ static assert(!__traits(compiles, tuple!("x", int)(2)));
+}
+
+@safe unittest
+{
+ class C {}
+ Tuple!(Rebindable!(const C)) a;
+ Tuple!(const C) b;
+ a = b;
+}
+
+@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 $(LREF Tuple) object instantiated and initialized according to
+ the given arguments.
+
+ Params:
+ 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.
+ */
+ auto tuple(Args...)(Args args)
+ {
+ static if (Names.length == 0)
+ {
+ // No specified names, just infer types from Args...
+ return Tuple!Args(args);
+ }
+ else static if (!is(typeof(Names[0]) : string))
+ {
+ // Names[0] isn't a string, must be explicit types.
+ return Tuple!Names(args);
+ }
+ else
+ {
+ // Names[0] is a string, so must be specifying names.
+ static assert(Names.length == Args.length,
+ "Insufficient number of names given.");
+
+ // Interleave(a, b).and(c, d) == (a, c, b, d)
+ // This is to get the interleaving of types and names for Tuple
+ // e.g. Tuple!(int, "x", string, "y")
+ template Interleave(A...)
+ {
+ template and(B...) if (B.length == 1)
+ {
+ alias and = AliasSeq!(A[0], B[0]);
+ }
+
+ template and(B...) if (B.length != 1)
+ {
+ alias and = AliasSeq!(A[0], B[0],
+ Interleave!(A[1..$]).and!(B[1..$]));
+ }
+ }
+ return Tuple!(Interleave!(Args).and!(Names))(args);
+ }
+ }
+}
+
+///
+@safe unittest
+{
+ auto value = tuple(5, 6.7, "hello");
+ assert(value[0] == 5);
+ assert(value[1] == 6.7);
+ assert(value[2] == "hello");
+
+ // Field names can be provided.
+ auto entry = tuple!("index", "value")(4, "Hello");
+ assert(entry.index == 4);
+ assert(entry.value == "Hello");
+}
+
+/**
+ 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.
+ */
+enum isTuple(T) = __traits(compiles,
+ {
+ void f(Specs...)(Tuple!Specs tup) {}
+ f(T.init);
+ } );
+
+///
+@safe unittest
+{
+ static assert(isTuple!(Tuple!()));
+ static assert(isTuple!(Tuple!(int)));
+ static assert(isTuple!(Tuple!(int, real, string)));
+ static assert(isTuple!(Tuple!(int, "x", real, "y")));
+ static assert(isTuple!(Tuple!(int, Tuple!(real), string)));
+}
+
+@safe unittest
+{
+ static assert(isTuple!(const Tuple!(int)));
+ static assert(isTuple!(immutable Tuple!(int)));
+
+ static assert(!isTuple!(int));
+ static assert(!isTuple!(const int));
+
+ struct S {}
+ static assert(!isTuple!(S));
+}
+
+// used by both Rebindable and UnqualRef
+private mixin template RebindableCommon(T, U, alias This)
+ if (is(T == class) || is(T == interface) || isAssociativeArray!T)
+{
+ private union
+ {
+ T original;
+ U stripped;
+ }
+
+ @trusted pure nothrow @nogc
+ {
+ void opAssign(T another)
+ {
+ stripped = cast(U) another;
+ }
+
+ void opAssign(typeof(this) another)
+ {
+ stripped = another.stripped;
+ }
+
+ static if (is(T == const U) && is(T == const shared U))
+ {
+ // safely assign immutable to const / const shared
+ void opAssign(This!(immutable U) another)
+ {
+ stripped = another.stripped;
+ }
+ }
+
+ this(T initializer)
+ {
+ opAssign(initializer);
+ }
+
+ @property inout(T) get() inout
+ {
+ return original;
+ }
+ }
+
+ alias get 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.
+
+You may want to use $(D Rebindable) when you want to have mutable
+storage referring to $(D const) objects, for example an array of
+references that must be sorted in place. $(D Rebindable) does not
+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, array slice type, or associative array type.
+ */
+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))
+ {
+ static if (isDynamicArray!T)
+ {
+ import std.range.primitives : ElementEncodingType;
+ alias Rebindable = const(ElementEncodingType!T)[];
+ }
+ else
+ {
+ struct Rebindable
+ {
+ mixin RebindableCommon!(T, U, Rebindable);
+ }
+ }
+ }
+ else
+ {
+ alias Rebindable = T;
+ }
+}
+
+///Regular $(D const) object references cannot be reassigned.
+@system unittest
+{
+ class Widget { int x; int y() const { return x; } }
+ const a = new Widget;
+ // Fine
+ a.y();
+ // error! can't modify const a
+ // a.x = 5;
+ // error! can't modify const a
+ // a = new Widget;
+}
+
+/**
+ However, $(D Rebindable!(Widget)) does allow reassignment,
+ while otherwise behaving exactly like a $(D const Widget).
+ */
+@system unittest
+{
+ class Widget { int x; int y() const { return x; } }
+ auto a = Rebindable!(const Widget)(new Widget);
+ // Fine
+ a.y();
+ // error! can't modify const a
+ // a.x = 5;
+ // Fine
+ 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, 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 || isAssociativeArray!T)
+{
+ typeof(return) ret;
+ ret = obj;
+ return ret;
+}
+
+/**
+This function simply returns the $(D Rebindable) object passed in. It's useful
+in generic programming cases when a given object may be either a regular
+$(D class) or a $(D Rebindable).
+
+Params:
+ obj = An instance of Rebindable!T.
+
+Returns:
+ `obj` without any modification.
+*/
+Rebindable!T rebindable(T)(Rebindable!T obj)
+{
+ return obj;
+}
+
+@system unittest
+{
+ interface CI { int foo() const; }
+ class C : CI {
+ int foo() const { return 42; }
+ @property int bar() const { return 23; }
+ }
+ Rebindable!(C) obj0;
+ static assert(is(typeof(obj0) == C));
+
+ Rebindable!(const(C)) obj1;
+ static assert(is(typeof(obj1.get) == const(C)), typeof(obj1.get).stringof);
+ static assert(is(typeof(obj1.stripped) == C));
+ obj1 = new C;
+ assert(obj1.get !is null);
+ obj1 = new const(C);
+ assert(obj1.get !is null);
+
+ Rebindable!(immutable(C)) obj2;
+ static assert(is(typeof(obj2.get) == immutable(C)));
+ static assert(is(typeof(obj2.stripped) == C));
+ obj2 = new immutable(C);
+ assert(obj1.get !is null);
+
+ // test opDot
+ assert(obj2.foo() == 42);
+ assert(obj2.bar == 23);
+
+ interface I { final int foo() const { return 42; } }
+ Rebindable!(I) obj3;
+ static assert(is(typeof(obj3) == I));
+
+ Rebindable!(const I) obj4;
+ static assert(is(typeof(obj4.get) == const I));
+ static assert(is(typeof(obj4.stripped) == I));
+ static assert(is(typeof(obj4.foo()) == int));
+ obj4 = new class I {};
+
+ Rebindable!(immutable C) obj5i;
+ Rebindable!(const C) obj5c;
+ obj5c = obj5c;
+ obj5c = obj5i;
+ obj5i = obj5i;
+ static assert(!__traits(compiles, obj5i = obj5c));
+
+ // Test the convenience functions.
+ auto obj5convenience = rebindable(obj5i);
+ assert(obj5convenience is obj5i);
+
+ auto obj6 = rebindable(new immutable(C));
+ static assert(is(typeof(obj6) == Rebindable!(immutable C)));
+ assert(obj6.foo() == 42);
+
+ auto obj7 = rebindable(new C);
+ CI interface1 = obj7;
+ auto interfaceRebind1 = rebindable(interface1);
+ assert(interfaceRebind1.foo() == 42);
+
+ const interface2 = interface1;
+ auto interfaceRebind2 = rebindable(interface2);
+ assert(interfaceRebind2.foo() == 42);
+
+ auto arr = [1,2,3,4,5];
+ const arrConst = arr;
+ assert(rebindable(arr) == arr);
+ assert(rebindable(arrConst) == arr);
+
+ // Issue 7654
+ immutable(char[]) s7654;
+ Rebindable!(typeof(s7654)) r7654 = s7654;
+
+ foreach (T; AliasSeq!(char, wchar, char, int))
+ {
+ static assert(is(Rebindable!(immutable(T[])) == immutable(T)[]));
+ static assert(is(Rebindable!(const(T[])) == const(T)[]));
+ static assert(is(Rebindable!(T[]) == T[]));
+ }
+
+ // 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.
+ */
+template UnqualRef(T)
+ if (is(T == class) || is(T == interface))
+{
+ static if (is(T == const U, U)
+ || is(T == immutable U, U)
+ || is(T == shared U, U)
+ || is(T == const shared U, U))
+ {
+ struct UnqualRef
+ {
+ mixin RebindableCommon!(T, U, UnqualRef);
+ }
+ }
+ else
+ {
+ alias UnqualRef = T;
+ }
+}
+
+///
+@system unittest
+{
+ class Data {}
+
+ static shared(Data) a;
+ static UnqualRef!(shared Data) b;
+
+ import core.thread;
+
+ auto thread = new core.thread.Thread({
+ a = new shared Data();
+ b = new shared Data();
+ });
+
+ thread.start();
+ thread.join();
+
+ assert(a !is null);
+ assert(b is null);
+}
+
+@safe unittest
+{
+ class C { }
+ alias T = UnqualRef!(const shared C);
+ static assert(is(typeof(T.stripped) == C));
+}
+
+
+
+/**
+ 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
+ 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...)(const char[][] names...)
+{
+ // Sort all of the members by .alignof.
+ // BUG: Alignment is not always optimal for align(1) structs
+ // or 80-bit reals or 64-bit primitives on x86.
+ // TRICK: Use the fact that .alignof is always a power of 2,
+ // and maximum 16 on extant systems. Thus, we can perform
+ // a very limited radix sort.
+ // Contains the members with .alignof = 64,32,16,8,4,2,1
+
+ assert(E.length == names.length,
+ "alignForSize: There should be as many member names as the types");
+
+ string[7] declaration = ["", "", "", "", "", "", ""];
+
+ 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;
+ declaration[k] ~= T.stringof ~ " " ~ names[i] ~ ";\n";
+ }
+
+ auto s = "";
+ foreach (decl; declaration)
+ s ~= decl;
+ return s;
+}
+
+///
+@safe unittest
+{
+ struct Banner {
+ mixin(alignForSize!(byte[6], double)(["name", "height"]));
+ }
+}
+
+@safe unittest
+{
+ enum x = alignForSize!(int[], char[3], short, double[5])("x", "y","z", "w");
+ struct Foo { int x; }
+ enum y = alignForSize!(ubyte, Foo, cdouble)("x", "y", "z");
+
+ enum passNormalX = x == "double[5] w;\nint[] x;\nshort z;\nchar[3] y;\n";
+ enum passNormalY = y == "cdouble z;\nFoo y;\nubyte x;\n";
+
+ enum passAbnormalX = x == "int[] x;\ndouble[5] w;\nshort z;\nchar[3] y;\n";
+ enum passAbnormalY = y == "Foo y;\ncdouble z;\nubyte x;\n";
+ // ^ blame http://d.puremagic.com/issues/show_bug.cgi?id=231
+
+ static assert(passNormalX || passAbnormalX && double.alignof <= (int[]).alignof);
+ 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
+Nullable!T) object starts in the null state. Assigning it renders it
+non-null. Calling $(D nullify) can nullify it again.
+
+Practically $(D Nullable!T) stores a $(D T) and a $(D bool).
+ */
+struct Nullable(T)
+{
+ private T _value;
+ private bool _isNull = true;
+
+/**
+Constructor initializing $(D this) with $(D value).
+
+Params:
+ value = The value to initialize this `Nullable` with.
+ */
+ this(inout T value) inout
+ {
+ _value = 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(U)(auto ref const(U) rhs) const
+ if (is(typeof(this.get == rhs)))
+ {
+ 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));
+ }
+
+ // Issue 17482
+ @system unittest
+ {
+ import std.variant : Variant;
+ Nullable!Variant a = Variant(12);
+ assert(a == 12);
+ Nullable!Variant e;
+ assert(e != 12);
+ }
+
+ template toString()
+ {
+ import std.format : FormatSpec, formatValue;
+ // Needs to be a template because of DMD @@BUG@@ 13737.
+ void toString()(scope void delegate(const(char)[]) sink, FormatSpec!char fmt)
+ {
+ if (isNull)
+ {
+ sink.formatValue("Nullable.null", fmt);
+ }
+ else
+ {
+ 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);
+ }
+ }
+ }
+
+/**
+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.
+ */
+ void nullify()()
+ {
+ .destroy(_value);
+ _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)
+ {
+ _value = value;
+ _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
+ {
+ enum message = "Called `get' on null Nullable!" ~ T.stringof ~ ".";
+ assert(!isNull, message);
+ return _value;
+ }
+
+///
+@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
+ }
+}
+
+///
+@system unittest
+{
+ import std.exception : assertThrown;
+
+ auto a = 42.nullable;
+ assert(!a.isNull);
+ assert(a.get == 42);
+
+ a.nullify();
+ assert(a.isNull);
+ assertThrown!Throwable(a.get);
+}
+
+@system unittest
+{
+ import std.exception : assertThrown;
+
+ Nullable!int a;
+ assert(a.isNull);
+ assertThrown!Throwable(a.get);
+ a = 5;
+ assert(!a.isNull);
+ assert(a == 5);
+ assert(a != 3);
+ assert(a.get != 3);
+ a.nullify();
+ assert(a.isNull);
+ a = 3;
+ assert(a == 3);
+ a *= 6;
+ assert(a == 18);
+ a = a;
+ assert(a == 18);
+ a.nullify();
+ assertThrown!Throwable(a += 2);
+}
+@safe unittest
+{
+ auto k = Nullable!int(74);
+ assert(k == 74);
+ k.nullify();
+ assert(k.isNull);
+}
+@safe unittest
+{
+ static int f(in Nullable!int x) {
+ return x.isNull ? 42 : x.get;
+ }
+ Nullable!int a;
+ assert(f(a) == 42);
+ a = 8;
+ assert(f(a) == 8);
+ a.nullify();
+ assert(f(a) == 42);
+}
+@system unittest
+{
+ import std.exception : assertThrown;
+
+ static struct S { int x; }
+ Nullable!S s;
+ assert(s.isNull);
+ s = S(6);
+ assert(s == S(6));
+ assert(s != S(0));
+ assert(s.get != S(0));
+ s.x = 9190;
+ assert(s.x == 9190);
+ s.nullify();
+ assertThrown!Throwable(s.x = 9441);
+}
+@safe unittest
+{
+ // Ensure Nullable can be used in pure/nothrow/@safe environment.
+ function() @safe pure nothrow
+ {
+ Nullable!int n;
+ assert(n.isNull);
+ n = 4;
+ assert(!n.isNull);
+ assert(n == 4);
+ n.nullify();
+ assert(n.isNull);
+ }();
+}
+@system unittest
+{
+ // Ensure Nullable can be used when the value is not pure/nothrow/@safe
+ static struct S
+ {
+ int x;
+ this(this) @system {}
+ }
+
+ Nullable!S s;
+ assert(s.isNull);
+ s = S(5);
+ assert(!s.isNull);
+ assert(s.x == 5);
+ s.nullify();
+ assert(s.isNull);
+}
+@safe unittest
+{
+ // Bugzilla 9404
+ alias N = Nullable!int;
+
+ void foo(N a)
+ {
+ N b;
+ b = a; // `N b = a;` works fine
+ }
+ N n;
+ foo(n);
+}
+@safe unittest
+{
+ //Check nullable immutable is constructable
+ {
+ auto a1 = Nullable!(immutable int)();
+ auto a2 = Nullable!(immutable int)(1);
+ auto i = a2.get;
+ }
+ //Check immutable nullable is constructable
+ {
+ auto a1 = immutable (Nullable!int)();
+ auto a2 = immutable (Nullable!int)(1);
+ auto i = a2.get;
+ }
+}
+@safe unittest
+{
+ alias NInt = Nullable!int;
+
+ //Construct tests
+ {
+ //from other Nullable null
+ NInt a1;
+ NInt b1 = a1;
+ assert(b1.isNull);
+
+ //from other Nullable non-null
+ NInt a2 = NInt(1);
+ NInt b2 = a2;
+ assert(b2 == 1);
+
+ //Construct from similar nullable
+ auto a3 = immutable(NInt)();
+ NInt b3 = a3;
+ assert(b3.isNull);
+ }
+
+ //Assign tests
+ {
+ //from other Nullable null
+ NInt a1;
+ NInt b1;
+ b1 = a1;
+ assert(b1.isNull);
+
+ //from other Nullable non-null
+ NInt a2 = NInt(1);
+ NInt b2;
+ b2 = a2;
+ assert(b2 == 1);
+
+ //Construct from similar nullable
+ auto a3 = immutable(NInt)();
+ NInt b3 = a3;
+ b3 = a3;
+ assert(b3.isNull);
+ }
+}
+@safe unittest
+{
+ //Check nullable is nicelly embedable in a struct
+ static struct S1
+ {
+ Nullable!int ni;
+ }
+ static struct S2 //inspired from 9404
+ {
+ Nullable!int ni;
+ this(S2 other)
+ {
+ ni = other.ni;
+ }
+ void opAssign(S2 other)
+ {
+ ni = other.ni;
+ }
+ }
+ foreach (S; AliasSeq!(S1, S2))
+ {
+ S a;
+ S b = a;
+ S c;
+ c = a;
+ }
+}
+@system unittest
+{
+ // Bugzilla 10268
+ import std.json;
+ JSONValue value = null;
+ auto na = Nullable!JSONValue(value);
+
+ struct S1 { int val; }
+ struct S2 { int* val; }
+ struct S3 { immutable int* val; }
+
+ {
+ auto sm = S1(1);
+ immutable si = immutable S1(1);
+ 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;
+ immutable ni = 10;
+
+ {
+ auto sm = S2(&nm);
+ immutable si = immutable S2(&ni);
+ 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);
+ 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);
+ }
+}
+@safe unittest
+{
+ // Bugzila 10357
+ import std.datetime;
+ Nullable!SysTime time = SysTime(0);
+}
+@system unittest
+{
+ import std.conv : to;
+ import std.array;
+
+ // Bugzilla 10915
+ Appender!string buffer;
+
+ Nullable!int ni;
+ assert(ni.to!string() == "Nullable.null");
+
+ struct Test { string s; }
+ alias NullableTest = Nullable!Test;
+
+ NullableTest nt = Test("test");
+ assert(nt.to!string() == `Test("test")`);
+
+ NullableTest ntn = Test("null");
+ assert(ntn.to!string() == `Test("null")`);
+
+ class TestToString
+ {
+ double d;
+
+ this (double d)
+ {
+ this.d = d;
+ }
+
+ override string toString()
+ {
+ return d.to!string();
+ }
+ }
+ Nullable!TestToString ntts = new TestToString(2.5);
+ assert(ntts.to!string() == "2.5");
+}
+
+/**
+Just like $(D Nullable!T), except that the null state is defined as a
+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)
+{
+ private T _value = nullValue;
+
+/**
+Constructor initializing $(D this) with $(D value).
+
+Params:
+ value = The value to initialize this `Nullable` with.
+ */
+ this(T value)
+ {
+ _value = value;
+ }
+
+ template toString()
+ {
+ import std.format : FormatSpec, formatValue;
+ // Needs to be a template because of DMD @@BUG@@ 13737.
+ void toString()(scope void delegate(const(char)[]) sink, FormatSpec!char fmt)
+ {
+ if (isNull)
+ {
+ sink.formatValue("Nullable.null", fmt);
+ }
+ else
+ {
+ sink.formatValue(_value, fmt);
+ }
+ }
+ }
+
+/**
+Check if `this` is in the null state.
+
+Returns:
+ true $(B iff) `this` is in the null state, otherwise false.
+ */
+ @property bool isNull() const
+ {
+ //Need to use 'is' if T is a nullable type and
+ //nullValue is null, or it's a compiler error
+ static if (is(CommonType!(T, typeof(null)) == T) && nullValue is null)
+ {
+ 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) @system 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.
+ */
+ void nullify()()
+ {
+ _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. 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
+ {
+ //@@@6169@@@: We avoid any call that might evaluate nullValue's %s,
+ //Because it might messup get's purity and safety inference.
+ enum message = "Called `get' on null Nullable!(" ~ T.stringof ~ ",nullValue).";
+ assert(!isNull, message);
+ 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).
+$(D this) must not be in the null state.
+ */
+ alias get this;
+}
+
+/// 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;
+
+ Nullable!(int, int.min) a;
+ assert(a.isNull);
+ assertThrown!Throwable(a.get);
+ a = 5;
+ assert(!a.isNull);
+ assert(a == 5);
+ static assert(a.sizeof == int.sizeof);
+}
+
+///
+@safe unittest
+{
+ auto a = nullable!(int.min)(8);
+ assert(a == 8);
+ a.nullify();
+ assert(a.isNull);
+}
+
+@safe unittest
+{
+ static int f(in Nullable!(int, int.min) x) {
+ return x.isNull ? 42 : x.get;
+ }
+ Nullable!(int, int.min) a;
+ assert(f(a) == 42);
+ a = 8;
+ assert(f(a) == 8);
+ a.nullify();
+ assert(f(a) == 42);
+}
+@safe unittest
+{
+ // Ensure Nullable can be used in pure/nothrow/@safe environment.
+ function() @safe pure nothrow
+ {
+ Nullable!(int, int.min) n;
+ assert(n.isNull);
+ n = 4;
+ assert(!n.isNull);
+ assert(n == 4);
+ n.nullify();
+ assert(n.isNull);
+ }();
+}
+@system unittest
+{
+ // Ensure Nullable can be used when the value is not pure/nothrow/@system
+ static struct S
+ {
+ int x;
+ bool opEquals(const S s) const @system { return s.x == x; }
+ }
+
+ Nullable!(S, S(711)) s;
+ assert(s.isNull);
+ s = S(5);
+ assert(!s.isNull);
+ assert(s.x == 5);
+ s.nullify();
+ assert(s.isNull);
+}
+@safe unittest
+{
+ //Check nullable is nicelly embedable in a struct
+ static struct S1
+ {
+ Nullable!(int, 0) ni;
+ }
+ static struct S2 //inspired from 9404
+ {
+ Nullable!(int, 0) ni;
+ this(S2 other)
+ {
+ ni = other.ni;
+ }
+ void opAssign(S2 other)
+ {
+ ni = other.ni;
+ }
+ }
+ foreach (S; AliasSeq!(S1, S2))
+ {
+ S a;
+ S b = a;
+ S c;
+ c = a;
+ }
+}
+@system unittest
+{
+ import std.conv : to;
+
+ // Bugzilla 10915
+ Nullable!(int, 1) ni = 1;
+ assert(ni.to!string() == "Nullable.null");
+
+ struct Test { string s; }
+ alias NullableTest = Nullable!(Test, Test("null"));
+
+ NullableTest nt = Test("test");
+ assert(nt.to!string() == `Test("test")`);
+
+ NullableTest ntn = Test("null");
+ assert(ntn.to!string() == "Nullable.null");
+
+ class TestToString
+ {
+ double d;
+
+ this(double d)
+ {
+ this.d = d;
+ }
+
+ override string toString()
+ {
+ return d.to!string();
+ }
+ }
+ alias NullableTestToString = Nullable!(TestToString, null);
+
+ NullableTestToString ntts = new TestToString(2.5);
+ assert(ntts.to!string() == "2.5");
+}
+
+
+/**
+Just like $(D Nullable!T), except that the object refers to a value
+sitting elsewhere in memory. This makes assignments overwrite the
+initially assigned value. Internally $(D NullableRef!T) only stores a
+pointer to $(D T) (i.e., $(D Nullable!T.sizeof == (T*).sizeof)).
+ */
+struct NullableRef(T)
+{
+ private T* _value;
+
+/**
+Constructor binding $(D this) to $(D value).
+
+Params:
+ value = The value to bind to.
+ */
+ this(T* value) @safe pure nothrow
+ {
+ _value = value;
+ }
+
+ template toString()
+ {
+ import std.format : FormatSpec, formatValue;
+ // Needs to be a template because of DMD @@BUG@@ 13737.
+ void toString()(scope void delegate(const(char)[]) sink, FormatSpec!char fmt)
+ {
+ if (isNull)
+ {
+ sink.formatValue("Nullable.null", fmt);
+ }
+ else
+ {
+ sink.formatValue(*_value, fmt);
+ }
+ }
+ }
+
+/**
+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.
+ */
+ void nullify() @safe pure nothrow
+ {
+ _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@@@
+ {
+ enum message = "Called `opAssign' on null NullableRef!" ~ T.stringof ~ ".";
+ assert(!isNull, message);
+ *_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).
+ */
+ @property ref inout(T) get() inout @safe pure nothrow
+ {
+ enum message = "Called `get' on null NullableRef!" ~ T.stringof ~ ".";
+ assert(!isNull, message);
+ 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.
+ */
+ alias get this;
+}
+
+/// ditto
+auto nullableRef(T)(T* t)
+{
+ return NullableRef!T(t);
+}
+
+///
+@system unittest
+{
+ import std.exception : assertThrown;
+
+ int x = 5, y = 7;
+ auto a = nullableRef(&x);
+ assert(!a.isNull);
+ assert(a == 5);
+ assert(x == 5);
+ a = 42;
+ assert(x == 42);
+ assert(!a.isNull);
+ assert(a == 42);
+ a.nullify();
+ assert(x == 42);
+ assert(a.isNull);
+ assertThrown!Throwable(a.get);
+ assertThrown!Throwable(a = 71);
+ a.bind(&y);
+ assert(a == 7);
+ y = 135;
+ assert(a == 135);
+}
+@system unittest
+{
+ static int f(in NullableRef!int x) {
+ return x.isNull ? 42 : x.get;
+ }
+ int x = 5;
+ auto a = nullableRef(&x);
+ assert(f(a) == 5);
+ a.nullify();
+ assert(f(a) == 42);
+}
+@safe unittest
+{
+ // Ensure NullableRef can be used in pure/nothrow/@safe environment.
+ function() @safe pure nothrow
+ {
+ auto storage = new int;
+ *storage = 19902;
+ NullableRef!int n;
+ assert(n.isNull);
+ n.bind(storage);
+ assert(!n.isNull);
+ assert(n == 19902);
+ n = 2294;
+ assert(n == 2294);
+ assert(*storage == 2294);
+ n.nullify();
+ assert(n.isNull);
+ }();
+}
+@system unittest
+{
+ // Ensure NullableRef can be used when the value is not pure/nothrow/@safe
+ static struct S
+ {
+ int x;
+ this(this) @system {}
+ bool opEquals(const S s) const @system { return s.x == x; }
+ }
+
+ auto storage = S(5);
+
+ NullableRef!S s;
+ assert(s.isNull);
+ s.bind(&storage);
+ assert(!s.isNull);
+ assert(s.x == 5);
+ s.nullify();
+ assert(s.isNull);
+}
+@safe unittest
+{
+ //Check nullable is nicelly embedable in a struct
+ static struct S1
+ {
+ NullableRef!int ni;
+ }
+ static struct S2 //inspired from 9404
+ {
+ NullableRef!int ni;
+ this(S2 other)
+ {
+ ni = other.ni;
+ }
+ void opAssign(S2 other)
+ {
+ ni = other.ni;
+ }
+ }
+ foreach (S; AliasSeq!(S1, S2))
+ {
+ S a;
+ S b = a;
+ S c;
+ c = a;
+ }
+}
+@system unittest
+{
+ import std.conv : to;
+
+ // Bugzilla 10915
+ NullableRef!int nri;
+ assert(nri.to!string() == "Nullable.null");
+
+ struct Test
+ {
+ string s;
+ }
+ NullableRef!Test nt = new Test("test");
+ assert(nt.to!string() == `Test("test")`);
+
+ class TestToString
+ {
+ double d;
+
+ this(double d)
+ {
+ this.d = d;
+ }
+
+ override string toString()
+ {
+ return d.to!string();
+ }
+ }
+ TestToString tts = new TestToString(2.5);
+ NullableRef!TestToString ntts = &tts;
+ assert(ntts.to!string() == "2.5");
+}
+
+
+/**
+$(D BlackHole!Base) is a subclass of $(D Base) which automatically implements
+all abstract member functions in $(D Base) as do-nothing functions. Each
+auto-implemented function just returns the default value of the return type
+without doing anything.
+
+The name came from
+$(HTTP search.cpan.org/~sburke/Class-_BlackHole-0.04/lib/Class/_BlackHole.pm, Class::_BlackHole)
+Perl module by Sean M. Burke.
+
+Params:
+ Base = A non-final class for `BlackHole` to inherit from.
+
+See_Also:
+ $(LREF AutoImplement), $(LREF generateEmptyFunction)
+ */
+alias BlackHole(Base) = AutoImplement!(Base, generateEmptyFunction, isAbstractFunction);
+
+///
+@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);
+ assert(c.value == 42);
+
+ // Returns real.init which is NaN
+ assert(c.realValue.isNaN);
+ // Abstract functions are implemented as do-nothing
+ c.doSomething();
+}
+
+@system unittest
+{
+ import std.math : isNaN;
+
+ // return default
+ {
+ interface I_1 { real test(); }
+ auto o = new BlackHole!I_1;
+ assert(o.test().isNaN()); // NaN
+ }
+ // doc example
+ {
+ static 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);
+ assert(c.value == 42);
+
+ assert(c.realValue.isNaN); // NaN
+ c.doSomething();
+ }
+
+ // Bugzilla 12058
+ interface Foo
+ {
+ inout(Object) foo() inout;
+ }
+ BlackHole!Foo o;
+}
+
+
+/**
+$(D WhiteHole!Base) is a subclass of $(D Base) which automatically implements
+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
+$(HTTP search.cpan.org/~mschwern/Class-_WhiteHole-0.04/lib/Class/_WhiteHole.pm, Class::_WhiteHole)
+Perl module by Michael G Schwern.
+
+Params:
+ Base = A non-final class for `WhiteHole` to inherit from.
+
+See_Also:
+ $(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
+{
+ this(string method)
+ {
+ super(method ~ " is not implemented");
+ }
+}
+
+@system unittest
+{
+ import std.exception : assertThrown;
+ // nothrow
+ {
+ interface I_1
+ {
+ void foo();
+ void bar() nothrow;
+ }
+ auto o = new WhiteHole!I_1;
+ assertThrown!NotImplementedError(o.foo());
+ assertThrown!NotImplementedError(o.bar());
+ }
+ // doc example
+ {
+ static class C
+ {
+ abstract void notYetImplemented();
+ }
+
+ auto c = new WhiteHole!C;
+ try
+ {
+ c.notYetImplemented();
+ assert(0);
+ }
+ catch (Error e) {}
+ }
+}
+
+
+/**
+$(D AutoImplement) automatically implements (by default) all abstract member
+functions in the class or interface $(D Base) in specified way.
+
+The second version of $(D AutoImplement) automatically implements
+$(D Interface), while deriving from $(D BaseClass).
+
+Params:
+ how = template which specifies _how functions will be implemented/overridden.
+
+ Two arguments are passed to $(D how): the type $(D Base) and an alias
+ to an implemented function. Then $(D how) must return an implemented
+ function body as a string.
+
+ The generated function body can use these keywords:
+ $(UL
+ $(LI $(D a0), $(D a1), &hellip;: arguments passed to the function;)
+ $(LI $(D args): a tuple of the arguments;)
+ $(LI $(D self): an alias to the function itself;)
+ $(LI $(D parent): an alias to the overridden function (if any).)
+ )
+
+ You may want to use templated property functions (instead of Implicit
+ Template Properties) to generate complex functions:
+--------------------
+// Prints log messages for each call to overridden functions.
+string generateLogger(C, alias fun)() @property
+{
+ import std.traits;
+ enum qname = C.stringof ~ "." ~ __traits(identifier, fun);
+ string stmt;
+
+ stmt ~= q{ struct Importer { import std.stdio; } };
+ stmt ~= `Importer.writeln("Log: ` ~ qname ~ `(", args, ")");`;
+ static if (!__traits(isAbstractFunction, fun))
+ {
+ static if (is(ReturnType!fun == void))
+ stmt ~= q{ parent(args); };
+ else
+ stmt ~= q{
+ auto r = parent(args);
+ Importer.writeln("--> ", r);
+ return r;
+ };
+ }
+ return stmt;
+}
+--------------------
+
+ what = template which determines _what functions should be
+ implemented/overridden.
+
+ An argument is passed to $(D what): an alias to a non-final member
+ function in $(D Base). Then $(D what) must return a boolean value.
+ Return $(D true) to indicate that the passed function should be
+ implemented/overridden.
+
+--------------------
+// Sees if fun returns something.
+enum bool hasValue(alias fun) = !is(ReturnType!(fun) == void);
+--------------------
+
+
+Note:
+
+Generated code is inserted in the scope of $(D std.typecons) module. Thus,
+any useful functions outside $(D std.typecons) cannot be used in the generated
+code. To workaround this problem, you may $(D import) necessary things in a
+local struct, as done in the $(D generateLogger()) template in the above
+example.
+
+
+BUGS:
+
+$(UL
+ $(LI Variadic arguments to constructors are not forwarded to super.)
+ $(LI Deep interface inheritance causes compile error with messages like
+ "Error: function std.typecons._AutoImplement!(Foo)._AutoImplement.bar
+ does not override any function". [$(BUGZILLA 2525), $(BUGZILLA 3525)] )
+ $(LI The $(D parent) keyword is actually a delegate to the super class'
+ corresponding member function. [$(BUGZILLA 2540)] )
+ $(LI Using alias template parameter in $(D how) and/or $(D what) may cause
+ strange compile error. Use template tuple parameter instead to workaround
+ this problem. [$(BUGZILLA 4217)] )
+)
+ */
+class AutoImplement(Base, alias how, alias what = isAbstractFunction) : Base
+ if (!is(how == class))
+{
+ private alias autoImplement_helper_ =
+ AutoImplement_Helper!("autoImplement_helper_", "Base", Base, typeof(this), how, what);
+ mixin(autoImplement_helper_.code);
+}
+
+/// ditto
+class AutoImplement(
+ Interface, BaseClass, alias how,
+ alias what = isAbstractFunction) : BaseClass, Interface
+ if (is(Interface == interface) && is(BaseClass == class))
+{
+ private alias autoImplement_helper_ = AutoImplement_Helper!(
+ "autoImplement_helper_", "Interface", Interface, typeof(this), how, what);
+ mixin(autoImplement_helper_.code);
+}
+
+/*
+ * Code-generating stuffs are encupsulated in this helper template so that
+ * namespace pollution, which can cause name confliction with Base's public
+ * members, should be minimized.
+ */
+private template AutoImplement_Helper(string myName, string baseName,
+ Base, Self, alias generateMethodBody, alias cherrypickMethod)
+{
+private static:
+ //:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::://
+ // Internal stuffs
+ //:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::://
+
+ // Returns function overload sets in the class C, filtered with pred.
+ template enumerateOverloads(C, alias pred)
+ {
+ template Impl(names...)
+ {
+ 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 = AliasSeq!(OverloadSet!(names[0], methods), next);
+ else
+ alias Impl = next;
+ }
+ else
+ alias Impl = AliasSeq!();
+ }
+
+ alias enumerateOverloads = Impl!(__traits(allMembers, C));
+ }
+
+ //:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::://
+ // Target functions
+ //:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::://
+
+ // Add a non-final check to the cherrypickMethod.
+ enum bool canonicalPicker(fun.../+[BUG 4217]+/) =
+ !__traits(isFinalFunction, fun[0]) && cherrypickMethod!(fun);
+
+ /*
+ * A tuple of overload sets, each item of which consists of functions to be
+ * implemented by the generated code.
+ */
+ alias targetOverloadSets = enumerateOverloads!(Base, canonicalPicker);
+
+ /*
+ * Super class of this AutoImplement instance
+ */
+ alias Super = BaseTypeTuple!(Self)[0];
+ static assert(is(Super == class));
+ static assert(is(Base == interface) || is(Super == Base));
+
+ /*
+ * A tuple of the super class' constructors. Used for forwarding
+ * constructor calls.
+ */
+ static if (__traits(hasMember, Super, "__ctor"))
+ alias ctorOverloadSet = OverloadSet!("__ctor", __traits(getOverloads, Super, "__ctor"));
+ else
+ alias ctorOverloadSet = OverloadSet!("__ctor"); // empty
+
+
+ //:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::://
+ // Type information
+ //:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::://
+
+ /*
+ * The generated code will be mixed into AutoImplement, which will be
+ * instantiated in this module's scope. Thus, any user-defined types are
+ * out of scope and cannot be used directly (i.e. by their names).
+ *
+ * We will use FuncInfo instances for accessing return types and parameter
+ * types of the implemented functions. The instances will be populated to
+ * the AutoImplement's scope in a certain way; see the populate() below.
+ */
+
+ // Returns the preferred identifier for the FuncInfo instance for the i-th
+ // overloaded function with the name.
+ template INTERNAL_FUNCINFO_ID(string name, size_t i)
+ {
+ import std.format : format;
+
+ enum string INTERNAL_FUNCINFO_ID = format("F_%s_%s", name, i);
+ }
+
+ /*
+ * Insert FuncInfo instances about all the target functions here. This
+ * enables the generated code to access type information via, for example,
+ * "autoImplement_helper_.F_foo_1".
+ */
+ template populate(overloads...)
+ {
+ static if (overloads.length > 0)
+ {
+ mixin populate!(overloads[0].name, overloads[0].contents);
+ mixin populate!(overloads[1 .. $]);
+ }
+ }
+ template populate(string name, methods...)
+ {
+ static if (methods.length > 0)
+ {
+ mixin populate!(name, methods[0 .. $ - 1]);
+ //
+ alias target = methods[$ - 1];
+ enum ith = methods.length - 1;
+ mixin("alias " ~ INTERNAL_FUNCINFO_ID!(name, ith) ~ " = FuncInfo!target;");
+ }
+ }
+
+ public mixin populate!(targetOverloadSets);
+ public mixin populate!( ctorOverloadSet );
+
+
+ //:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::://
+ // Code-generating policies
+ //:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::://
+
+ /* Common policy configurations for generating constructors and methods. */
+ template CommonGeneratingPolicy()
+ {
+ // base class identifier which generated code should use
+ enum string BASE_CLASS_ID = baseName;
+
+ // FuncInfo instance identifier which generated code should use
+ template FUNCINFO_ID(string name, size_t i)
+ {
+ enum string FUNCINFO_ID =
+ myName ~ "." ~ INTERNAL_FUNCINFO_ID!(name, i);
+ }
+ }
+
+ /* Policy configurations for generating constructors. */
+ template ConstructorGeneratingPolicy()
+ {
+ mixin CommonGeneratingPolicy;
+
+ /* Generates constructor body. Just forward to the base class' one. */
+ string generateFunctionBody(ctor.../+[BUG 4217]+/)() @property
+ {
+ enum varstyle = variadicFunctionStyle!(typeof(&ctor[0]));
+
+ static if (varstyle & (Variadic.c | Variadic.d))
+ {
+ // the argptr-forwarding problem
+ //pragma(msg, "Warning: AutoImplement!(", Base, ") ",
+ // "ignored variadic arguments to the constructor ",
+ // FunctionTypeOf!(typeof(&ctor[0])) );
+ }
+ return "super(args);";
+ }
+ }
+
+ /* Policy configurations for genearting target methods. */
+ template MethodGeneratingPolicy()
+ {
+ mixin CommonGeneratingPolicy;
+
+ /* Geneartes method body. */
+ string generateFunctionBody(func.../+[BUG 4217]+/)() @property
+ {
+ return generateMethodBody!(Base, func); // given
+ }
+ }
+
+
+ //:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::://
+ // Generated code
+ //:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::://
+
+ alias ConstructorGenerator = MemberFunctionGenerator!(ConstructorGeneratingPolicy!());
+ alias MethodGenerator = MemberFunctionGenerator!(MethodGeneratingPolicy!());
+
+ public enum string code =
+ ConstructorGenerator.generateCode!( ctorOverloadSet ) ~ "\n" ~
+ MethodGenerator.generateCode!(targetOverloadSets);
+
+ debug (SHOW_GENERATED_CODE)
+ {
+ pragma(msg, "-------------------- < ", Base, " >");
+ pragma(msg, code);
+ pragma(msg, "--------------------");
+ }
+}
+
+//debug = SHOW_GENERATED_CODE;
+@system unittest
+{
+ import core.vararg;
+ // no function to implement
+ {
+ interface I_1 {}
+ auto o = new BlackHole!I_1;
+ }
+ // parameters
+ {
+ interface I_3 { void test(int, in int, out int, ref int, lazy int); }
+ auto o = new BlackHole!I_3;
+ }
+ // use of user-defined type
+ {
+ struct S {}
+ interface I_4 { S test(); }
+ auto o = new BlackHole!I_4;
+ }
+ // overloads
+ {
+ interface I_5
+ {
+ void test(string);
+ real test(real);
+ int test();
+ }
+ auto o = new BlackHole!I_5;
+ }
+ // constructor forwarding
+ {
+ static class C_6
+ {
+ this(int n) { assert(n == 42); }
+ this(string s) { assert(s == "Deeee"); }
+ this(...) {}
+ }
+ auto o1 = new BlackHole!C_6(42);
+ auto o2 = new BlackHole!C_6("Deeee");
+ auto o3 = new BlackHole!C_6(1, 2, 3, 4);
+ }
+ // attributes
+ {
+ interface I_7
+ {
+ ref int test_ref();
+ int test_pure() pure;
+ int test_nothrow() nothrow;
+ int test_property() @property;
+ int test_safe() @safe;
+ int test_trusted() @trusted;
+ int test_system() @system;
+ int test_pure_nothrow() pure nothrow;
+ }
+ auto o = new BlackHole!I_7;
+ }
+ // storage classes
+ {
+ interface I_8
+ {
+ void test_const() const;
+ void test_immutable() immutable;
+ void test_shared() shared;
+ void test_shared_const() shared const;
+ }
+ auto o = new BlackHole!I_8;
+ }
+ // use baseclass
+ {
+ static class C_9
+ {
+ private string foo_;
+
+ this(string s) {
+ foo_ = s;
+ }
+
+ protected string boilerplate() @property
+ {
+ return "Boilerplate stuff.";
+ }
+
+ public string foo() @property
+ {
+ return foo_;
+ }
+ }
+
+ interface I_10
+ {
+ string testMethod(size_t);
+ }
+
+ static string generateTestMethod(C, alias fun)() @property
+ {
+ return "return this.boilerplate[0 .. a0];";
+ }
+
+ auto o = new AutoImplement!(I_10, C_9, generateTestMethod)("Testing");
+ assert(o.testMethod(11) == "Boilerplate");
+ assert(o.foo == "Testing");
+ }
+ /+ // deep inheritance
+ {
+ // XXX [BUG 2525,3525]
+ // NOTE: [r494] func.c(504-571) FuncDeclaration::semantic()
+ interface I { void foo(); }
+ interface J : I {}
+ interface K : J {}
+ static abstract class C_9 : K {}
+ auto o = new BlackHole!C_9;
+ }+/
+}
+
+// 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
+ // Add prefix "issue10647_" as a workaround for issue 1238
+ private string issue10647_generateDoNothing(C, alias fun)() @property
+ {
+ string stmt;
+
+ static if (is(ReturnType!fun == void))
+ stmt ~= "";
+ else
+ {
+ string returnType = ReturnType!fun.stringof;
+ stmt ~= "return "~returnType~".init;";
+ }
+ return stmt;
+ }
+
+ private template issue10647_isAlwaysTrue(alias fun)
+ {
+ enum issue10647_isAlwaysTrue = true;
+ }
+
+ // Do nothing template
+ private template issue10647_DoNothing(Base)
+ {
+ alias issue10647_DoNothing = AutoImplement!(Base, issue10647_generateDoNothing, issue10647_isAlwaysTrue);
+ }
+
+ // A class to be overridden
+ private class issue10647_Foo{
+ void bar(int a) { }
+ }
+}
+@system unittest
+{
+ auto foo = new issue10647_DoNothing!issue10647_Foo();
+ foo.bar(13);
+}
+
+/*
+Used by MemberFunctionGenerator.
+ */
+package template OverloadSet(string nam, T...)
+{
+ enum string name = nam;
+ alias contents = T;
+}
+
+/*
+Used by MemberFunctionGenerator.
+ */
+package template FuncInfo(alias func, /+[BUG 4217 ?]+/ T = typeof(&func))
+{
+ alias RT = ReturnType!T;
+ alias PT = Parameters!T;
+}
+package template FuncInfo(Func)
+{
+ alias RT = ReturnType!Func;
+ alias PT = Parameters!Func;
+}
+
+/*
+General-purpose member function generator.
+--------------------
+template GeneratingPolicy()
+{
+ // [optional] the name of the class where functions are derived
+ enum string BASE_CLASS_ID;
+
+ // [optional] define this if you have only function types
+ enum bool WITHOUT_SYMBOL;
+
+ // [optional] Returns preferred identifier for i-th parameter.
+ template PARAMETER_VARIABLE_ID(size_t i);
+
+ // Returns the identifier of the FuncInfo instance for the i-th overload
+ // of the specified name. The identifier must be accessible in the scope
+ // where generated code is mixed.
+ template FUNCINFO_ID(string name, size_t i);
+
+ // Returns implemented function body as a string. When WITHOUT_SYMBOL is
+ // defined, the latter is used.
+ template generateFunctionBody(alias func);
+ template generateFunctionBody(string name, FuncType);
+}
+--------------------
+ */
+package template MemberFunctionGenerator(alias Policy)
+{
+private static:
+ //:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::://
+ // Internal stuffs
+ //:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::://
+ import std.format;
+
+ enum CONSTRUCTOR_NAME = "__ctor";
+
+ // true if functions are derived from a base class
+ enum WITH_BASE_CLASS = __traits(hasMember, Policy, "BASE_CLASS_ID");
+
+ // true if functions are specified as types, not symbols
+ enum WITHOUT_SYMBOL = __traits(hasMember, Policy, "WITHOUT_SYMBOL");
+
+ // preferred identifier for i-th parameter variable
+ static if (__traits(hasMember, Policy, "PARAMETER_VARIABLE_ID"))
+ {
+ alias PARAMETER_VARIABLE_ID = Policy.PARAMETER_VARIABLE_ID;
+ }
+ else
+ {
+ enum string PARAMETER_VARIABLE_ID(size_t i) = format("a%s", i);
+ // default: a0, a1, ...
+ }
+
+ // Returns a tuple consisting of 0,1,2,...,n-1. For static foreach.
+ template CountUp(size_t n)
+ {
+ static if (n > 0)
+ alias CountUp = AliasSeq!(CountUp!(n - 1), n - 1);
+ else
+ alias CountUp = AliasSeq!();
+ }
+
+
+ //:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::://
+ // Code generator
+ //:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::://
+
+ /*
+ * Runs through all the target overload sets and generates D code which
+ * implements all the functions in the overload sets.
+ */
+ public string generateCode(overloads...)() @property
+ {
+ string code = "";
+
+ // run through all the overload sets
+ foreach (i_; CountUp!(0 + overloads.length)) // workaround
+ {
+ enum i = 0 + i_; // workaround
+ alias oset = overloads[i];
+
+ code ~= generateCodeForOverloadSet!(oset);
+
+ static if (WITH_BASE_CLASS && oset.name != CONSTRUCTOR_NAME)
+ {
+ // The generated function declarations may hide existing ones
+ // in the base class (cf. HiddenFuncError), so we put an alias
+ // declaration here to reveal possible hidden functions.
+ code ~= format("alias %s = %s.%s;\n",
+ oset.name,
+ Policy.BASE_CLASS_ID, // [BUG 2540] super.
+ oset.name);
+ }
+ }
+ return code;
+ }
+
+ // handle each overload set
+ private string generateCodeForOverloadSet(alias oset)() @property
+ {
+ string code = "";
+
+ foreach (i_; CountUp!(0 + oset.contents.length)) // workaround
+ {
+ enum i = 0 + i_; // workaround
+ code ~= generateFunction!(
+ Policy.FUNCINFO_ID!(oset.name, i), oset.name,
+ oset.contents[i]) ~ "\n";
+ }
+ return code;
+ }
+
+ /*
+ * Returns D code which implements the function func. This function
+ * actually generates only the declarator part; the function body part is
+ * generated by the functionGenerator() policy.
+ */
+ public string generateFunction(
+ string myFuncInfo, string name, func... )() @property
+ {
+ import std.format : format;
+
+ enum isCtor = (name == CONSTRUCTOR_NAME);
+
+ string code; // the result
+
+ auto paramsRes = generateParameters!(myFuncInfo, func)();
+ code ~= paramsRes.imports;
+
+ /*** Function Declarator ***/
+ {
+ alias Func = FunctionTypeOf!(func);
+ alias FA = FunctionAttribute;
+ enum atts = functionAttributes!(func);
+ enum realName = isCtor ? "this" : name;
+
+ // FIXME?? Make it so that these aren't CTFE funcs any more, since
+ // Format is deprecated, and format works at compile time?
+ /* Made them CTFE funcs just for the sake of Format!(...) */
+
+ // return type with optional "ref"
+ static string make_returnType()
+ {
+ string rtype = "";
+
+ if (!isCtor)
+ {
+ if (atts & FA.ref_) rtype ~= "ref ";
+ rtype ~= myFuncInfo ~ ".RT";
+ }
+ return rtype;
+ }
+ enum returnType = make_returnType();
+
+ // function attributes attached after declaration
+ static string make_postAtts()
+ {
+ string poatts = "";
+ if (atts & FA.pure_ ) poatts ~= " pure";
+ if (atts & FA.nothrow_) poatts ~= " nothrow";
+ if (atts & FA.property) poatts ~= " @property";
+ if (atts & FA.safe ) poatts ~= " @safe";
+ if (atts & FA.trusted ) poatts ~= " @trusted";
+ return poatts;
+ }
+ enum postAtts = make_postAtts();
+
+ // function storage class
+ static string make_storageClass()
+ {
+ string postc = "";
+ if (is(Func == shared)) postc ~= " shared";
+ if (is(Func == const)) postc ~= " const";
+ if (is(Func == inout)) postc ~= " inout";
+ if (is(Func == immutable)) postc ~= " immutable";
+ return postc;
+ }
+ enum storageClass = make_storageClass();
+
+ //
+ if (__traits(isVirtualMethod, func))
+ code ~= "override ";
+ code ~= format("extern(%s) %s %s(%s) %s %s\n",
+ functionLinkage!(func),
+ returnType,
+ realName,
+ paramsRes.params,
+ postAtts, storageClass );
+ }
+
+ /*** Function Body ***/
+ code ~= "{\n";
+ {
+ enum nparams = Parameters!(func).length;
+
+ /* Declare keywords: args, self and parent. */
+ string preamble;
+
+ preamble ~= "alias args = AliasSeq!(" ~ enumerateParameters!(nparams) ~ ");\n";
+ if (!isCtor)
+ {
+ preamble ~= "alias self = " ~ name ~ ";\n";
+ if (WITH_BASE_CLASS && !__traits(isAbstractFunction, func))
+ preamble ~= "alias parent = AliasSeq!(__traits(getMember, super, \"" ~ name ~ "\"))[0];";
+ }
+
+ // Function body
+ static if (WITHOUT_SYMBOL)
+ enum fbody = Policy.generateFunctionBody!(name, func);
+ else
+ enum fbody = Policy.generateFunctionBody!(func);
+
+ code ~= preamble;
+ code ~= fbody;
+ }
+ code ~= "}";
+
+ return code;
+ }
+
+ /*
+ * Returns D code which declares function parameters,
+ * and optionally any imports (e.g. core.vararg)
+ * "ref int a0, real a1, ..."
+ */
+ static struct GenParams { string imports, params; }
+ private GenParams generateParameters(string myFuncInfo, func...)()
+ {
+ alias STC = ParameterStorageClass;
+ alias stcs = ParameterStorageClassTuple!(func);
+ enum nparams = stcs.length;
+
+ string imports = ""; // any imports required
+ string params = ""; // parameters
+
+ foreach (i, stc; stcs)
+ {
+ if (i > 0) params ~= ", ";
+
+ // Parameter storage classes.
+ if (stc & STC.scope_) params ~= "scope ";
+ if (stc & STC.out_ ) params ~= "out ";
+ if (stc & STC.ref_ ) params ~= "ref ";
+ if (stc & STC.lazy_ ) params ~= "lazy ";
+
+ // Take parameter type from the FuncInfo.
+ params ~= format("%s.PT[%s]", myFuncInfo, i);
+
+ // Declare a parameter variable.
+ params ~= " " ~ PARAMETER_VARIABLE_ID!(i);
+ }
+
+ // Add some ellipsis part if needed.
+ auto style = variadicFunctionStyle!(func);
+ final switch (style)
+ {
+ case Variadic.no:
+ break;
+
+ case Variadic.c, Variadic.d:
+ imports ~= "import core.vararg;\n";
+ // (...) or (a, b, ...)
+ params ~= (nparams == 0) ? "..." : ", ...";
+ break;
+
+ case Variadic.typesafe:
+ params ~= " ...";
+ break;
+ }
+
+ return typeof(return)(imports, params);
+ }
+
+ // Returns D code which enumerates n parameter variables using comma as the
+ // separator. "a0, a1, a2, a3"
+ private string enumerateParameters(size_t n)() @property
+ {
+ string params = "";
+
+ foreach (i_; CountUp!(n))
+ {
+ enum i = 0 + i_; // workaround
+ if (i > 0) params ~= ", ";
+ params ~= PARAMETER_VARIABLE_ID!(i);
+ }
+ return params;
+ }
+}
+
+
+/**
+Predefined how-policies for $(D AutoImplement). These templates are also used by
+$(D BlackHole) and $(D WhiteHole), respectively.
+ */
+template generateEmptyFunction(C, func.../+[BUG 4217]+/)
+{
+ static if (is(ReturnType!(func) == void))
+ enum string generateEmptyFunction = q{
+ };
+ else static if (functionAttributes!(func) & FunctionAttribute.ref_)
+ enum string generateEmptyFunction = q{
+ static typeof(return) dummy;
+ return dummy;
+ };
+ else
+ enum string generateEmptyFunction = q{
+ return typeof(return).init;
+ };
+}
+
+/// ditto
+template generateAssertTrap(C, func...)
+{
+ enum string generateAssertTrap =
+ `throw new NotImplementedError("` ~ C.stringof ~ "."
+ ~ __traits(identifier, func) ~ `");`;
+}
+
+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());
+}
+
+/**
+ * Supports structural based typesafe conversion.
+ *
+ * If $(D Source) has structural conformance with the $(D interface) $(D Targets),
+ * wrap creates internal wrapper class which inherits $(D Targets) and
+ * wrap $(D src) object, then return it.
+ */
+template wrap(Targets...)
+if (Targets.length >= 1 && allSatisfy!(isMutable, Targets))
+{
+ import std.meta : staticMap;
+
+ // strict upcast
+ auto wrap(Source)(inout Source src) @trusted pure nothrow
+ 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!(Bind!(isImplicitlyConvertible, Source), Targets))
+ {
+ auto wrap(inout Source src)
+ {
+ static assert(hasRequireMethods!(),
+ "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);
+ }
+
+ template FuncInfo(string s, F)
+ {
+ enum name = s;
+ alias type = F;
+ }
+
+ // Concat all Targets function members into one tuple
+ template Concat(size_t i = 0)
+ {
+ static if (i >= Targets.length)
+ alias Concat = AliasSeq!();
+ else
+ {
+ 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 = 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
+ {
+ enum ptrdiff_t check =
+ __traits(identifier, func) == __traits(identifier, mem[i]) &&
+ !is(DerivedFunctionType!(type, FunctionTypeOf!(mem[i])) == void)
+ ? i : 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 = Uniq!(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 Uniq = AliasSeq!(FuncInfo!(name, F), remain[1 .. $]);
+ }
+ else
+ alias Uniq = AliasSeq!(FuncInfo!(name, typex), remain);
+ }
+ else
+ {
+ alias Uniq = AliasSeq!(FuncInfo!(name, type), Uniq!(members[1 .. $]));
+ }
+ }
+ }
+ alias TargetMembers = Uniq!(Concat!()); // list of FuncInfo
+ alias SourceMembers = GetOverloadedMethods!Source; // list of function symbols
+
+ // Check whether all of SourceMembers satisfy covariance target in TargetMembers
+ template hasRequireMethods(size_t i = 0)
+ {
+ static if (i >= TargetMembers.length)
+ enum hasRequireMethods = true;
+ else
+ {
+ enum hasRequireMethods =
+ findCovariantFunction!(TargetMembers[i], Source, SourceMembers) != -1 &&
+ hasRequireMethods!(i + 1);
+ }
+ }
+
+ // Internal wrapper class
+ final class Impl : Structural, 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; }
+
+ // BUG: making private should work with NVI.
+ protected final inout(Object) _wrap_getSource() inout @trusted
+ {
+ return dynamicCast!(inout Object)(_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 @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!(TargetMembers[i].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;
+ }
+ enum n = to!string(i);
+ static if (fa & FunctionAttribute.property)
+ {
+ static if (Parameters!(TargetMembers[i].type).length == 0)
+ enum fbody = "_wrap_source."~name;
+ else
+ enum fbody = "_wrap_source."~name~" = forward!args";
+ }
+ else
+ {
+ enum fbody = "_wrap_source."~name~"(forward!args)";
+ }
+ enum generateFun =
+ "override "~stc~"ReturnType!(TargetMembers["~n~"].type) "
+ ~ name~"(Parameters!(TargetMembers["~n~"].type) args) "~mod~
+ "{ return "~fbody~"; }";
+ }
+
+ public:
+ mixin mixinAll!(
+ staticMap!(generateFun, staticIota!(0, TargetMembers.length)));
+ }
+ }
+}
+/// ditto
+template wrap(Targets...)
+if (Targets.length >= 1 && !allSatisfy!(isMutable, Targets))
+{
+ import std.meta : staticMap;
+
+ alias wrap = .wrap!(staticMap!(Unqual, Targets));
+}
+
+// Internal class to support dynamic cross-casting
+private interface Structural
+{
+ inout(Object) _wrap_getSource() inout @safe pure nothrow;
+}
+
+/**
+ * Extract object which wrapped by $(D wrap).
+ */
+template unwrap(Target)
+if (isMutable!Target)
+{
+ // strict downcast
+ auto unwrap(Source)(inout Source src) @trusted pure nothrow
+ if (is(Target : Source))
+ {
+ alias T = Select!(is(Source == shared), shared Target, Target);
+ return dynamicCast!(inout T)(src);
+ }
+ // structural downcast
+ auto unwrap(Source)(inout Source src) @trusted pure nothrow
+ if (!is(Target : Source))
+ {
+ alias T = Select!(is(Source == shared), shared Target, Target);
+ Object o = dynamicCast!(Object)(src); // remove qualifier
+ do
+ {
+ if (auto a = dynamicCast!(Structural)(o))
+ {
+ if (auto d = dynamicCast!(inout T)(o = a._wrap_getSource()))
+ return d;
+ }
+ else if (auto d = dynamicCast!(inout T)(o))
+ return d;
+ else
+ break;
+ } while (o);
+ return null;
+ }
+}
+/// ditto
+template unwrap(Target)
+if (!isMutable!Target)
+{
+ alias unwrap = .unwrap!(Unqual!Target);
+}
+
+///
+@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; }
+ }
+
+ Duck d1 = new Duck();
+ Human h1 = new Human();
+
+ interface Refleshable
+ {
+ int reflesh();
+ }
+ // does not have structural conformance
+ static assert(!__traits(compiles, d1.wrap!Refleshable));
+ static assert(!__traits(compiles, h1.wrap!Refleshable));
+
+ // 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;
+ assert(qh.quack() == 2); // calls Human.quack
+ // structural downcast
+ Human h2 = qh.unwrap!Human;
+ assert(h2 is h1);
+
+ // structural upcast (two steps)
+ Quack qx = h1.wrap!Quack; // Human -> Quack
+ Flyer fx = qx.wrap!Flyer; // Quack -> Flyer
+ assert(fx.height == 20); // calls Human.height
+ // strucural downcast (two steps)
+ Quack qy = fx.unwrap!Quack; // Flyer -> Quack
+ Human hy = qy.unwrap!Human; // Quack -> Human
+ assert(hy is h1);
+ // strucural downcast (one step)
+ Human hz = fx.unwrap!Human; // Flyer -> Human
+ assert(hz is h1);
+}
+///
+@system unittest
+{
+ import std.traits : FunctionAttribute, functionAttributes;
+ 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);
+}
+@system unittest
+{
+ 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);
+}
+
+// Make a tuple of non-static function symbols
+package template GetOverloadedMethods(T)
+{
+ import std.meta : Filter;
+
+ alias allMembers = AliasSeq!(__traits(allMembers, T));
+ template follows(size_t i = 0)
+ {
+ static if (i >= allMembers.length)
+ {
+ alias follows = AliasSeq!();
+ }
+ else static if (!__traits(compiles, mixin("T."~allMembers[i])))
+ {
+ alias follows = follows!(i + 1);
+ }
+ else
+ {
+ enum name = allMembers[i];
+
+ template isMethod(alias f)
+ {
+ static if (is(typeof(&f) F == F*) && is(F == function))
+ enum isMethod = !__traits(isStaticFunction, f);
+ else
+ enum isMethod = false;
+ }
+ alias follows = AliasSeq!(
+ std.meta.Filter!(isMethod, __traits(getOverloads, T, name)),
+ follows!(i + 1));
+ }
+ }
+ alias GetOverloadedMethods = follows!();
+}
+// 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;
+}
+
+private enum TypeModifier
+{
+ mutable = 0, // type is mutable
+ const_ = 1, // type is const
+ immutable_ = 2, // type is immutable
+ shared_ = 4, // type is shared
+ inout_ = 8, // type is wild
+}
+private template TypeMod(T)
+{
+ static if (is(T == immutable))
+ {
+ enum mod1 = TypeModifier.immutable_;
+ enum mod2 = 0;
+ }
+ else
+ {
+ enum mod1 = is(T == shared) ? TypeModifier.shared_ : 0;
+ static if (is(T == const))
+ enum mod2 = TypeModifier.const_;
+ else static if (is(T == inout))
+ enum mod2 = TypeModifier.inout_;
+ else
+ enum mod2 = TypeModifier.mutable;
+ }
+ enum TypeMod = cast(TypeModifier)(mod1 | mod2);
+}
+
+version (unittest)
+{
+ private template UnittestFuncInfo(alias f)
+ {
+ enum name = __traits(identifier, f);
+ alias type = FunctionTypeOf!f;
+ }
+}
+@system unittest
+{
+ class A
+ {
+ int draw() { return 1; }
+ @property int value() { return 2; }
+ final int run() { return 3; }
+ }
+ alias methods = GetOverloadedMethods!A;
+
+ alias int F1();
+ alias @property int F2();
+ alias string F3();
+ alias nothrow @trusted uint F4();
+ alias int F5(Object);
+ alias bool F6(Object);
+ static assert(methods.length == 3 + 4);
+ static assert(__traits(identifier, methods[0]) == "draw" && is(typeof(&methods[0]) == F1*));
+ 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();
+ @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);
+ static assert(findCovariantFunction!(UnittestFuncInfo!nomatch, A, methods) == -1);
+
+ // considering opDispatch
+ class B
+ {
+ void opDispatch(string name, A...)(A) {}
+ }
+ alias methodsB = GetOverloadedMethods!B;
+ static assert(findCovariantFunction!(UnittestFuncInfo!draw, B, methodsB) == ptrdiff_t.max);
+ static assert(findCovariantFunction!(UnittestFuncInfo!value, B, methodsB) == ptrdiff_t.max);
+ static assert(findCovariantFunction!(UnittestFuncInfo!opEquals, B, methodsB) == ptrdiff_t.max);
+ static assert(findCovariantFunction!(UnittestFuncInfo!nomatch, B, methodsB) == ptrdiff_t.max);
+}
+
+package template DerivedFunctionType(T...)
+{
+ static if (!T.length)
+ {
+ alias DerivedFunctionType = void;
+ }
+ else static if (T.length == 1)
+ {
+ static if (is(T[0] == function))
+ {
+ alias DerivedFunctionType = T[0];
+ }
+ else
+ {
+ alias DerivedFunctionType = void;
+ }
+ }
+ else static if (is(T[0] P0 == function) && is(T[1] P1 == function))
+ {
+ alias FA = FunctionAttribute;
+
+ alias F0 = T[0], R0 = ReturnType!F0, PSTC0 = ParameterStorageClassTuple!F0;
+ alias F1 = T[1], R1 = ReturnType!F1, PSTC1 = ParameterStorageClassTuple!F1;
+ enum FA0 = functionAttributes!F0;
+ enum FA1 = functionAttributes!F1;
+
+ template CheckParams(size_t i = 0)
+ {
+ static if (i >= P0.length)
+ enum CheckParams = true;
+ else
+ {
+ enum CheckParams = (is(P0[i] == P1[i]) && PSTC0[i] == PSTC1[i]) &&
+ CheckParams!(i + 1);
+ }
+ }
+ static if (R0.sizeof == R1.sizeof && !is(CommonType!(R0, R1) == void) &&
+ P0.length == P1.length && CheckParams!() && TypeMod!F0 == TypeMod!F1 &&
+ variadicFunctionStyle!F0 == variadicFunctionStyle!F1 &&
+ functionLinkage!F0 == functionLinkage!F1 &&
+ ((FA0 ^ FA1) & (FA.ref_ | FA.property)) == 0)
+ {
+ alias R = Select!(is(R0 : R1), R0, R1);
+ alias FX = FunctionTypeOf!(R function(P0));
+ // @system is default
+ alias FY = SetFunctionAttributes!(FX, functionLinkage!F0, (FA0 | FA1) & ~FA.system);
+ alias DerivedFunctionType = DerivedFunctionType!(FY, T[2 .. $]);
+ }
+ else
+ alias DerivedFunctionType = void;
+ }
+ else
+ alias DerivedFunctionType = void;
+}
+@safe unittest
+{
+ // attribute covariance
+ alias int F1();
+ static assert(is(DerivedFunctionType!(F1, F1) == F1));
+ alias int F2() pure nothrow;
+ static assert(is(DerivedFunctionType!(F1, F2) == F2));
+ alias int F3() @safe;
+ alias int F23() @safe pure nothrow;
+ static assert(is(DerivedFunctionType!(F2, F3) == F23));
+
+ // return type covariance
+ alias long F4();
+ static assert(is(DerivedFunctionType!(F1, F4) == void));
+ class C {}
+ class D : C {}
+ alias C F5();
+ alias D F6();
+ static assert(is(DerivedFunctionType!(F5, F6) == F6));
+ alias typeof(null) F7();
+ alias int[] F8();
+ alias int* F9();
+ static assert(is(DerivedFunctionType!(F5, F7) == F7));
+ static assert(is(DerivedFunctionType!(F7, F8) == void));
+ static assert(is(DerivedFunctionType!(F7, F9) == F7));
+
+ // variadic type equality
+ alias int F10(int);
+ alias int F11(int...);
+ alias int F12(int, ...);
+ static assert(is(DerivedFunctionType!(F10, F11) == void));
+ static assert(is(DerivedFunctionType!(F10, F12) == void));
+ static assert(is(DerivedFunctionType!(F11, F12) == void));
+
+ // linkage equality
+ alias extern(C) int F13(int);
+ alias extern(D) int F14(int);
+ alias extern(Windows) int F15(int);
+ static assert(is(DerivedFunctionType!(F13, F14) == void));
+ static assert(is(DerivedFunctionType!(F13, F15) == void));
+ static assert(is(DerivedFunctionType!(F14, F15) == void));
+
+ // ref & @property equality
+ alias int F16(int);
+ alias ref int F17(int);
+ alias @property int F18(int);
+ static assert(is(DerivedFunctionType!(F16, F17) == void));
+ static assert(is(DerivedFunctionType!(F16, F18) == void));
+ static assert(is(DerivedFunctionType!(F17, F18) == void));
+}
+
+package template staticIota(int beg, int end)
+{
+ static if (beg + 1 >= end)
+ {
+ static if (beg >= end)
+ {
+ alias staticIota = AliasSeq!();
+ }
+ else
+ {
+ alias staticIota = AliasSeq!(+beg);
+ }
+ }
+ else
+ {
+ enum mid = beg + (end - beg) / 2;
+ alias staticIota = AliasSeq!(staticIota!(beg, mid), staticIota!(mid, end));
+ }
+}
+
+package template mixinAll(mixins...)
+{
+ static if (mixins.length == 1)
+ {
+ static if (is(typeof(mixins[0]) == string))
+ {
+ mixin(mixins[0]);
+ }
+ else
+ {
+ alias it = mixins[0];
+ mixin it;
+ }
+ }
+ else static if (mixins.length >= 2)
+ {
+ mixin mixinAll!(mixins[ 0 .. $/2]);
+ mixin mixinAll!(mixins[$/2 .. $ ]);
+ }
+}
+
+package template Bind(alias Template, args1...)
+{
+ alias Bind(args2...) = Template!(args1, args2);
+}
+
+
+/**
+Options regarding auto-initialization of a $(D RefCounted) object (see
+the definition of $(D RefCounted) below).
+ */
+enum RefCountedAutoInitialize
+{
+ /// Do not auto-initialize the object
+ no,
+ /// Auto-initialize the object
+ yes,
+}
+
+/**
+Defines a reference-counted object containing a $(D T) value as
+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.
+
+The $(D autoInit) option makes the object ensure the store is
+automatically initialized. Leaving $(D autoInit ==
+RefCountedAutoInitialize.yes) (the default option) is convenient but
+has the cost of a test whenever the payload is accessed. If $(D
+autoInit == RefCountedAutoInitialize.no), user code must call either
+$(D refCountedStore.isInitialized) or $(D refCountedStore.ensureInitialized)
+before attempting to access the payload. Not doing so results in null
+pointer dereference.
+ */
+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;
+ size_t _count;
+ }
+
+ private Impl* _store;
+
+ private void initialize(A...)(auto ref A args)
+ {
+ import core.exception : onOutOfMemoryError;
+ import std.conv : emplace;
+
+ _store = cast(Impl*) pureMalloc(Impl.sizeof);
+ if (_store is null)
+ onOutOfMemoryError();
+ static if (hasIndirections!T)
+ 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 pure @nogc
+ bool isInitialized() const
+ {
+ return _store !is null;
+ }
+
+ /**
+ Returns underlying reference count if it is allocated and initialized
+ (a positive integer), and $(D 0) otherwise.
+ */
+ @property nothrow @safe pure @nogc
+ size_t refCount() const
+ {
+ return isInitialized ? _store._count : 0;
+ }
+
+ /**
+ Makes sure the payload was properly initialized. Such a
+ call is typically inserted before using the payload.
+ */
+ void ensureInitialized()
+ {
+ if (!isInitialized) initialize();
+ }
+
+ }
+ RefCountedStore _refCounted;
+
+ /// Returns storage implementation struct.
+ @property nothrow @safe
+ ref inout(RefCountedStore) refCountedStore() inout
+ {
+ return _refCounted;
+ }
+
+/**
+Constructor that initializes the payload.
+
+Postcondition: $(D refCountedStore.isInitialized)
+ */
+ this(A...)(auto ref A args) if (A.length > 0)
+ {
+ _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) @safe pure nothrow @nogc
+ {
+ if (!_refCounted.isInitialized) return;
+ ++_refCounted._store._count;
+ }
+
+/**
+Destructor that tracks the reference count appropriately. If $(D
+!refCountedStore.isInitialized), does nothing. When the reference count goes
+down to zero, calls $(D destroy) agaist the payload and calls $(D free)
+to deallocate the corresponding resource.
+ */
+ ~this()
+ {
+ if (!_refCounted.isInitialized) return;
+ assert(_refCounted._store._count > 0);
+ if (--_refCounted._store._count)
+ return;
+ // Done, deallocate
+ .destroy(_refCounted._store._payload);
+ static if (hasIndirections!T)
+ {
+ pureGcRemoveRange(&_refCounted._store._payload);
+ }
+
+ pureFree(_refCounted._store);
+ _refCounted._store = null;
+ }
+
+/**
+Assignment operators
+ */
+ void opAssign(typeof(this) rhs)
+ {
+ import std.algorithm.mutation : swap;
+
+ swap(_refCounted._store, rhs._refCounted._store);
+ }
+
+/// Ditto
+ void opAssign(T rhs)
+ {
+ import std.algorithm.mutation : move;
+
+ static if (autoInit == RefCountedAutoInitialize.yes)
+ {
+ _refCounted.ensureInitialized();
+ }
+ else
+ {
+ assert(_refCounted.isInitialized);
+ }
+ move(rhs, _refCounted._store._payload);
+ }
+
+ //version to have a single properly ddoc'ed function (w/ correct sig)
+ version (StdDdoc)
+ {
+ /**
+ Returns a reference to the payload. If (autoInit ==
+ RefCountedAutoInitialize.yes), calls $(D
+ refCountedStore.ensureInitialized). Otherwise, just issues $(D
+ assert(refCountedStore.isInitialized)). Used with $(D alias
+ refCountedPayload this;), so callers can just use the $(D RefCounted)
+ object as a $(D T).
+
+ $(BLUE The first overload exists only if $(D autoInit == RefCountedAutoInitialize.yes).)
+ So if $(D autoInit == RefCountedAutoInitialize.no)
+ or called for a constant or immutable object, then
+ $(D refCountedPayload) will also be qualified as safe and nothrow
+ (but will still assert if not initialized).
+ */
+ @property
+ ref T refCountedPayload() return;
+
+ /// ditto
+ @property nothrow @safe pure @nogc
+ ref inout(T) refCountedPayload() inout return;
+ }
+ else
+ {
+ static if (autoInit == RefCountedAutoInitialize.yes)
+ {
+ //Can't use inout here because of potential mutation
+ @property
+ ref T refCountedPayload() return
+ {
+ _refCounted.ensureInitialized();
+ return _refCounted._store._payload;
+ }
+ }
+
+ @property nothrow @safe pure @nogc
+ ref inout(T) refCountedPayload() inout return
+ {
+ assert(_refCounted.isInitialized, "Attempted to access an uninitialized payload.");
+ return _refCounted._store._payload;
+ }
+ }
+
+/**
+Returns a reference to the payload. If (autoInit ==
+RefCountedAutoInitialize.yes), calls $(D
+refCountedStore.ensureInitialized). Otherwise, just issues $(D
+assert(refCountedStore.isInitialized)).
+ */
+ alias refCountedPayload this;
+}
+
+///
+pure @system nothrow @nogc unittest
+{
+ // A pair of an `int` and a `size_t` - the latter being the
+ // reference count - will be dynamically allocated
+ auto rc1 = RefCounted!int(5);
+ assert(rc1 == 5);
+ // No more allocation, add just one extra reference count
+ auto rc2 = rc1;
+ // Reference semantics
+ rc2 = 42;
+ assert(rc1 == 42);
+ // the pair will be freed when rc1 and rc2 go out of scope
+}
+
+pure @system unittest
+{
+ RefCounted!int* p;
+ {
+ auto rc1 = RefCounted!int(5);
+ p = &rc1;
+ assert(rc1 == 5);
+ assert(rc1._refCounted._store._count == 1);
+ auto rc2 = rc1;
+ assert(rc1._refCounted._store._count == 2);
+ // Reference semantics
+ rc2 = 42;
+ assert(rc1 == 42);
+ rc2 = rc2;
+ assert(rc2._refCounted._store._count == 2);
+ rc1 = rc2;
+ assert(rc1._refCounted._store._count == 2);
+ }
+ assert(p._refCounted._store == null);
+
+ // RefCounted as a member
+ struct A
+ {
+ RefCounted!int x;
+ this(int y)
+ {
+ x._refCounted.initialize(y);
+ }
+ A copy()
+ {
+ auto another = this;
+ return another;
+ }
+ }
+ auto a = A(4);
+ auto b = a.copy();
+ assert(a.x._refCounted._store._count == 2, "BUG 4356 still unfixed");
+}
+
+pure @system nothrow @nogc unittest
+{
+ import std.algorithm.mutation : swap;
+
+ RefCounted!int p1, p2;
+ swap(p1, p2);
+}
+
+// 6606
+@safe pure nothrow @nogc unittest
+{
+ union U {
+ size_t i;
+ void* p;
+ }
+
+ struct S {
+ U u;
+ }
+
+ alias SRC = RefCounted!S;
+}
+
+// 6436
+@system pure unittest
+{
+ struct S { this(ref int val) { assert(val == 3); ++val; } }
+
+ int val = 3;
+ auto s = RefCounted!S(val);
+ assert(val == 4);
+}
+
+// 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
+ assert(a == 5);
+
+ 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,
+ 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.
+ */
+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((T* self){ cast(void) mixin("(*self)."~__traits(identifier, a)); }));
+
+ static if (is(typeof(this) == class))
+ {
+ override bool opEquals(Object o)
+ {
+ if (auto b = cast(typeof(this))o)
+ {
+ return a == mixin("b."~__traits(identifier, a));
+ }
+ return false;
+ }
+
+ bool opEquals(T)(T b)
+ if (is(ValueType : T) || is(typeof(a.opEquals(b))) || is(typeof(b.opEquals(a))))
+ {
+ static if (is(typeof(a.opEquals(b))))
+ return a.opEquals(b);
+ else static if (is(typeof(b.opEquals(a))))
+ return b.opEquals(a);
+ else
+ return a == b;
+ }
+
+ override int opCmp(Object o)
+ {
+ if (auto b = cast(typeof(this))o)
+ {
+ 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);
+ else
+ throw new Exception("Attempt to compare a "~typeid(this).toString~" and a "~typeid(o).toString);
+ }
+
+ int opCmp(T)(auto ref const T b)
+ if (is(ValueType : T) || is(typeof(a.opCmp(b))) || is(typeof(b.opCmp(a))))
+ {
+ static if (is(typeof(a.opCmp(b))))
+ return a.opCmp(b);
+ else static if (is(typeof(b.opCmp(a))))
+ return -b.opCmp(b);
+ else
+ return a < b ? -1 : a > b ? +1 : 0;
+ }
+
+ static if (accessibleFrom!(const typeof(this)))
+ {
+ override hash_t toHash() const nothrow @trusted
+ {
+ static if (is(typeof(&a) == ValueType*))
+ alias v = a;
+ else
+ auto v = a; // if a is (property) function
+ return typeid(ValueType).getHash(cast(const void*)&v);
+ }
+ }
+ }
+ else
+ {
+ auto ref opEquals(this X, B)(auto ref B b)
+ {
+ static if (is(immutable B == immutable typeof(this)))
+ {
+ return a == mixin("b."~__traits(identifier, a));
+ }
+ else
+ return a == b;
+ }
+
+ auto ref opCmp(this X, B)(auto ref B b)
+ if (!is(typeof(a.opCmp(b))) || !is(typeof(b.opCmp(a))))
+ {
+ static if (is(typeof(a.opCmp(b))))
+ 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);
+ }
+
+ static if (accessibleFrom!(const typeof(this)))
+ {
+ hash_t toHash() const nothrow @trusted
+ {
+ static if (is(typeof(&a) == ValueType*))
+ alias v = a;
+ else
+ auto v = a; // if a is (property) function
+ return typeid(ValueType).getHash(cast(const void*)&v);
+ }
+ }
+ }
+
+ 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 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 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 opBinary(string op, this X, B)(auto ref B b)
+ if (op == "in" && is(typeof(a in b)) || op != "in")
+ {
+ return mixin("a "~op~" b");
+ }
+ auto ref opBinaryRight(string op, this X, B)(auto ref B b) { return mixin("b "~op~" a"); }
+
+ static if (!is(typeof(this) == class))
+ {
+ import std.traits;
+ static if (isAssignable!ValueType)
+ {
+ auto ref opAssign(this X)(auto ref typeof(this) v)
+ {
+ a = mixin("v."~__traits(identifier, a));
+ return this;
+ }
+ }
+ else
+ {
+ @disable void opAssign(this X)(auto ref typeof(this) v);
+ }
+ }
+
+ 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 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");
+ }
+
+ template opDispatch(string name)
+ {
+ static if (is(typeof(__traits(getMember, a, name)) == function))
+ {
+ // non template function
+ auto ref opDispatch(this X, Args...)(auto ref Args args) { return mixin("a."~name~"(args)"); }
+ }
+ else static if (is(typeof({ enum x = mixin("a."~name); })))
+ {
+ // built-in type field, manifest constant, and static non-mutable field
+ enum opDispatch = mixin("a."~name);
+ }
+ else static if (is(typeof(mixin("a."~name))) || __traits(getOverloads, a, name).length != 0)
+ {
+ // field or property function
+ @property auto ref opDispatch(this X)() { return mixin("a."~name); }
+ @property auto ref opDispatch(this X, V)(auto ref V v) { return mixin("a."~name~" = v"); }
+ }
+ else
+ {
+ // member template
+ template opDispatch(T...)
+ {
+ enum targs = T.length ? "!T" : "";
+ auto ref opDispatch(this X, Args...)(auto ref Args args){ return mixin("a."~name~targs~"(args)"); }
+ }
+ }
+ }
+
+ import std.traits : isArray;
+
+ static if (isArray!ValueType)
+ {
+ auto opDollar() const { return a.length; }
+ }
+ else static if (is(typeof(a.opDollar!0)))
+ {
+ auto ref opDollar(size_t pos)() { return a.opDollar!pos(); }
+ }
+ else static if (is(typeof(a.opDollar) == function))
+ {
+ auto ref opDollar() { return a.opDollar(); }
+ }
+ else static if (is(typeof(a.opDollar)))
+ {
+ alias opDollar = a.opDollar;
+ }
+}
+
+///
+@safe unittest
+{
+ struct MyInt
+ {
+ private int value;
+ mixin Proxy!value;
+
+ this(int n){ value = n; }
+ }
+
+ MyInt n = 10;
+
+ // Enable operations that original type has.
+ ++n;
+ assert(n == 11);
+ assert(n * 2 == 22);
+
+ void func(int n) { }
+
+ // Disable implicit conversions to original type.
+ //int x = n;
+ //func(n);
+}
+
+///The proxied value must be an $(B lvalue).
+@safe unittest
+{
+ struct NewIntType
+ {
+ //Won't work; the literal '1'
+ //is an rvalue, not an lvalue
+ //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. $(DDSUBLINK spec/function,pseudo-member, Pseudo-member)
+ functions are usable with the new type; they will be forwarded on to the
+ proxied value.
+ */
+@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);
+}
+
+@safe unittest
+{
+ static struct MyInt
+ {
+ private int value;
+ mixin Proxy!value;
+ this(int n) inout { value = n; }
+
+ enum str = "str";
+ static immutable arr = [1,2,3];
+ }
+
+ foreach (T; AliasSeq!(MyInt, const MyInt, immutable MyInt))
+ {
+ T m = 10;
+ static assert(!__traits(compiles, { int x = m; }));
+ static assert(!__traits(compiles, { void func(int n){} func(m); }));
+ assert(m == 10);
+ assert(m != 20);
+ assert(m < 20);
+ assert(+m == 10);
+ assert(-m == -10);
+ assert(cast(double) m == 10.0);
+ assert(m + 10 == 20);
+ assert(m - 5 == 5);
+ assert(m * 20 == 200);
+ assert(m / 2 == 5);
+ assert(10 + m == 20);
+ assert(15 - m == 5);
+ assert(20 * m == 200);
+ assert(50 / m == 5);
+ static if (is(T == MyInt)) // mutable
+ {
+ assert(++m == 11);
+ assert(m++ == 11); assert(m == 12);
+ assert(--m == 11);
+ assert(m-- == 11); assert(m == 10);
+ m = m;
+ m = 20; assert(m == 20);
+ }
+ static assert(T.max == int.max);
+ static assert(T.min == int.min);
+ static assert(T.init == int.init);
+ static assert(T.str == "str");
+ static assert(T.arr == [1,2,3]);
+ }
+}
+@system unittest
+{
+ static struct MyArray
+ {
+ private int[] value;
+ mixin Proxy!value;
+ this(int[] arr) { value = arr; }
+ this(immutable int[] arr) immutable { value = arr; }
+ }
+
+ 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
+ else
+ T a = [1,2,3,4];
+ assert(a == [1,2,3,4]);
+ assert(a != [5,6,7,8]);
+ assert(+a[0] == 1);
+ version (LittleEndian)
+ 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(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]);
+ 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] += 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]);
+ }
+ }
+}
+@system unittest
+{
+ class Foo
+ {
+ int field;
+
+ @property int val1() const { return field; }
+ @property void val1(int n) { field = n; }
+
+ @property ref int val2() { return field; }
+
+ int func(int x, int y) const { return x; }
+ void func1(ref int a) { a = 9; }
+
+ T ifti1(T)(T t) { return t; }
+ void ifti2(Args...)(Args args) { }
+ void ifti3(T, Args...)(Args args) { }
+
+ T opCast(T)(){ return T.init; }
+
+ T tempfunc(T)() { return T.init; }
+ }
+ class Hoge
+ {
+ Foo foo;
+ mixin Proxy!foo;
+ this(Foo f) { foo = f; }
+ }
+
+ auto h = new Hoge(new Foo());
+ int n;
+
+ static assert(!__traits(compiles, { Foo f = h; }));
+
+ // field
+ h.field = 1; // lhs of assign
+ n = h.field; // rhs of assign
+ assert(h.field == 1); // lhs of BinExp
+ assert(1 == h.field); // rhs of BinExp
+ assert(n == 1);
+
+ // getter/setter property function
+ h.val1 = 4;
+ n = h.val1;
+ assert(h.val1 == 4);
+ assert(4 == h.val1);
+ assert(n == 4);
+
+ // ref getter property function
+ h.val2 = 8;
+ n = h.val2;
+ assert(h.val2 == 8);
+ assert(8 == h.val2);
+ assert(n == 8);
+
+ // member function
+ assert(h.func(2,4) == 2);
+ h.func1(n);
+ assert(n == 9);
+
+ // IFTI
+ assert(h.ifti1(4) == 4);
+ h.ifti2(4);
+ h.ifti3!int(4, 3);
+
+ // bug5896 test
+ assert(h.opCast!int() == 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));
+
+ // template member function
+ assert(h.tempfunc!int() == 0);
+}
+
+@system unittest // about Proxy inside a class
+{
+ class MyClass
+ {
+ int payload;
+ mixin Proxy!payload;
+ this(int i){ payload = i; }
+ string opCall(string msg){ return msg; }
+ int pow(int i){ return payload ^^ i; }
+ }
+
+ class MyClass2
+ {
+ MyClass payload;
+ mixin Proxy!payload;
+ this(int i){ payload = new MyClass(i); }
+ }
+
+ class MyClass3
+ {
+ int payload;
+ mixin Proxy!payload;
+ this(int i){ payload = i; }
+ }
+
+ // opEquals
+ Object a = new MyClass(5);
+ Object b = new MyClass(5);
+ 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(a != d);
+
+ assert(c != a);
+ // oops! above line is unexpected, isn't it?
+ // the reason is below.
+ // MyClass2.opEquals knows MyClass but,
+ // 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));
+
+ // opCmp
+ Object e = new MyClass2(7);
+ 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);
+
+ // opCall
+ assert((cast(MyClass2) e)("hello") == "hello");
+
+ // opCast
+ assert((cast(MyClass)(cast(MyClass2) c)) == a);
+ assert((cast(int)(cast(MyClass2) c)) == 5);
+
+ // opIndex
+ class MyClass4
+ {
+ string payload;
+ mixin Proxy!payload;
+ this(string s){ payload = s; }
+ }
+ class MyClass5
+ {
+ MyClass4 payload;
+ mixin Proxy!payload;
+ this(string s){ payload = new MyClass4(s); }
+ }
+ auto f = new MyClass4("hello");
+ assert(f[1] == 'e');
+ auto g = new MyClass5("hello");
+ assert(f[1] == 'e');
+
+ // opSlice
+ assert(f[2 .. 4] == "ll");
+
+ // opUnary
+ assert(-(cast(MyClass2) c) == -5);
+
+ // opBinary
+ 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);
+
+ // opOpAssign
+ assert((cast(MyClass2) c) += 4);
+ assert((cast(MyClass2) c) == 17);
+
+ // opDispatch
+ assert((cast(MyClass2) c).pow(2) == 289);
+
+ // opDollar
+ assert(f[2..$-1] == "ll");
+
+ // toHash
+ int[Object] hash;
+ hash[a] = 19;
+ hash[c] = 21;
+ assert(hash[b] == 19);
+ assert(hash[c] == 21);
+}
+
+@safe unittest
+{
+ struct MyInt
+ {
+ int payload;
+
+ mixin Proxy!payload;
+ }
+
+ MyInt v;
+ v = v;
+
+ struct Foo
+ {
+ @disable void opAssign(typeof(this));
+ }
+ struct MyFoo
+ {
+ Foo payload;
+
+ mixin Proxy!payload;
+ }
+ MyFoo f;
+ static assert(!__traits(compiles, f = f));
+
+ struct MyFoo2
+ {
+ Foo payload;
+
+ mixin Proxy!payload;
+
+ // override default Proxy behavior
+ void opAssign(typeof(this) rhs){}
+ }
+ MyFoo2 f2;
+ f2 = f2;
+}
+@safe unittest
+{
+ // bug8613
+ static struct Name
+ {
+ mixin Proxy!val;
+ private string val;
+ this(string s) { val = s; }
+ }
+
+ bool[Name] names;
+ names[Name("a")] = true;
+ bool* b = Name("a") in names;
+}
+
+@system unittest
+{
+ // bug14213, using function for the payload
+ static struct S
+ {
+ int foo() { return 12; }
+ mixin Proxy!foo;
+ }
+ static class C
+ {
+ int foo() { return 12; }
+ mixin Proxy!foo;
+ }
+ S s;
+ assert(s + 1 == 13);
+ C c = new C();
+ 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,
+$(B Typedef) ensures the two types are not considered as equals.
+
+Example:
+----
+alias MyInt = Typedef!int;
+static void takeInt(int) { }
+static void takeMyInt(MyInt) { }
+
+int i;
+takeInt(i); // ok
+takeMyInt(i); // fails
+
+MyInt myInt;
+takeInt(myInt); // fails
+takeMyInt(myInt); // ok
+----
+
+Params:
+
+init = Optional initial value for the new type. For example:
+
+----
+alias MyInt = Typedef!(int, 10);
+MyInt myInt;
+assert(myInt == 10); // default-initialized to 10
+----
+
+cookie = Optional, used to create multiple unique types which are
+based on the same origin type $(D T). For example:
+
+----
+alias TypeInt1 = Typedef!int;
+alias TypeInt2 = Typedef!int;
+
+// The two Typedefs are the same type.
+static assert(is(TypeInt1 == TypeInt2));
+
+alias MoneyEuros = Typedef!(float, float.init, "euros");
+alias MoneyDollars = Typedef!(float, float.init, "dollars");
+
+// The two Typedefs are _not_ the same type.
+static assert(!is(MoneyEuros == MoneyDollars));
+----
+
+Note: If a library routine cannot handle the Typedef type,
+you can use the $(D TypedefType) template to extract the
+type which the Typedef wraps.
+ */
+struct Typedef(T, T init = T.init, string cookie=null)
+{
+ private T Typedef_payload = init;
+
+ this(T init)
+ {
+ Typedef_payload = init;
+ }
+
+ this(Typedef tdef)
+ {
+ this(tdef.Typedef_payload);
+ }
+
+ // 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);
+ }
+
+ auto ref opCast(T2, this X)()
+ {
+ 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);}
+ }
+ }
+}
+
+/**
+Get the underlying type which a $(D Typedef) wraps.
+If $(D T) is not a $(D Typedef) it will alias itself to $(D T).
+*/
+template TypedefType(T)
+{
+ static if (is(T : Typedef!Arg, Arg))
+ alias TypedefType = Arg;
+ else
+ alias TypedefType = T;
+}
+
+///
+@safe unittest
+{
+ import std.typecons : Typedef, TypedefType;
+ import std.conv : to;
+
+ alias MyInt = Typedef!int;
+ static assert(is(TypedefType!MyInt == int));
+
+ /// Instantiating with a non-Typedef will return that type
+ static assert(is(TypedefType!int == int));
+
+ string num = "5";
+
+ // extract the needed type
+ MyInt myInt = MyInt( num.to!(TypedefType!MyInt) );
+ assert(myInt == 5);
+
+ // cast to the underlying type to get the value that's being wrapped
+ int x = cast(TypedefType!MyInt) myInt;
+
+ alias MyIntInit = Typedef!(int, 42);
+ static assert(is(TypedefType!MyIntInit == int));
+ static assert(MyIntInit() == 42);
+}
+
+@safe unittest
+{
+ Typedef!int x = 10;
+ static assert(!__traits(compiles, { int y = x; }));
+ static assert(!__traits(compiles, { long z = x; }));
+
+ Typedef!int y = 10;
+ assert(x == y);
+
+ static assert(Typedef!int.init == int.init);
+
+ Typedef!(float, 1.0) z; // specifies the init
+ assert(z == 1.0);
+
+ static assert(typeof(z).init == 1.0);
+
+ alias Dollar = Typedef!(int, 0, "dollar");
+ alias Yen = Typedef!(int, 0, "yen");
+ static assert(!is(Dollar == Yen));
+
+ Typedef!(int[3]) sa;
+ static assert(sa.length == 3);
+ static assert(typeof(sa).length == 3);
+
+ Typedef!(int[3]) dollar1;
+ assert(dollar1[0..$] is dollar1[0 .. 3]);
+
+ Typedef!(int[]) dollar2;
+ dollar2.length = 3;
+ assert(dollar2[0..$] is dollar2[0 .. 3]);
+
+ static struct Dollar1
+ {
+ static struct DollarToken {}
+ enum opDollar = DollarToken.init;
+ auto opSlice(size_t, DollarToken) { return 1; }
+ auto opSlice(size_t, size_t) { return 2; }
+ }
+
+ Typedef!Dollar1 drange1;
+ assert(drange1[0..$] == 1);
+ assert(drange1[0 .. 1] == 2);
+
+ static struct Dollar2
+ {
+ size_t opDollar(size_t pos)() { return pos == 0 ? 1 : 100; }
+ size_t opIndex(size_t i, size_t j) { return i + j; }
+ }
+
+ Typedef!Dollar2 drange2;
+ assert(drange2[$, $] == 101);
+
+ static struct Dollar3
+ {
+ size_t opDollar() { return 123; }
+ size_t opIndex(size_t i) { return i; }
+ }
+
+ Typedef!Dollar3 drange3;
+ assert(drange3[$] == 123);
+}
+
+@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;
+ import std.bitmanip;
+ static import core.stdc.config;
+
+ alias c_ulong = Typedef!(core.stdc.config.c_ulong);
+
+ static struct Foo
+ {
+ mixin(bitfields!(
+ c_ulong, "NameOffset", 31,
+ c_ulong, "NameIsString", 1
+ ));
+ }
+}
+
+@safe unittest // Issue 12596
+{
+ import std.typecons;
+ alias TD = Typedef!int;
+ TD x = TD(1);
+ TD y = TD(x);
+ assert(x == y);
+}
+
+@safe unittest // about toHash
+{
+ import std.typecons;
+ {
+ alias TD = Typedef!int;
+ int[TD] td;
+ td[TD(1)] = 1;
+ assert(td[TD(1)] == 1);
+ }
+
+ {
+ alias TD = Typedef!(int[]);
+ int[TD] td;
+ td[TD([1,2,3,4])] = 2;
+ assert(td[TD([1,2,3,4])] == 2);
+ }
+
+ {
+ alias TD = Typedef!(int[][]);
+ int[TD] td;
+ td[TD([[1,0,0,0], [0,1,0,0], [0,0,1,0], [0,0,0,1]])] = 3;
+ assert(td[TD([[1,0,0,0], [0,1,0,0], [0,0,1,0], [0,0,0,1]])] == 3);
+ }
+
+ {
+ struct MyStruct{ int x; }
+ alias TD = Typedef!MyStruct;
+ int[TD] td;
+ td[TD(MyStruct(10))] = 4;
+ assert(TD(MyStruct(20)) !in td);
+ assert(td[TD(MyStruct(10))] == 4);
+ }
+
+ {
+ static struct MyStruct2
+ {
+ int x;
+ size_t toHash() const nothrow @safe { return x; }
+ bool opEquals(ref const MyStruct2 r) const { return r.x == x; }
+ }
+
+ alias TD = Typedef!MyStruct2;
+ int[TD] td;
+ td[TD(MyStruct2(50))] = 5;
+ assert(td[TD(MyStruct2(50))] == 5);
+ }
+
+ {
+ class MyClass{}
+ alias TD = Typedef!MyClass;
+ int[TD] td;
+ auto c = new MyClass;
+ td[TD(c)] = 6;
+ assert(TD(new MyClass) !in td);
+ assert(td[TD(c)] == 6);
+ }
+}
+
+@system unittest
+{
+ alias String = Typedef!(char[]);
+ alias CString = Typedef!(const(char)[]);
+ CString cs = "fubar";
+ String s = cast(String) cs;
+ assert(cs == s);
+ char[] s2 = cast(char[]) cs;
+ const(char)[] cs2 = cast(const(char)[])s;
+ assert(s2 == cs2);
+}
+
+/**
+Allocates a $(D class) object right inside the current scope,
+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.
+
+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)
+ if (is(T == class))
+{
+ // _d_newclass now use default GC alignment (looks like (void*).sizeof * 2 for
+ // small objects). We will just use the maximum of filed alignments.
+ alias alignment = classInstanceAlignment!T;
+ alias aligned = _alignUp!alignment;
+
+ static struct Scoped
+ {
+ // Addition of `alignment` is required as `Scoped_store` can be misaligned in memory.
+ private void[aligned(__traits(classInstanceSize, T) + size_t.sizeof) + alignment] Scoped_store = void;
+
+ @property inout(T) Scoped_payload() inout
+ {
+ 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)
+ {
+ import core.stdc.string : memmove;
+ memmove(alignedStore, Scoped_store.ptr + *currD, __traits(classInstanceSize, T));
+ *currD = d;
+ }
+ return cast(inout(T)) alignedStore;
+ }
+ alias Scoped_payload this;
+
+ @disable this();
+ @disable this(this);
+
+ ~this()
+ {
+ // `destroy` will also write .init but we have no functions in druntime
+ // for deterministic finalization and memory releasing for now.
+ .destroy(Scoped_payload);
+ }
+ }
+
+ /** 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(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;
+ }
+}
+
+///
+@system unittest
+{
+ class A
+ {
+ int x;
+ this() {x = 0;}
+ this(int i){x = i;}
+ ~this() {}
+ }
+
+ // Standard usage, constructing A on the stack
+ auto a1 = scoped!A();
+ a1.x = 42;
+
+ // 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([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); // 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);
+ }
+ }
+
+ // 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 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;
+}
+
+@system unittest // Issue 6580 testcase
+{
+ enum alignment = (void*).alignof;
+
+ static class C0 { }
+ static class C1 { byte b; }
+ static class C2 { byte[2] b; }
+ static class C3 { byte[3] b; }
+ static class C7 { byte[7] b; }
+ static assert(scoped!C0().sizeof % alignment == 0);
+ static assert(scoped!C1().sizeof % alignment == 0);
+ static assert(scoped!C2().sizeof % alignment == 0);
+ static assert(scoped!C3().sizeof % alignment == 0);
+ static assert(scoped!C7().sizeof % alignment == 0);
+
+ enum longAlignment = long.alignof;
+ static class C1long
+ {
+ long long_; byte byte_ = 4;
+ this() { }
+ this(long _long, ref int i) { long_ = _long; ++i; }
+ }
+ static class C2long { byte[2] byte_ = [5, 6]; long long_ = 7; }
+ static assert(scoped!C1long().sizeof % longAlignment == 0);
+ static assert(scoped!C2long().sizeof % longAlignment == 0);
+
+ void alignmentTest()
+ {
+ int var = 5;
+ auto c1long = scoped!C1long(3, var);
+ assert(var == 6);
+ auto c2long = scoped!C2long();
+ 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);
+ }
+
+ alignmentTest();
+
+ version (DigitalMars)
+ {
+ void test(size_t size)
+ {
+ import core.stdc.stdlib;
+ alloca(size);
+ alignmentTest();
+ }
+ foreach (i; 0 .. 10)
+ test(i);
+ }
+ else
+ {
+ void test(size_t size)()
+ {
+ byte[size] arr;
+ alignmentTest();
+ }
+ foreach (i; AliasSeq!(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10))
+ test!i();
+ }
+}
+
+@system unittest // Original Issue 6580 testcase
+{
+ class C { int i; byte b; }
+
+ auto sa = [scoped!C(), scoped!C()];
+ assert(cast(uint)&sa[0].i % int.alignof == 0);
+ assert(cast(uint)&sa[1].i % int.alignof == 0); // fails
+}
+
+@system unittest
+{
+ class A { int x = 1; }
+ auto a1 = scoped!A();
+ assert(a1.x == 1);
+ auto a2 = scoped!A();
+ a1.x = 42;
+ a2.x = 53;
+ assert(a1.x == 42);
+}
+
+@system unittest
+{
+ class A { int x = 1; this() { x = 2; } }
+ auto a1 = scoped!A();
+ assert(a1.x == 2);
+ auto a2 = scoped!A();
+ a1.x = 42;
+ a2.x = 53;
+ assert(a1.x == 42);
+}
+
+@system unittest
+{
+ class A { int x = 1; this(int y) { x = y; } ~this() {} }
+ auto a1 = scoped!A(5);
+ assert(a1.x == 5);
+ auto a2 = scoped!A(42);
+ a1.x = 42;
+ a2.x = 53;
+ assert(a1.x == 42);
+}
+
+@system unittest
+{
+ class A { static bool dead; ~this() { dead = true; } }
+ class B : A { static bool dead; ~this() { dead = true; } }
+ {
+ auto b = scoped!B();
+ }
+ assert(B.dead, "asdasd");
+ assert(A.dead, "asdasd");
+}
+
+@system unittest // Issue 8039 testcase
+{
+ static int dels;
+ static struct S { ~this(){ ++dels; } }
+
+ static class A { S s; }
+ dels = 0; { scoped!A(); }
+ assert(dels == 1);
+
+ static class B { S[2] s; }
+ dels = 0; { scoped!B(); }
+ assert(dels == 2);
+
+ static struct S2 { S[3] s; }
+ static class C { S2[2] s; }
+ dels = 0; { scoped!C(); }
+ assert(dels == 6);
+
+ static class D: A { S2[2] s; }
+ dels = 0; { scoped!D(); }
+ assert(dels == 1+6);
+}
+
+@system unittest
+{
+ // bug4500
+ class A
+ {
+ this() { a = this; }
+ this(int i) { a = this; }
+ A a;
+ bool check() { return this is a; }
+ }
+
+ auto a1 = scoped!A();
+ assert(a1.check());
+
+ auto a2 = scoped!A(1);
+ assert(a2.check());
+
+ a1.a = a1;
+ assert(a1.check());
+}
+
+@system unittest
+{
+ static class A
+ {
+ static int sdtor;
+
+ this() { ++sdtor; assert(sdtor == 1); }
+ ~this() { assert(sdtor == 1); --sdtor; }
+ }
+
+ interface Bob {}
+
+ static class ABob : A, Bob
+ {
+ this() { ++sdtor; assert(sdtor == 2); }
+ ~this() { assert(sdtor == 2); --sdtor; }
+ }
+
+ A.sdtor = 0;
+ scope(exit) assert(A.sdtor == 0);
+ auto abob = scoped!ABob();
+}
+
+@safe unittest
+{
+ static class A { this(int) {} }
+ static assert(!__traits(compiles, scoped!A()));
+}
+
+@system unittest
+{
+ static class A { @property inout(int) foo() inout { return 1; } }
+
+ auto a1 = scoped!A();
+ assert(a1.foo == 1);
+ static assert(is(typeof(a1.foo) == int));
+
+ auto a2 = scoped!(const(A))();
+ assert(a2.foo == 1);
+ static assert(is(typeof(a2.foo) == const(int)));
+
+ auto a3 = scoped!(immutable(A))();
+ assert(a3.foo == 1);
+ static assert(is(typeof(a3.foo) == immutable(int)));
+
+ const c1 = scoped!A();
+ assert(c1.foo == 1);
+ static assert(is(typeof(c1.foo) == const(int)));
+
+ const c2 = scoped!(const(A))();
+ assert(c2.foo == 1);
+ static assert(is(typeof(c2.foo) == const(int)));
+
+ const c3 = scoped!(immutable(A))();
+ assert(c3.foo == 1);
+ static assert(is(typeof(c3.foo) == immutable(int)));
+}
+
+@system unittest
+{
+ class C { this(ref int val) { assert(val == 3); ++val; } }
+
+ int val = 3;
+ auto s = scoped!C(val);
+ assert(val == 4);
+}
+
+@system unittest
+{
+ class C
+ {
+ this(){}
+ this(int){}
+ this(int, int){}
+ }
+ alias makeScopedC = scoped!C;
+
+ auto a = makeScopedC();
+ auto b = makeScopedC(1);
+ auto c = makeScopedC(1, 1);
+
+ static assert(is(typeof(a) == typeof(b)));
+ static assert(is(typeof(b) == typeof(c)));
+}
+
+/**
+Defines a simple, self-documenting yes/no flag. This makes it easy for
+APIs to define functions accepting flags without resorting to $(D
+bool), which is opaque in calls, and without needing to define an
+enumerated type separately. Using $(D Flag!"Name") instead of $(D
+bool) makes the flag's meaning visible in calls. Each yes/no flag has
+its own type, which makes confusions and mix-ups impossible.
+
+Example:
+
+Code calling $(D getLine) (usually far away from its definition) can't be
+understood without looking at the documentation, even by users familiar with
+the API:
+----
+string getLine(bool keepTerminator)
+{
+ ...
+ if (keepTerminator) ...
+ ...
+}
+...
+auto line = getLine(false);
+----
+
+Assuming the reverse meaning (i.e. "ignoreTerminator") and inserting the wrong
+code compiles and runs with erroneous results.
+
+After replacing the boolean parameter with an instantiation of $(D Flag), code
+calling $(D getLine) can be easily read and understood even by people not
+fluent with the API:
+
+----
+string getLine(Flag!"keepTerminator" keepTerminator)
+{
+ ...
+ if (keepTerminator) ...
+ ...
+}
+...
+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.
+ */
+template Flag(string name) {
+ ///
+ enum Flag : bool
+ {
+ /**
+ When creating a value of type $(D Flag!"Name"), use $(D
+ Flag!"Name".no) for the negative option. When using a value
+ of type $(D Flag!"Name"), compare it against $(D
+ Flag!"Name".no) or just $(D false) or $(D 0). */
+ no = false,
+
+ /** When creating a value of type $(D Flag!"Name"), use $(D
+ Flag!"Name".yes) for the affirmative option. When using a
+ value of type $(D Flag!"Name"), compare it against $(D
+ Flag!"Name".yes).
+ */
+ yes = true
+ }
+}
+
+/**
+Convenience names that allow using e.g. $(D Yes.encryption) instead of
+$(D Flag!"encryption".yes) and $(D No.encryption) instead of $(D
+Flag!"encryption".no).
+*/
+struct Yes
+{
+ template opDispatch(string name)
+ {
+ enum opDispatch = Flag!name.yes;
+ }
+}
+//template yes(string name) { enum Flag!name yes = Flag!name.yes; }
+
+/// Ditto
+struct No
+{
+ template opDispatch(string name)
+ {
+ enum opDispatch = Flag!name.no;
+ }
+}
+
+///
+@safe unittest
+{
+ Flag!"abc" flag1;
+ assert(flag1 == Flag!"abc".no);
+ assert(flag1 == No.abc);
+ assert(!flag1);
+ if (flag1) assert(false);
+ flag1 = Yes.abc;
+ assert(flag1);
+ if (!flag1) assert(false);
+ if (flag1) {} else assert(false);
+ assert(flag1 == Yes.abc);
+}
+
+/**
+Detect whether an enum is of integral type and has only "flag" values
+(i.e. values with a bit count of exactly 1).
+Additionally, a zero value is allowed for compatibility with enums including
+a "None" value.
+*/
+template isBitFlagEnum(E)
+{
+ static if (is(E Base == enum) && isIntegral!Base)
+ {
+ enum isBitFlagEnum = (E.min >= 0) &&
+ {
+ foreach (immutable flag; EnumMembers!E)
+ {
+ Base value = flag;
+ value &= value - 1;
+ if (value != 0) return false;
+ }
+ return true;
+ }();
+ }
+ else
+ {
+ enum isBitFlagEnum = false;
+ }
+}
+
+///
+@safe pure nothrow unittest
+{
+ enum A
+ {
+ None,
+ A = 1 << 0,
+ B = 1 << 1,
+ C = 1 << 2,
+ D = 1 << 3,
+ }
+
+ static assert(isBitFlagEnum!A);
+
+ enum B
+ {
+ A,
+ B,
+ C,
+ D // D == 3
+ }
+
+ static assert(!isBitFlagEnum!B);
+
+ enum C: double
+ {
+ A = 1 << 0,
+ B = 1 << 1
+ }
+
+ static assert(!isBitFlagEnum!C);
+}
+
+/**
+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
+a bit count of at most 1, or if the $(D unsafe) parameter is explicitly set to
+Yes.
+This is much safer than using the enum itself to store
+the OR combination, which can produce surprising effects like this:
+----
+enum E
+{
+ A = 1 << 0,
+ B = 1 << 1
+}
+E e = E.A | E.B;
+// will throw SwitchError
+final switch (e)
+{
+ case E.A:
+ return;
+ case E.B:
+ return;
+}
+----
+*/
+struct BitFlags(E, Flag!"unsafe" unsafe = No.unsafe) if (unsafe || isBitFlagEnum!(E))
+{
+@safe @nogc pure nothrow:
+private:
+ enum isBaseEnumType(T) = is(E == T);
+ alias Base = OriginalType!E;
+ Base mValue;
+ static struct Negation
+ {
+ @safe @nogc pure nothrow:
+ private:
+ Base mValue;
+
+ // Prevent non-copy construction outside the module.
+ @disable this();
+ this(Base value)
+ {
+ mValue = value;
+ }
+ }
+
+public:
+ this(E flag)
+ {
+ this = flag;
+ }
+
+ this(T...)(T flags)
+ if (allSatisfy!(isBaseEnumType, T))
+ {
+ this = flags;
+ }
+
+ bool opCast(B: bool)() const
+ {
+ return mValue != 0;
+ }
+
+ Base opCast(B)() const
+ if (isImplicitlyConvertible!(Base, B))
+ {
+ return mValue;
+ }
+
+ Negation opUnary(string op)() const
+ if (op == "~")
+ {
+ return Negation(~mValue);
+ }
+
+ auto ref opAssign(T...)(T flags)
+ if (allSatisfy!(isBaseEnumType, T))
+ {
+ mValue = 0;
+ foreach (E flag; flags)
+ {
+ mValue |= flag;
+ }
+ return this;
+ }
+
+ auto ref opAssign(E flag)
+ {
+ mValue = flag;
+ return this;
+ }
+
+ auto ref opOpAssign(string op: "|")(BitFlags flags)
+ {
+ mValue |= flags.mValue;
+ return this;
+ }
+
+ auto ref opOpAssign(string op: "&")(BitFlags flags)
+ {
+ mValue &= flags.mValue;
+ return this;
+ }
+
+ auto ref opOpAssign(string op: "|")(E flag)
+ {
+ mValue |= flag;
+ return this;
+ }
+
+ auto ref opOpAssign(string op: "&")(E flag)
+ {
+ mValue &= flag;
+ return this;
+ }
+
+ auto ref opOpAssign(string op: "&")(Negation negatedFlags)
+ {
+ mValue &= negatedFlags.mValue;
+ return this;
+ }
+
+ auto opBinary(string op)(BitFlags flags) const
+ if (op == "|" || op == "&")
+ {
+ BitFlags result = this;
+ result.opOpAssign!op(flags);
+ return result;
+ }
+
+ auto opBinary(string op)(E flag) const
+ if (op == "|" || op == "&")
+ {
+ BitFlags result = this;
+ result.opOpAssign!op(flag);
+ return result;
+ }
+
+ auto opBinary(string op: "&")(Negation negatedFlags) const
+ {
+ BitFlags result = this;
+ result.opOpAssign!op(negatedFlags);
+ return result;
+ }
+
+ auto opBinaryRight(string op)(E flag) const
+ if (op == "|" || op == "&")
+ {
+ return opBinary!op(flag);
+ }
+}
+
+/// BitFlags can be manipulated with the usual operators
+@safe @nogc pure nothrow unittest
+{
+ import std.traits : EnumMembers;
+
+ // You can use such an enum with BitFlags straight away
+ enum Enum
+ {
+ None,
+ A = 1 << 0,
+ B = 1 << 1,
+ C = 1 << 2
+ }
+ BitFlags!Enum flags1;
+ assert(!(flags1 & (Enum.A | Enum.B | Enum.C)));
+
+ // You need to specify the `unsafe` parameter for enum with custom values
+ enum UnsafeEnum
+ {
+ A,
+ B,
+ C,
+ D = B|C
+ }
+ 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
+ assert(!(flags_empty & Enum.A) && !(flags_empty & Enum.B) && !(flags_empty & Enum.C));
+
+ // Value can be set with the | operator
+ immutable BitFlags!Enum flags_A = flags_empty | Enum.A;
+
+ // And tested with the & operator
+ assert(flags_A & Enum.A);
+
+ // Which commutes
+ assert(Enum.A & flags_A);
+
+ // BitFlags can be variadically initialized
+ immutable BitFlags!Enum flags_AB = BitFlags!Enum(Enum.A, Enum.B);
+ assert((flags_AB & Enum.A) && (flags_AB & Enum.B) && !(flags_AB & Enum.C));
+
+ // Use the ~ operator for subtracting flags
+ immutable BitFlags!Enum flags_B = flags_AB & ~BitFlags!Enum(Enum.A);
+ assert(!(flags_B & Enum.A) && (flags_B & Enum.B) && !(flags_B & Enum.C));
+
+ // You can use the EnumMembers template to set all flags
+ immutable BitFlags!Enum flags_all = EnumMembers!Enum;
+
+ // use & between BitFlags for intersection
+ immutable BitFlags!Enum flags_BC = BitFlags!Enum(Enum.B, Enum.C);
+ assert(flags_B == (flags_BC & flags_AB));
+
+ // All the binary operators work in their assignment version
+ BitFlags!Enum temp = flags_empty;
+ temp |= flags_AB;
+ assert(temp == (flags_empty | flags_AB));
+ temp = flags_empty;
+ temp |= Enum.B;
+ assert(temp == (flags_empty | Enum.B));
+ temp = flags_empty;
+ temp &= flags_AB;
+ assert(temp == (flags_empty & flags_AB));
+ temp = flags_empty;
+ temp &= Enum.A;
+ assert(temp == (flags_empty & Enum.A));
+
+ // BitFlags with no value set evaluate to false
+ assert(!flags_empty);
+
+ // BitFlags with at least one value set evaluate to true
+ assert(flags_A);
+
+ // This can be useful to check intersection between BitFlags
+ assert(flags_A & flags_AB);
+ assert(flags_AB & Enum.A);
+
+ // Finally, you can of course get you raw value out of flags
+ 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/libphobos/src/std/typetuple.d b/libphobos/src/std/typetuple.d
new file mode 100644
index 0000000..dedbdc2
--- /dev/null
+++ b/libphobos/src/std/typetuple.d
@@ -0,0 +1,40 @@
+/**
+ * This module was renamed to disambiguate the term tuple, use
+ * $(MREF std, meta) instead.
+ *
+ * Copyright: Copyright Digital Mars 2005 - 2015.
+ * License: $(HTTP www.boost.org/LICENSE_1_0.txt, Boost License 1.0).
+ * Authors:
+ * Source: $(PHOBOSSRC std/_typetuple.d)
+ *
+ * $(SCRIPT inhibitQuickIndex = 1;)
+ */
+module std.typetuple;
+
+public import std.meta;
+
+/**
+ * Alternate name for $(REF AliasSeq, std,meta) for legacy compatibility.
+ */
+alias TypeTuple = AliasSeq;
+
+///
+@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];
+ }
+}
+
+///
+@safe unittest
+{
+ alias TL = TypeTuple!(int, double);
+
+ alias Types = TypeTuple!(TL, char);
+ static assert(is(Types == TypeTuple!(int, double, char)));
+}
diff --git a/libphobos/src/std/uni.d b/libphobos/src/std/uni.d
new file mode 100644
index 0000000..5f24ad1
--- /dev/null
+++ b/libphobos/src/std/uni.d
@@ -0,0 +1,9756 @@
+// Written in the D programming language.
+
+/++
+ $(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 $(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 $(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:
+ )
+ $(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).
+ )
+ )
+ $(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:
+ )
+ $(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 $(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)
+ ---
+ import std.uni;
+ void main()
+ {
+ // initialize code point sets using script/block or property name
+ // now 'set' contains code points from both scripts.
+ auto set = unicode("Cyrillic") | unicode("Armenian");
+ // same thing but simpler and checked at compile-time
+ auto ascii = unicode.ASCII;
+ auto currency = unicode.Currency_Symbol;
+
+ // easy set ops
+ auto a = set & ascii;
+ assert(a.empty); // as it has no intersection with ascii
+ a = set | ascii;
+ auto b = currency - a; // subtract all ASCII, Cyrillic and Armenian
+
+ // some properties of code point sets
+ assert(b.length > 45); // 46 items in Unicode 6.1, even more in 6.2
+ // testing presence of a code point in a set
+ // is just fine, it is O(logN)
+ assert(!b['$']);
+ assert(!b['\u058F']); // Armenian dram sign
+ assert(b['¥']);
+
+ // building fast lookup tables, these guarantee O(1) complexity
+ // 1-level Trie lookup table essentially a huge bit-set ~262Kb
+ auto oneTrie = toTrie!1(b);
+ // 2-level far more compact but typically slightly slower
+ auto twoTrie = toTrie!2(b);
+ // 3-level even smaller, and a bit slower yet
+ auto threeTrie = toTrie!3(b);
+ assert(oneTrie['£']);
+ assert(twoTrie['£']);
+ assert(threeTrie['£']);
+
+ // build the trie with the most sensible trie level
+ // and bind it as a functor
+ auto cyrillicOrArmenian = toDelegate(set);
+ auto balance = find!(cyrillicOrArmenian)("Hello ընկեր!");
+ assert(balance == "ընկեր!");
+ // compatible with bool delegate(dchar)
+ bool delegate(dchar) bindIt = cyrillicOrArmenian;
+
+ // Normalization
+ string s = "Plain ascii (and not only), is always normalized!";
+ assert(s is normalize(s));// is the same string
+
+ string nonS = "A\u0308ffin"; // A ligature
+ auto nS = normalize(nonS); // to NFC, the W3C endorsed standard
+ assert(nS == "Äffin");
+ assert(nS != nonS);
+ string composed = "Äffin";
+
+ assert(normalize!NFD(composed) == "A\u0308ffin");
+ // to NFKD, compatibility decomposition useful for fuzzy matching/searching
+ assert(normalize!NFKD("2¹⁰") == "210");
+ }
+ ---
+ $(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 $(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:
+ $(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.
+ )
+ )
+ )
+ $(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
+ $(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 $(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.
+ )
+ $(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).
+ )
+ $(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.
+ )
+ $(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.)
+ )
+ $(P $(DEF Combining character) A character with the General Category
+ of Combining Mark(M).
+ $(UL
+ $(LI All characters with non-zero canonical combining class
+ are combining characters, but the reverse is not the case:
+ there are combining characters with a zero combining class.
+ )
+ $(LI These characters are not normally used in isolation
+ unless they are being described. They include such characters
+ as accents, diacritics, Hebrew points, Arabic vowel signs,
+ and Indic matras.
+ )
+ )
+ )
+ $(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.
+ )
+ $(P $(DEF Compatibility equivalent)
+ 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.
+ )
+ $(P $(DEF Glyph) The actual, concrete image of a glyph representation
+ 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.
+ )
+ $(P $(DEF Grapheme cluster) Defined as the text between
+ grapheme boundaries as specified by Unicode Standard Annex #29,
+ $(HTTP www.unicode.org/reports/tr29/, Unicode text segmentation).
+ Important general properties of a grapheme:
+ $(UL
+ $(LI The grapheme cluster represents a horizontally segmentable
+ unit of text, consisting of some grapheme base (which may
+ consist of a Korean syllable) together with any number of
+ nonspacing marks applied to it.
+ )
+ $(LI A grapheme cluster typically starts with a grapheme base
+ and then extends across any subsequent sequence of nonspacing marks.
+ A grapheme cluster is most directly relevant to text rendering and
+ processes such as cursor placement and text selection in editing,
+ but may also be relevant to comparison and searching.
+ )
+ $(LI For many processes, a grapheme cluster behaves as if it was a
+ single character with the same properties as its grapheme base.
+ Effectively, nonspacing marks apply $(I graphically) to the base,
+ but do not change its properties.
+ )
+ )
+ $(P This module defines a number of primitives that work with graphemes:
+ $(LREF Grapheme), $(LREF decodeGrapheme) and $(LREF graphemeStride).
+ All of them are using $(I extended grapheme) boundaries
+ 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 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.
+ )
+ $(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.
+ )
+ $(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)
+ )
+ )
+ $(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.
+ )
+ $(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.
+ )
+ $(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.
+ )
+ $(P The recommended solution (see Unicode Implementation Guidelines)
+ 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
+ and the other is number of bits in each page of the trie.
+ 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
+ just a couple of operations - slice the upper bits,
+ lookup an index for these, take a page at this index and use
+ the lower bits as an offset within this page.
+
+ Assuming that pages are laid out consequently
+ in one array at $(D pages), the pseudo-code is:
+ )
+ ---
+ auto elemsPerPage = (2 ^^ bits_per_page) / Value.sizeOfInBits;
+ 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'.
+ )
+
+ $(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.
+ )
+ $(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.
+ )
+ $(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
+ $(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))
+ )
+ $(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) )
+ )
+ $(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.
+ )
+ $(BOOKTABLE $(B Blocks),
+ $(TR $(TD Aegean Numbers) $(TD Ethiopic Extended) $(TD Mongolian))
+ $(TR $(TD Alchemical Symbols) $(TD Ethiopic Extended-A) $(TD Musical Symbols))
+ $(TR $(TD Alphabetic Presentation Forms) $(TD Ethiopic Supplement) $(TD Myanmar))
+ $(TR $(TD Ancient Greek Musical Notation) $(TD General Punctuation) $(TD Myanmar Extended-A))
+ $(TR $(TD Ancient Greek Numbers) $(TD Geometric Shapes) $(TD New Tai Lue))
+ $(TR $(TD Ancient Symbols) $(TD Georgian) $(TD NKo))
+ $(TR $(TD Arabic) $(TD Georgian Supplement) $(TD Number Forms))
+ $(TR $(TD Arabic Extended-A) $(TD Glagolitic) $(TD Ogham))
+ $(TR $(TD Arabic Mathematical Alphabetic Symbols) $(TD Gothic) $(TD Ol Chiki))
+ $(TR $(TD Arabic Presentation Forms-A) $(TD Greek and Coptic) $(TD Old Italic))
+ $(TR $(TD Arabic Presentation Forms-B) $(TD Greek Extended) $(TD Old Persian))
+ $(TR $(TD Arabic Supplement) $(TD Gujarati) $(TD Old South Arabian))
+ $(TR $(TD Armenian) $(TD Gurmukhi) $(TD Old Turkic))
+ $(TR $(TD Arrows) $(TD Halfwidth and Fullwidth Forms) $(TD Optical Character Recognition))
+ $(TR $(TD Avestan) $(TD Hangul Compatibility Jamo) $(TD Oriya))
+ $(TR $(TD Balinese) $(TD Hangul Jamo) $(TD Osmanya))
+ $(TR $(TD Bamum) $(TD Hangul Jamo Extended-A) $(TD Phags-pa))
+ $(TR $(TD Bamum Supplement) $(TD Hangul Jamo Extended-B) $(TD Phaistos Disc))
+ $(TR $(TD Basic Latin) $(TD Hangul Syllables) $(TD Phoenician))
+ $(TR $(TD Batak) $(TD Hanunoo) $(TD Phonetic Extensions))
+ $(TR $(TD Bengali) $(TD Hebrew) $(TD Phonetic Extensions Supplement))
+ $(TR $(TD Block Elements) $(TD High Private Use Surrogates) $(TD Playing Cards))
+ $(TR $(TD Bopomofo) $(TD High Surrogates) $(TD Private Use Area))
+ $(TR $(TD Bopomofo Extended) $(TD Hiragana) $(TD Rejang))
+ $(TR $(TD Box Drawing) $(TD Ideographic Description Characters) $(TD Rumi Numeral Symbols))
+ $(TR $(TD Brahmi) $(TD Imperial Aramaic) $(TD Runic))
+ $(TR $(TD Braille Patterns) $(TD Inscriptional Pahlavi) $(TD Samaritan))
+ $(TR $(TD Buginese) $(TD Inscriptional Parthian) $(TD Saurashtra))
+ $(TR $(TD Buhid) $(TD IPA Extensions) $(TD Sharada))
+ $(TR $(TD Byzantine Musical Symbols) $(TD Javanese) $(TD Shavian))
+ $(TR $(TD Carian) $(TD Kaithi) $(TD Sinhala))
+ $(TR $(TD Chakma) $(TD Kana Supplement) $(TD Small Form Variants))
+ $(TR $(TD Cham) $(TD Kanbun) $(TD Sora Sompeng))
+ $(TR $(TD Cherokee) $(TD Kangxi Radicals) $(TD Spacing Modifier Letters))
+ $(TR $(TD CJK Compatibility) $(TD Kannada) $(TD Specials))
+ $(TR $(TD CJK Compatibility Forms) $(TD Katakana) $(TD Sundanese))
+ $(TR $(TD CJK Compatibility Ideographs) $(TD Katakana Phonetic Extensions) $(TD Sundanese Supplement))
+ $(TR $(TD CJK Compatibility Ideographs Supplement) $(TD Kayah Li) $(TD Superscripts and Subscripts))
+ $(TR $(TD CJK Radicals Supplement) $(TD Kharoshthi) $(TD Supplemental Arrows-A))
+ $(TR $(TD CJK Strokes) $(TD Khmer) $(TD Supplemental Arrows-B))
+ $(TR $(TD CJK Symbols and Punctuation) $(TD Khmer Symbols) $(TD Supplemental Mathematical Operators))
+ $(TR $(TD CJK Unified Ideographs) $(TD Lao) $(TD Supplemental Punctuation))
+ $(TR $(TD CJK Unified Ideographs Extension A) $(TD Latin-1 Supplement) $(TD Supplementary Private Use Area-A))
+ $(TR $(TD CJK Unified Ideographs Extension B) $(TD Latin Extended-A) $(TD Supplementary Private Use Area-B))
+ $(TR $(TD CJK Unified Ideographs Extension C) $(TD Latin Extended Additional) $(TD Syloti Nagri))
+ $(TR $(TD CJK Unified Ideographs Extension D) $(TD Latin Extended-B) $(TD Syriac))
+ $(TR $(TD Combining Diacritical Marks) $(TD Latin Extended-C) $(TD Tagalog))
+ $(TR $(TD Combining Diacritical Marks for Symbols) $(TD Latin Extended-D) $(TD Tagbanwa))
+ $(TR $(TD Combining Diacritical Marks Supplement) $(TD Lepcha) $(TD Tags))
+ $(TR $(TD Combining Half Marks) $(TD Letterlike Symbols) $(TD Tai Le))
+ $(TR $(TD Common Indic Number Forms) $(TD Limbu) $(TD Tai Tham))
+ $(TR $(TD Control Pictures) $(TD Linear B Ideograms) $(TD Tai Viet))
+ $(TR $(TD Coptic) $(TD Linear B Syllabary) $(TD Tai Xuan Jing Symbols))
+ $(TR $(TD Counting Rod Numerals) $(TD Lisu) $(TD Takri))
+ $(TR $(TD Cuneiform) $(TD Low Surrogates) $(TD Tamil))
+ $(TR $(TD Cuneiform Numbers and Punctuation) $(TD Lycian) $(TD Telugu))
+ $(TR $(TD Currency Symbols) $(TD Lydian) $(TD Thaana))
+ $(TR $(TD Cypriot Syllabary) $(TD Mahjong Tiles) $(TD Thai))
+ $(TR $(TD Cyrillic) $(TD Malayalam) $(TD Tibetan))
+ $(TR $(TD Cyrillic Extended-A) $(TD Mandaic) $(TD Tifinagh))
+ $(TR $(TD Cyrillic Extended-B) $(TD Mathematical Alphanumeric Symbols) $(TD Transport And Map Symbols))
+ $(TR $(TD Cyrillic Supplement) $(TD Mathematical Operators) $(TD Ugaritic))
+ $(TR $(TD Deseret) $(TD Meetei Mayek) $(TD Unified Canadian Aboriginal Syllabics))
+ $(TR $(TD Devanagari) $(TD Meetei Mayek Extensions) $(TD Unified Canadian Aboriginal Syllabics Extended))
+ $(TR $(TD Devanagari Extended) $(TD Meroitic Cursive) $(TD Vai))
+ $(TR $(TD Dingbats) $(TD Meroitic Hieroglyphs) $(TD Variation Selectors))
+ $(TR $(TD Domino Tiles) $(TD Miao) $(TD Variation Selectors Supplement))
+ $(TR $(TD Egyptian Hieroglyphs) $(TD Miscellaneous Mathematical Symbols-A) $(TD Vedic Extensions))
+ $(TR $(TD Emoticons) $(TD Miscellaneous Mathematical Symbols-B) $(TD Vertical Forms))
+ $(TR $(TD Enclosed Alphanumerics) $(TD Miscellaneous Symbols) $(TD Yijing Hexagram Symbols))
+ $(TR $(TD Enclosed Alphanumeric Supplement) $(TD Miscellaneous Symbols and Arrows) $(TD Yi Radicals))
+ $(TR $(TD Enclosed CJK Letters and Months) $(TD Miscellaneous Symbols And Pictographs) $(TD Yi Syllables))
+ $(TR $(TD Enclosed Ideographic Supplement) $(TD Miscellaneous Technical) )
+ $(TR $(TD Ethiopic) $(TD Modifier Tone Letters) )
+ )
+ $(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 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:
+ $(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)
+ $(HTTP www.unicode.org/uni2book/ch03.pdf,
+ Unicode Conformance)
+ Trademarks:
+ Unicode(tm) is a trademark of Unicode, Inc.
+
+ Copyright: Copyright 2013 -
+ License: $(HTTP www.boost.org/LICENSE_1_0.txt, Boost License 1.0).
+ Authors: Dmitry Olshansky
+ Source: $(PHOBOSSRC std/_uni.d)
+ Standards: $(HTTP www.unicode.org/versions/Unicode6.2.0/, Unicode v6.2)
+
+Macros:
+
+SECTION = <h3><a id="$1">$0</a></h3>
+DEF = <div><a id="$1"><i>$0</i></a></div>
+S_LINK = <a href="#$1">$+</a>
+CODEPOINT = $(S_LINK Code point, code point)
+CODEPOINTS = $(S_LINK Code point, code points)
+CHARACTER = $(S_LINK Character, character)
+CHARACTERS = $(S_LINK Character, characters)
+CLUSTER = $(S_LINK Grapheme cluster, grapheme cluster)
++/
+module std.uni;
+
+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
+import std.exception;// : enforce;
+import core.memory; //: pureMalloc, pureRealloc, pureFree;
+import core.exception; // : onOutOfMemoryError;
+static import std.ascii;
+// debug = std_uni;
+
+debug(std_uni) import std.stdio; // writefln, writeln
+
+private:
+
+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;
+ }
+ }
+}
+
+void copyBackwards(T,U)(T[] src, U[] dest)
+{
+ assert(src.length == dest.length);
+ 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++)
+ dest[i] = src[i];
+}
+
+// TODO: update to reflect all major CPUs supporting unaligned reads
+version (X86)
+ enum hasUnalignedReads = true;
+else version (X86_64)
+ enum hasUnalignedReads = true;
+else
+ enum hasUnalignedReads = false; // better be safe then sorry
+
+public enum dchar lineSep = '\u2028'; /// Constant $(CODEPOINT) (0x2028) - line separator.
+public enum dchar paraSep = '\u2029'; /// Constant $(CODEPOINT) (0x2029) - paragraph separator.
+public enum dchar nelSep = '\u0085'; /// Constant $(CODEPOINT) (0x0085) - next line.
+
+// test the intro example
+@safe unittest
+{
+ 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");
+ // or simpler and statically-checked look
+ auto ascii = unicode.ASCII;
+ auto currency = unicode.Currency_Symbol;
+
+ // easy set ops
+ auto a = set & ascii;
+ assert(a.empty); // as it has no intersection with ascii
+ a = set | ascii;
+ auto b = currency - a; // subtract all ASCII, Cyrillic and Armenian
+
+ // some properties of code point sets
+ assert(b.length > 45); // 46 items in Unicode 6.1, even more in 6.2
+ // testing presence of a code point in a set
+ // is just fine, it is O(logN)
+ assert(!b['$']);
+ assert(!b['\u058F']); // Armenian dram sign
+ assert(b['¥']);
+
+ // building fast lookup tables, these guarantee O(1) complexity
+ // 1-level Trie lookup table essentially a huge bit-set ~262Kb
+ auto oneTrie = toTrie!1(b);
+ // 2-level far more compact but typically slightly slower
+ auto twoTrie = toTrie!2(b);
+ // 3-level even smaller, and a bit slower yet
+ auto threeTrie = toTrie!3(b);
+ assert(oneTrie['£']);
+ assert(twoTrie['£']);
+ assert(threeTrie['£']);
+
+ // build the trie with the most sensible trie level
+ // and bind it as a functor
+ auto cyrillicOrArmenian = toDelegate(set);
+ auto balance = find!(cyrillicOrArmenian)("Hello ընկեր!");
+ assert(balance == "ընկեր!");
+ // compatible with bool delegate(dchar)
+ bool delegate(dchar) bindIt = cyrillicOrArmenian;
+
+ // Normalization
+ string s = "Plain ascii (and not only), is always normalized!";
+ assert(s is normalize(s));// is the same string
+
+ string nonS = "A\u0308ffin"; // A ligature
+ auto nS = normalize(nonS); // to NFC, the W3C endorsed standard
+ assert(nS == "Äffin");
+ assert(nS != nonS);
+ string composed = "Äffin";
+
+ assert(normalize!NFD(composed) == "A\u0308ffin");
+ // to NFKD, compatibility decomposition useful for fuzzy matching/searching
+ assert(normalize!NFKD("2¹⁰") == "210");
+}
+
+enum lastDchar = 0x10FFFF;
+
+auto force(T, F)(F from)
+if (isIntegral!T && !is(T == F))
+{
+ assert(from <= T.max && from >= T.min);
+ return cast(T) from;
+}
+
+auto force(T, F)(F from)
+if (isBitPacked!T && !is(T == F))
+{
+ assert(from <= 2^^bitSizeOf!T-1);
+ return T(cast(TypeOfBitPacked!T) from);
+}
+
+auto force(T, F)(F from)
+if (is(T == F))
+{
+ return 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)
+ return val;
+ else static if (bits == 1)
+ {
+ static if (times == size_t.sizeof*8)
+ return val ? size_t.max : 0;
+ else
+ return val ? (1 << times)-1 : 0;
+ }
+ else static if (times % 2)
+ return (replicateBits!(times-1, bits)(val)<<bits) | val;
+ else
+ return replicateBits!(times/2, bits*2)((val << bits) | val);
+}
+
+@safe pure nothrow @nogc unittest // for replicate
+{
+ import std.algorithm.iteration : sum, map;
+ import std.range : iota;
+ size_t m = 0b111;
+ size_t m2 = 0b01;
+ 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());
+ }
+}
+
+// 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)
+ {
+ full_size += spaceFor!(bitSizeOf!v)(sizes[i]);
+ sz[i] = sizes[i];
+ static if (i >= 1)
+ offsets[i] = offsets[i-1] +
+ spaceFor!(bitSizeOf!(Types[i-1]))(sizes[i-1]);
+ }
+
+ storage = new size_t[full_size];
+ }
+
+ this(const(size_t)[] raw_offsets,
+ const(size_t)[] raw_sizes, const(size_t)[] data)const @safe pure nothrow @nogc
+ {
+ offsets[] = raw_offsets[];
+ sz[] = raw_sizes[];
+ storage = data;
+ }
+
+ @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 @nogc
+ {
+ auto ptr = raw_ptr!n;
+ return inout(PackedPtr!(Types[n]))(ptr);
+ }
+
+ template length(size_t 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])
+ {// extend
+ size_t delta = (new_size - sz[n]);
+ sz[n] += delta;
+ delta = spaceFor!(bitSizeOf!(Types[n]))(delta);
+ 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)
+ {
+ 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]);
+
+ start[0 .. delta] = 0;
+ // offsets are used for raw_slice, ptr etc.
+ foreach (i; n+1 .. dim)
+ offsets[i] += delta;
+ }
+ }
+ 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)
+ {
+ auto start = raw_ptr!(n+1);
+ size_t len = (storage.ptr+storage.length-start);
+ copyForward(start[0 .. len-delta], start[delta .. len]);
+
+ // adjust offsets last, they affect raw_slice
+ foreach (i; n+1 .. dim)
+ offsets[i] -= delta;
+ }
+ storage.length -= delta;
+ }
+ // else - NOP
+ }
+ }
+
+ @property size_t bytes(size_t n=size_t.max)() const @safe
+ {
+ static if (n == size_t.max)
+ return storage.length*size_t.sizeof;
+ 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))
+ {
+ import std.format : formattedWrite;
+ formattedWrite(sink, "[%( 0x%x, %)]", offsets[]);
+ formattedWrite(sink, ", [%( 0x%x, %)]", sz[]);
+ formattedWrite(sink, ", [%( 0x%x, %)]", storage);
+ }
+
+private:
+ import std.meta : staticMap;
+ @property auto raw_ptr(size_t n)()inout pure nothrow @nogc
+ {
+ static if (n == 0)
+ return storage.ptr;
+ else
+ {
+ return storage.ptr+offsets[n];
+ }
+ }
+ enum dim = Types.length;
+ size_t[dim] offsets;// offset for level x
+ size_t[dim] sz;// size of level x
+ alias bitWidth = staticMap!(bitSizeOf, Types);
+ size_t[] storage;
+}
+
+@system unittest
+{
+ import std.conv : text;
+ enum dg = (){
+ // sizes are:
+ // lvl0: 3, lvl1 : 2, lvl2: 1
+ auto m = MultiArray!(int, ubyte, int)(3,2,1);
+
+ 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]));
+ }
+
+ 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]));
+ }
+
+ static void fill(size_t k, T)(ref T m, int 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)
+ m.slice!(k)[i] = force!ubyte(n-i);
+ }
+
+ m.length!1 = 100;
+ fill!1(m, 100);
+ check!1(m, 100);
+
+ m.length!0 = 220;
+ fill!0(m, 220);
+ check!1(m, 100);
+ check!0(m, 220);
+
+ m.length!2 = 17;
+ fillB!2(m, 17);
+ checkB!2(m, 17);
+ check!0(m, 220);
+ check!1(m, 100);
+
+ m.length!2 = 33;
+ checkB!2(m, 17);
+ fillB!2(m, 33);
+ checkB!2(m, 33);
+ check!0(m, 220);
+ check!1(m, 100);
+
+ m.length!1 = 195;
+ fillB!1(m, 195);
+ checkB!1(m, 195);
+ checkB!2(m, 33);
+ check!0(m, 220);
+
+ auto marr = MultiArray!(BitPacked!(uint, 4), BitPacked!(uint, 6))(20, 10);
+ marr.length!0 = 15;
+ marr.length!1 = 30;
+ fill!1(marr, 30);
+ fill!0(marr, 15);
+ check!1(marr, 30);
+ check!0(marr, 15);
+ return 0;
+ };
+ enum ct = dg();
+ auto rt = dg();
+}
+
+@system unittest
+{// more bitpacking tests
+ import std.conv : text;
+
+ alias Bitty =
+ MultiArray!(BitPacked!(size_t, 3)
+ , BitPacked!(size_t, 4)
+ , BitPacked!(size_t, 3)
+ , BitPacked!(size_t, 6)
+ , bool);
+ alias fn1 = sliceBits!(13, 16);
+ alias fn2 = sliceBits!( 9, 13);
+ 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++)
+ 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++)
+ arr.slice!(lvl)[i] = i;
+ }
+ Bitty m1;
+
+ m1.length!4 = 10;
+ m1.length!3 = 2^^6;
+ m1.length!2 = 2^^3;
+ m1.length!1 = 2^^4;
+ m1.length!0 = 2^^3;
+
+ m1.length!4 = 2^^16;
+
+ for (size_t i = 0; i< m1.length!4; i++)
+ m1.slice!(4)[i] = i % 2;
+
+ fillIdx!1(m1);
+ check!1(m1);
+ fillIdx!2(m1);
+ check!2(m1);
+ fillIdx!3(m1);
+ check!3(m1);
+ fillIdx!0(m1);
+ check!0(m1);
+ check!3(m1);
+ check!2(m1);
+ check!1(m1);
+ for (size_t i=0; i < 2^^16; i++)
+ {
+ m1.slice!(4)[i] = i % 2;
+ m1.slice!(0)[fn1(i)] = fn1(i);
+ m1.slice!(1)[fn2(i)] = fn2(i);
+ m1.slice!(2)[fn3(i)] = fn3(i);
+ m1.slice!(3)[fn4(i)] = fn4(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));
+ assert(m1.slice!(1)[fn2(i)] == fn2(i));
+ assert(m1.slice!(2)[fn3(i)] == fn3(i));
+ assert(m1.slice!(3)[fn4(i)] == fn4(i));
+ }
+}
+
+size_t spaceFor(size_t _bits)(size_t new_len) @safe pure nothrow @nogc
+{
+ 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);
+ }
+ else
+ {
+ enum factor = size_t.sizeof*8/bits;
+ return (new_len+factor-1)/factor; // rounded up
+ }
+}
+
+template isBitPackableType(T)
+{
+ enum isBitPackableType = isBitPacked!T
+ || isIntegral!T || is(T == bool) || isSomeChar!T;
+}
+
+//============================================================================
+template PackedArrayView(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 ? 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)
+{
+ import std.math : nextPow2;
+ private enum bits = bitSizeOf!T;
+ alias PackedPtr = PackedPtrImpl!(T, bits > 1 ? nextPow2(bits - 1) : 1);
+}
+
+struct PackedPtrImpl(T, size_t bits)
+{
+pure nothrow:
+ static assert(isPow2OrZero(bits));
+
+ this(inout(size_t)* ptr)inout @safe @nogc
+ {
+ origin = ptr;
+ }
+
+ private T simpleIndex(size_t n) inout
+ {
+ 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)
+ assert(val <= mask);
+ }
+ body
+ {
+ 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
+ || factor == 1 // a whole word at a time
+ || ((factor == bytesPerWord/2 || factor == bytesPerWord/4)
+ && hasUnalignedReads)) // this needs unaligned reads
+ {
+ static if (factor == bytesPerWord)
+ alias U = ubyte;
+ else static if (factor == bytesPerWord/2)
+ alias U = ushort;
+ else static if (factor == bytesPerWord/4)
+ alias U = uint;
+ 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];
+ }
+
+ static if (isBitPacked!T) // lack of user-defined implicit conversion
+ {
+ void opIndexAssign(T val, size_t idx)
+ {
+ return opIndexAssign(cast(TypeOfBitPacked!T) val, idx);
+ }
+ }
+
+ void opIndexAssign(TypeOfBitPacked!T val, size_t idx)
+ {
+ if (__ctfe)
+ simpleWrite(val, idx);
+ else
+ (cast(U*) origin)[idx] = cast(U) val;
+ }
+ }
+ else
+ {
+ T opIndex(size_t n) inout
+ {
+ return simpleIndex(n);
+ }
+
+ static if (isBitPacked!T) // lack of user-defined implicit conversion
+ {
+ void opIndexAssign(T val, size_t idx)
+ {
+ return opIndexAssign(cast(TypeOfBitPacked!T) val, idx);
+ }
+ }
+
+ void opIndexAssign(TypeOfBitPacked!T val, size_t n)
+ {
+ return simpleWrite(val, n);
+ }
+ }
+
+private:
+ // factor - number of elements in one machine word
+ enum factor = size_t.sizeof*8/bits, mask = 2^^bits-1;
+ enum bytesPerWord = size_t.sizeof;
+ size_t* origin;
+}
+
+// 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
+struct PackedArrayViewImpl(T, size_t bits)
+{
+pure nothrow:
+
+ this(inout(size_t)* origin, size_t offset, size_t items) inout @safe
+ {
+ ptr = inout(PackedPtr!(T))(origin);
+ ofs = offset;
+ limit = items;
+ }
+
+ bool zeros(size_t s, size_t e)
+ in
+ {
+ assert(s <= e);
+ }
+ body
+ {
+ s += ofs;
+ e += ofs;
+ immutable pad_s = roundUp(s);
+ if ( s >= e)
+ {
+ foreach (i; s .. e)
+ if (ptr[i])
+ return false;
+ return true;
+ }
+ immutable pad_e = roundDown(e);
+ size_t 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])
+ return false;
+ for (; i<e; i++)
+ if (ptr[i])
+ return false;
+ return true;
+ }
+
+ T opIndex(size_t idx) inout
+ in
+ {
+ assert(idx < limit);
+ }
+ body
+ {
+ return ptr[ofs + idx];
+ }
+
+ static if (isBitPacked!T) // lack of user-defined implicit conversion
+ {
+ void opIndexAssign(T val, size_t idx)
+ {
+ return opIndexAssign(cast(TypeOfBitPacked!T) val, idx);
+ }
+ }
+
+ void opIndexAssign(TypeOfBitPacked!T val, size_t idx)
+ in
+ {
+ assert(idx < limit);
+ }
+ body
+ {
+ ptr[ofs + idx] = val;
+ }
+
+ 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);
+ }
+ }
+
+ void opSliceAssign(TypeOfBitPacked!T val, size_t start, size_t end)
+ in
+ {
+ assert(start <= end);
+ assert(end <= limit);
+ }
+ body
+ {
+ // account for ofsetted view
+ start += ofs;
+ end += ofs;
+ // rounded to factor granularity
+ 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)
+ ptr[i] = val;
+ return;
+ }
+ immutable pad_end = roundDown(end); // rounded down
+ size_t i;
+ for (i=start; i<pad_start; i++)
+ ptr[i] = val;
+ // all in between is x*factor elements
+ if (pad_start != pad_end)
+ {
+ 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++)
+ ptr[i] = val;
+ }
+
+ auto opSlice(size_t from, size_t to)inout
+ in
+ {
+ assert(from <= to);
+ assert(ofs + to <= limit);
+ }
+ body
+ {
+ return typeof(this)(ptr.origin, ofs + from, to - from);
+ }
+
+ auto opSlice(){ return opSlice(0, length); }
+
+ bool opEquals(T)(auto ref T arr) const
+ {
+ 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)
+ {
+ 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])
+ return false;
+ return true;
+ }
+
+ @property size_t length()const{ return limit; }
+
+private:
+ auto roundUp()(size_t val){ return (val+factor-1)/factor*factor; }
+ auto roundDown()(size_t val){ return val/factor*factor; }
+ // factor - number of elements in one machine word
+ enum factor = size_t.sizeof*8/bits;
+ PackedPtr!(T) ptr;
+ size_t ofs, limit;
+}
+
+
+private struct SliceOverIndexed(T)
+{
+ enum assignableIndex = is(typeof((){ T.init[0] = Item.init; }));
+ enum assignableSlice = is(typeof((){ T.init[0 .. 0] = Item.init; }));
+ auto opIndex(size_t idx)const
+ in
+ {
+ assert(idx < to - from);
+ }
+ body
+ {
+ return (*arr)[from+idx];
+ }
+
+ static if (assignableIndex)
+ void opIndexAssign(Item val, size_t idx)
+ in
+ {
+ assert(idx < to - from);
+ }
+ body
+ {
+ (*arr)[from+idx] = val;
+ }
+
+ auto opSlice(size_t a, size_t b)
+ {
+ return typeof(this)(from+a, from+b, arr);
+ }
+
+ // static if (assignableSlice)
+ void opSliceAssign(T)(T val, size_t start, size_t end)
+ {
+ (*arr)[start+from .. end+from] = val;
+ }
+
+ auto opSlice()
+ {
+ return typeof(this)(from, to, arr);
+ }
+
+ @property size_t length()const { return to-from;}
+
+ auto opDollar()const { return length; }
+
+ @property bool empty()const { return from == to; }
+
+ @property auto front()const { return (*arr)[from]; }
+
+ static if (assignableIndex)
+ @property void front(Item val) { (*arr)[from] = val; }
+
+ @property auto back()const { return (*arr)[to-1]; }
+
+ static if (assignableIndex)
+ @property void back(Item val) { (*arr)[to-1] = val; }
+
+ @property auto save() inout { return this; }
+
+ void popFront() { from++; }
+
+ void popBack() { to--; }
+
+ bool opEquals(T)(auto ref T arr) const
+ {
+ if (arr.length != length)
+ return false;
+ for (size_t i=0; i <length; i++)
+ if (this[i] != arr[i])
+ return false;
+ return true;
+ }
+private:
+ alias Item = typeof(T.init[0]);
+ size_t from, to;
+ T* arr;
+}
+
+static assert(isRandomAccessRange!(SliceOverIndexed!(int[])));
+
+SliceOverIndexed!(const(T)) sliceOverIndexed(T)(size_t a, size_t b, const(T)* x)
+if (is(Unqual!T == T))
+{
+ return SliceOverIndexed!(const(T))(a, b, 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))
+{
+ return SliceOverIndexed!T(a, b, x);
+}
+
+@system unittest
+{
+ int[] idxArray = [2, 3, 5, 8, 13];
+ auto sliced = sliceOverIndexed(0, idxArray.length, &idxArray);
+
+ assert(!sliced.empty);
+ assert(sliced.front == 2);
+ sliced.front = 1;
+ assert(sliced.front == 1);
+ assert(sliced.back == 13);
+ sliced.popFront();
+ assert(sliced.front == 3);
+ assert(sliced.back == 13);
+ sliced.back = 11;
+ assert(sliced.back == 11);
+ sliced.popBack();
+
+ assert(sliced.front == 3);
+ assert(sliced[$-1] == 8);
+ sliced = sliced[];
+ assert(sliced[0] == 3);
+ assert(sliced.back == 8);
+ sliced = sliced[1..$];
+ assert(sliced.front == 5);
+ sliced = sliced[0..$-1];
+ assert(sliced[$-1] == 5);
+
+ int[] other = [2, 5];
+ assert(sliced[] == sliceOverIndexed(1, 2, &other));
+ 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);
+}
+
+private auto packedArrayView(T)(inout(size_t)* ptr, size_t items) @trusted pure nothrow
+{
+ return inout(PackedArrayView!T)(ptr, 0, items);
+}
+
+
+//============================================================================
+// Partially unrolled binary search using Shar's method
+//============================================================================
+
+string genUnrolledSwitchSearch(size_t size) @safe pure nothrow
+{
+ import core.bitop : bsr;
+ import std.array : replace;
+ import std.conv : to;
+ assert(isPow2OrZero(size));
+ string code = `
+ import core.bitop : bsr;
+ auto power = bsr(m)+1;
+ switch (power){`;
+ size_t i = bsr(size);
+ foreach_reverse (val; 0 .. bsr(size))
+ {
+ auto v = 2^^val;
+ code ~= `
+ case pow:
+ if (pred(range[idx+m], needle))
+ idx += m;
+ goto case;
+ `.replace("m", to!string(v))
+ .replace("pow", to!string(i));
+ i--;
+ }
+ code ~= `
+ case 0:
+ if (pred(range[idx], needle))
+ idx += 1;
+ goto default;
+ `;
+ code ~= `
+ default:
+ }`;
+ return code;
+}
+
+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))
+{
+ assert(isPow2OrZero(range.length));
+ size_t idx = 0, m = range.length/2;
+ while (m != 0)
+ {
+ if (pred(range[idx+m], needle))
+ idx += m;
+ m /= 2;
+ }
+ 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))
+{
+ assert(isPow2OrZero(range.length));
+ size_t idx = 0, m = range.length/2;
+ enum max = 1 << 10;
+ while (m >= max)
+ {
+ if (pred(range[idx+m], needle))
+ idx += m;
+ m /= 2;
+ }
+ mixin(genUnrolledSwitchSearch(max));
+ return idx;
+}
+
+template sharMethod(alias uniLowerBound)
+{
+ size_t sharMethod(alias _pred="a<b", Range, T)(Range range, T needle)
+ if (is(T : ElementType!Range))
+ {
+ import std.functional : binaryFun;
+ import std.math : nextPow2, truncPow2;
+ alias pred = binaryFun!_pred;
+ if (range.length == 0)
+ return 0;
+ if (isPow2OrZero(range.length))
+ return uniLowerBound!pred(range, 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 = nextPow2(range.length - n + 1);
+ return range.length - k + uniLowerBound!pred(range[$-k..$], needle);
+ }
+ else
+ return uniLowerBound!pred(range[0 .. n], needle);
+ }
+}
+
+alias sharLowerBound = sharMethod!uniformLowerBound;
+alias sharSwitchLowerBound = sharMethod!switchUniformLowerBound;
+
+@safe unittest
+{
+ import std.array : array;
+ import std.range : assumeSorted, iota;
+
+ auto stdLowerBound(T)(T[] range, T needle)
+ {
+ return assumeSorted(range).lowerBound(needle).length;
+ }
+ immutable MAX = 5*1173;
+ auto arr = array(iota(5, MAX, 5));
+ assert(arr.length == MAX/5-1);
+ foreach (i; 0 .. MAX+5)
+ {
+ auto st = stdLowerBound(arr, i);
+ assert(st == sharLowerBound(arr, i));
+ assert(st == sharSwitchLowerBound(arr, i));
+ }
+ arr = [];
+ auto st = stdLowerBound(arr, 33);
+ assert(st == sharLowerBound(arr, 33));
+ assert(st == sharSwitchLowerBound(arr, 33));
+}
+//============================================================================
+
+@safe
+{
+// hope to see simillar stuff in public interface... once Allocators are out
+//@@@BUG moveFront and friends? dunno, for now it's POD-only
+
+@trusted size_t genericReplace(Policy=void, T, Range)
+ (ref T dest, size_t from, size_t to, Range stuff)
+{
+ import std.algorithm.mutation : copy;
+ size_t delta = to - from;
+ size_t stuff_end = from+stuff.length;
+ if (stuff.length > delta)
+ {// replace increases length
+ delta = stuff.length - delta;// now, new is > old by delta
+ 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]);
+ }
+ else if (stuff.length == delta)
+ {
+ 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))
+ dest.length = dest.length - delta;//@@@BUG lame @property
+ else
+ dest = Policy.realloc(dest, dest.length-delta);
+ }
+ return stuff_end;
+}
+
+
+// Simple storage manipulation policy
+@trusted private struct GcPolicy
+{
+ import std.traits : isDynamicArray;
+
+ static T[] dup(T)(const T[] arr)
+ {
+ return arr.dup;
+ }
+
+ static T[] alloc(T)(size_t size)
+ {
+ return new T[size];
+ }
+
+ static T[] realloc(T)(T[] arr, size_t sz)
+ {
+ arr.length = sz;
+ return arr;
+ }
+
+ static void replaceImpl(T, Range)(ref T[] dest, size_t from, size_t to, Range stuff)
+ {
+ replaceInPlace(dest, from, to, stuff);
+ }
+
+ static void append(T, V)(ref T[] arr, V value)
+ if (!isInputRange!V)
+ {
+ arr ~= force!T(value);
+ }
+
+ static void append(T, V)(ref T[] arr, V value)
+ if (isInputRange!V)
+ {
+ insertInPlace(arr, arr.length, value);
+ }
+
+ static void destroy(T)(ref T arr)
+ if (isDynamicArray!T && is(Unqual!T == T))
+ {
+ debug
+ {
+ arr[] = cast(typeof(T.init[0]))(0xdead_beef);
+ }
+ arr = null;
+ }
+
+ static void destroy(T)(ref T arr)
+ if (isDynamicArray!T && !is(Unqual!T == T))
+ {
+ arr = null;
+ }
+}
+
+// ditto
+@trusted struct ReallocPolicy
+{
+ import std.range.primitives : hasLength;
+
+ static T[] dup(T)(const T[] arr)
+ {
+ auto result = alloc!T(arr.length);
+ result[] = arr[];
+ return result;
+ }
+
+ static T[] alloc(T)(size_t size)
+ {
+ import core.stdc.stdlib : malloc;
+ import std.exception : enforce;
+
+ 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)
+ {
+ destroy(arr);
+ return null;
+ }
+
+ 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)
+ {
+ genericReplace!(ReallocPolicy)(dest, from, to, stuff);
+ }
+
+ static void append(T, V)(ref T[] arr, V value)
+ 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)
+ {
+ 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)
+ {
+ import core.stdc.stdlib : free;
+ if (arr.ptr)
+ free(arr.ptr);
+ arr = null;
+ }
+}
+
+//build hack
+alias _RealArray = CowArray!ReallocPolicy;
+
+@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,
+ string file = __FILE__, size_t line = __LINE__)
+ {
+ {
+ replaceImpl(orig, from, to, toReplace);
+ scope(exit) destroy(orig);
+ if (!equal(orig, result))
+ return false;
+ }
+ return true;
+ }
+ static T[] arr(T)(T[] args... )
+ {
+ return dup(args);
+ }
+
+ assert(test(arr([1, 2, 3, 4]), 0, 0, [5, 6, 7], [5, 6, 7, 1, 2, 3, 4]));
+ assert(test(arr([1, 2, 3, 4]), 0, 2, cast(int[])[], [3, 4]));
+ assert(test(arr([1, 2, 3, 4]), 0, 4, [5, 6, 7], [5, 6, 7]));
+ assert(test(arr([1, 2, 3, 4]), 0, 2, [5, 6, 7], [5, 6, 7, 3, 4]));
+ assert(test(arr([1, 2, 3, 4]), 2, 3, [5, 6, 7], [1, 2, 5, 6, 7, 4]));
+ }
+}
+
+/**
+ Tests if T is some kind a set of code points. Intended for template constraints.
+*/
+public template isCodepointSet(T)
+{
+ static if (is(T dummy == InversionList!(Args), Args...))
+ enum isCodepointSet = true;
+ else
+ enum isCodepointSet = false;
+}
+
+/**
+ Tests if $(D T) is a pair of integers that implicitly convert to $(D V).
+ The following code must compile for any pair $(D T):
+ ---
+ (T x){ V a = x[0]; V b = x[1];}
+ ---
+ The following must not compile:
+ ---
+ (T x){ V c = x[2];}
+ ---
+*/
+public template isIntegralPair(T, V=uint)
+{
+ enum isIntegralPair = is(typeof((T x){ V a = x[0]; V b = x[1];}))
+ && !is(typeof((T x){ V c = x[2]; }));
+}
+
+
+/**
+ The recommended default type for set of $(CODEPOINTS).
+ For details, see the current implementation: $(LREF InversionList).
+*/
+public alias CodepointSet = InversionList!GcPolicy;
+
+
+//@@@BUG: std.typecons tuples depend on std.format to produce fields mixin
+// which relies on std.uni.isGraphical and this chain blows up with Forward reference error
+// hence below doesn't seem to work
+// public alias CodepointInterval = Tuple!(uint, "a", uint, "b");
+
+/**
+ 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.
+*/
+public struct CodepointInterval
+{
+pure:
+ uint[2] _tuple;
+ alias _tuple this;
+
+@safe pure nothrow @nogc:
+
+ this(uint low, uint high)
+ {
+ _tuple[0] = low;
+ _tuple[1] = high;
+ }
+ bool opEquals(T)(T val) const
+ {
+ return this[0] == val[0] && this[1] == val[1];
+ }
+ @property ref inout(uint) a() inout { return _tuple[0]; }
+ @property ref inout(uint) b() inout { return _tuple[1]; }
+}
+
+/**
+ $(P
+ $(D InversionList) is a set of $(CODEPOINTS)
+ represented as an array of open-right [a, b$(RPAREN)
+ intervals (see $(LREF CodepointInterval) above).
+ The name comes from the way the representation reads left to right.
+ For instance a set of all values [10, 50$(RPAREN), [80, 90$(RPAREN),
+ plus a singular value 60 looks like this:
+ )
+ ---
+ 10, 50, 60, 61, 80, 90
+ ---
+ $(P
+ The way to read this is: start with negative meaning that all numbers
+ smaller then the next one are not present in this set (and positive
+ - the contrary). Then switch positive/negative after each
+ number passed from left to right.
+ )
+ $(P This way negative spans until 10, then positive until 50,
+ then negative until 60, then positive until 61, and so on.
+ 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 $(LINK2 https://en.wikipedia.org/wiki/Run-length_encoding, RLE encoding).
+ )
+
+ $(P Sets are value types (just like $(D int) is) thus they
+ are never aliased.
+ )
+ Example:
+ ---
+ auto a = CodepointSet('a', 'z'+1);
+ auto b = CodepointSet('A', 'Z'+1);
+ auto c = a;
+ a = a | b;
+ assert(a == CodepointSet('A', 'Z'+1, 'a', 'z'+1));
+ assert(a != c);
+ ---
+ $(P See also $(LREF unicode) for simpler construction of sets
+ from predefined ones.
+ )
+
+ $(P Memory usage is 8 bytes per each contiguous interval in a set.
+ The value semantics are achieved by using the
+ $(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).
+ )
+
+ Note:
+ $(P It's not recommended to rely on the template parameters
+ or the exact type of a current $(CODEPOINT) set in $(D std.uni).
+ The type and parameters may change when the standard
+ allocators design is finalized.
+ Use $(LREF isCodepointSet) with templates or just stick with the default
+ alias $(LREF CodepointSet) throughout the whole code base.
+ )
+*/
+@trusted public struct InversionList(SP=GcPolicy)
+{
+ import std.range : assumeSorted;
+
+ /**
+ Construct from another code point set of any type.
+ */
+ this(Set)(Set set) pure
+ if (isCodepointSet!Set)
+ {
+ uint[] arr;
+ foreach (v; set.byInterval)
+ {
+ arr ~= v.a;
+ arr ~= v.b;
+ }
+ data = CowArray!(SP).reuse(arr);
+ }
+
+ /**
+ Construct a set from a forward range of code point intervals.
+ */
+ this(Range)(Range intervals) pure
+ if (isForwardRange!Range && isIntegralPair!(ElementType!Range))
+ {
+ uint[] arr;
+ foreach (v; intervals)
+ {
+ SP.append(arr, v.a);
+ SP.append(arr, v.b);
+ }
+ data = CowArray!(SP).reuse(arr);
+ sanitize(); //enforce invariant: sort intervals etc.
+ }
+
+ //helper function that avoids sanity check to be CTFE-friendly
+ private static fromIntervals(Range)(Range intervals) pure
+ {
+ import std.algorithm.iteration : map;
+ import std.range : roundRobin;
+ auto flattened = roundRobin(intervals.save.map!"a[0]"(),
+ intervals.save.map!"a[1]"());
+ InversionList set;
+ set.data = CowArray!(SP)(flattened);
+ return set;
+ }
+ //ditto untill sort is CTFE-able
+ private static fromIntervals()(uint[] intervals...) pure
+ in
+ {
+ import std.conv : text;
+ assert(intervals.length % 2 == 0, "Odd number of interval bounds [a, b)!");
+ for (uint i = 0; i < intervals.length; i += 2)
+ {
+ auto a = intervals[i], b = intervals[i+1];
+ assert(a < b, text("illegal interval [a, b): ", a, " > ", b));
+ }
+ }
+ body
+ {
+ InversionList set;
+ set.data = CowArray!(SP)(intervals);
+ return set;
+ }
+
+ /**
+ Construct a set from plain values of code point intervals.
+ */
+ this()(uint[] intervals...)
+ in
+ {
+ import std.conv : text;
+ assert(intervals.length % 2 == 0, "Odd number of interval bounds [a, b)!");
+ for (uint i = 0; i < intervals.length; i += 2)
+ {
+ auto a = intervals[i], b = intervals[i+1];
+ assert(a < b, text("illegal interval [a, b): ", a, " > ", b));
+ }
+ }
+ body
+ {
+ data = CowArray!(SP)(intervals);
+ 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.comparison : equal;
+ import std.typecons : tuple;
+
+ auto set = CodepointSet('A', 'D'+1, 'a', 'd'+1);
+
+ assert(set.byInterval.equal([tuple('A','E'), tuple('a','e')]));
+ -----------
+ */
+ @property auto byInterval()
+ {
+ return Intervals!(typeof(data))(data);
+ }
+
+ /**
+ 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;
+ }
+
+ ///
+ @safe unittest
+ {
+ auto gothic = unicode.Gothic;
+ // Gothic letter ahsa
+ assert(gothic['\U00010330']);
+ // no ascii in Gothic obviously
+ assert(!gothic['$']);
+ }
+
+
+ // Linear scan for $(D ch). Useful only for small sets.
+ // TODO:
+ // used internally in std.regex
+ // should be properly exposed in a public API ?
+ package auto scanFor()(dchar ch) const
+ {
+ immutable len = data.length;
+ for (size_t i = 0; i < len; i++)
+ if (ch < data[i])
+ return i & 1;
+ return 0;
+ }
+
+ /// Number of $(CODEPOINTS) in this set
+ @property size_t length()
+ {
+ size_t sum = 0;
+ foreach (iv; byInterval)
+ {
+ sum += iv.b - iv.a;
+ }
+ return sum;
+ }
+
+// bootstrap full set operations from 4 primitives (suitable as a template mixin):
+// addInterval, skipUpTo, dropUpTo & byInterval iteration
+//============================================================================
+public:
+ /**
+ $(P Sets support natural syntax for set algebra, namely: )
+ $(BOOKTABLE ,
+ $(TR $(TH Operator) $(TH Math notation) $(TH Description) )
+ $(TR $(TD &) $(TD a ∩ b) $(TD intersection) )
+ $(TR $(TD |) $(TD a ∪ b) $(TD union) )
+ $(TR $(TD -) $(TD a ∖ b) $(TD subtraction) )
+ $(TR $(TD ~) $(TD a ~ b) $(TD symmetric set difference i.e. (a ∪ b) \ (a ∩ b)) )
+ )
+ */
+ This opBinary(string op, U)(U rhs)
+ if (isCodepointSet!U || is(U:dchar))
+ {
+ static if (op == "&" || op == "|" || op == "~")
+ {// symmetric ops thus can swap arguments to reuse r-value
+ static if (is(U:dchar))
+ {
+ auto tmp = this;
+ mixin("tmp "~op~"= rhs; ");
+ return tmp;
+ }
+ else
+ {
+ static if (is(Unqual!U == U))
+ {
+ // try hard to reuse r-value
+ mixin("rhs "~op~"= this;");
+ return rhs;
+ }
+ else
+ {
+ auto tmp = this;
+ mixin("tmp "~op~"= rhs;");
+ return tmp;
+ }
+ }
+ }
+ else static if (op == "-") // anti-symmetric
+ {
+ auto tmp = this;
+ tmp -= rhs;
+ return tmp;
+ }
+ else
+ 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))
+ {
+ static if (op == "|") // union
+ {
+ static if (is(U:dchar))
+ {
+ this.addInterval(rhs, rhs+1);
+ return this;
+ }
+ else
+ return this.add(rhs);
+ }
+ else static if (op == "&") // intersection
+ return this.intersect(rhs);// overloaded
+ else static if (op == "-") // set difference
+ return this.sub(rhs);// overloaded
+ else static if (op == "~") // symmetric set difference
+ {
+ auto copy = this & rhs;
+ this |= rhs;
+ this -= copy;
+ return this;
+ }
+ else
+ static assert(0, "no operator "~op~" defined for Set");
+ }
+
+ /**
+ Tests the presence of codepoint $(D ch) in this set,
+ the same as $(LREF opIndex).
+ */
+ bool opBinaryRight(string op: "in", U)(U ch) const
+ if (is(U : dchar))
+ {
+ return this[ch];
+ }
+
+ ///
+ @safe unittest
+ {
+ assert('я' in unicode.Cyrillic);
+ assert(!('z' in unicode.Cyrillic));
+ }
+
+
+
+ /**
+ * Obtains a set that is the inversion of this set.
+ *
+ * See_Also: $(LREF inverted)
+ */
+ auto opUnary(string op: "!")()
+ {
+ return this.inverted;
+ }
+
+ /**
+ A range that spans each $(CODEPOINT) in this set.
+ */
+ @property auto byCodepoint()
+ {
+ @trusted static struct CodepointRange
+ {
+ this(This set)
+ {
+ r = set.byInterval;
+ if (!r.empty)
+ cur = r.front.a;
+ }
+
+ @property dchar front() const
+ {
+ return cast(dchar) cur;
+ }
+
+ @property bool empty() const
+ {
+ return r.empty;
+ }
+
+ void popFront()
+ {
+ cur++;
+ while (cur >= r.front.b)
+ {
+ r.popFront();
+ if (r.empty)
+ break;
+ cur = r.front.a;
+ }
+ }
+ private:
+ uint cur;
+ typeof(This.init.byInterval) r;
+ }
+
+ 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
+ $(REF formattedWrite, std,_format), $(REF write, std,_stdio),
+ $(REF writef, std,_stdio), $(REF to, std,_conv) and others.
+ )
+ Example:
+ ---
+ import std.conv;
+ assert(unicode.ASCII.to!string == "[0..128$(RPAREN)");
+ ---
+ */
+
+ private import std.format : FormatSpec;
+
+ /***************************************
+ * Obtain a textual representation of this InversionList
+ * 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)
+ */
+ void toString(Writer)(scope Writer sink,
+ FormatSpec!char fmt) /* const */
+ {
+ import std.format : formatValue;
+ auto range = byInterval;
+ if (range.empty)
+ return;
+
+ while (1)
+ {
+ auto i = range.front;
+ range.popFront();
+
+ put(sink, "[");
+ formatValue(sink, i.a, fmt);
+ put(sink, "..");
+ formatValue(sink, i.b, fmt);
+ put(sink, ")");
+ if (range.empty) return;
+ put(sink, " ");
+ }
+ }
+
+ ///
+ @safe unittest
+ {
+ import std.conv : to;
+ import std.format : format;
+ import std.uni : unicode;
+
+ assert(unicode.Cyrillic.to!string ==
+ "[1024..1157) [1159..1320) [7467..7468) [7544..7545) [11744..11776) [42560..42648) [42655..42656)");
+
+ // The specs '%s' and '%d' are equivalent to the to!string call above.
+ 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)");
+
+ assert(format("%#X", unicode.Cyrillic) ==
+ "[0X400..0X485) [0X487..0X528) [0X1D2B..0X1D2C) [0X1D78..0X1D79) [0X2DE0..0X2E00) "
+ ~"[0XA640..0XA698) [0XA69F..0XA6A0)");
+ }
+
+ @safe unittest
+ {
+ import std.exception : assertThrown;
+ import std.format : format, FormatException;
+ assertThrown!FormatException(format("%a", unicode.ASCII));
+ }
+
+
+ /**
+ Add an interval [a, b$(RPAREN) to this set.
+ */
+ 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);
+ assert(someSet['0']);
+ assert(someSet['5']);
+ assert(someSet['9']);
+ assert(someSet['Z']);
+ }
+
+private:
+
+ package(std) // used from: std.regex.internal.parser
+ ref intersect(U)(U rhs)
+ if (isCodepointSet!U)
+ {
+ Marker mark;
+ foreach ( i; rhs.byInterval)
+ {
+ mark = this.dropUpTo(i.a, mark);
+ mark = this.skipUpTo(i.b, mark);
+ }
+ this.dropUpTo(uint.max, mark);
+ return this;
+ }
+
+ ref intersect()(dchar ch)
+ {
+ foreach (i; byInterval)
+ if (i.a <= ch && ch < i.b)
+ return this = This.init.add(ch, ch+1);
+ this = This.init;
+ return this;
+ }
+
+ @safe unittest
+ {
+ assert(unicode.Cyrillic.intersect('-').byInterval.empty);
+ }
+
+ ref sub()(dchar ch)
+ {
+ return subChar(ch);
+ }
+
+ // 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)
+ {
+ Marker mark;
+ foreach (i; rhs.byInterval)
+ {
+ mark = this.skipUpTo(i.a, mark);
+ mark = this.dropUpTo(i.b, mark);
+ }
+ return this;
+ }
+
+ package(std) // used from: std.regex.internal.parse
+ ref add(U)(U rhs)
+ if (isCodepointSet!U)
+ {
+ Marker start;
+ foreach (i; rhs.byInterval)
+ {
+ start = addInterval(i.a, i.b, start);
+ }
+ return this;
+ }
+
+// end of mixin-able part
+//============================================================================
+public:
+ /**
+ Obtains a set that is the inversion of this set.
+
+ See the '!' $(LREF opUnary) for the same but using operators.
+ */
+ @property auto inverted()
+ {
+ InversionList inversion = this;
+ if (inversion.data.length == 0)
+ {
+ inversion.addInterval(0, lastDchar+1);
+ return inversion;
+ }
+ 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,
+ inversion.data.length, inversion.data.length, [lastDchar+1]);
+ else
+ genericReplace(inversion.data,
+ 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
+ the code is adjusted to be a lambda function.
+
+ The function generated tests if the $(CODEPOINT) passed
+ belongs to this set or not. The result is to be used with string mixin.
+ The intended usage area is aggressive optimization via meta programming
+ in parser generators and the like.
+
+ Note: Use with care for relatively small or regular sets. It
+ could end up being slower then just using multi-staged tables.
+
+ Example:
+ ---
+ import std.stdio;
+
+ // construct set directly from [a, b$RPAREN intervals
+ auto set = CodepointSet(10, 12, 45, 65, 100, 200);
+ writeln(set);
+ writeln(set.toSourceCode("func"));
+ ---
+
+ The above outputs something along the lines of:
+ ---
+ bool func(dchar ch) @safe pure nothrow @nogc
+ {
+ if (ch < 45)
+ {
+ 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;
+ return false;
+ }
+ }
+ ---
+ */
+ string toSourceCode(string funcName="")
+ {
+ import std.algorithm.searching : countUntil;
+ import std.array : array;
+ import std.format : format;
+ enum maxBinary = 3;
+ static string linearScope(R)(R ivals, string indent)
+ {
+ string result = indent~"{\n";
+ string deeper = indent~" ";
+ foreach (ival; ivals)
+ {
+ immutable span = ival[1] - ival[0];
+ assert(span != 0);
+ if (span == 1)
+ {
+ result ~= format("%sif (ch == %s) return true;\n", deeper, ival[0]);
+ }
+ else if (span == 2)
+ {
+ 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]);
+ }
+ }
+ result ~= format("%sreturn false;\n%s}\n", deeper, indent); // including empty range of intervals
+ return result;
+ }
+
+ static string binaryScope(R)(R ivals, string indent)
+ {
+ // time to do unrolled comparisons?
+ if (ivals.length < maxBinary)
+ return linearScope(ivals, indent);
+ else
+ return bisect(ivals, ivals.length/2, indent);
+ }
+
+ // not used yet if/elsebinary search is far better with DMD as of 2.061
+ // and GDC is doing fine job either way
+ static string switchScope(R)(R ivals, string indent)
+ {
+ string result = indent~"switch (ch){\n";
+ string deeper = indent~" ";
+ foreach (ival; ivals)
+ {
+ if (ival[0]+1 == ival[1])
+ {
+ result ~= format("%scase %s: return true;\n",
+ deeper, ival[0]);
+ }
+ else
+ {
+ result ~= format("%scase %s: .. case %s: return true;\n",
+ deeper, ival[0], ival[1]-1);
+ }
+ }
+ result ~= deeper~"default: return false;\n"~indent~"}\n";
+ return result;
+ }
+
+ static string bisect(R)(R range, size_t idx, string indent)
+ {
+ string deeper = indent ~ " ";
+ // 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));
+ // middle point, >= a && < b
+ result ~= format("%selse if (ch < %s) return true;\n",
+ deeper, range[idx][1]);
+ // greater or equal branch, >= b
+ result ~= format("%selse\n%s",
+ deeper, binaryScope(range[idx+1..$], deeper));
+ return result~indent~"}\n";
+ }
+
+ string code = format("bool %s(dchar ch) @safe pure nothrow @nogc\n",
+ funcName.empty ? "function" : funcName);
+ 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)
+ code ~= binaryScope(range, "");
+ else
+ code ~= bisect(range, tillAscii, "");
+ return code;
+ }
+
+ /**
+ True if this set doesn't contain any $(CODEPOINTS).
+ */
+ @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;
+
+ // a random-access range of integral pairs
+ static struct Intervals(Range)
+ {
+ this(Range sp)
+ {
+ slice = sp;
+ start = 0;
+ end = sp.length;
+ }
+
+ this(Range sp, size_t s, size_t e)
+ {
+ slice = sp;
+ start = s;
+ end = e;
+ }
+
+ @property auto front()const
+ {
+ 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 void front(CodepointInterval val)
+ {
+ slice[start] = val.a;
+ slice[start+1] = val.b;
+ }
+
+ @property auto back()const
+ {
+ immutable a = slice[end-2];
+ immutable b = slice[end-1];
+ return CodepointInterval(a, b);
+ }
+
+ //ditto about package
+ package @property void back(CodepointInterval val)
+ {
+ slice[end-2] = val.a;
+ slice[end-1] = val.b;
+ }
+
+ void popFront()
+ {
+ start += 2;
+ }
+
+ void popBack()
+ {
+ end -= 2;
+ }
+
+ auto opIndex(size_t idx) const
+ {
+ immutable a = slice[start+idx*2];
+ immutable b = slice[start+idx*2+1];
+ return CodepointInterval(a, b);
+ }
+
+ //ditto about package
+ package void opIndexAssign(CodepointInterval val, size_t idx)
+ {
+ slice[start+idx*2] = val.a;
+ slice[start+idx*2+1] = val.b;
+ }
+
+ auto opSlice(size_t s, size_t e)
+ {
+ return Intervals(slice, s*2+start, e*2+start);
+ }
+
+ @property size_t length()const { return slice.length/2; }
+
+ @property bool empty()const { return start == end; }
+
+ @property auto save(){ return this; }
+ private:
+ size_t start, end;
+ Range slice;
+ }
+
+ // called after construction from intervals
+ // to make sure invariants hold
+ void sanitize()
+ {
+ import std.algorithm.comparison : max;
+ import std.algorithm.mutation : SwapStrategy;
+ import std.algorithm.sorting : sort;
+ if (data.length == 0)
+ return;
+ alias Ival = CodepointInterval;
+ //intervals wrapper for a _range_ over packed array
+ auto ivals = Intervals!(typeof(data[]))(data[]);
+ //@@@BUG@@@ can't use "a.a < b.a" see issue 12265
+ sort!((a,b) => a.a < b.a, SwapStrategy.stable)(ivals);
+ // what follows is a variation on stable remove
+ // differences:
+ // - predicate is binary, and is tested against
+ // the last kept element (at 'i').
+ // - predicate mutates lhs (merges rhs into lhs)
+ size_t len = ivals.length;
+ size_t i = 0;
+ size_t j = 1;
+ while (j < len)
+ {
+ if (ivals[i].b >= ivals[j].a)
+ {
+ ivals[i] = Ival(ivals[i].a, max(ivals[i].b, ivals[j].b));
+ j++;
+ }
+ else //unmergable
+ {
+ // check if there is a hole after merges
+ // (in the best case we do 0 writes to ivals)
+ if (j != i+1)
+ ivals[i+1] = ivals[j]; //copy over
+ i++;
+ j++;
+ }
+ }
+ len = i + 1;
+ for (size_t k=0; k + 1 < len; k++)
+ {
+ assert(ivals[k].a < ivals[k].b);
+ assert(ivals[k].b < ivals[k+1].a);
+ }
+ data.length = len * 2;
+ }
+
+ // special case for normal InversionList
+ ref subChar(dchar ch)
+ {
+ auto mark = skipUpTo(ch);
+ if (mark != data.length
+ && data[mark] == ch && data[mark-1] == ch)
+ {
+ // it has split, meaning that ch happens to be in one of intervals
+ data[mark] = data[mark]+1;
+ }
+ return this;
+ }
+
+ //
+ Marker addInterval(int a, int b, Marker hint=Marker.init)
+ in
+ {
+ assert(a <= b);
+ }
+ body
+ {
+ import std.range : assumeSorted, SearchPolicy;
+ auto range = assumeSorted(data[]);
+ size_t pos;
+ size_t a_idx = hint + range[hint..$].lowerBound!(SearchPolicy.gallop)(a).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;
+ 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)
+ {
+ // [-------++++++++----++++++-]
+ // [ s a b]
+ if (a_idx & 1)// a in positive
+ {
+ buf[0] = b;
+ to_insert = 1;
+ }
+ else// a in negative
+ {
+ buf[0] = a;
+ buf[1] = b;
+ to_insert = 2;
+ }
+ pos = genericReplace(data, a_idx, b_idx, buf[0 .. to_insert]);
+ return pos - 1;
+ }
+
+ uint top = data[b_idx];
+
+ debug(std_uni)
+ {
+ 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)
+ {// a in positive
+ if (b_idx & 1)// b in positive
+ {
+ // [-------++++++++----++++++-]
+ // [ s a b ]
+ buf[0] = top;
+ to_insert = 1;
+ }
+ else // b in negative
+ {
+ // [-------++++++++----++++++-]
+ // [ s a 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]);
+ return pos - 1;
+ }
+ buf[0] = b;
+ buf[1] = top;
+ to_insert = 2;
+ }
+ }
+ else
+ { // a in negative
+ if (b_idx & 1) // b in positive
+ {
+ // [----------+++++----++++++-]
+ // [ a b ]
+ buf[0] = a;
+ buf[1] = top;
+ to_insert = 2;
+ }
+ else// b in negative
+ {
+ // [----------+++++----++++++-]
+ // [ a s 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]);
+ return pos - 1;
+ }
+ buf[0] = a;
+ buf[1] = b;
+ buf[2] = top;
+ to_insert = 3;
+ }
+ }
+ 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]);
+ }
+ return pos - 1;
+ }
+
+ //
+ Marker dropUpTo(uint a, Marker pos=Marker.init)
+ in
+ {
+ assert(pos % 2 == 0); // at start of interval
+ }
+ body
+ {
+ auto range = assumeSorted!"a <= b"(data[pos .. data.length]);
+ if (range.empty)
+ return pos;
+ size_t idx = pos;
+ idx += range.lowerBound(a).length;
+
+ debug(std_uni)
+ {
+ writeln("dropUpTo full length=", data.length);
+ writeln(pos,"~~~", idx);
+ }
+ if (idx == data.length)
+ return genericReplace(data, pos, idx, cast(uint[])[]);
+ if (idx & 1)
+ { // a in positive
+ //[--+++----++++++----+++++++------...]
+ // |<---si s a t
+ genericReplace(data, pos, idx, [a]);
+ }
+ else
+ { // a in negative
+ //[--+++----++++++----+++++++-------+++...]
+ // |<---si s a t
+ genericReplace(data, pos, idx, cast(uint[])[]);
+ }
+ return pos;
+ }
+
+ //
+ Marker skipUpTo(uint a, Marker pos=Marker.init)
+ out(result)
+ {
+ assert(result % 2 == 0);// always start of interval
+ //(may be 0-width after-split)
+ }
+ body
+ {
+ assert(data.length % 2 == 0);
+ 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
+ return data.length;
+
+ if (idx & 1)// inside of interval, check for split
+ {
+
+ immutable top = data[idx];
+ if (top == a)// no need to split, it's end
+ return idx+1;
+ immutable start = data[idx-1];
+ if (a == start)
+ return idx-1;
+ // split it up
+ genericReplace(data, idx, idx+1, [a, a, top]);
+ return idx+1; // avoid odd index
+ }
+ return idx;
+ }
+
+ CowArray!SP data;
+}
+
+@system unittest
+{
+ import std.conv : to;
+ assert(unicode.ASCII.to!string() == "[0..128)");
+}
+
+// pedantic version for ctfe, and aligned-access only architectures
+@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);
+ else
+ return (cast(uint) ptr[idx]<<16) + (cast(uint) ptr[idx+1]<<8)
+ + ptr[idx+2];
+}
+
+// ditto
+@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;
+ }
+ else
+ {
+ ptr[idx] = (val >> 16) & 0xFF;
+ ptr[idx+1] = (val >> 8) & 0xFF;
+ ptr[idx+2] = val & 0xFF;
+ }
+}
+
+// unaligned x86-like read/write functions
+@system private uint unalignedRead24(scope const ubyte* ptr, size_t idx) pure nothrow @nogc
+{
+ uint* src = cast(uint*)(ptr+3*idx);
+ version (LittleEndian)
+ return *src & 0xFF_FFFF;
+ else
+ return *src >> 8;
+}
+
+// ditto
+@system private void unalignedWrite24(scope ubyte* ptr, uint val, size_t idx) pure nothrow @nogc
+{
+ uint* dest = cast(uint*)(cast(ubyte*) ptr + 3*idx);
+ version (LittleEndian)
+ *dest = val | (*dest & 0xFF00_0000);
+ else
+ *dest = (val << 8) | (*dest & 0xFF);
+}
+
+@system private uint read24(scope const ubyte* ptr, size_t idx) pure nothrow @nogc
+{
+ static if (hasUnalignedReads)
+ return __ctfe ? safeRead24(ptr, idx) : unalignedRead24(ptr, idx);
+ else
+ return safeRead24(ptr, idx);
+}
+
+@system private void write24(scope ubyte* ptr, uint val, size_t idx) pure nothrow @nogc
+{
+ static if (hasUnalignedReads)
+ return __ctfe ? safeWrite24(ptr, val, idx) : unalignedWrite24(ptr, val, idx);
+ else
+ return safeWrite24(ptr, val, idx);
+}
+
+struct CowArray(SP=GcPolicy)
+{
+ import std.range.primitives : hasLength;
+
+ @safe:
+ static auto reuse(uint[] arr)
+ {
+ CowArray cow;
+ cow.data = arr;
+ SP.append(cow.data, 1);
+ assert(cow.refCount == 1);
+ assert(cow.length == arr.length);
+ return cow;
+ }
+
+ this(Range)(Range range)
+ if (isInputRange!Range && hasLength!Range)
+ {
+ import std.algorithm.mutation : copy;
+ length = range.length;
+ copy(range, data[0..$-1]);
+ }
+
+ this(Range)(Range range)
+ if (isForwardRange!Range && !hasLength!Range)
+ {
+ 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)
+ {
+ refCount = refCount + 1;
+ }
+ }
+
+ ~this()
+ {
+ if (!empty)
+ {
+ immutable cnt = refCount;
+ if (cnt == 1)
+ SP.destroy(data);
+ else
+ refCount = cnt - 1;
+ }
+ }
+
+ // no ref-count for empty U24 array
+ @property bool empty() const { return data.length == 0; }
+
+ // report one less then actual size
+ @property size_t length() const
+ {
+ return data.length ? data.length - 1 : 0;
+ }
+
+ //+ an extra slot for ref-count
+ @property void length(size_t len)
+ {
+ import std.algorithm.comparison : min;
+ import std.algorithm.mutation : copy;
+ if (len == 0)
+ {
+ if (!empty)
+ freeThisReference();
+ return;
+ }
+ immutable total = len + 1; // including ref-count
+ if (empty)
+ {
+ data = SP.alloc!uint(total);
+ refCount = 1;
+ return;
+ }
+ 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]);
+ data = new_data; // before setting refCount!
+ refCount = 1;
+ }
+ else // 'this' is the only reference
+ {
+ // use the realloc (hopefully in-place operation)
+ data = SP.realloc(data, total);
+ refCount = 1; // setup a ref-count in the new end of the array
+ }
+ }
+
+ alias opDollar = length;
+
+ uint opIndex()(size_t idx)const
+ {
+ return data[idx];
+ }
+
+ void opIndexAssign(uint val, size_t idx)
+ {
+ auto cnt = refCount;
+ if (cnt != 1)
+ dupThisReference(cnt);
+ data[idx] = val;
+ }
+
+ //
+ auto opSlice(size_t from, size_t to)
+ {
+ if (!empty)
+ {
+ auto cnt = refCount;
+ if (cnt != 1)
+ dupThisReference(cnt);
+ }
+ return data[from .. to];
+
+ }
+
+ //
+ auto opSlice(size_t from, size_t to) const
+ {
+ return data[from .. to];
+ }
+
+ // length slices before the ref count
+ auto opSlice()
+ {
+ return opSlice(0, length);
+ }
+
+ // ditto
+ auto opSlice() const
+ {
+ return opSlice(0, length);
+ }
+
+ void append(Range)(Range range)
+ if (isInputRange!Range && hasLength!Range && is(ElementType!Range : uint))
+ {
+ size_t nl = length + range.length;
+ length = nl;
+ copy(range, this[nl-range.length .. nl]);
+ }
+
+ void append()(uint[] val...)
+ {
+ length = length + val.length;
+ data[$-val.length-1 .. $-1] = val[];
+ }
+
+ bool opEquals()(auto const ref CowArray rhs)const
+ {
+ if (empty ^ rhs.empty)
+ return false; // one is empty and the other isn't
+ return empty || data[0..$-1] == rhs.data[0..$-1];
+ }
+
+private:
+ // ref-count is right after the data
+ @property uint refCount() const
+ {
+ return data[$-1];
+ }
+
+ @property void refCount(uint cnt)
+ {
+ data[$-1] = cnt;
+ }
+
+ void freeThisReference()
+ {
+ immutable count = refCount;
+ if (count != 1) // have more references to this memory
+ {
+ // dec shared ref-count
+ refCount = count - 1;
+ data = [];
+ }
+ else
+ SP.destroy(data);
+ assert(!data.ptr);
+ }
+
+ void dupThisReference(uint count)
+ in
+ {
+ assert(!empty && count != 1 && count == refCount);
+ }
+ body
+ {
+ import std.algorithm.mutation : copy;
+ // dec shared ref-count
+ refCount = count - 1;
+ // copy to the new chunk of RAM
+ auto new_data = SP.alloc!uint(data.length);
+ // bit-blit old stuff except the counter
+ copy(data[0..$-1], new_data[0..$-1]);
+ data = new_data; // before setting refCount!
+ refCount = 1; // so that this updates the right one
+ }
+
+ uint[] data;
+}
+
+@safe unittest// Uint24 tests
+{
+ 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;
+ u24[1] = 1024;
+ T u24_c = u24;
+ assert(u24[1] == 1024);
+ u24.length = 0;
+ assert(u24.empty);
+ u24.append([1, 2]);
+ assert(equal(u24[], [1, 2]));
+ u24.append(111);
+ assert(equal(u24[], [1, 2, 111]));
+ assert(!u24_c.empty && u24_c[1] == 1024);
+ u24.length = 3;
+ copy(iota(0, 3), u24[]);
+ assert(equal(u24[], iota(0, 3)));
+ assert(u24_c[1] == 1024);
+ }
+
+ void func2(T)(T u24)
+ {
+ T u24_2 = u24;
+ T u24_3;
+ u24_3 = u24_2;
+ assert(u24_2 == u24_3);
+ assert(equal(u24[], u24_2[]));
+ assert(equal(u24_2[], u24_3[]));
+ funcRef(u24_3);
+
+ assert(equal(u24_3[], iota(0, 3)));
+ assert(!equal(u24_2[], u24_3[]));
+ assert(equal(u24_2[], u24[]));
+ u24_2 = u24_3;
+ assert(equal(u24_2[], iota(0, 3)));
+ // to test that passed arg is intact outside
+ // plus try out opEquals
+ u24 = u24_3;
+ u24 = T.init;
+ u24_3 = T.init;
+ assert(u24.empty);
+ assert(u24 == u24_3);
+ assert(u24 != u24_2);
+ }
+
+ foreach (Policy; AliasSeq!(GcPolicy, ReallocPolicy))
+ {
+ alias Range = typeof(CowArray!Policy.init[]);
+ alias U24A = CowArray!Policy;
+ static assert(isForwardRange!Range);
+ static assert(isBidirectionalRange!Range);
+ static assert(isOutputRange!(Range, uint));
+ static assert(isRandomAccessRange!(Range));
+
+ auto arr = U24A([42u, 36, 100]);
+ assert(arr[0] == 42);
+ assert(arr[1] == 36);
+ arr[0] = 72;
+ arr[1] = 0xFE_FEFE;
+ assert(arr[0] == 72);
+ assert(arr[1] == 0xFE_FEFE);
+ assert(arr[2] == 100);
+ U24A arr2 = arr;
+ assert(arr2[0] == 72);
+ arr2[0] = 11;
+ // test COW-ness
+ assert(arr[0] == 72);
+ assert(arr2[0] == 11);
+ // set this to about 100M to stress-test COW memory management
+ foreach (v; 0 .. 10_000)
+ func2(arr);
+ assert(equal(arr[], [72, 0xFE_FEFE, 100]));
+
+ auto r2 = U24A(iota(0, 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 = AliasSeq!(InversionList!GcPolicy, InversionList!ReallocPolicy);
+}
+
+@safe unittest// core set primitives test
+{
+ import std.conv : text;
+ foreach (CodeList; AllSets)
+ {
+ CodeList a;
+ //"plug a hole" test
+ a.add(10, 20).add(25, 30).add(15, 27);
+ assert(a == CodeList(10, 30), text(a));
+
+ auto x = CodeList.init;
+ x.add(10, 20).add(30, 40).add(50, 60);
+
+ a = x;
+ a.add(20, 49);//[10, 49) [50, 60)
+ assert(a == CodeList(10, 49, 50 ,60));
+
+ a = x;
+ a.add(20, 50);
+ assert(a == CodeList(10, 60), text(a));
+
+ // simple unions, mostly edge effects
+ x = CodeList.init;
+ x.add(10, 20).add(40, 60);
+
+ a = x;
+ a.add(10, 25); //[10, 25) [40, 60)
+ assert(a == CodeList(10, 25, 40, 60));
+
+ a = x;
+ a.add(5, 15); //[5, 20) [40, 60)
+ assert(a == CodeList(5, 20, 40, 60));
+
+ a = x;
+ a.add(0, 10); // [0, 20) [40, 60)
+ assert(a == CodeList(0, 20, 40, 60));
+
+ a = x;
+ a.add(0, 5); // prepand
+ assert(a == CodeList(0, 5, 10, 20, 40, 60), text(a));
+
+ a = x;
+ a.add(5, 20);
+ assert(a == CodeList(5, 20, 40, 60));
+
+ a = x;
+ a.add(3, 37);
+ assert(a == CodeList(3, 37, 40, 60));
+
+ a = x;
+ a.add(37, 65);
+ assert(a == CodeList(10, 20, 37, 65));
+
+ // some tests on helpers for set intersection
+ x = CodeList.init.add(10, 20).add(40, 60).add(100, 120);
+ a = x;
+
+ auto m = a.skipUpTo(60);
+ a.dropUpTo(110, m);
+ assert(a == CodeList(10, 20, 40, 60, 110, 120), text(a.data[]));
+
+ a = x;
+ a.dropUpTo(100);
+ assert(a == CodeList(100, 120), text(a.data[]));
+
+ a = x;
+ m = a.skipUpTo(50);
+ a.dropUpTo(140, m);
+ assert(a == CodeList(10, 20, 40, 50), text(a.data[]));
+ a = x;
+ a.dropUpTo(60);
+ assert(a == CodeList(100, 120), text(a.data[]));
+ }
+}
+
+
+//test constructor to work with any order of intervals
+@safe unittest
+{
+ 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)))
+ assert(ch in c1, to!string(ch));
+
+ //contiguos
+ assert(CodepointSet(1000, 1006, 1006, 1009)
+ .byInterval.equal([tuple(1000, 1009)]));
+ //contains
+ assert(CodepointSet(900, 1200, 1000, 1100)
+ .byInterval.equal([tuple(900, 1200)]));
+ //intersect left
+ assert(CodepointSet(900, 1100, 1000, 1200)
+ .byInterval.equal([tuple(900, 1200)]));
+ //intersect right
+ assert(CodepointSet(1000, 1200, 900, 1100)
+ .byInterval.equal([tuple(900, 1200)]));
+
+ //ditto with extra items at end
+ assert(CodepointSet(1000, 1200, 900, 1100, 800, 850)
+ .byInterval.equal([tuple(800, 850), tuple(900, 1200)]));
+ assert(CodepointSet(900, 1100, 1000, 1200, 800, 850)
+ .byInterval.equal([tuple(800, 850), tuple(900, 1200)]));
+
+ //"plug a hole" test
+ auto c2 = CodepointSet(20, 40,
+ 60, 80, 100, 140, 150, 200,
+ 40, 60, 80, 100, 140, 150
+ );
+ assert(c2.byInterval.equal([tuple(20, 200)]));
+
+ auto c3 = CodepointSet(
+ 20, 40, 60, 80, 100, 140, 150, 200,
+ 0, 10, 15, 100, 10, 20, 200, 220);
+ assert(c3.byInterval.equal([tuple(0, 140), tuple(150, 220)]));
+}
+
+
+@safe unittest
+{ // full set operations
+ import std.conv : text;
+ foreach (CodeList; AllSets)
+ {
+ CodeList a, b, c, d;
+
+ //"plug a hole"
+ a.add(20, 40).add(60, 80).add(100, 140).add(150, 200);
+ b.add(40, 60).add(80, 100).add(140, 150);
+ c = a | b;
+ d = b | a;
+ assert(c == CodeList(20, 200), text(CodeList.stringof," ", c));
+ assert(c == d, text(c," vs ", d));
+
+ b = CodeList.init.add(25, 45).add(65, 85).add(95,110).add(150, 210);
+ c = a | b; //[20,45) [60, 85) [95, 140) [150, 210)
+ d = b | a;
+ assert(c == CodeList(20, 45, 60, 85, 95, 140, 150, 210), text(c));
+ assert(c == d, text(c," vs ", d));
+
+ b = CodeList.init.add(10, 20).add(30,100).add(145,200);
+ c = a | b;//[10, 140) [145, 200)
+ d = b | a;
+ assert(c == CodeList(10, 140, 145, 200));
+ assert(c == d, text(c," vs ", d));
+
+ b = CodeList.init.add(0, 10).add(15, 100).add(10, 20).add(200, 220);
+ c = a | b;//[0, 140) [150, 220)
+ d = b | a;
+ assert(c == CodeList(0, 140, 150, 220));
+ assert(c == d, text(c," vs ", d));
+
+
+ a = CodeList.init.add(20, 40).add(60, 80);
+ b = CodeList.init.add(25, 35).add(65, 75);
+ c = a & b;
+ d = b & a;
+ assert(c == CodeList(25, 35, 65, 75), text(c));
+ assert(c == d, text(c," vs ", d));
+
+ a = CodeList.init.add(20, 40).add(60, 80).add(100, 140).add(150, 200);
+ b = CodeList.init.add(25, 35).add(65, 75).add(110, 130).add(160, 180);
+ c = a & b;
+ d = b & a;
+ assert(c == CodeList(25, 35, 65, 75, 110, 130, 160, 180), text(c));
+ assert(c == d, text(c," vs ", d));
+
+ a = CodeList.init.add(20, 40).add(60, 80).add(100, 140).add(150, 200);
+ b = CodeList.init.add(10, 30).add(60, 120).add(135, 160);
+ c = a & b;//[20, 30)[60, 80) [100, 120) [135, 140) [150, 160)
+ d = b & a;
+
+ assert(c == CodeList(20, 30, 60, 80, 100, 120, 135, 140, 150, 160),text(c));
+ assert(c == d, text(c, " vs ",d));
+ assert((c & a) == c);
+ assert((d & b) == d);
+ assert((c & d) == d);
+
+ b = CodeList.init.add(40, 60).add(80, 100).add(140, 200);
+ c = a & b;
+ d = b & a;
+ assert(c == CodeList(150, 200), text(c));
+ assert(c == d, text(c, " vs ",d));
+ assert((c & a) == c);
+ assert((d & b) == d);
+ assert((c & d) == d);
+
+ assert((a & a) == a);
+ assert((b & b) == b);
+
+ a = CodeList.init.add(20, 40).add(60, 80).add(100, 140).add(150, 200);
+ b = CodeList.init.add(30, 60).add(75, 120).add(190, 300);
+ c = a - b;// [30, 40) [60, 75) [120, 140) [150, 190)
+ d = b - a;// [40, 60) [80, 100) [200, 300)
+ assert(c == CodeList(20, 30, 60, 75, 120, 140, 150, 190), text(c));
+ assert(d == CodeList(40, 60, 80, 100, 200, 300), text(d));
+ assert(c - d == c, text(c-d, " vs ", c));
+ assert(d - c == d, text(d-c, " vs ", d));
+ assert(c - c == CodeList.init);
+ assert(d - d == CodeList.init);
+
+ a = CodeList.init.add(20, 40).add( 60, 80).add(100, 140).add(150, 200);
+ b = CodeList.init.add(10, 50).add(60, 160).add(190, 300);
+ c = a - b;// [160, 190)
+ d = b - a;// [10, 20) [40, 50) [80, 100) [140, 150) [200, 300)
+ assert(c == CodeList(160, 190), text(c));
+ assert(d == CodeList(10, 20, 40, 50, 80, 100, 140, 150, 200, 300), text(d));
+ assert(c - d == c, text(c-d, " vs ", c));
+ assert(d - c == d, text(d-c, " vs ", d));
+ assert(c - c == CodeList.init);
+ assert(d - d == CodeList.init);
+
+ a = CodeList.init.add(20, 40).add(60, 80).add(100, 140).add(150, 200);
+ b = CodeList.init.add(10, 30).add(45, 100).add(130, 190);
+ c = a ~ b; // [10, 20) [30, 40) [45, 60) [80, 130) [140, 150) [190, 200)
+ d = b ~ a;
+ assert(c == CodeList(10, 20, 30, 40, 45, 60, 80, 130, 140, 150, 190, 200),
+ text(c));
+ assert(c == d, text(c, " vs ", d));
+ }
+}
+
+}
+
+@safe unittest// vs single dchar
+{
+ 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));
+}
+
+@safe unittest// iteration & opIndex
+{
+ 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(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)
+ {
+ 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(equal(achr, arr), text(a.byCodepoint));
+ foreach (ch; a.byCodepoint)
+ assert(a[ch]);
+ auto x = CodeList(100, 500, 600, 900, 1200, 1500);
+ 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))
+ {
+ auto y = CodeList(x.byInterval);
+ assert(equal(x.byInterval, y.byInterval));
+ }
+ assert(equal(CodepointSet.init.byInterval, cast(Tuple!(uint, uint)[])[]));
+ assert(equal(CodepointSet.init.byCodepoint, cast(dchar[])[]));
+ }
+}
+
+//============================================================================
+// Generic Trie template and various ways to build it
+//============================================================================
+
+// debug helper to get a shortened array dump
+auto arrayRepr(T)(T x)
+{
+ import std.conv : text;
+ if (x.length > 32)
+ {
+ return text(x[0 .. 16],"~...~", x[x.length-16 .. x.length]);
+ }
+ else
+ return text(x);
+}
+
+/**
+ Maps $(D Key) to a suitable integer index within the range of $(D size_t).
+ The mapping is constructed by applying predicates from $(D Prefix) left to right
+ and concatenating the resulting bits.
+
+ The first (leftmost) predicate defines the most significant bits of
+ the resulting index.
+ */
+template mapTrieIndex(Prefix...)
+{
+ size_t mapTrieIndex(Key)(Key key)
+ if (isValidPrefixForTrie!(Key, Prefix))
+ {
+ alias p = Prefix;
+ size_t idx;
+ foreach (i, v; p[0..$-1])
+ {
+ idx |= p[i](key);
+ idx <<= p[i+1].bitSize;
+ }
+ idx |= p[$-1](key);
+ return idx;
+ }
+}
+
+/*
+ $(D TrieBuilder) is a type used for incremental construction
+ of $(LREF Trie)s.
+
+ See $(LREF buildTrie) for generic helpers built on top of it.
+*/
+@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
+ alias V = BitPacked!(Value, 1);
+ else
+ alias V = Value;
+ static auto deduceMaxIndex(Preds...)()
+ {
+ size_t idx = 1;
+ foreach (v; Preds)
+ idx *= 2^^v.bitSize;
+ return idx;
+ }
+
+ static if (is(typeof(Args[0]) : Key)) // Args start with upper bound on Key
+ {
+ alias Prefix = Args[1..$];
+ enum lastPageSize = 2^^Prefix[$-1].bitSize;
+ enum translatedMaxIndex = mapTrieIndex!(Prefix)(Args[0]);
+ enum roughedMaxIndex =
+ (translatedMaxIndex + lastPageSize-1)/lastPageSize*lastPageSize;
+ // check warp around - if wrapped, use the default deduction rule
+ enum maxIndex = roughedMaxIndex < translatedMaxIndex ?
+ deduceMaxIndex!(Prefix)() : roughedMaxIndex;
+ }
+ else
+ {
+ alias Prefix = Args;
+ enum maxIndex = deduceMaxIndex!(Prefix)();
+ }
+
+ alias getIndex = mapTrieIndex!(Prefix);
+
+ enum lastLevel = Prefix.length-1;
+ struct ConstructState
+ {
+ size_t idx_zeros, idx_ones;
+ }
+ // iteration over levels of Trie, each indexes its own level and thus a shortened domain
+ size_t[Prefix.length] indices;
+ // default filler value to use
+ Value defValue;
+ // this is a full-width index of next item
+ size_t curIndex;
+ // all-zeros page index, all-ones page index (+ indicator if there is such a page)
+ ConstructState[Prefix.length] state;
+ // the table being constructed
+ MultiArray!(idxTypes!(Key, fullBitSize!(Prefix), Prefix[0..$]), V) table;
+
+ @disable this();
+
+ //shortcut for index variable at level 'level'
+ @property ref idx(size_t level)(){ return indices[level]; }
+
+ // this function assumes no holes in the input so
+ // indices are going one by one
+ void addValue(size_t level, T)(T val, size_t numVals)
+ {
+ alias j = idx!level;
+ enum pageSize = 1 << Prefix[level].bitSize;
+ if (numVals == 0)
+ return;
+ auto ptr = table.slice!(level);
+ if (numVals == 1)
+ {
+ static if (level == Prefix.length-1)
+ ptr[j] = val;
+ else
+ {// can incur narrowing conversion
+ assert(j < ptr.length);
+ ptr[j] = force!(typeof(ptr[j]))(val);
+ }
+ j++;
+ if (j % pageSize == 0)
+ spillToNextPage!level(ptr);
+ return;
+ }
+ // longer row of values
+ // get to the next page boundary
+ 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;
+ j += numVals;
+ return;
+ }
+ 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;
+ 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)
+ {
+ alias NextIdx = typeof(table.slice!(level-1)[0]);
+ addValue!(level-1)(force!NextIdx(state[level].idx_zeros),
+ numVals/pageSize);
+ ptr = table.slice!level; //table structure might have changed
+ numVals %= pageSize;
+ }
+ else
+ {
+ while (numVals >= pageSize)
+ {
+ numVals -= pageSize;
+ ptr[j .. j+pageSize] = val;
+ j += pageSize;
+ spillToNextPage!level(ptr);
+ }
+ }
+ if (numVals)
+ {
+ // the leftovers, an incomplete page
+ ptr[j .. j+numVals] = val;
+ j += numVals;
+ }
+ }
+ }
+
+ void spillToNextPage(size_t level, Slice)(ref Slice ptr)
+ {
+ // last level (i.e. topmost) has 1 "page"
+ // thus it need not to add a new page on upper level
+ static if (level != 0)
+ spillToNextPageImpl!(level)(ptr);
+ }
+
+ // this can re-use the current page if duplicate or allocate a new one
+ // it also makes sure that previous levels point to the correct page in this level
+ void spillToNextPageImpl(size_t level, Slice)(ref Slice ptr)
+ {
+ alias NextIdx = typeof(table.slice!(level-1)[0]);
+ NextIdx next_lvl_index;
+ enum pageSize = 1 << Prefix[level].bitSize;
+ assert(idx!level % pageSize == 0);
+ immutable last = idx!level-pageSize;
+ const slice = ptr[idx!level - pageSize .. idx!level];
+ size_t j;
+ for (j=0; j<last; j+=pageSize)
+ {
+ 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)
+ {
+ 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]));
+ writeln("LEVEL(", level
+ , ") src page is :", ptr, ": ", arrayRepr(slice[0 .. pageSize]));
+ }
+ idx!level -= pageSize; // reuse this page, it is duplicate
+ break;
+ }
+ }
+ 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))
+ {
+ 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]));
+ writefln("LEVEL(%s) index: %s ; page at this index %s"
+ , level
+ , next_lvl_index
+ , arrayRepr(
+ table.slice!(level)
+ [pageSize*next_lvl_index..(next_lvl_index+1)*pageSize]
+ ));
+ }
+ table.length!level = table.length!level + pageSize;
+ }
+ L_know_index:
+ // for the previous level, values are indices to the pages in the current level
+ addValue!(level-1)(next_lvl_index, 1);
+ ptr = table.slice!level; //re-load the slice after moves
+ }
+
+ // idx - full-width index to fill with v (full-width index != key)
+ // fills everything in the range of [curIndex, idx) with filler
+ void putAt(size_t idx, Value v)
+ {
+ assert(idx >= curIndex);
+ immutable numFillers = idx - curIndex;
+ addValue!lastLevel(defValue, numFillers);
+ addValue!lastLevel(v, 1);
+ curIndex = idx + 1;
+ }
+
+ // ditto, but sets the range of [idxA, idxB) to v
+ void putRangeAt(size_t idxA, size_t idxB, Value v)
+ {
+ assert(idxA >= curIndex);
+ assert(idxB >= idxA);
+ size_t numFillers = idxA - curIndex;
+ addValue!lastLevel(defValue, numFillers);
+ addValue!lastLevel(v, idxB - idxA);
+ curIndex = idxB; // open-right
+ }
+
+ enum errMsg = "non-monotonic prefix function(s), an unsorted range or "~
+ "duplicate key->value mapping";
+
+public:
+ /**
+ Construct a builder, where $(D filler) is a value
+ to indicate empty slots (or "not found" condition).
+ */
+ this(Value filler)
+ {
+ curIndex = 0;
+ defValue = filler;
+ // zeros-page index, ones-page index
+ 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);
+ }
+
+ /**
+ Put a value $(D v) into interval as
+ mapped by keys from $(D a) to $(D b).
+ All slots prior to $(D a) are filled with
+ the default filler.
+ */
+ void putRange(Key a, Key b, Value v)
+ {
+ auto idxA = getIndex(a), idxB = getIndex(b);
+ // indexes of key should always grow
+ enforce(idxB >= idxA && idxA >= curIndex, errMsg);
+ putRangeAt(idxA, idxB, v);
+ }
+
+ /**
+ Put a value $(D v) into slot mapped by $(D key).
+ All slots prior to $(D key) are filled with the
+ default filler.
+ */
+ void putValue(Key key, Value v)
+ {
+ import std.conv : text;
+ auto idx = getIndex(key);
+ enforce(idx >= curIndex, text(errMsg, " ", idx));
+ putAt(idx, v);
+ }
+
+ /// Finishes construction of Trie, yielding an immutable Trie instance.
+ auto build()
+ {
+ 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
+ || (Prefix.length != 1 && indices[lastLevel] == 0)) // can be just empty
+ {
+ addValue!lastLevel(defValue, size_t.max - curIndex);
+ addValue!lastLevel(defValue, 1);
+ }
+ // else curIndex already completed the full range of size_t by wrapping around
+ }
+ return Trie!(V, Key, maxIndex, Prefix)(table);
+ }
+}
+
+/**
+ $(P A generic Trie data-structure for a fixed number of stages.
+ The design goal is optimal speed with smallest footprint size.
+ )
+ $(P It's intentionally read-only and doesn't provide constructors.
+ To construct one use a special builder,
+ see $(LREF TrieBuilder) and $(LREF buildTrie).
+ )
+
+*/
+@trusted private struct Trie(Value, Key, Args...)
+if (isValidPrefixForTrie!(Key, Args)
+ || (isValidPrefixForTrie!(Key, Args[1..$])
+ && is(typeof(Args[0]) : size_t)))
+{
+ import std.range.primitives : isOutputRange;
+ static if (is(typeof(Args[0]) : size_t))
+ {
+ private enum maxIndex = Args[0];
+ private enum hasBoundsCheck = true;
+ private alias Prefix = Args[1..$];
+ }
+ else
+ {
+ private enum hasBoundsCheck = false;
+ private alias Prefix = Args;
+ }
+
+ private this()(typeof(_table) table)
+ {
+ _table = table;
+ }
+
+ // only for constant Tries constructed from precompiled tables
+ private this()(const(size_t)[] offsets, const(size_t)[] sizes,
+ const(size_t)[] data) const
+ {
+ _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
+ provided during construction. The whole domain defined
+ is covered so instead of not found condition
+ the sentinel (filler) value could be used. )
+
+ $(P See $(LREF buildTrie), $(LREF TrieBuilder) for how to
+ define a domain of $(D Trie) keys and the sentinel value. )
+
+ Note:
+ Domain range-checking is only enabled in debug builds
+ and results in assertion failure.
+ */
+ TypeOfBitPacked!Value opIndex()(Key key) const
+ {
+ 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)((_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))
+ {
+ _table.store(sink);
+ }
+
+private:
+ MultiArray!(idxTypes!(Key, fullBitSize!(Prefix), Prefix[0..$]), Value) _table;
+}
+
+// create a tuple of 'sliceBits' that slice the 'top' of bits into pieces of sizes 'sizes'
+// left-to-right, the most significant bits first
+template GetBitSlicing(size_t top, sizes...)
+{
+ static if (sizes.length > 0)
+ alias GetBitSlicing =
+ AliasSeq!(sliceBits!(top - sizes[0], top),
+ GetBitSlicing!(top - sizes[0], sizes[1..$]));
+ else
+ alias GetBitSlicing = AliasSeq!();
+}
+
+template callableWith(T)
+{
+ template callableWith(alias Pred)
+ {
+ static if (!is(typeof(Pred(T.init))))
+ enum callableWith = false;
+ else
+ {
+ alias Result = typeof(Pred(T.init));
+ enum callableWith = isBitPackableType!(TypeOfBitPacked!(Result));
+ }
+ }
+}
+
+/*
+ Check if $(D Prefix) is a valid set of predicates
+ for $(D Trie) template having $(D Key) as the type of keys.
+ This requires all predicates to be callable, take
+ single argument of type $(D Key) and return unsigned value.
+*/
+template isValidPrefixForTrie(Key, Prefix...)
+{
+ import std.meta : allSatisfy;
+ enum isValidPrefixForTrie = allSatisfy!(callableWith!Key, Prefix); // TODO: tighten the screws
+}
+
+/*
+ Check if $(D Args) is a set of maximum key value followed by valid predicates
+ for $(D Trie) template having $(D Key) as the type of keys.
+*/
+template isValidArgsForTrie(Key, Args...)
+{
+ static if (Args.length > 1)
+ {
+ enum isValidArgsForTrie = isValidPrefixForTrie!(Key, Args)
+ || (isValidPrefixForTrie!(Key, Args[1..$]) && is(typeof(Args[0]) : Key));
+ }
+ else
+ enum isValidArgsForTrie = isValidPrefixForTrie!Args;
+}
+
+@property size_t sumOfIntegerTuple(ints...)()
+{
+ size_t count=0;
+ foreach (v; ints)
+ count += v;
+ return count;
+}
+
+/**
+ A shorthand for creating a custom multi-level fixed Trie
+ from a $(D CodepointSet). $(D sizes) are numbers of bits per level,
+ with the most significant bits used first.
+
+ Note: The sum of $(D sizes) must be equal 21.
+
+ See_Also: $(LREF toTrie), which is even simpler.
+
+ Example:
+ ---
+ {
+ import std.stdio;
+ auto set = unicode("Number");
+ auto trie = codepointSetTrie!(8, 5, 8)(set);
+ writeln("Input code points to test:");
+ foreach (line; stdin.byLine)
+ {
+ int count=0;
+ foreach (dchar ch; line)
+ if (trie[ch])// is number
+ count++;
+ writefln("Contains %d number code points.", count);
+ }
+ }
+ ---
+*/
+public template codepointSetTrie(sizes...)
+if (sumOfIntegerTuple!sizes == 21)
+{
+ auto codepointSetTrie(Set)(Set set)
+ if (isCodepointSet!Set)
+ {
+ auto builder = TrieBuilder!(bool, dchar, lastDchar+1, GetBitSlicing!(21, sizes))(false);
+ foreach (ival; set.byInterval)
+ builder.putRange(ival[0], ival[1], true);
+ return builder.build();
+ }
+}
+
+/// Type of Trie generated by codepointSetTrie function.
+public template CodepointSetTrie(sizes...)
+if (sumOfIntegerTuple!sizes == 21)
+{
+ alias Prefix = GetBitSlicing!(21, sizes);
+ alias CodepointSetTrie = typeof(TrieBuilder!(bool, dchar, lastDchar+1, Prefix)(false).build());
+}
+
+/**
+ A slightly more general tool for building fixed $(D Trie)
+ for the Unicode data.
+
+ Specifically unlike $(D codepointSetTrie) it's allows creating mappings
+ of $(D dchar) to an arbitrary type $(D T).
+
+ Note: Overload taking $(D CodepointSet)s will naturally convert
+ only to bool mapping $(D Trie)s.
+*/
+public template codepointTrie(T, sizes...)
+if (sumOfIntegerTuple!sizes == 21)
+{
+ alias Prefix = GetBitSlicing!(21, sizes);
+
+ static if (is(TypeOfBitPacked!T == bool))
+ {
+ auto codepointTrie(Set)(in Set set)
+ if (isCodepointSet!Set)
+ {
+ return codepointSetTrie(set);
+ }
+ }
+
+ auto codepointTrie()(T[dchar] map, T defValue=T.init)
+ {
+ return buildTrie!(T, dchar, Prefix)(map, defValue);
+ }
+
+ // unsorted range of pairs
+ auto codepointTrie(R)(R range, T defValue=T.init)
+ if (isInputRange!R
+ && is(typeof(ElementType!R.init[0]) : T)
+ && is(typeof(ElementType!R.init[1]) : dchar))
+ {
+ // build from unsorted array of pairs
+ // TODO: expose index sorting functions for Trie
+ return buildTrie!(T, dchar, Prefix)(range, defValue, true);
+ }
+}
+
+@system pure unittest
+{
+ import std.algorithm.comparison : max;
+ import std.algorithm.searching : count;
+
+ // 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(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);
+
+}
+
+/// Type of Trie as generated by codepointTrie function.
+public template CodepointTrie(T, sizes...)
+if (sumOfIntegerTuple!sizes == 21)
+{
+ alias Prefix = GetBitSlicing!(21, sizes);
+ alias CodepointTrie = typeof(TrieBuilder!(T, dchar, lastDchar+1, Prefix)(T.init).build());
+}
+
+package template cmpK0(alias Pred)
+{
+ import std.typecons : Tuple;
+ static bool cmpK0(Value, Key)
+ (Tuple!(Value, Key) a, Tuple!(Value, Key) b)
+ {
+ return Pred(a[1]) < Pred(b[1]);
+ }
+}
+
+/**
+ The most general utility for construction of $(D Trie)s
+ short of using $(D TrieBuilder) directly.
+
+ Provides a number of convenience overloads.
+ $(D Args) is tuple of maximum key value followed by
+ predicates to construct index from key.
+
+ Alternatively if the first argument is not a value convertible to $(D Key)
+ then the whole tuple of $(D Args) is treated as predicates
+ and the maximum Key is deduced from predicates.
+*/
+private template buildTrie(Value, Key, Args...)
+if (isValidArgsForTrie!(Key, Args))
+{
+ static if (is(typeof(Args[0]) : Key)) // prefix starts with upper bound on Key
+ {
+ alias Prefix = Args[1..$];
+ }
+ else
+ alias Prefix = Args;
+
+ alias getIndex = mapTrieIndex!(Prefix);
+
+ // for multi-sort
+ template GetComparators(size_t n)
+ {
+ static if (n > 0)
+ alias GetComparators =
+ AliasSeq!(GetComparators!(n-1), cmpK0!(Prefix[n-1]));
+ else
+ alias GetComparators = AliasSeq!();
+ }
+
+ /*
+ Build $(D Trie) from a range of a Key-Value pairs,
+ assuming it is sorted by Key as defined by the following lambda:
+ ------
+ (a, b) => mapTrieIndex!(Prefix)(a) < mapTrieIndex!(Prefix)(b)
+ ------
+ Exception is thrown if it's detected that the above order doesn't hold.
+
+ In other words $(LREF mapTrieIndex) should be a
+ monotonically increasing function that maps $(D Key) to an integer.
+
+ 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)
+ && is(typeof(Range.init.front[1]) : Key))
+ {
+ auto builder = TrieBuilder!(Value, Key, Prefix)(filler);
+ foreach (v; range)
+ builder.putValue(v[1], v[0]);
+ return builder.build();
+ }
+
+ /*
+ If $(D Value) is bool (or BitPacked!(bool, x)) then it's possible
+ to build $(D Trie) from a range of open-right intervals of $(D Key)s.
+ The requirement on the ordering of keys (and the behavior on the
+ violation of it) is the same as for Key-Value range overload.
+
+ Intervals denote ranges of !$(D filler) i.e. the opposite of filler.
+ If no filler provided keys inside of the intervals map to true,
+ and $(D filler) is false.
+ */
+ auto buildTrie(Range)(Range range, Value filler=Value.init)
+ 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)
+ builder.putRange(ival[0], ival[1], !filler);
+ return builder.build();
+ }
+
+ auto buildTrie(Range)(Range range, Value filler, bool unsorted)
+ if (isInputRange!Range
+ && is(typeof(Range.init.front[0]) : Value)
+ && is(typeof(Range.init.front[1]) : Key))
+ {
+ import std.algorithm.sorting : multiSort;
+ alias Comps = GetComparators!(Prefix.length);
+ if (unsorted)
+ multiSort!(Comps)(range);
+ return buildTrie(range, filler);
+ }
+
+ /*
+ If $(D Value) is bool (or BitPacked!(bool, x)) then it's possible
+ to build $(D Trie) simply from an input range of $(D Key)s.
+ The requirement on the ordering of keys (and the behavior on the
+ violation of it) is the same as for Key-Value range overload.
+
+ Keys found in range denote !$(D filler) i.e. the opposite of filler.
+ 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)
+ && isInputRange!Range && is(typeof(Range.init.front) : Key))
+ {
+ auto builder = TrieBuilder!(Value, Key, Prefix)(filler);
+ foreach (v; range)
+ builder.putValue(v, !filler);
+ return builder.build();
+ }
+
+ /*
+ If $(D Key) is unsigned integer $(D Trie) could be constructed from array
+ of values where array index serves as key.
+ */
+ auto buildTrie()(Value[] array, Value filler=Value.init)
+ if (isUnsigned!Key)
+ {
+ auto builder = TrieBuilder!(Value, Key, Prefix)(filler);
+ foreach (idx, v; array)
+ builder.putValue(idx, v);
+ return builder.build();
+ }
+
+ /*
+ Builds $(D Trie) from associative array.
+ */
+ auto buildTrie(Key, Value)(Value[Key] map, Value filler=Value.init)
+ {
+ import std.array : array;
+ import std.range : zip;
+ auto range = array(zip(map.values, map.keys));
+ return buildTrie(range, filler, true); // sort it
+ }
+}
+
+// helper in place of assumeSize to
+//reduce mangled name & help DMD inline Trie functors
+struct clamp(size_t bits)
+{
+ static size_t opCall(T)(T arg){ return arg; }
+ enum bitSize = bits;
+}
+
+struct clampIdx(size_t idx, size_t bits)
+{
+ static size_t opCall(T)(T arg){ return arg[idx]; }
+ enum bitSize = bits;
+}
+
+/**
+ Conceptual type that outlines the common properties of all UTF Matchers.
+
+ Note: For illustration purposes only, every method
+ call results in assertion failure.
+ Use $(LREF utfMatcher) to obtain a concrete matcher
+ for UTF-8 or UTF-16 encodings.
+*/
+public struct MatcherConcept
+{
+ /**
+ $(P Perform a semantic equivalent 2 operations:
+ decoding a $(CODEPOINT) at front of $(D inp) and testing if
+ it belongs to the set of $(CODEPOINTS) of this matcher. )
+
+ $(P The effect on $(D inp) depends on the kind of function called:)
+
+ $(P Match. If the codepoint is found in the set then range $(D inp)
+ is advanced by its size in $(S_LINK Code unit, code units),
+ otherwise the range is not modifed.)
+
+ $(P Skip. The range is always advanced by the size
+ of the tested $(CODEPOINT) regardless of the result of test.)
+
+ $(P Test. The range is left unaffected regardless
+ of the result of test.)
+ */
+ public bool match(Range)(ref Range inp)
+ if (isRandomAccessRange!Range && is(ElementType!Range : char))
+ {
+ assert(false);
+ }
+
+ ///ditto
+ public bool skip(Range)(ref Range inp)
+ if (isRandomAccessRange!Range && is(ElementType!Range : char))
+ {
+ assert(false);
+ }
+
+ ///ditto
+ public bool test(Range)(ref Range inp)
+ if (isRandomAccessRange!Range && is(ElementType!Range : char))
+ {
+ assert(false);
+ }
+ ///
+ @safe unittest
+ {
+ string truth = "2² = 4";
+ auto m = utfMatcher!char(unicode.Number);
+ assert(m.match(truth)); // '2' is a number all right
+ assert(truth == "² = 4"); // skips on match
+ assert(m.match(truth)); // so is the superscript '2'
+ assert(!m.match(truth)); // space is not a number
+ assert(truth == " = 4"); // unaffected on no match
+ assert(!m.skip(truth)); // same test ...
+ assert(truth == "= 4"); // but skips a codepoint regardless
+ assert(!m.test(truth)); // '=' is not a number
+ 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
+ operations per any $(D test)/$(D match).
+
+ Use with care as the sub-matcher won't match
+ any $(CODEPOINTS) that have encoded length that doesn't belong
+ to the selected set of lengths. Also the sub-matcher object references
+ the parent matcher and must not be used past the liftetime
+ of the latter.
+
+ Another caveat of using sub-matcher is that skip is not available
+ preciesly because sub-matcher doesn't detect all lengths.
+ */
+ @property auto subMatcher(Lengths...)()
+ {
+ assert(0);
+ return this;
+ }
+
+ @safe unittest
+ {
+ auto m = utfMatcher!char(unicode.Number);
+ string square = "2²";
+ // about sub-matchers
+ assert(!m.subMatcher!(2,3,4).test(square)); // ASCII no covered
+ assert(m.subMatcher!1.match(square)); // ASCII-only, works
+ assert(!m.subMatcher!1.test(square)); // unicode '²'
+ assert(m.subMatcher!(2,3,4).match(square)); //
+ assert(square == "");
+ wstring wsquare = "2²";
+ auto m16 = utfMatcher!wchar(unicode.Number);
+ // may keep ref, but the orignal (m16) must be kept alive
+ auto bmp = m16.subMatcher!1;
+ assert(bmp.match(wsquare)); // Okay, in basic multilingual plan
+ assert(bmp.match(wsquare)); // And '²' too
+ }
+}
+
+/**
+ Test if $(D M) is an UTF Matcher for ranges of $(D Char).
+*/
+public enum isUtfMatcher(M, C) = __traits(compiles, (){
+ C[] s;
+ auto d = s.decoder;
+ M m;
+ assert(is(typeof(m.match(d)) == bool));
+ assert(is(typeof(m.test(d)) == bool));
+ static if (is(typeof(m.skip(d))))
+ {
+ assert(is(typeof(m.skip(d)) == bool));
+ assert(is(typeof(m.skip(s)) == bool));
+ }
+ assert(is(typeof(m.match(s)) == bool));
+ assert(is(typeof(m.test(s)) == bool));
+});
+
+@safe unittest
+{
+ alias CharMatcher = typeof(utfMatcher!char(CodepointSet.init));
+ alias WcharMatcher = typeof(utfMatcher!wchar(CodepointSet.init));
+ static assert(isUtfMatcher!(CharMatcher, char));
+ static assert(isUtfMatcher!(CharMatcher, immutable(char)));
+ static assert(isUtfMatcher!(WcharMatcher, wchar));
+ static assert(isUtfMatcher!(WcharMatcher, immutable(wchar)));
+}
+
+enum Mode {
+ alwaysSkip,
+ neverSkip,
+ skipOnMatch
+}
+
+mixin template ForwardStrings()
+{
+ private bool fwdStr(string fn, C)(ref C[] str) const pure
+ {
+ 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;
+
+ void badEncoding() pure @safe
+ {
+ import std.utf : UTFException;
+ throw new UTFException("Invalid UTF-8 sequence");
+ }
+
+ //for 1-stage ASCII
+ alias AsciiSpec = AliasSeq!(bool, char, clamp!7);
+ //for 2-stage lookup of 2 byte UTF-8 sequences
+ alias Utf8Spec2 = AliasSeq!(bool, char[2],
+ clampIdx!(0, 5), clampIdx!(1, 6));
+ //ditto for 3 byte
+ alias Utf8Spec3 = AliasSeq!(bool, char[3],
+ clampIdx!(0, 4),
+ clampIdx!(1, 6),
+ clampIdx!(2, 6)
+ );
+ //ditto for 4 byte
+ alias Utf8Spec4 = AliasSeq!(bool, char[4],
+ clampIdx!(0, 3), clampIdx!(1, 6),
+ clampIdx!(2, 6), clampIdx!(3, 6)
+ );
+ alias Tables = AliasSeq!(
+ typeof(TrieBuilder!(AsciiSpec)(false).build()),
+ typeof(TrieBuilder!(Utf8Spec2)(false).build()),
+ typeof(TrieBuilder!(Utf8Spec3)(false).build()),
+ typeof(TrieBuilder!(Utf8Spec4)(false).build())
+ );
+ 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);
+
+ char truncate()(char ch) pure @safe
+ {
+ ch -= 0x80;
+ if (ch < 0x40)
+ {
+ return ch;
+ }
+ else
+ {
+ badEncoding();
+ return cast(char) 0;
+ }
+ }
+
+ static auto encode(size_t sz)(dchar ch)
+ if (sz > 1)
+ {
+ import std.utf : encodeUTF = encode;
+ char[4] buf;
+ encodeUTF(buf, ch);
+ char[sz] ret;
+ buf[0] &= leadMask!sz;
+ foreach (n; 1 .. sz)
+ buf[n] = buf[n] & 0x3f; //keep 6 lower bits
+ ret[] = buf[0 .. sz];
+ return ret;
+ }
+
+ auto build(Set)(Set set)
+ {
+ 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 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);
+ alias Ret = Impl!(1,2,3,4);
+ return Ret(asciiT, utf8_2T, utf8_3T, utf8_4T);
+ }
+
+ // Bootstrap UTF-8 static matcher interface
+ // from 3 primitives: tab!(size), lookup and Sizes
+ mixin template DefMatcher()
+ {
+ import std.format : format;
+ import std.meta : Erase, staticIndexOf;
+ enum hasASCII = staticIndexOf!(1, Sizes) >= 0;
+ alias UniSizes = Erase!(1, Sizes);
+
+ //generate dispatch code sequence for unicode parts
+ static auto genDispatch()
+ {
+ string code;
+ foreach (size; UniSizes)
+ code ~= format(q{
+ if ((ch & ~leadMask!%d) == encMask!(%d))
+ return lookup!(%d, mode)(inp);
+ else
+ }, size, size, size);
+ static if (Sizes.length == 4) //covers all code unit cases
+ code ~= "{ badEncoding(); return false; }";
+ else
+ code ~= "return false;"; //may be just fine but not covered
+ return code;
+ }
+ enum dispatch = genDispatch();
+
+ public bool match(Range)(ref Range inp) const pure
+ if (isRandomAccessRange!Range && is(ElementType!Range : char))
+ {
+ enum mode = Mode.skipOnMatch;
+ assert(!inp.empty);
+ immutable ch = inp[0];
+ static if (hasASCII)
+ {
+ if (ch < 0x80)
+ {
+ immutable r = tab!1[ch];
+ if (r)
+ inp.popFront();
+ return r;
+ }
+ else
+ mixin(dispatch);
+ }
+ else
+ mixin(dispatch);
+ }
+
+ 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))
+ {
+ enum mode = Mode.alwaysSkip;
+ assert(!inp.empty);
+ auto ch = inp[0];
+ static if (hasASCII)
+ {
+ if (ch < 0x80)
+ {
+ inp.popFront();
+ return tab!1[ch];
+ }
+ else
+ mixin(dispatch);
+ }
+ else
+ mixin(dispatch);
+ }
+ }
+
+ public bool test(Range)(ref Range inp) const pure @trusted
+ if (isRandomAccessRange!Range && is(ElementType!Range : char))
+ {
+ enum mode = Mode.neverSkip;
+ assert(!inp.empty);
+ auto ch = inp[0];
+ static if (hasASCII)
+ {
+ if (ch < 0x80)
+ return tab!1[ch];
+ else
+ mixin(dispatch);
+ }
+ else
+ mixin(dispatch);
+ }
+
+ bool match(C)(ref C[] str) const pure @trusted
+ if (isSomeChar!C)
+ {
+ return fwdStr!"match"(str);
+ }
+
+ bool skip(C)(ref C[] str) const pure @trusted
+ if (isSomeChar!C)
+ {
+ return fwdStr!"skip"(str);
+ }
+
+ bool test(C)(ref C[] str) const pure @trusted
+ if (isSomeChar!C)
+ {
+ return fwdStr!"test"(str);
+ }
+
+ mixin ForwardStrings;
+ }
+
+ 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:
+ //pick tables for chosen sizes
+ alias OurTabs = staticMap!(Table, Sizes);
+ OurTabs tables;
+ mixin DefMatcher;
+ //static disptach helper UTF size ==> table
+ alias tab(int i) = tables[i - 1];
+
+ package @property auto subMatcher(SizesToPick...)() @trusted
+ {
+ return CherryPick!(Impl, SizesToPick)(&this);
+ }
+
+ bool lookup(int size, Mode mode, Range)(ref Range inp) const pure @trusted
+ {
+ 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))
+ {
+ needle[i] = truncate(inp[i]);
+ }
+ //overlong encoding checks
+ 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();
+ }
+ 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();
+ }
+ else static if (size == 4)
+ {
+ //0x800-0xFFFF
+ //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();
+ }
+ static if (mode == Mode.alwaysSkip)
+ {
+ inp.popFrontN(size);
+ return tab!size[needle];
+ }
+ else static if (mode == Mode.neverSkip)
+ {
+ return tab!size[needle];
+ }
+ else
+ {
+ static assert(mode == Mode.skipOnMatch);
+ if (tab!size[needle])
+ {
+ inp.popFrontN(size);
+ return true;
+ }
+ else
+ return false;
+ }
+ }
+ }
+
+ 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:
+ I* m;
+ @property ref tab(int i)() const pure { return m.tables[i - 1]; }
+ bool lookup(int size, Mode mode, Range)(ref Range inp) const pure
+ {
+ return m.lookup!(size, mode)(inp);
+ }
+ mixin DefMatcher;
+ }
+}
+
+template Utf16Matcher()
+{
+ enum validSize(int sz) = sz >= 1 && sz <= 2;
+
+ void badEncoding() pure
+ {
+ import std.utf : UTFException;
+ throw new UTFException("Invalid UTF-16 sequence");
+ }
+
+ // 1-stage ASCII
+ alias AsciiSpec = AliasSeq!(bool, wchar, clamp!7);
+ //2-stage BMP
+ 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 = 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),
+ );
+ alias Ascii = typeof(TrieBuilder!(AsciiSpec)(false).build());
+ alias Bmp = typeof(TrieBuilder!(BmpSpec)(false).build());
+ alias Uni = typeof(TrieBuilder!(UniSpec)(false).build());
+
+ auto encode2(dchar ch)
+ {
+ ch -= 0x1_0000;
+ assert(ch <= 0xF_FFFF);
+ wchar[2] ret;
+ //do not put surrogate bits, they are sliced off
+ ret[0] = cast(wchar)(ch >> 10);
+ ret[1] = (ch & 0xFFF);
+ return ret;
+ }
+
+ auto build(Set)(Set set)
+ {
+ 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 otherT = other.byCodepoint.map!(x=>encode2(x)).buildTrie!(UniSpec);
+ alias Ret = Impl!(1,2);
+ return Ret(asciiT, bmpT, otherT);
+ }
+
+ //bootstrap full UTF-16 matcher interace from
+ //sizeFlags, lookupUni and ascii
+ mixin template DefMatcher()
+ {
+ public bool match(Range)(ref Range inp) const pure @trusted
+ if (isRandomAccessRange!Range && is(ElementType!Range : wchar))
+ {
+ enum mode = Mode.skipOnMatch;
+ assert(!inp.empty);
+ immutable ch = inp[0];
+ static if (sizeFlags & 1)
+ {
+ if (ch < 0x80)
+ {
+ if (ascii[ch])
+ {
+ inp.popFront();
+ return true;
+ }
+ else
+ return false;
+ }
+ return lookupUni!mode(inp);
+ }
+ else
+ return lookupUni!mode(inp);
+ }
+
+ static if (Sizes.length == 2)
+ {
+ public bool skip(Range)(ref Range inp) const pure @trusted
+ if (isRandomAccessRange!Range && is(ElementType!Range : wchar))
+ {
+ enum mode = Mode.alwaysSkip;
+ assert(!inp.empty);
+ immutable ch = inp[0];
+ static if (sizeFlags & 1)
+ {
+ if (ch < 0x80)
+ {
+ inp.popFront();
+ return ascii[ch];
+ }
+ else
+ return lookupUni!mode(inp);
+ }
+ else
+ return lookupUni!mode(inp);
+ }
+ }
+
+ public bool test(Range)(ref Range inp) const pure @trusted
+ if (isRandomAccessRange!Range && is(ElementType!Range : wchar))
+ {
+ enum mode = Mode.neverSkip;
+ assert(!inp.empty);
+ auto ch = inp[0];
+ 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)
+ {
+ return fwdStr!"match"(str);
+ }
+
+ bool skip(C)(ref C[] str) const pure @trusted
+ if (isSomeChar!C)
+ {
+ return fwdStr!"skip"(str);
+ }
+
+ bool test(C)(ref C[] str) const pure @trusted
+ if (isSomeChar!C)
+ {
+ return fwdStr!"test"(str);
+ }
+
+ mixin ForwardStrings; //dispatch strings to range versions
+ }
+
+ struct Impl(Sizes...)
+ 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)
+ enum sizeFlags = Sizes[0] | Sizes[1];
+ else
+ enum sizeFlags = Sizes[0];
+
+ static if (sizeFlags & 1)
+ {
+ Ascii ascii;
+ Bmp bmp;
+ }
+ static if (sizeFlags & 2)
+ {
+ Uni uni;
+ }
+ mixin DefMatcher;
+
+ package @property auto subMatcher(SizesToPick...)() @trusted
+ {
+ return CherryPick!(Impl, SizesToPick)(&this);
+ }
+
+ bool lookupUni(Mode mode, Range)(ref Range inp) const pure
+ {
+ wchar x = cast(wchar)(inp[0] - 0xD800);
+ //not a high surrogate
+ if (x > 0x3FF)
+ {
+ //low surrogate
+ if (x <= 0x7FF) badEncoding();
+ static if (sizeFlags & 1)
+ {
+ auto ch = inp[0];
+ static if (mode == Mode.alwaysSkip)
+ inp.popFront();
+ static if (mode == Mode.skipOnMatch)
+ {
+ if (bmp[ch])
+ {
+ inp.popFront();
+ return true;
+ }
+ else
+ return false;
+ }
+ else
+ return bmp[ch];
+ }
+ else //skip is not available for sub-matchers, so just false
+ return false;
+ }
+ else
+ {
+ static if (sizeFlags & 2)
+ {
+ if (inp.length < 2)
+ badEncoding();
+ wchar y = cast(wchar)(inp[1] - 0xDC00);
+ //not a low surrogate
+ if (y > 0x3FF)
+ badEncoding();
+ wchar[2] needle = [inp[0] & 0x3ff, inp[1] & 0x3ff];
+ static if (mode == Mode.alwaysSkip)
+ inp.popFrontN(2);
+ static if (mode == Mode.skipOnMatch)
+ {
+ if (uni[needle])
+ {
+ inp.popFrontN(2);
+ return true;
+ }
+ else
+ return false;
+ }
+ else
+ return uni[needle];
+ }
+ else //ditto
+ return false;
+ }
+ }
+ }
+
+ struct CherryPick(I, Sizes...)
+ if (Sizes.length >= 1 && Sizes.length <= 2)
+ {
+ private:
+ import std.meta : allSatisfy;
+ I* m;
+ enum sizeFlags = I.sizeFlags;
+
+ static if (sizeFlags & 1)
+ {
+ @property ref ascii()() const pure{ return m.ascii; }
+ }
+
+ bool lookupUni(Mode mode, Range)(ref Range inp) const pure
+ {
+ return m.lookupUni!mode(inp);
+ }
+ mixin DefMatcher;
+ static assert(allSatisfy!(validSize, Sizes),
+ "Only lengths of 1 and 2 code units are possible in UTF-16");
+ }
+}
+
+private auto utf8Matcher(Set)(Set set) @trusted
+{
+ return Utf8Matcher!().build(set);
+}
+
+private auto utf16Matcher(Set)(Set set) @trusted
+{
+ return Utf16Matcher!().build(set);
+}
+
+/**
+ Constructs a matcher object
+ to classify $(CODEPOINTS) from the $(D set) for encoding
+ that has $(D Char) as code unit.
+
+ See $(LREF MatcherConcept) for API outline.
+*/
+public auto utfMatcher(Char, Set)(Set set) @trusted
+if (isCodepointSet!Set)
+{
+ static if (is(Char : char))
+ return utf8Matcher(set);
+ else static if (is(Char : wchar))
+ return utf16Matcher(set);
+ else static if (is(Char : dchar))
+ static assert(false, "UTF-32 needs no decoding,
+ and thus not supported by utfMatcher");
+ else
+ static assert(false, "Only character types 'char' and 'wchar' are allowed");
+}
+
+
+//a range of code units, packed with index to speed up forward iteration
+package auto decoder(C)(C[] s, size_t offset=0) @safe pure nothrow @nogc
+if (is(C : wchar) || is(C : char))
+{
+ static struct Decoder
+ {
+ pure nothrow:
+ C[] str;
+ size_t idx;
+ @property C front(){ return str[idx]; }
+ @property C back(){ return str[$-1]; }
+ void popFront(){ idx++; }
+ void popBack(){ str = str[0..$-1]; }
+ void popFrontN(size_t n){ idx += n; }
+ @property bool empty(){ return idx == str.length; }
+ @property auto save(){ return this; }
+ 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); }
+ }
+ static assert(isRandomAccessRange!Decoder);
+ static assert(is(ElementType!Decoder : C));
+ return Decoder(s, offset);
+}
+
+@safe unittest
+{
+ string rs = "hi! ネемног砀 текста";
+ auto codec = rs.decoder;
+ auto utf8 = utf8Matcher(unicode.Letter);
+ auto asc = utf8.subMatcher!(1);
+ auto uni = utf8.subMatcher!(2,3,4);
+ assert(asc.test(codec));
+ assert(!uni.match(codec));
+ assert(utf8.skip(codec));
+ assert(codec.idx == 1);
+
+ assert(!uni.match(codec));
+ assert(asc.test(codec));
+ assert(utf8.skip(codec));
+ assert(codec.idx == 2);
+ assert(!asc.match(codec));
+
+ assert(!utf8.test(codec));
+ assert(!utf8.skip(codec));
+
+ assert(!asc.test(codec));
+ assert(!utf8.test(codec));
+ assert(!utf8.skip(codec));
+ assert(utf8.test(codec));
+ foreach (i; 0 .. 7)
+ {
+ assert(!asc.test(codec));
+ assert(uni.test(codec));
+ assert(utf8.skip(codec));
+ }
+ assert(!utf8.test(codec));
+ assert(!utf8.skip(codec));
+ //the same with match where applicable
+ codec = rs.decoder;
+ assert(utf8.match(codec));
+ assert(codec.idx == 1);
+ assert(utf8.match(codec));
+ assert(codec.idx == 2);
+ assert(!utf8.match(codec));
+ assert(codec.idx == 2);
+ assert(!utf8.skip(codec));
+ assert(!utf8.skip(codec));
+
+ foreach (i; 0 .. 7)
+ {
+ assert(!asc.test(codec));
+ assert(utf8.test(codec));
+ assert(utf8.match(codec));
+ }
+ auto i = codec.idx;
+ assert(!utf8.match(codec));
+ assert(codec.idx == i);
+}
+
+@safe unittest
+{
+ import std.range : stride;
+ static bool testAll(Matcher, Range)(ref Matcher m, ref Range r)
+ {
+ bool t = m.test(r);
+ auto save = r.idx;
+ 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))))
+ {
+ assert(t == m.skip(r));
+ assert(r.idx != save); //always changed
+ r.idx = save;
+ }
+ return t;
+ }
+ auto utf16 = utfMatcher!wchar(unicode.L);
+ auto bmp = utf16.subMatcher!1;
+ auto nonBmp = utf16.subMatcher!1;
+ auto utf8 = utfMatcher!char(unicode.L);
+ auto ascii = utf8.subMatcher!1;
+ auto uni2 = utf8.subMatcher!2;
+ auto uni3 = utf8.subMatcher!3;
+ auto uni24 = utf8.subMatcher!(2,4);
+ foreach (ch; unicode.L.byCodepoint.stride(3))
+ {
+ import std.utf : encode;
+ char[4] buf;
+ wchar[2] buf16;
+ 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);
+
+ assert(testAll(utf8, c8));
+
+ //submatchers return false on out of their domain
+ 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));
+ }
+}
+
+// cover decode fail cases of Matcher
+@system unittest
+{
+ import std.algorithm.iteration : map;
+ import std.exception : collectException;
+ import std.format : format;
+ auto utf16 = utfMatcher!wchar(unicode.L);
+ auto utf8 = utfMatcher!char(unicode.L);
+ //decode failure cases UTF-8
+ 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)
+ {
+ assert(collectException((){
+ auto s = msg;
+ size_t idx = 0;
+ utf8.test(s);
+ }()), format("%( %2x %)", cast(ubyte[]) msg));
+ }
+ //decode failure cases UTF-16
+ alias fails16 = AliasSeq!([0xD811], [0xDC02]);
+ foreach (msg; fails16)
+ {
+ assert(collectException((){
+ auto s = msg.map!(x => cast(wchar) x);
+ utf16.test(s);
+ }()));
+ }
+}
+
+/++
+ Convenience function to construct optimal configurations for
+ packed Trie from any $(D set) of $(CODEPOINTS).
+
+ The parameter $(D level) indicates the number of trie levels to use,
+ allowed values are: 1, 2, 3 or 4. Levels represent different trade-offs
+ speed-size wise.
+
+ $(P Level 1 is fastest and the most memory hungry (a bit array). )
+ $(P Level 4 is the slowest and has the smallest footprint. )
+
+ See the $(S_LINK Synopsis, Synopsis) section for example.
+
+ Note:
+ Level 4 stays very practical (being faster and more predictable)
+ compared to using direct lookup on the $(D set) itself.
+
+
++/
+public auto toTrie(size_t level, Set)(Set set)
+if (isCodepointSet!Set)
+{
+ static if (level == 1)
+ return codepointSetTrie!(21)(set);
+ else static if (level == 2)
+ return codepointSetTrie!(10, 11)(set);
+ else static if (level == 3)
+ return codepointSetTrie!(8, 5, 8)(set);
+ else static if (level == 4)
+ return codepointSetTrie!(6, 4, 4, 7)(set);
+ else
+ static assert(false,
+ "Sorry, toTrie doesn't support levels > 4, use codepointSetTrie directly");
+}
+
+/**
+ $(P Builds a $(D Trie) with typically optimal speed-size trade-off
+ and wraps it into a delegate of the following type:
+ $(D bool delegate(dchar ch)). )
+
+ $(P Effectively this creates a 'tester' lambda suitable
+ for algorithms like std.algorithm.find that take unary predicates. )
+
+ See the $(S_LINK Synopsis, Synopsis) section for example.
+*/
+public auto toDelegate(Set)(Set 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);
+ return (dchar ch) => t[ch];
+}
+
+/**
+ $(P Opaque wrapper around unsigned built-in integers and
+ code unit (char/wchar/dchar) types.
+ Parameter $(D sz) indicates that the value is confined
+ to the range of [0, 2^^sz$(RPAREN). With this knowledge it can be
+ packed more tightly when stored in certain
+ data-structures like trie. )
+
+ Note:
+ $(P The $(D BitPacked!(T, sz)) is implicitly convertible to $(D T)
+ but not vise-versa. Users have to ensure the value fits in
+ the range required and use the $(D cast)
+ operator to perform the conversion.)
+*/
+struct BitPacked(T, size_t sz)
+if (isIntegral!T || is(T:dchar))
+{
+ enum bitSize = sz;
+ T _value;
+ alias _value this;
+}
+
+/*
+ Depending on the form of the passed argument $(D bitSizeOf) returns
+ the amount of bits required to represent a given type
+ or a return type of a given functor.
+*/
+template bitSizeOf(Args...)
+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))
+ {
+ enum bitSizeOf = T.bitSize;
+ }
+ else static if (is(ReturnType!T dummy == BitPacked!(U, bits), U, size_t bits))
+ {
+ enum bitSizeOf = bitSizeOf!(ReturnType!T);
+ }
+ else
+ {
+ enum bitSizeOf = T.sizeof*8;
+ }
+}
+
+/**
+ Tests if $(D T) is some instantiation of $(LREF BitPacked)!(U, x)
+ and thus suitable for packing.
+*/
+template isBitPacked(T)
+{
+ static if (is(T dummy == BitPacked!(U, bits), U, size_t bits))
+ enum isBitPacked = true;
+ else
+ enum isBitPacked = false;
+}
+
+/**
+ Gives the type $(D U) from $(LREF BitPacked)!(U, x)
+ or $(D T) itself for every other type.
+*/
+template TypeOfBitPacked(T)
+{
+ static if (is(T dummy == BitPacked!(U, bits), U, size_t bits))
+ alias TypeOfBitPacked = U;
+ else
+ alias TypeOfBitPacked = T;
+}
+
+/*
+ Wrapper, used in definition of custom data structures from $(D Trie) template.
+ Applying it to a unary lambda function indicates that the returned value always
+ fits within $(D bits) of bits.
+*/
+struct assumeSize(alias Fn, size_t bits)
+{
+ enum bitSize = bits;
+ static auto ref opCall(T)(auto ref T arg)
+ {
+ return Fn(arg);
+ }
+}
+
+/*
+ A helper for defining lambda function that yields a slice
+ of certain bits from an unsigned integral value.
+ The resulting lambda is wrapped in assumeSize and can be used directly
+ with $(D Trie) template.
+*/
+struct sliceBits(size_t from, size_t to)
+{
+ //for now bypass assumeSize, DMD has trouble inlining it
+ enum bitSize = to-from;
+ static auto opCall(T)(T x)
+ out(result)
+ {
+ assert(result < (1 << to-from));
+ }
+ body
+ {
+ static assert(from < to);
+ static if (from == 0)
+ return x & ((1 << to)-1);
+ else
+ return (x >> from) & ((1<<(to-from))-1);
+ }
+}
+
+@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);
+
+static assert(bitSizeOf!lo8 == 8);
+static assert(bitSizeOf!(sliceBits!(4, 7)) == 3);
+static assert(bitSizeOf!(BitPacked!(uint, 2)) == 2);
+
+template Sequence(size_t start, size_t end)
+{
+ static if (start < end)
+ alias Sequence = AliasSeq!(start, Sequence!(start+1, end));
+ else
+ alias Sequence = AliasSeq!();
+}
+
+//---- TRIE TESTS ----
+@system unittest
+{
+ 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 : writefln, writeln;
+ writeln("---TRIE FOOTPRINT STATS---");
+ foreach (i; staticIota!(0, t.table.dim) )
+ {
+ writefln("lvl%s = %s bytes; %s pages"
+ , i, t.bytes!i, t.pages!i);
+ }
+ writefln("TOTAL: %s bytes", t.bytes);
+ 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]);
+ }
+ writeln("---------------------------");
+ }
+ }
+ //@@@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 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++)
+ assert(trie[a]);
+ for (int a='A'; a<'Z';a++)
+ assert(trie[a]);
+ for (int a=0; a<'A'; a++)
+ assert(!trie[a]);
+ for (int a ='Z'; a<'a'; a++)
+ assert(!trie[a]);
+ trieStats(trie);
+
+ auto redundant2 = Set(
+ 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)
+ {
+ assert(trie2[i] == (i in redundant2));
+ }
+
+
+ auto redundant3 = Set(
+ 2, 4, 6, 8, 16,
+ 2+16, 4+16, 16+6, 16+8, 16+16,
+ 2+32, 4+32, 32+6, 32+8,
+ );
+
+ enum max3 = 256;
+ // sliceBits
+ auto trie3 = buildTrie!(bool, uint, max3,
+ 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));
+
+ auto redundant4 = Set(
+ 10, 64, 64+10, 128, 128+10, 256, 256+10, 512,
+ 1000, 2000, 3000, 4000, 5000, 6000
+ );
+ enum max4 = 2^^16;
+ 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));
+ }
+ trieStats(trie4);
+
+ alias mapToS = mapTrieIndex!(useItemAt!(0, char));
+ string[] redundantS = ["tea", "start", "orange"];
+ redundantS.sort!((a,b) => mapToS(a) < mapToS(b))();
+ auto strie = buildTrie!(bool, string, useItemAt!(0, char))(redundantS);
+ // using first char only
+ assert(redundantS == ["orange", "start", "tea"]);
+ assert(strie["test"], text(strie["test"]));
+ assert(!strie["aea"]);
+ assert(strie["s"]);
+
+ // a bit size test
+ 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]);
+}
+
+template useItemAt(size_t idx, T)
+if (isIntegral!T || is(T: dchar))
+{
+ size_t impl(in T[] arr){ return arr[idx]; }
+ alias useItemAt = assumeSize!(impl, 8*T.sizeof);
+}
+
+template useLastItem(T)
+{
+ size_t impl(in T[] arr){ return arr[$-1]; }
+ alias useLastItem = assumeSize!(impl, 8*T.sizeof);
+}
+
+template fullBitSize(Prefix...)
+{
+ static if (Prefix.length > 0)
+ enum fullBitSize = bitSizeOf!(Prefix[0])+fullBitSize!(Prefix[1..$]);
+ else
+ enum fullBitSize = 0;
+}
+
+template idxTypes(Key, size_t fullBits, Prefix...)
+{
+ static if (Prefix.length == 1)
+ {// the last level is value level, so no index once reduced to 1-level
+ alias idxTypes = AliasSeq!();
+ }
+ else
+ {
+ // Important note on bit packing
+ // Each level has to hold enough of bits to address the next one
+ // The bottom level is known to hold full bit width
+ // thus it's size in pages is full_bit_width - size_of_last_prefix
+ // Recourse on this notion
+ alias idxTypes =
+ AliasSeq!(
+ idxTypes!(Key, fullBits - bitSizeOf!(Prefix[$-1]), Prefix[0..$-1]),
+ BitPacked!(typeof(Prefix[$-2](Key.init)), fullBits - bitSizeOf!(Prefix[$-1]))
+ );
+ }
+}
+
+//============================================================================
+
+@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;
+ static bool pred(dchar c) {return !c.isWhite && c != '-' && c != '_';}
+ return cmp(
+ a.map!toLower.filter!pred,
+ b.map!toLower.filter!pred);
+}
+
+@safe pure unittest
+{
+ assert(!comparePropertyName("foo-bar", "fooBar"));
+}
+
+bool propertyNameLess(Char1, Char2)(const(Char1)[] a, const(Char2)[] b) @safe pure
+if (is(Char1 : dchar) && is(Char2 : dchar))
+{
+ return comparePropertyName(a, b) < 0;
+}
+
+//============================================================================
+// Utilities for compression of Unicode code point sets
+//============================================================================
+
+@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))
+ {
+ arr ~= (0b1_00 << 5) | cast(ubyte)(val >> 8);
+ arr ~= val & 0xFF;
+ }
+ else
+ {
+ assert(val < (1 << 21));
+ arr ~= (0b1_01 << 5) | cast(ubyte)(val >> 16);
+ arr ~= (val >> 8) & 0xFF;
+ arr ~= val & 0xFF;
+ }
+}
+
+@safe uint decompressFrom(const(ubyte)[] arr, ref size_t idx) pure
+{
+ import std.exception : enforce;
+ immutable first = arr[idx++];
+ if (!(first & 0x80)) // no top bit -> [0 .. 127]
+ return first;
+ 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];
+ idx += extra;
+ return val;
+}
+
+
+package ubyte[] compressIntervals(Range)(Range intervals)
+if (isInputRange!Range && isIntegralPair!(ElementType!Range))
+{
+ ubyte[] storage;
+ uint base = 0;
+ // RLE encode
+ 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
+ {
+ compressTo(val[1]-base, storage);
+ base = val[1];
+ }
+ }
+ return storage;
+}
+
+@safe pure unittest
+{
+ 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
+ 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));
+ idx = 0;
+ assert(decompressFrom(enc2, idx) == 0);
+ 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.
+@safe package auto decompressIntervals(const(ubyte)[] data) pure
+{
+ return DecompressedIntervals(data);
+}
+
+@safe struct DecompressedIntervals
+{
+pure:
+ const(ubyte)[] _stream;
+ size_t _idx;
+ CodepointInterval _front;
+
+ this(const(ubyte)[] stream)
+ {
+ _stream = stream;
+ popFront();
+ }
+
+ @property CodepointInterval front()
+ {
+ assert(!empty);
+ return _front;
+ }
+
+ void popFront()
+ {
+ 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
+ _front[1] = lastDchar+1;
+ else
+ {
+ base = _front[0];
+ _front[1] = base + decompressFrom(_stream, _idx);
+ }
+ }
+
+ @property bool empty() const
+ {
+ return _idx == size_t.max;
+ }
+
+ @property DecompressedIntervals save() { return this; }
+}
+
+static assert(isInputRange!DecompressedIntervals);
+static assert(isForwardRange!DecompressedIntervals);
+//============================================================================
+
+version (std_uni_bootstrap){}
+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;
+ 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)
+ return idx;
+ return -1;
+}
+
+// another one that loads it
+@trusted bool loadUnicodeSet(alias table, Set, C)(in C[] name, ref Set dest) pure
+{
+ auto idx = findUnicodeSet!table(name);
+ if (idx >= 0)
+ {
+ dest = Set(asSet(table[idx].compressed));
+ return true;
+ }
+ return false;
+}
+
+@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)
+ {
+ target = asSet(uniProps.Lu);
+ target |= asSet(uniProps.Ll);
+ target |= asSet(uniProps.Lt);
+ target |= asSet(uniProps.Lo);
+ target |= asSet(uniProps.Lm);
+ }
+ 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)
+ {
+ target = asSet(uniProps.Mn);
+ target |= asSet(uniProps.Mc);
+ target |= asSet(uniProps.Me);
+ }
+ 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)
+ {
+ target = asSet(uniProps.Pc);
+ target |= asSet(uniProps.Pd);
+ target |= asSet(uniProps.Ps);
+ target |= asSet(uniProps.Pe);
+ target |= asSet(uniProps.Pi);
+ target |= asSet(uniProps.Pf);
+ target |= asSet(uniProps.Po);
+ }
+ 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)
+ {
+ target = asSet(uniProps.Zs);
+ target |= asSet(uniProps.Zl);
+ target |= asSet(uniProps.Zp);
+ }
+ else if (ucmp(name, "C") == 0 || ucmp(name, "Other") == 0)
+ {
+ target = asSet(uniProps.Co);
+ target |= asSet(uniProps.Lo);
+ target |= asSet(uniProps.No);
+ target |= asSet(uniProps.So);
+ target |= asSet(uniProps.Po);
+ }
+ else if (ucmp(name, "graphical") == 0)
+ {
+ target = asSet(uniProps.Alphabetic);
+
+ target |= asSet(uniProps.Mn);
+ target |= asSet(uniProps.Mc);
+ target |= asSet(uniProps.Me);
+
+ target |= asSet(uniProps.Nd);
+ target |= asSet(uniProps.Nl);
+ target |= asSet(uniProps.No);
+
+ target |= asSet(uniProps.Pc);
+ target |= asSet(uniProps.Pd);
+ target |= asSet(uniProps.Ps);
+ target |= asSet(uniProps.Pe);
+ target |= asSet(uniProps.Pi);
+ target |= asSet(uniProps.Pf);
+ target |= asSet(uniProps.Po);
+
+ target |= asSet(uniProps.Zs);
+
+ target |= asSet(uniProps.Sm);
+ target |= asSet(uniProps.Sc);
+ target |= asSet(uniProps.Sk);
+ target |= asSet(uniProps.So);
+ }
+ else if (ucmp(name, "any") == 0)
+ target = Set.fromIntervals(0, 0x110000);
+ else if (ucmp(name, "ascii") == 0)
+ target = Set.fromIntervals(0, 0x80);
+ else
+ return loadUnicodeSet!(uniProps.tab)(name, target);
+ return true;
+}
+
+// CTFE-only helper for checking property names at compile-time
+@safe bool isPrettyPropertyName(C)(in C[] name)
+{
+ import std.algorithm.searching : find;
+ auto names = [
+ "L", "Letter",
+ "LC", "Cased Letter",
+ "M", "Mark",
+ "N", "Number",
+ "P", "Punctuation",
+ "S", "Symbol",
+ "Z", "Separator",
+ "Graphical",
+ "any",
+ "ascii"
+ ];
+ auto x = find!(x => comparePropertyName(x, name) == 0)(names);
+ return !x.empty;
+}
+
+// ditto, CTFE-only, not optimized
+@safe private static bool findSetName(alias table, C)(in C[] name)
+{
+ return findUnicodeSet!table(name) >= 0;
+}
+
+template SetSearcher(alias table, string kind)
+{
+ /// Run-time checked search.
+ static auto opCall(C)(in C[] name)
+ if (is(C : dchar))
+ {
+ import std.conv : to;
+ CodepointSet set;
+ if (loadUnicodeSet!table(name, set))
+ return set;
+ throw new Exception("No unicode set for "~kind~" by name "
+ ~name.to!string()~" was found.");
+ }
+ /// Compile-time checked search.
+ static @property auto opDispatch(string name)()
+ {
+ static if (findSetName!table(name))
+ {
+ CodepointSet set;
+ loadUnicodeSet!table(name, set);
+ return set;
+ }
+ else
+ static assert(false, "No unicode set for "~kind~" by name "
+ ~name~" was found.");
+ }
+}
+
+/**
+ A single entry point to lookup Unicode $(CODEPOINT) sets by name or alias of
+ a block, script or general category.
+
+ It uses well defined standard rules of property name lookup.
+ This includes fuzzy matching of names, so that
+ 'White_Space', 'white-SpAce' and 'whitespace' are all considered equal
+ and yield the same set of white space $(CHARACTERS).
+*/
+@safe public struct unicode
+{
+ /**
+ Performs the lookup of set of $(CODEPOINTS)
+ with compile-time correctness checking.
+ This short-cut version combines 3 searches:
+ across blocks, scripts, and common binary properties.
+
+ Note that since scripts and blocks overlap the
+ usual trick to disambiguate is used - to get a block use
+ $(D unicode.InBlockName), to search a script
+ use $(D unicode.ScriptName).
+
+ See_Also: $(LREF block), $(LREF script)
+ and (not included in this search) $(LREF hangulSyllableType).
+ */
+
+ 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['~']);
+ assert(!ascii['\u00e0']);
+ // matching is case-insensitive
+ assert(ascii == unicode.ascII);
+ assert(!ascii['à']);
+ // underscores, '-' and whitespace in names are ignored too
+ auto latin = unicode.in_latin1_Supplement;
+ assert(latin['à']);
+ assert(!latin['$']);
+ // BTW Latin 1 Supplement is a block, hence "In" prefix
+ assert(latin == unicode("In Latin 1 Supplement"));
+ // run-time look up throws if no such set is found
+ assert(collectException(unicode("InCyrilliac")));
+ }
+
+ /**
+ The same lookup across blocks, scripts, or binary properties,
+ but performed at run-time.
+ This version is provided for cases where $(D name)
+ is not known beforehand; otherwise compile-time
+ checked $(LREF opDispatch) is typically a better choice.
+
+ See the $(S_LINK Unicode properties, table of properties) for available
+ sets.
+ */
+ static auto opCall(C)(in C[] name)
+ if (is(C : dchar))
+ {
+ return loadAny(name);
+ }
+
+ /**
+ Narrows down the search for sets of $(CODEPOINTS) to all Unicode blocks.
+
+ Note:
+ Here block names are unambiguous as no scripts are searched
+ and thus to search use simply $(D unicode.block.BlockName) notation.
+
+ See $(S_LINK Unicode properties, table of properties) for available sets.
+ 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");
+ }
+
+ ///
+ @safe unittest
+ {
+ 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);
+ }
+
+ /**
+ Fetch a set of $(CODEPOINTS) that have the given hangul syllable type.
+
+ Other non-binary properties (once supported) follow the same
+ notation - $(D unicode.propertyName.propertyValue) for compile-time
+ checked access and $(D unicode.propertyName(propertyValue))
+ for run-time checked one.
+
+ 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");
+ }
+
+ ///
+ @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')
+ assert(leadingVowel[vowel]);
+ assert(leadingVowel == unicode.hangulSyllableType.L);
+ }
+
+private:
+ alias ucmp = comparePropertyName;
+
+ 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..$]));
+ }
+
+ 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;
+ 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)
+ return set;
+ throw new Exception("No unicode set by name "~name.to!string()~" was found.");
+ }
+
+ // FIXME: re-disable once the compiler is fixed
+ // Disabled to prevent the mistake of creating instances of this pseudo-struct.
+ //@disable ~this();
+}
+
+@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));
+}
+
+enum EMPTY_CASE_TRIE = ushort.max;// from what gen_uni uses internally
+
+// control - '\r'
+enum controlSwitch = `
+ case '\u0000':..case '\u0008':case '\u000E':..case '\u001F':case '\u007F':..
+ case '\u0084':case '\u0086':..case '\u009F': case '\u0009':..case '\u000C': case '\u0085':
+`;
+// TODO: redo the most of hangul stuff algorithmically in case of Graphemes too
+// kill unrolled switches
+
+private static bool isRegionalIndicator(dchar ch) @safe pure @nogc nothrow
+{
+ return ch >= '\U0001F1E6' && ch <= '\U0001F1FF';
+}
+
+template genericDecodeGrapheme(bool getValue)
+{
+ alias graphemeExtend = graphemeExtendTrie;
+ alias spacingMark = mcTrie;
+ 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,
+ RI,
+ L,
+ V,
+ LVT
+ }
+ static if (getValue)
+ Grapheme grapheme;
+ auto state = GraphemeState.Start;
+ enum eat = q{
+ static if (getValue)
+ grapheme ~= ch;
+ range.popFront();
+ };
+
+ dchar ch;
+ assert(!range.empty, "Attempting to decode grapheme from an empty " ~ Input.stringof);
+ while (!range.empty)
+ {
+ ch = range.front;
+ final switch (state) with(GraphemeState)
+ {
+ case Start:
+ mixin(eat);
+ if (ch == '\r')
+ state = CR;
+ else if (isRegionalIndicator(ch))
+ state = RI;
+ else if (isHangL(ch))
+ state = L;
+ else if (hangLV[ch] || isHangV(ch))
+ state = V;
+ else if (hangLVT[ch])
+ state = LVT;
+ else if (isHangT(ch))
+ state = LVT;
+ else
+ {
+ switch (ch)
+ {
+ mixin(controlSwitch);
+ goto L_End;
+ default:
+ goto L_End_Extend;
+ }
+ }
+ break;
+ case CR:
+ if (ch == '\n')
+ mixin(eat);
+ goto L_End_Extend;
+ case RI:
+ if (isRegionalIndicator(ch))
+ mixin(eat);
+ else
+ goto L_End_Extend;
+ break;
+ case L:
+ if (isHangL(ch))
+ mixin(eat);
+ else if (isHangV(ch) || hangLV[ch])
+ {
+ state = V;
+ mixin(eat);
+ }
+ else if (hangLVT[ch])
+ {
+ state = LVT;
+ mixin(eat);
+ }
+ else
+ goto L_End_Extend;
+ break;
+ case V:
+ if (isHangV(ch))
+ mixin(eat);
+ else if (isHangT(ch))
+ {
+ state = LVT;
+ mixin(eat);
+ }
+ else
+ goto L_End_Extend;
+ break;
+ case LVT:
+ if (isHangT(ch))
+ {
+ mixin(eat);
+ }
+ else
+ goto L_End_Extend;
+ break;
+ }
+ }
+ L_End_Extend:
+ while (!range.empty)
+ {
+ ch = range.front;
+ // extend & spacing marks
+ if (!graphemeExtend[ch] && !spacingMark[ch])
+ break;
+ mixin(eat);
+ }
+ L_End:
+ static if (getValue)
+ return grapheme;
+ }
+
+}
+
+public: // Public API continues
+
+/++
+ Computes the length of grapheme cluster starting at $(D index).
+ Both the resulting length and the $(D index) are measured
+ in $(S_LINK Code unit, code units).
+
+ Params:
+ C = type that is implicitly convertible to $(D dchars)
+ input = array of grapheme clusters
+ index = starting index into $(D input[])
+
+ Returns:
+ length of grapheme cluster
++/
+size_t graphemeStride(C)(in C[] input, size_t index)
+if (is(C : dchar))
+{
+ auto src = input[index..$];
+ auto n = src.length;
+ genericDecodeGrapheme!(false)(src);
+ return n - src.length;
+}
+
+///
+@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[first..$] == "rhus");
+}
+
+/++
+ Reads one full grapheme cluster from an input range of dchar $(D inp).
+
+ For examples see the $(LREF Grapheme) below.
+
+ Note:
+ This function modifies $(D inp) and thus $(D inp)
+ must be an L-value.
++/
+Grapheme decodeGrapheme(Input)(ref Input inp)
+if (isInputRange!Input && is(Unqual!(ElementType!Input) == dchar))
+{
+ return genericDecodeGrapheme!true(inp);
+}
+
+@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 && equal(gr[0 .. 2], " \u0308"));
+ s = "\u0300\u0308\u1100";
+ assert(equal(decodeGrapheme(s)[], "\u0300\u0308"));
+ assert(equal(decodeGrapheme(s)[], "\u1100"));
+ s = "\u11A8\u0308\uAC01";
+ assert(equal(decodeGrapheme(s)[], "\u11A8\u0308"));
+ assert(equal(decodeGrapheme(s)[], "\uAC01"));
+}
+
+/++
+ $(P Iterate a string by grapheme.)
+
+ $(P Useful for doing string manipulation that needs to be aware
+ of graphemes.)
+
+ See_Also:
+ $(LREF byCodePoint)
++/
+auto byGrapheme(Range)(Range range)
+if (isInputRange!Range && is(Unqual!(ElementType!Range) == dchar))
+{
+ // TODO: Bidirectional access
+ static struct Result(R)
+ {
+ private R _range;
+ private Grapheme _front;
+
+ bool empty() @property
+ {
+ return _front.length == 0;
+ }
+
+ Grapheme front() @property
+ {
+ return _front;
+ }
+
+ void popFront()
+ {
+ _front = _range.empty ? Grapheme.init : _range.decodeGrapheme();
+ }
+
+ static if (isForwardRange!R)
+ {
+ Result save() @property
+ {
+ return Result(_range.save, _front);
+ }
+ }
+ }
+
+ auto result = Result!(Range)(range);
+ result.popFront();
+ return result;
+}
+
+///
+@safe unittest
+{
+ import std.algorithm.comparison : equal;
+ import std.range.primitives : walkLength;
+ import std.range : take, drop;
+ auto text = "noe\u0308l"; // noël using e + combining diaeresis
+ assert(text.walkLength == 5); // 5 code points
+
+ auto gText = text.byGrapheme;
+ assert(gText.walkLength == 4); // 4 graphemes
+
+ assert(gText.take(3).equal("noe\u0308".byGrapheme));
+ assert(gText.drop(3).equal("l".byGrapheme));
+}
+
+// For testing non-forward-range input ranges
+version (unittest)
+private static struct InputRangeString
+{
+ private string s;
+
+ bool empty() @property { return s.empty; }
+ dchar front() @property { return s.front; }
+ void popFront() { s.popFront(); }
+}
+
+@system unittest
+{
+ 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";
+ assert(reverse.walkLength == 5);
+
+ auto gReverse = reverse.byGrapheme;
+ assert(gReverse.walkLength == 4);
+
+ foreach (text; AliasSeq!("noe\u0308l"c, "noe\u0308l"w, "noe\u0308l"d))
+ {
+ assert(text.walkLength == 5);
+ static assert(isForwardRange!(typeof(text)));
+
+ auto gText = text.byGrapheme;
+ static assert(isForwardRange!(typeof(gText)));
+ assert(gText.walkLength == 4);
+ assert(gText.array.retro.equal(gReverse));
+ }
+
+ auto nonForwardRange = InputRangeString("noe\u0308l").byGrapheme;
+ static assert(!isForwardRange!(typeof(nonForwardRange)));
+ assert(nonForwardRange.walkLength == 4);
+}
+
+/++
+ $(P Lazily transform a range of $(LREF Grapheme)s to a range of code points.)
+
+ $(P Useful for converting the result to a string after doing operations
+ on graphemes.)
+
+ $(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))
+{
+ // TODO: Propagate bidirectional access
+ static struct Result
+ {
+ private Range _range;
+ private size_t i = 0;
+
+ bool empty() @property
+ {
+ return _range.empty;
+ }
+
+ dchar front() @property
+ {
+ return _range.front[i];
+ }
+
+ void popFront()
+ {
+ ++i;
+
+ if (i >= _range.front.length)
+ {
+ _range.popFront();
+ i = 0;
+ }
+ }
+
+ static if (isForwardRange!Range)
+ {
+ Result save() @property
+ {
+ return Result(_range.save, i);
+ }
+ }
+ }
+
+ return Result(range);
+}
+
+/// Ditto
+Range byCodePoint(Range)(Range range)
+if (isInputRange!Range && is(Unqual!(ElementType!Range) == dchar))
+{
+ return range;
+}
+
+///
+@safe unittest
+{
+ import std.array : array;
+ import std.conv : text;
+ import std.range : retro;
+
+ string s = "noe\u0308l"; // noël
+
+ // reverse it and convert the result to a string
+ string reverse = s.byGrapheme
+ .array
+ .retro
+ .byCodePoint
+ .text;
+
+ assert(reverse == "le\u0308on"); // lëon
+}
+
+@system unittest
+{
+ import std.algorithm.comparison : equal;
+ import std.range.primitives : walkLength;
+ assert("".byGrapheme.byCodePoint.equal(""));
+
+ string text = "noe\u0308l";
+ static assert(is(typeof(text.byCodePoint) == string));
+
+ auto gText = InputRangeString(text).byGrapheme;
+ static assert(!isForwardRange!(typeof(gText)));
+
+ auto cpText = gText.byCodePoint;
+ static assert(!isForwardRange!(typeof(cpText)));
+
+ assert(cpText.walkLength == text.walkLength);
+}
+
+@trusted:
+
+/++
+ $(P A structure designed to effectively pack $(CHARACTERS)
+ of a $(CLUSTER).
+ )
+
+ $(P $(D Grapheme) has value semantics so 2 copies of a $(D Grapheme)
+ always refer to distinct objects. In most actual scenarios a $(D Grapheme)
+ fits on the stack and avoids memory allocation overhead for all but quite
+ long clusters.
+ )
+
+ See_Also: $(LREF decodeGrapheme), $(LREF graphemeStride)
++/
+@trusted struct Grapheme
+{
+ import std.traits : isDynamicArray;
+
+public:
+ /// Ctor
+ this(C)(in C[] chars...)
+ if (is(C : dchar))
+ {
+ this ~= chars;
+ }
+
+ ///ditto
+ this(Input)(Input seq)
+ if (!isDynamicArray!Input
+ && isInputRange!Input && is(ElementType!Input : dchar))
+ {
+ this ~= seq;
+ }
+
+ /// Gets a $(CODEPOINT) at the given index in this cluster.
+ dchar opIndex(size_t index) const pure nothrow @nogc
+ {
+ assert(index < length);
+ return read24(isBig ? ptr_ : small_.ptr, index);
+ }
+
+ /++
+ Writes a $(CODEPOINT) $(D ch) at given index in this cluster.
+
+ 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);
+ }
+
+ ///
+ @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);
+ }
+
+ /++
+ Random-access range over Grapheme's $(CHARACTERS).
+
+ Warning: Invalidates when this Grapheme leaves the scope,
+ attempts to use it then would lead to memory corruption.
+ +/
+ @system SliceOverIndexed!Grapheme opSlice(size_t a, size_t b) pure nothrow @nogc
+ {
+ return sliceOverIndexed(a, b, &this);
+ }
+
+ /// ditto
+ @system SliceOverIndexed!Grapheme opSlice() pure nothrow @nogc
+ {
+ return sliceOverIndexed(0, length, &this);
+ }
+
+ /// Grapheme cluster length in $(CODEPOINTS).
+ @property size_t length() const pure nothrow @nogc
+ {
+ return isBig ? len_ : slen_ & 0x7F;
+ }
+
+ /++
+ Append $(CHARACTER) $(D ch) to this grapheme.
+ Warning:
+ Use of this facility may invalidate grapheme cluster,
+ see also $(D valid).
+
+ See_Also: $(LREF Grapheme.valid)
+ +/
+ ref opOpAssign(string op)(dchar ch)
+ {
+ static if (op == "~")
+ {
+ if (!isBig)
+ {
+ if (slen_ == small_cap)
+ convertToBig();// & fallthrough to "big" branch
+ else
+ {
+ write24(small_.ptr, ch, smallLength);
+ slen_++;
+ return this;
+ }
+ }
+
+ assert(isBig);
+ if (len_ == cap_)
+ {
+ 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*) pureRealloc(ptr_, nelems);
+ if (ptr_ is null) onOutOfMemoryError();
+ }
+ write24(ptr_, ch, len_++);
+ return this;
+ }
+ else
+ 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))
+ {
+ static if (op == "~")
+ {
+ foreach (dchar ch; inp)
+ this ~= ch;
+ return this;
+ }
+ else
+ static assert(false, "No operation "~op~" defined for Grapheme");
+ }
+
+ /++
+ True if this object contains valid extended grapheme cluster.
+ Decoding primitives of this module always return a valid $(D Grapheme).
+
+ Appending to and direct manipulation of grapheme's $(CHARACTERS) may
+ render it no longer valid. Certain applications may chose to use
+ Grapheme as a "small string" of any $(CODEPOINTS) and ignore this property
+ entirely.
+ +/
+ @property bool valid()() /*const*/
+ {
+ auto r = this[];
+ genericDecodeGrapheme!false(r);
+ return r.length == 0;
+ }
+
+ this(this) pure @nogc nothrow
+ {
+ if (isBig)
+ {// dup it
+ 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*) pureMalloc(raw_cap);
+ if (p is null) onOutOfMemoryError();
+ p[0 .. raw_cap] = ptr_[0 .. raw_cap];
+ ptr_ = p;
+ }
+ }
+
+ ~this() pure @nogc nothrow
+ {
+ if (isBig)
+ {
+ pureFree(ptr_);
+ }
+ }
+
+
+private:
+ enum small_bytes = ((ubyte*).sizeof+3*size_t.sizeof-1);
+ // "out of the blue" grow rate, needs testing
+ // (though graphemes are typically small < 9)
+ enum grow = 20;
+ enum small_cap = small_bytes/3;
+ enum small_flag = 0x80, small_mask = 0x7F;
+ // 16 bytes in 32bits, should be enough for the majority of cases
+ union
+ {
+ struct
+ {
+ ubyte* ptr_;
+ size_t cap_;
+ size_t len_;
+ size_t padding_;
+ }
+ struct
+ {
+ ubyte[small_bytes] small_;
+ ubyte slen_;
+ }
+ }
+
+ void convertToBig() pure @nogc nothrow
+ {
+ static assert(grow.max / 3 - 1 >= grow);
+ enum nbytes = 3 * (grow + 1);
+ size_t k = smallLength;
+ ubyte* p = cast(ubyte*) pureMalloc(nbytes);
+ if (p is null) onOutOfMemoryError();
+ for (int i=0; i<k; i++)
+ write24(p, read24(small_.ptr, i), i);
+ // now we can overwrite small array data
+ ptr_ = p;
+ len_ = slen_;
+ assert(grow > len_);
+ cap_ = grow;
+ setBig();
+ }
+
+ void setBig() pure nothrow @nogc { slen_ |= small_flag; }
+
+ @property size_t smallLength() const pure nothrow @nogc
+ {
+ return slen_ & small_mask;
+ }
+ @property ubyte isBig() const pure nothrow @nogc
+ {
+ return slen_ & small_flag;
+ }
+}
+
+static assert(Grapheme.sizeof == size_t.sizeof*4);
+
+
+@system pure /*nothrow @nogc*/ unittest // TODO: string .front is GC and throw
+{
+ import std.algorithm.comparison : equal;
+ Grapheme[3] data = [Grapheme("Ю"), Grapheme("У"), Grapheme("З")];
+ assert(byGrapheme("ЮУЗ").equal(data[]));
+}
+
+///
+@system unittest
+{
+ 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
+ auto first = decodeGrapheme(bold);
+
+ assert(first.length == 1);
+ assert(first[0] == 'k');
+
+ // the next grapheme is 2 characters long
+ auto wideOne = decodeGrapheme(bold);
+ // slicing a grapheme yields a random-access range of dchar
+ 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().equal("\u0308"));
+
+ 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"));
+}
+
+@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);
+}
+
+@system unittest
+{
+ 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');
+ assert(g[0] == 'a');
+ assert(g[1] == 'b');
+ assert(g[2] == 'c');
+ assert(g[3] == 'd');
+ assert(g[4] == 'e');
+ g[3] = 'Й';
+ assert(g[2] == 'c');
+ assert(g[3] == 'Й', text(g[3], " vs ", 'Й'));
+ assert(g[4] == 'e');
+ assert(!g.valid);
+
+ g ~= 'ц';
+ g ~= '~';
+ assert(g[0] == 'a');
+ assert(g[1] == 'b');
+ assert(g[2] == 'c');
+ assert(g[3] == 'Й');
+ assert(g[4] == 'e');
+ assert(g[5] == 'ц');
+ assert(g[6] == '~');
+ assert(!g.valid);
+
+ Grapheme copy = g;
+ copy[0] = 'X';
+ copy[1] = '-';
+ assert(g[0] == 'a' && copy[0] == 'X');
+ assert(g[1] == 'b' && copy[1] == '-');
+ assert(equal(g[2 .. g.length], copy[2 .. copy.length]));
+ copy = Grapheme("АБВГДЕЁЖЗИКЛМ");
+ assert(equal(copy[0 .. 8], "АБВГДЕЁЖ"), text(copy[0 .. 8]));
+ copy ~= "xyz";
+ 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"())
+ h ~= v;
+ assert(equal(h[], iota(cast(int)'A', cast(int)'Z'+1)));
+}
+
+/++
+ $(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:
+ r1 = an input range of characters
+ r2 = an input range of characters
+
+ Returns:
+ An $(D int) that is 0 if the strings match,
+ &lt;0 if $(D r1) is lexicographically "less" than $(D r2),
+ &gt;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.
+
+ See_Also:
+ $(LREF icmp)
+ $(REF cmp, std,algorithm,comparison)
++/
+int sicmp(S1, S2)(S1 r1, S2 r2)
+if (isInputRange!S1 && isSomeChar!(ElementEncodingType!S1)
+ && isInputRange!S2 && isSomeChar!(ElementEncodingType!S2))
+{
+ import std.internal.unicode_tables : sTable = simpleCaseTable; // generated file
+ import std.utf : byDchar;
+
+ auto str1 = r1.byDchar;
+ auto str2 = r2.byDchar;
+
+ foreach (immutable lhs; str1)
+ {
+ if (str2.empty)
+ return 1;
+ immutable rhs = str2.front;
+ str2.popFront();
+ int diff = lhs - rhs;
+ if (!diff)
+ continue;
+ size_t idx = simpleCaseTrie[lhs];
+ size_t idx2 = simpleCaseTrie[rhs];
+ // simpleCaseTrie is packed index table
+ if (idx != 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
+ continue;
+ else// not the same bucket
+ diff = sTable[idx].ch - sTable[idx2].ch;
+ }
+ else
+ diff = sTable[idx - sTable[idx].n].ch - rhs;
+ }
+ 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 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 @nogc pure nothrow
+{
+ int sicmp(const(char)[] str1, const(char)[] str2)
+ { return sicmp!(const(char)[], const(char)[])(str1, str2); }
+ int sicmp(const(wchar)[] str1, const(wchar)[] str2)
+ { return sicmp!(const(wchar)[], const(wchar)[])(str1, str2); }
+ int sicmp(const(dchar)[] str1, const(dchar)[] str2)
+ { return sicmp!(const(dchar)[], const(dchar)[])(str1, str2); }
+}
+
+private int fullCasedCmp(Range)(dchar lhs, dchar rhs, ref Range rtail)
+{
+ 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)
+ return lhs;
+ immutable start = idx - fTable[idx].n;
+ immutable end = fTable[idx].size + start;
+ assert(fTable[start].entry_len == 1);
+ for (idx=start; idx<end; idx++)
+ {
+ auto entryLen = fTable[idx].entry_len;
+ if (entryLen == 1)
+ {
+ 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]
+ && rtail.skipOver(seq[1..$]))
+ {
+ // note that this path modifies rtail
+ // iff we managed to get there
+ return 0;
+ }
+ }
+ }
+ return fTable[start].seq[0]; // new remapped character for accurate diffs
+}
+
+/++
+ 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 `icmp` being pedantically correct is
+ slightly worse performance.
+
+ Params:
+ r1 = a forward range of characters
+ r2 = a forward range of characters
+
+ Returns:
+ An $(D int) that is 0 if the strings match,
+ &lt;0 if $(D str1) is lexicographically "less" than $(D str2),
+ &gt;0 if $(D str1) is lexicographically "greater" than $(D str2)
+
+ See_Also:
+ $(LREF sicmp)
+ $(REF cmp, std,algorithm,comparison)
++/
+int icmp(S1, S2)(S1 r1, S2 r2)
+if (isForwardRange!S1 && isSomeChar!(ElementEncodingType!S1)
+ && isForwardRange!S2 && isSomeChar!(ElementEncodingType!S2))
+{
+ import std.utf : byDchar;
+
+ auto str1 = r1.byDchar;
+ auto str2 = r2.byDchar;
+
+ for (;;)
+ {
+ if (str1.empty)
+ return str2.empty ? 0 : -1;
+ immutable lhs = str1.front;
+ if (str2.empty)
+ return 1;
+ immutable rhs = str2.front;
+ str1.popFront();
+ str2.popFront();
+ if (!(lhs - rhs))
+ continue;
+ // first try to match lhs to <rhs,right-tail> sequence
+ immutable cmpLR = fullCasedCmp(lhs, rhs, str2);
+ if (!cmpLR)
+ continue;
+ // then rhs to <lhs,left-tail> sequence
+ immutable cmpRL = fullCasedCmp(rhs, lhs, str1);
+ if (!cmpRL)
+ continue;
+ // cmpXX contain remapped codepoints
+ // to obtain stable ordering of icmp
+ 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 @nogc pure nothrow
+{
+ int icmp(const(char)[] str1, const(char)[] str2)
+ { return icmp!(const(char)[], const(char)[])(str1, str2); }
+ int icmp(const(wchar)[] str1, const(wchar)[] str2)
+ { return icmp!(const(wchar)[], const(wchar)[])(str1, str2); }
+ int icmp(const(dchar)[] str1, const(dchar)[] str2)
+ { return icmp!(const(dchar)[], const(dchar)[])(str1, str2); }
+}
+
+@safe unittest
+{
+ import std.algorithm.sorting : sort;
+ import std.conv : to;
+ import std.exception : assertCTFEable;
+ assertCTFEable!(
+ {
+ foreach (cfunc; AliasSeq!(icmp, sicmp))
+ {
+ 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);
+ assert(cfunc("".to!S1(), "0".to!S2()) < 0);
+ assert(cfunc("abc".to!S1(), "abc".to!S2()) == 0);
+ assert(cfunc("abcd".to!S1(), "abc".to!S2()) > 0);
+ assert(cfunc("abc".to!S1(), "abcd".to!S2()) < 0);
+ assert(cfunc("Abc".to!S1(), "aBc".to!S2()) == 0);
+ assert(cfunc("авГуст".to!S1(), "АВгУСТ".to!S2()) == 0);
+ // Check example:
+ assert(cfunc("Август".to!S1(), "авгусТ".to!S2()) == 0);
+ assert(cfunc("ΌΎ".to!S1(), "όύ".to!S2()) == 0);
+ }();
+ // check that the order is properly agnostic to the case
+ auto strs = [ "Apple", "ORANGE", "orAcle", "amp", "banana"];
+ sort!((a,b) => cfunc(a,b) < 0)(strs);
+ assert(strs == ["amp", "Apple", "banana", "orAcle", "ORANGE"]);
+ }
+ assert(icmp("ßb", "ssa") > 0);
+ // Check example:
+ assert(icmp("Russland", "Rußland") == 0);
+ assert(icmp("ᾩ -> \u1F70\u03B9", "\u1F61\u03B9 -> ᾲ") == 0);
+ assert(icmp("ΐ"w, "\u03B9\u0308\u0301") == 0);
+ assert(sicmp("ΐ", "\u03B9\u0308\u0301") != 0);
+ //bugzilla 11057
+ assert( icmp("K", "L") < 0 );
+ });
+}
+
+// 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) @safe
+{
+ import std.internal.unicode_tables : simpleCaseTable; // generated file
+ alias sTable = simpleCaseTable;
+ static struct Range
+ {
+ @safe pure nothrow:
+ uint idx; //if == uint.max, then read c.
+ union
+ {
+ dchar c; // == 0 - empty range
+ uint len;
+ }
+ @property bool isSmall() const { return idx == uint.max; }
+
+ this(dchar ch)
+ {
+ idx = uint.max;
+ c = ch;
+ }
+
+ this(uint start, uint size)
+ {
+ idx = start;
+ len = size;
+ }
+
+ @property dchar front() const
+ {
+ assert(!empty);
+ if (isSmall)
+ {
+ return c;
+ }
+ auto ch = sTable[idx].ch;
+ return ch;
+ }
+
+ @property bool empty() const
+ {
+ if (isSmall)
+ {
+ return c == 0;
+ }
+ return len == 0;
+ }
+
+ @property size_t length() const
+ {
+ if (isSmall)
+ {
+ return c == 0 ? 0 : 1;
+ }
+ return len;
+ }
+
+ void popFront()
+ {
+ if (isSmall)
+ c = 0;
+ else
+ {
+ idx++;
+ len--;
+ }
+ }
+ }
+ immutable idx = simpleCaseTrie[ch];
+ if (idx == EMPTY_CASE_TRIE)
+ return Range(ch);
+ auto entry = sTable[idx];
+ immutable start = idx - entry.n;
+ return Range(start, entry.size);
+}
+
+@system unittest
+{
+ import std.algorithm.comparison : equal;
+ import std.algorithm.searching : canFind;
+ import std.array : array;
+ import std.exception : assertCTFEable;
+ assertCTFEable!((){
+ auto r = simpleCaseFoldings('Э').array;
+ assert(r.length == 2);
+ assert(r.canFind('э') && r.canFind('Э'));
+ auto sr = simpleCaseFoldings('~');
+ assert(sr.equal("~"));
+ //A with ring above - casefolds to the same bucket as Angstrom sign
+ sr = simpleCaseFoldings('Å');
+ assert(sr.length == 3);
+ assert(sr.canFind('å') && sr.canFind('Å') && sr.canFind('\u212B'));
+ });
+}
+
+/++
+ $(P Returns the $(S_LINK Combining class, combining class) of $(D ch).)
++/
+ubyte combiningClass(dchar ch) @safe pure nothrow @nogc
+{
+ return combiningClassTrie[ch];
+}
+
+///
+@safe unittest
+{
+ // shorten the code
+ alias CC = combiningClass;
+
+ // combining tilda
+ assert(CC('\u0303') == 230);
+ // combining ring below
+ assert(CC('\u0325') == 220);
+ // the simple consequence is that "tilda" should be
+ // placed after a "ring below" in a sequence
+}
+
+@safe pure nothrow @nogc unittest
+{
+ foreach (ch; 0 .. 0x80)
+ assert(combiningClass(ch) == 0);
+ assert(combiningClass('\u05BD') == 22);
+ assert(combiningClass('\u0300') == 230);
+ assert(combiningClass('\u0317') == 220);
+ assert(combiningClass('\u1939') == 222);
+}
+
+/// Unicode character decomposition type.
+enum UnicodeDecomposition {
+ /// Canonical decomposition. The result is canonically equivalent sequence.
+ Canonical,
+ /**
+ Compatibility decomposition. The result is compatibility equivalent sequence.
+ Note: Compatibility decomposition is a $(B lossy) conversion,
+ typically suitable only for fuzzy matching and internal processing.
+ */
+ Compatibility
+}
+
+/**
+ Shorthand aliases for character decomposition type, passed as a
+ template parameter to $(LREF decompose).
+*/
+enum {
+ Canonical = UnicodeDecomposition.Canonical,
+ Compatibility = UnicodeDecomposition.Compatibility
+}
+
+/++
+ Try to canonically compose 2 $(CHARACTERS).
+ Returns the composed $(CHARACTER) if they do compose and dchar.init otherwise.
+
+ The assumption is that $(D first) comes before $(D second) in the original text,
+ usually meaning that the first is a starter.
+
+ Note: Hangul syllables are not covered by this function.
+ See $(D composeJamo) below.
++/
+public dchar compose(dchar first, dchar second) pure nothrow @safe
+{
+ import std.algorithm.iteration : map;
+ import std.internal.unicode_comp : compositionTable, composeCntShift, composeIdxMask;
+ import std.range : assumeSorted;
+ immutable packed = compositionJumpTrie[first];
+ if (packed == ushort.max)
+ return dchar.init;
+ // unpack offset and length
+ 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();
+ immutable target = r.lowerBound(second).length;
+ if (target == cnt)
+ return dchar.init;
+ 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)
+ decomposition of $(CHARACTER) $(D ch).
+ If no decomposition is available returns a $(LREF Grapheme)
+ with the $(D ch) itself.
+
+ Note:
+ This function also decomposes hangul syllables
+ as prescribed by the standard.
+
+ See_Also: $(LREF decomposeHangul) for a restricted version
+ that takes into account only hangul syllables but
+ no other decompositions.
++/
+public Grapheme decompose(UnicodeDecomposition decompType=Canonical)(dchar ch) @safe
+{
+ 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)
+ {
+ alias table = decompCompatTable;
+ alias mapping = compatMappingTrie;
+ }
+ immutable idx = mapping[ch];
+ if (!idx) // not found, check hangul arithmetic decomposition
+ return decomposeHangul(ch);
+ auto decomp = table[idx..$].until(0);
+ return Grapheme(decomp);
+}
+
+///
+@system unittest
+{
+ import std.algorithm.comparison : equal;
+
+ 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);
+
+ assert(decompose('Ĉ')[].equal("C\u0302"));
+ assert(decompose('D')[].equal("D"));
+ assert(decompose('\uD4DC')[].equal("\u1111\u1171\u11B7"));
+ assert(decompose!Compatibility('¹')[].equal("1"));
+}
+
+//----------------------------------------------------------------------------
+// Hangul specific composition/decomposition
+enum jamoSBase = 0xAC00;
+enum jamoLBase = 0x1100;
+enum jamoVBase = 0x1161;
+enum jamoTBase = 0x11A7;
+enum jamoLCount = 19, jamoVCount = 21, jamoTCount = 28;
+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 @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 @safe
+{
+ // first cmp rejects ~ 1M code points above trailing jamo range
+ // Note: ch == jamoTBase doesn't indicate trailing jamo (TIndex must be > 0)
+ return ch < jamoTBase+jamoTCount && ch > jamoTBase;
+}
+
+// Tests if $(D ch) is a Hangul trailnig consonant jamo.
+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 @safe
+{
+ 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 @safe
+{
+ for (size_t idx = 0; idx + 1 < seq.length; )
+ {
+ if (isJamoL(seq[idx]) && isJamoV(seq[idx+1]))
+ {
+ 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;
+ seq[idx+2] = dchar.init;
+ idx += 3;
+ }
+ else
+ {
+ seq[idx] = jamoSBase + indexLV;
+ seq[idx+1] = dchar.init;
+ idx += 2;
+ }
+ }
+ else
+ idx++;
+ }
+}
+
+//----------------------------------------------------------------------------
+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) @safe
+{
+ 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;
+
+ 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);
+}
+
+///
+@system unittest
+{
+ import std.algorithm.comparison : equal;
+ assert(decomposeHangul('\uD4DB')[].equal("\u1111\u1171\u11B6"));
+}
+
+/++
+ Try to compose hangul syllable out of a leading consonant ($(D lead)),
+ a $(D vowel) and optional $(D trailing) consonant jamos.
+
+ On success returns the composed LV or LVT hangul syllable.
+
+ 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 @safe
+{
+ if (!isJamoL(lead))
+ return dchar.init;
+ immutable indexL = lead - jamoLBase;
+ if (!isJamoV(vowel))
+ return dchar.init;
+ immutable indexV = vowel - jamoVBase;
+ immutable indexLV = indexL * jamoNCount + indexV * jamoTCount;
+ immutable dchar syllable = jamoSBase + indexLV;
+ return isJamoT(trailing) ? syllable + (trailing - jamoTBase) : syllable;
+}
+
+///
+@safe unittest
+{
+ assert(composeJamo('\u1111', '\u1171', '\u11B6') == '\uD4DB');
+ // leaving out T-vowel, or passing any codepoint
+ // that is not trailing consonant composes an LV-syllable
+ assert(composeJamo('\u1111', '\u1171') == '\uD4CC');
+ assert(composeJamo('\u1111', '\u1171', ' ') == '\uD4CC');
+ assert(composeJamo('\u1111', 'A') == dchar.init);
+ assert(composeJamo('A', '\u1171') == dchar.init);
+}
+
+@system unittest
+{
+ import std.algorithm.comparison : equal;
+ import std.conv : text;
+
+ static void testDecomp(UnicodeDecomposition T)(dchar ch, string r)
+ {
+ Grapheme g = decompose!T(ch);
+ assert(equal(g[], r), text(g[], " vs ", r));
+ }
+ testDecomp!Canonical('\u1FF4', "\u03C9\u0301\u0345");
+ testDecomp!Canonical('\uF907', "\u9F9C");
+ testDecomp!Compatibility('\u33FF', "\u0067\u0061\u006C");
+ testDecomp!Compatibility('\uA7F9', "\u0153");
+
+ // check examples
+ 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');
+ assert(composeJamo('\u1111', 'A') == dchar.init);
+ assert(composeJamo('A', '\u1171') == dchar.init);
+}
+
+/**
+ Enumeration type for normalization forms,
+ passed as template parameter for functions like $(LREF normalize).
+*/
+enum NormalizationForm {
+ NFC,
+ NFD,
+ NFKC,
+ NFKD
+}
+
+
+enum {
+ /**
+ Shorthand aliases from values indicating normalization forms.
+ */
+ NFC = NormalizationForm.NFC,
+ ///ditto
+ NFD = NormalizationForm.NFD,
+ ///ditto
+ NFKC = NormalizationForm.NFKC,
+ ///ditto
+ NFKD = NormalizationForm.NFKD
+}
+
+/++
+ Returns $(D input) string normalized to the chosen form.
+ Form C is used by default.
+
+ For more information on normalization forms see
+ the $(S_LINK Normalization, normalization section).
+
+ Note:
+ In cases where the string in question is already normalized,
+ it is returned unmodified and no memory allocation happens.
++/
+inout(C)[] normalize(NormalizationForm norm=NFC, C)(inout(C)[] input)
+{
+ 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)
+ return input;
+ dchar[] decomposed;
+ decomposed.reserve(31);
+ ubyte[] ccc;
+ ccc.reserve(31);
+ 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)
+ {
+ foreach (dchar c; decompose!Canonical(ch)[])
+ decomposed ~= c;
+ }
+ else // NFKD & NFKC
+ {
+ foreach (dchar c; decompose!Compatibility(ch)[])
+ decomposed ~= c;
+ }
+ ccc.length = decomposed.length;
+ size_t firstNonStable = 0;
+ ubyte lastClazz = 0;
+
+ foreach (idx, dchar ch; decomposed)
+ {
+ immutable clazz = combiningClass(ch);
+ ccc[idx] = clazz;
+ 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]));
+ firstNonStable = decomposed.length;
+ }
+ else if (clazz != 0 && lastClazz == 0)
+ {
+ // found first unstable code point after stable ones
+ firstNonStable = idx;
+ }
+ lastClazz = clazz;
+ }
+ sort!("a[0] < b[0]", SwapStrategy.stable)
+ (zip(ccc[firstNonStable..$], decomposed[firstNonStable..$]));
+ static if (norm == NFC || norm == NFKC)
+ {
+ import std.algorithm.searching : countUntil;
+ auto first = countUntil(ccc, 0);
+ if (first >= 0) // no starters?? no recomposition
+ {
+ for (;;)
+ {
+ immutable second = recompose(first, decomposed, ccc);
+ if (second == decomposed.length)
+ break;
+ first = second;
+ }
+ // 2nd pass for hangul syllables
+ hangulRecompose(decomposed);
+ }
+ }
+ static if (norm == NFD || norm == NFKD)
+ app.put(decomposed);
+ else
+ {
+ import std.algorithm.mutation : remove;
+ auto clean = remove!("a == dchar.init", SwapStrategy.stable)(decomposed);
+ app.put(decomposed[0 .. clean.length]);
+ }
+ // reset variables
+ decomposed.length = 0;
+ decomposed.assumeSafeAppend();
+ ccc.length = 0;
+ ccc.assumeSafeAppend();
+ input = input[anchors[1]..$];
+ // and move on
+ anchors = splitNormalized!norm(input);
+ }while (anchors[0] != input.length);
+ app.put(input[0 .. anchors[0]]);
+ return cast(inout(C)[])app.data;
+}
+
+///
+@safe unittest
+{
+ // any encoding works
+ wstring greet = "Hello world";
+ assert(normalize(greet) is greet); // the same exact slice
+
+ // An example of a character with all 4 forms being different:
+ // Greek upsilon with acute and hook symbol (code point 0x03D3)
+ assert(normalize!NFC("ϓ") == "\u03D3");
+ assert(normalize!NFD("ϓ") == "\u03D2\u0301");
+ assert(normalize!NFKC("ϓ") == "\u038E");
+ assert(normalize!NFKD("ϓ") == "\u03A5\u0301");
+}
+
+@safe unittest
+{
+ import std.conv : text;
+
+ assert(normalize!NFD("abc\uF904def") == "abc\u6ED1def", text(normalize!NFD("abc\uF904def")));
+ assert(normalize!NFKD("2¹⁰") == "210", normalize!NFKD("2¹⁰"));
+ assert(normalize!NFD("Äffin") == "A\u0308ffin");
+
+ // check example
+
+ // any encoding works
+ wstring greet = "Hello world";
+ assert(normalize(greet) is greet); // the same exact slice
+
+ // An example of a character with all 4 forms being different:
+ // Greek upsilon with acute and hook symbol (code point 0x03D3)
+ assert(normalize!NFC("ϓ") == "\u03D3");
+ assert(normalize!NFD("ϓ") == "\u03D2\u0301");
+ assert(normalize!NFKC("ϓ") == "\u038E");
+ assert(normalize!NFKD("ϓ") == "\u03A5\u0301");
+}
+
+// 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 @safe
+{
+ assert(input.length == ccc.length);
+ 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 (; ; )
+ {
+ if (i == input.length)
+ break;
+ 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
+ // is a starter or it has the same or higher combining class as C.
+ //------------------------
+ // Applying to our case:
+ // S is input[0]
+ // accumCC is the maximum CCC of characters between C and S,
+ // as ccc are sorted
+ // C is input[i]
+
+ if (curCC > accumCC)
+ {
+ 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
+ {
+ // if it was a starter then accumCC is now 0, end of loop
+ accumCC = curCC;
+ if (accumCC == 0)
+ break;
+ }
+ }
+ else
+ {
+ // ditto here
+ accumCC = curCC;
+ if (accumCC == 0)
+ break;
+ }
+ i++;
+ }
+ return i;
+}
+
+// returns tuple of 2 indexes that delimit:
+// normalized text, piece that needs normalization and
+// the rest of input starting with stable code point
+private auto splitNormalized(NormalizationForm norm, C)(const(C)[] input)
+{
+ import std.typecons : tuple;
+ ubyte lastCC = 0;
+
+ foreach (idx, dchar ch; input)
+ {
+ static if (norm == NFC)
+ if (ch < 0x0300)
+ {
+ lastCC = 0;
+ continue;
+ }
+ immutable ubyte CC = combiningClass(ch);
+ if (lastCC > CC && CC != 0)
+ {
+ return seekStable!norm(idx, input);
+ }
+
+ if (notAllowedIn!norm(ch))
+ {
+ return seekStable!norm(idx, input);
+ }
+ lastCC = CC;
+ }
+ return tuple(input.length, input.length);
+}
+
+private auto seekStable(NormalizationForm norm, C)(size_t idx, in C[] input)
+{
+ import std.typecons : tuple;
+ import std.utf : codeLength;
+
+ auto br = input[0 .. idx];
+ size_t region_start = 0;// default
+ for (;;)
+ {
+ if (br.empty)// start is 0
+ break;
+ dchar ch = br.back;
+ if (combiningClass(ch) == 0 && allowedIn!norm(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..$])
+ {
+ if (combiningClass(ch) == 0 && allowedIn!norm(ch))
+ {
+ region_end = i+idx;
+ break;
+ }
+ }
+ // writeln("Region to normalize: ", input[region_start .. region_end]);
+ return tuple(region_start, region_end);
+}
+
+/**
+ Tests if dchar $(D ch) is always allowed (Quick_Check=YES) in normalization
+ form $(D norm).
+*/
+public bool allowedIn(NormalizationForm norm)(dchar ch)
+{
+ return !notAllowedIn!norm(ch);
+}
+
+///
+@safe unittest
+{
+ // e.g. Cyrillic is always allowed, so is ASCII
+ assert(allowedIn!NFC('я'));
+ assert(allowedIn!NFD('я'));
+ assert(allowedIn!NFKC('я'));
+ assert(allowedIn!NFKD('я'));
+ assert(allowedIn!NFC('Z'));
+}
+
+// not user friendly name but more direct
+private bool notAllowedIn(NormalizationForm norm)(dchar ch)
+{
+ static if (norm == NFC)
+ alias qcTrie = nfcQCTrie;
+ else static if (norm == NFD)
+ alias qcTrie = nfdQCTrie;
+ else static if (norm == NFKC)
+ alias qcTrie = nfkcQCTrie;
+ else static if (norm == NFKD)
+ alias qcTrie = nfkdQCTrie;
+ else
+ static assert("Unknown normalization form "~norm);
+ return qcTrie[ch];
+}
+
+@safe unittest
+{
+ assert(allowedIn!NFC('я'));
+ assert(allowedIn!NFD('я'));
+ assert(allowedIn!NFKC('я'));
+ assert(allowedIn!NFKD('я'));
+ assert(allowedIn!NFC('Z'));
+}
+
+}
+
+version (std_uni_bootstrap)
+{
+ // old version used for bootstrapping of gen_uni.d that generates
+ // up to date optimal versions of all of isXXX functions
+ @safe pure nothrow @nogc public bool isWhite(dchar c)
+ {
+ import std.ascii : isWhite;
+ return isWhite(c) ||
+ c == lineSep || c == paraSep ||
+ c == '\u0085' || c == '\u00A0' || c == '\u1680' || c == '\u180E' ||
+ (c >= '\u2000' && c <= '\u200A') ||
+ c == '\u202F' || c == '\u205F' || c == '\u3000';
+ }
+}
+else
+{
+
+// trusted -> avoid bounds check
+@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]; }
+ dchar toLowerTab(size_t idx) { return toLowerTable[idx]; }
+
+ ushort toTitleIndex(dchar c) { return toTitleIndexTrie[c]; }
+ ushort toTitleSimpleIndex(dchar c) { return toTitleSimpleIndexTrie[c]; }
+ dchar toTitleTab(size_t idx) { return toTitleTable[idx]; }
+
+ ushort toUpperIndex(dchar c) { return toUpperIndexTrie[c]; }
+ ushort toUpperSimpleIndex(dchar c) { return toUpperSimpleIndexTrie[c]; }
+ dchar toUpperTab(size_t idx) { return toUpperTable[idx]; }
+}
+
+public:
+
+/++
+ Whether or not $(D c) is a Unicode whitespace $(CHARACTER).
+ (general Unicode category: Part of C0(tab, vertical tab, form feed,
+ carriage return, and linefeed characters), Zs, Zl, Zp, and NEL(U+0085))
++/
+@safe pure nothrow @nogc
+public bool isWhite(dchar c)
+{
+ import std.internal.unicode_tables : isWhiteGen; // generated file
+ return isWhiteGen(c); // call pregenerated binary search
+}
+
+/++
+ Return whether $(D c) is a Unicode lowercase $(CHARACTER).
++/
+@safe pure nothrow @nogc
+bool isLower(dchar c)
+{
+ import std.ascii : isLower, isASCII;
+ if (isASCII(c))
+ return isLower(c);
+ return lowerCaseTrie[c];
+}
+
+@safe unittest
+{
+ import std.ascii : isLower;
+ foreach (v; 0 .. 0x80)
+ assert(isLower(v) == .isLower(v));
+ assert(.isLower('я'));
+ assert(.isLower('й'));
+ assert(!.isLower('Ж'));
+ // Greek HETA
+ assert(!.isLower('\u0370'));
+ assert(.isLower('\u0371'));
+ assert(!.isLower('\u039C')); // capital MU
+ assert(.isLower('\u03B2')); // beta
+ // from extended Greek
+ assert(!.isLower('\u1F18'));
+ assert(.isLower('\u1F00'));
+ foreach (v; unicode.lowerCase.byCodepoint)
+ assert(.isLower(v) && !isUpper(v));
+}
+
+
+/++
+ Return whether $(D c) is a Unicode uppercase $(CHARACTER).
++/
+@safe pure nothrow @nogc
+bool isUpper(dchar c)
+{
+ import std.ascii : isUpper, isASCII;
+ if (isASCII(c))
+ return isUpper(c);
+ return upperCaseTrie[c];
+}
+
+@safe unittest
+{
+ import std.ascii : isLower;
+ foreach (v; 0 .. 0x80)
+ assert(isLower(v) == .isLower(v));
+ assert(!isUpper('й'));
+ assert(isUpper('Ж'));
+ // Greek HETA
+ assert(isUpper('\u0370'));
+ assert(!isUpper('\u0371'));
+ assert(isUpper('\u039C')); // capital MU
+ assert(!isUpper('\u03B2')); // beta
+ // from extended Greek
+ assert(!isUpper('\u1F00'));
+ assert(isUpper('\u1F18'));
+ foreach (v; unicode.upperCase.byCodepoint)
+ assert(isUpper(v) && !.isLower(v));
+}
+
+
+//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 < 'a')
+ return c;
+ if (c <= 'z')
+ return c - 32;
+ return c;
+ }
+ size_t idx = toTitleSimpleIndex(c);
+ if (idx != ushort.max)
+ {
+ return toTitleTab(idx);
+ }
+ return c;
+}
+
+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, alias asciiConvert, S)(S s) @trusted pure
+if (isSomeString!S)
+{
+ import std.array : appender;
+ import std.ascii : isASCII;
+
+ foreach (i, dchar cOuter; s)
+ {
+ ushort idx = indexFn(cOuter);
+ if (idx == ushort.max)
+ continue;
+ auto result = appender!S(s[0 .. i]);
+ result.reserve(s.length);
+ foreach (dchar c; s[i .. $])
+ {
+ if (c.isASCII)
+ {
+ result.put(asciiConvert(c));
+ }
+ else
+ {
+ 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;
+ }
+ return s;
+}
+
+@safe unittest //12428
+{
+ import std.array : replicate;
+ auto s = "abcdefghij".replicate(300);
+ s = s[0 .. 10];
+
+ toUpper(s);
+
+ assert(s == "abcdefghij");
+}
+
+
+// generic toUpper/toLower on whole range, returns range
+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)
+{
+ static struct ToCaserImpl
+ {
+ @property bool empty()
+ {
+ return !nLeft && r.empty;
+ }
+
+ @property auto front()
+ {
+ import std.ascii : isASCII;
+
+ if (!nLeft)
+ {
+ dchar c = r.front;
+ if (c.isASCII)
+ {
+ buf[0] = asciiConvert(c);
+ nLeft = 1;
+ }
+ else
+ {
+ 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];
+ }
+
+ void popFront()
+ {
+ if (!nLeft)
+ front;
+ assert(nLeft);
+ --nLeft;
+ if (!nLeft)
+ r.popFront();
+ }
+
+ static if (isForwardRange!Range)
+ {
+ @property auto save()
+ {
+ auto ret = this;
+ ret.r = r.save;
+ return ret;
+ }
+ }
+
+ private:
+ Range r;
+ uint nLeft;
+ dchar[3] buf = void;
+ }
+
+ return ToCaserImpl(str);
+}
+
+/*********************
+ * Convert input range or string to upper or 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)
+ */
+
+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 asLowerCase(str.byDchar);
+ }
+ else
+ {
+ static import std.ascii;
+ return toCaser!(LowerTriple, std.ascii.toLower)(str);
+ }
+}
+
+/// ditto
+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 asUpperCase(str.byDchar);
+ }
+ else
+ {
+ static import std.ascii;
+ return toCaser!(UpperTriple, std.ascii.toUpper)(str);
+ }
+}
+
+///
+@safe pure unittest
+{
+ 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);
+}
+
+@safe unittest
+{
+ assert(testAliasedString!asLowerCase("hEllo"));
+ assert(testAliasedString!asUpperCase("hEllo"));
+}
+
+@safe unittest
+{
+ import std.array : array;
+
+ auto a = "HELLo".asLowerCase;
+ auto savea = a.save;
+ auto s = a.array;
+ assert(s == "hello");
+ s = savea.array;
+ assert(s == "hello");
+
+ string[] lower = ["123", "abcфеж", "\u0131\u023f\u03c9", "i\u0307\u1Fe2"];
+ string[] upper = ["123", "ABCФЕЖ", "I\u2c7e\u2126", "\u0130\u03A5\u0308\u0300"];
+
+ foreach (i, slwr; lower)
+ {
+ import std.utf : byChar;
+
+ auto sx = slwr.asUpperCase.byChar.array;
+ assert(sx == toUpper(slwr));
+ auto sy = upper[i].asLowerCase.byChar.array;
+ assert(sy == toLower(upper[i]));
+ }
+
+ // Not necessary to call r.front
+ 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.comparison : equal;
+
+ "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(scope char[] buf, size_t idx, dchar c) @trusted pure nothrow @nogc
+{
+ if (c <= 0x7F)
+ {
+ buf[idx] = cast(char) c;
+ idx++;
+ }
+ else if (c <= 0x7FF)
+ {
+ buf[idx] = cast(char)(0xC0 | (c >> 6));
+ buf[idx+1] = cast(char)(0x80 | (c & 0x3F));
+ idx += 2;
+ }
+ else if (c <= 0xFFFF)
+ {
+ buf[idx] = cast(char)(0xE0 | (c >> 12));
+ buf[idx+1] = cast(char)(0x80 | ((c >> 6) & 0x3F));
+ buf[idx+2] = cast(char)(0x80 | (c & 0x3F));
+ idx += 3;
+ }
+ else if (c <= 0x10FFFF)
+ {
+ buf[idx] = cast(char)(0xF0 | (c >> 18));
+ buf[idx+1] = cast(char)(0x80 | ((c >> 12) & 0x3F));
+ buf[idx+2] = cast(char)(0x80 | ((c >> 6) & 0x3F));
+ buf[idx+3] = cast(char)(0x80 | (c & 0x3F));
+ idx += 4;
+ }
+ else
+ assert(0);
+ return idx;
+}
+
+@safe unittest
+{
+ char[] s = "abcd".dup;
+ size_t i = 0;
+ i = encodeTo(s, i, 'X');
+ assert(s == "Xbcd");
+
+ i = encodeTo(s, i, cast(dchar)'\u00A9');
+ assert(s == "X\xC2\xA9d");
+}
+
+// TODO: helper, I wish std.utf was more flexible (and stright)
+private size_t encodeTo(scope wchar[] buf, size_t idx, dchar c) @trusted pure
+{
+ 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;
+ idx++;
+ }
+ else if (c <= 0x10FFFF)
+ {
+ buf[idx] = cast(wchar)((((c - 0x10000) >> 10) & 0x3FF) + 0xD800);
+ buf[idx+1] = cast(wchar)(((c - 0x10000) & 0x3FF) + 0xDC00);
+ idx += 2;
+ }
+ else
+ assert(0);
+ return idx;
+}
+
+private size_t encodeTo(scope dchar[] buf, size_t idx, dchar c) @trusted pure nothrow @nogc
+{
+ buf[idx] = c;
+ idx++;
+ return idx;
+}
+
+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))
+{
+ import std.utf : decode, codeLength;
+ size_t curIdx = 0;
+ size_t destIdx = 0;
+ alias slowToCase = toCaseInPlaceAlloc!(indexFn, maxIdx, tableFn);
+ size_t lastUnchanged = 0;
+ // in-buffer move of bytes to a new start index
+ // the trick is that it may not need to copy at all
+ static size_t moveTo(C[] str, size_t dest, size_t from, size_t to)
+ {
+ // Interestingly we may just bump pointer for a while
+ // 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)
+ return to;
+ // got to copy
+ foreach (C c; str[from .. to])
+ str[dest++] = c;
+ return dest;
+ }
+ while (curIdx != s.length)
+ {
+ size_t startIdx = curIdx;
+ immutable ch = decode(s, curIdx);
+ // TODO: special case for ASCII
+ immutable caseIndex = indexFn(ch);
+ if (caseIndex == ushort.max) // unchanged, skip over
+ {
+ continue;
+ }
+ 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;
+ 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);
+ }
+ else
+ {
+ destIdx = encodeTo(s, destIdx, cased);
+ }
+ }
+ else // 1:m codepoint mapping, slow codepath
+ {
+ destIdx = moveTo(s, destIdx, lastUnchanged, startIdx);
+ lastUnchanged = curIdx;
+ return slowToCase(s, startIdx, destIdx);
+ }
+ assert(destIdx <= curIdx);
+ }
+ if (lastUnchanged != s.length)
+ {
+ destIdx = moveTo(s, destIdx, lastUnchanged, s.length);
+ }
+ s = s[0 .. destIdx];
+}
+
+// helper to precalculate size of case-converted string
+private template toCaseLength(alias indexFn, uint maxIdx, alias tableFn)
+{
+ size_t toCaseLength(C)(in C[] str)
+ {
+ import std.utf : decode, codeLength;
+ size_t codeLen = 0;
+ size_t lastNonTrivial = 0;
+ size_t curIdx = 0;
+ while (curIdx != str.length)
+ {
+ immutable startIdx = curIdx;
+ immutable ch = decode(str, curIdx);
+ immutable ushort caseIndex = indexFn(ch);
+ if (caseIndex == ushort.max)
+ continue;
+ else if (caseIndex < maxIdx)
+ {
+ codeLen += startIdx - lastNonTrivial;
+ lastNonTrivial = curIdx;
+ immutable cased = tableFn(caseIndex);
+ codeLen += codeLength!C(cased);
+ }
+ else
+ {
+ codeLen += startIdx - lastNonTrivial;
+ lastNonTrivial = curIdx;
+ immutable val = tableFn(caseIndex);
+ immutable len = val >> 24;
+ immutable dchar cased = val & 0xFF_FFFF;
+ codeLen += codeLength!C(cased);
+ foreach (j; caseIndex+1 .. caseIndex+len)
+ codeLen += codeLength!C(tableFn(j));
+ }
+ }
+ if (lastNonTrivial != str.length)
+ codeLen += str.length - lastNonTrivial;
+ return codeLen;
+ }
+}
+
+@safe unittest
+{
+ alias toLowerLength = toCaseLength!(LowerTriple);
+ assert(toLowerLength("abcd") == 4);
+ assert(toLowerLength("аБВгд456") == 10+3);
+}
+
+// slower code path that preallocates and then copies
+// case-converted stuf to the new string
+private template toCaseInPlaceAlloc(alias indexFn, uint maxIdx, alias tableFn)
+{
+ void toCaseInPlaceAlloc(C)(ref C[] s, size_t curIdx,
+ size_t destIdx) @trusted pure
+ if (is(C == char) || is(C == wchar) || is(C == dchar))
+ {
+ import std.utf : decode;
+ alias caseLength = toCaseLength!(indexFn, maxIdx, tableFn);
+ auto trueLength = destIdx + caseLength(s[curIdx..$]);
+ C[] ns = new C[trueLength];
+ ns[0 .. destIdx] = s[0 .. destIdx];
+ size_t lastUnchanged = curIdx;
+ while (curIdx != s.length)
+ {
+ 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
+ {
+ immutable cased = tableFn(caseIndex);
+ auto toCopy = startIdx - lastUnchanged;
+ ns[destIdx .. destIdx+toCopy] = s[lastUnchanged .. startIdx];
+ lastUnchanged = curIdx;
+ destIdx += toCopy;
+ destIdx = encodeTo(ns, destIdx, cased);
+ }
+ else // 1:m codepoint mapping, slow codepath
+ {
+ auto toCopy = startIdx - lastUnchanged;
+ ns[destIdx .. destIdx+toCopy] = s[lastUnchanged .. startIdx];
+ lastUnchanged = curIdx;
+ destIdx += toCopy;
+ auto val = tableFn(caseIndex);
+ // unpack length + codepoint
+ immutable uint len = val >> 24;
+ destIdx = encodeTo(ns, destIdx, cast(dchar)(val & 0xFF_FFFF));
+ foreach (j; caseIndex+1 .. caseIndex+len)
+ destIdx = encodeTo(ns, destIdx, tableFn(j));
+ }
+ }
+ if (lastUnchanged != s.length)
+ {
+ auto toCopy = s.length - lastUnchanged;
+ ns[destIdx .. destIdx+toCopy] = s[lastUnchanged..$];
+ destIdx += toCopy;
+ }
+ assert(ns.length == destIdx);
+ s = ns;
+ }
+}
+
+/++
+ Converts $(D s) to lowercase (by performing Unicode lowercase mapping) in place.
+ For a few characters string length may increase after the transformation,
+ in such a case the function reallocates exactly once.
+ 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))
+{
+ toCaseInPlace!(LowerTriple)(s);
+}
+// overloads for the most common cases to reduce compile time
+@safe pure /*TODO nothrow*/
+{
+ void toLowerInPlace(ref char[] s)
+ { toLowerInPlace!char(s); }
+ void toLowerInPlace(ref wchar[] s)
+ { toLowerInPlace!wchar(s); }
+ void toLowerInPlace(ref dchar[] s)
+ { toLowerInPlace!dchar(s); }
+}
+
+/++
+ Converts $(D s) to uppercase (by performing Unicode uppercase mapping) in place.
+ For a few characters string length may increase after the transformation,
+ in such a case the function reallocates exactly once.
+ 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))
+{
+ toCaseInPlace!(UpperTriple)(s);
+}
+// overloads for the most common cases to reduce compile time/code size
+@safe pure /*TODO nothrow*/
+{
+ void toUpperInPlace(ref char[] s)
+ { toUpperInPlace!char(s); }
+ void toUpperInPlace(ref wchar[] s)
+ { toUpperInPlace!wchar(s); }
+ void toUpperInPlace(ref dchar[] s)
+ { 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)
+{
+ 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*/
+{
+ string toLower(string s)
+ { return toLower!string(s); }
+ wstring toLower(wstring s)
+ { 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(""));
+ }
+ }
+}
+
+
+@system unittest //@@@BUG std.format is not @safe
+{
+ static import std.ascii;
+ import std.format : format;
+ foreach (ch; 0 .. 0x80)
+ assert(std.ascii.toLower(ch) == toLower(ch));
+ assert(toLower('Я') == 'я');
+ assert(toLower('Δ') == 'δ');
+ foreach (ch; unicode.upperCase.byCodepoint)
+ {
+ dchar low = ch.toLower();
+ assert(low == ch || isLower(low), format("%s -> %s", ch, low));
+ }
+ assert(toLower("АЯ") == "ая");
+
+ assert("\u1E9E".toLower == "\u00df");
+ assert("\u00df".toUpper == "SS");
+}
+
+//bugzilla 9629
+@safe unittest
+{
+ wchar[] test = "hello þ world"w.dup;
+ auto piece = test[6 .. 7];
+ toUpperInPlace(piece);
+ assert(test == "hello Þ world");
+}
+
+
+@safe unittest
+{
+ import std.algorithm.comparison : cmp;
+ string s1 = "FoL";
+ string s2 = toLower(s1);
+ assert(cmp(s2, "fol") == 0, s2);
+ assert(s2 != s1);
+
+ char[] s3 = s1.dup;
+ toLowerInPlace(s3);
+ assert(s3 == s2);
+
+ s1 = "A\u0100B\u0101d";
+ s2 = toLower(s1);
+ s3 = s1.dup;
+ assert(cmp(s2, "a\u0101b\u0101d") == 0);
+ assert(s2 !is s1);
+ toLowerInPlace(s3);
+ assert(s3 == s2);
+
+ s1 = "A\u0460B\u0461d";
+ s2 = toLower(s1);
+ s3 = s1.dup;
+ assert(cmp(s2, "a\u0461b\u0461d") == 0);
+ assert(s2 !is s1);
+ toLowerInPlace(s3);
+ assert(s3 == s2);
+
+ s1 = "\u0130";
+ s2 = toLower(s1);
+ s3 = s1.dup;
+ assert(s2 == "i\u0307");
+ assert(s2 !is s1);
+ toLowerInPlace(s3);
+ assert(s3 == s2);
+
+ // Test on wchar and dchar strings.
+ assert(toLower("Some String"w) == "some string"w);
+ assert(toLower("Some String"d) == "some string"d);
+
+ // bugzilla 12455
+ dchar c = 'İ'; // '\U0130' LATIN CAPITAL LETTER I WITH DOT ABOVE
+ assert(isUpper(c));
+ assert(toLower(c) == 'i');
+ // extend on 12455 reprot - check simple-case toUpper too
+ c = '\u1f87';
+ assert(isLower(c));
+ assert(toUpper(c) == '\u1F8F');
+}
+
+
+/++
+ If $(D c) is a Unicode lowercase $(CHARACTER), then its uppercase 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 toUpper which takes full string instead.
+
+ 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 < 'a')
+ return c;
+ if (c <= 'z')
+ return c - 32;
+ return c;
+ }
+ size_t idx = toUpperSimpleIndex(c);
+ if (idx != ushort.max)
+ {
+ return toUpperTab(idx);
+ }
+ return c;
+}
+
+///
+@system unittest
+{
+ 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");
+}
+
+@safe unittest
+{
+ static import std.ascii;
+ import std.format : format;
+ 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)
+ {
+ dchar up = ch.toUpper();
+ assert(up == ch || isUpper(up) || title[up],
+ format("%x -> %x", ch, up));
+ }
+}
+
+/++
+ Returns a string which is identical to $(D s) except that all of its
+ characters are converted to uppercase (by preforming Unicode uppercase mapping).
+ If none of $(D s) characters were affected, then $(D s) itself is returned.
++/
+S toUpper(S)(S s) @trusted pure
+if (isSomeString!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*/
+{
+ string toUpper(string s)
+ { return toUpper!string(s); }
+ wstring toUpper(wstring s)
+ { 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(""));
+ }
+ }
+}
+
+@safe unittest
+{
+ import std.algorithm.comparison : cmp;
+
+ string s1 = "FoL";
+ string s2;
+ char[] s3;
+
+ s2 = toUpper(s1);
+ s3 = s1.dup; toUpperInPlace(s3);
+ assert(s3 == s2, s3);
+ assert(cmp(s2, "FOL") == 0);
+ assert(s2 !is s1);
+
+ s1 = "a\u0100B\u0101d";
+ s2 = toUpper(s1);
+ s3 = s1.dup; toUpperInPlace(s3);
+ assert(s3 == s2);
+ assert(cmp(s2, "A\u0100B\u0100D") == 0);
+ assert(s2 !is s1);
+
+ s1 = "a\u0460B\u0461d";
+ s2 = toUpper(s1);
+ s3 = s1.dup; toUpperInPlace(s3);
+ assert(s3 == s2);
+ assert(cmp(s2, "A\u0460B\u0460D") == 0);
+ assert(s2 !is s1);
+}
+
+@system unittest
+{
+ static void doTest(C)(const(C)[] s, const(C)[] trueUp, const(C)[] trueLow)
+ {
+ import std.format : format;
+ string diff = "src: %( %x %)\nres: %( %x %)\ntru: %( %x %)";
+ auto low = s.toLower() , up = s.toUpper();
+ auto lowInp = s.dup, upInp = s.dup;
+ lowInp.toLowerInPlace();
+ upInp.toUpperInPlace();
+ 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));
+ assert(upInp == trueUp,
+ format(diff, cast(ubyte[]) s, cast(ubyte[]) upInp, cast(ubyte[]) trueUp));
+ }
+ foreach (S; AliasSeq!(dstring, wstring, string))
+ {
+
+ S easy = "123";
+ S good = "abCФеж";
+ S awful = "\u0131\u023f\u2126";
+ S wicked = "\u0130\u1FE2";
+ auto options = [easy, good, awful, wicked];
+ S[] lower = ["123", "abcфеж", "\u0131\u023f\u03c9", "i\u0307\u1Fe2"];
+ S[] upper = ["123", "ABCФЕЖ", "I\u2c7e\u2126", "\u0130\u03A5\u0308\u0300"];
+
+ foreach (val; AliasSeq!(easy, good))
+ {
+ auto e = val.dup;
+ auto g = e;
+ e.toUpperInPlace();
+ assert(e is g);
+ e.toLowerInPlace();
+ assert(e is g);
+ }
+ 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)
+ {
+ auto sample = options[i] ~ options[j] ~ options[k];
+ auto sample2 = options[k] ~ options[j] ~ options[i];
+ doTest(sample, upper[i] ~ upper[j] ~ upper[k],
+ lower[i] ~ lower[j] ~ lower[k]);
+ doTest(sample2, upper[k] ~ upper[j] ~ upper[i],
+ lower[k] ~ lower[j] ~ lower[i]);
+ }
+ }
+}
+
+
+/++
+ Returns whether $(D c) is a Unicode alphabetic $(CHARACTER)
+ (general Unicode category: Alphabetic).
++/
+@safe pure nothrow @nogc
+bool isAlpha(dchar c)
+{
+ // optimization
+ if (c < 0xAA)
+ {
+ size_t x = c - 'A';
+ if (x <= 'Z' - 'A')
+ return true;
+ else
+ {
+ x = c - 'a';
+ if (x <= 'z'-'a')
+ return true;
+ }
+ return false;
+ }
+
+ return alphaTrie[c];
+}
+
+@safe unittest
+{
+ auto alpha = unicode("Alphabetic");
+ foreach (ch; alpha.byCodepoint)
+ assert(isAlpha(ch));
+ foreach (ch; 0 .. 0x4000)
+ assert((ch in alpha) == isAlpha(ch));
+}
+
+
+/++
+ Returns whether $(D c) is a Unicode mark
+ (general Unicode category: Mn, Me, Mc).
++/
+@safe pure nothrow @nogc
+bool isMark(dchar c)
+{
+ return markTrie[c];
+}
+
+@safe unittest
+{
+ auto mark = unicode("Mark");
+ foreach (ch; mark.byCodepoint)
+ assert(isMark(ch));
+ foreach (ch; 0 .. 0x4000)
+ assert((ch in mark) == isMark(ch));
+}
+
+/++
+ Returns whether $(D c) is a Unicode numerical $(CHARACTER)
+ (general Unicode category: Nd, Nl, No).
++/
+@safe pure nothrow @nogc
+bool isNumber(dchar 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)
+ assert(isNumber(ch));
+ 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)
+ (general Unicode category: Pd, Ps, Pe, Pc, Po, Pi, Pf).
++/
+@safe pure nothrow @nogc
+bool isPunctuation(dchar c)
+{
+ static import std.ascii;
+
+ // optimization for ascii case
+ if (c <= 0x7F)
+ {
+ return std.ascii.isPunctuation(c);
+ }
+ else
+ {
+ return punctuationTrie[c];
+ }
+}
+
+@safe unittest
+{
+ assert(isPunctuation('\u0021'));
+ assert(isPunctuation('\u0028'));
+ assert(isPunctuation('\u0029'));
+ assert(isPunctuation('\u002D'));
+ assert(isPunctuation('\u005F'));
+ assert(isPunctuation('\u00AB'));
+ assert(isPunctuation('\u00BB'));
+ foreach (ch; unicode("P").byCodepoint)
+ assert(isPunctuation(ch));
+}
+
+/++
+ Returns whether $(D c) is a Unicode symbol $(CHARACTER)
+ (general Unicode category: Sm, Sc, Sk, So).
++/
+@safe pure nothrow @nogc
+bool isSymbol(dchar c)
+{
+ return symbolTrie[c];
+}
+
+@safe unittest
+{
+ import std.format : format;
+ assert(isSymbol('\u0024'));
+ assert(isSymbol('\u002B'));
+ assert(isSymbol('\u005E'));
+ assert(isSymbol('\u00A6'));
+ foreach (ch; unicode("S").byCodepoint)
+ assert(isSymbol(ch), format("%04x", ch));
+}
+
+/++
+ Returns whether $(D c) is a Unicode space $(CHARACTER)
+ (general Unicode category: Zs)
+ Note: This doesn't include '\n', '\r', \t' and other non-space $(CHARACTER).
+ For commonly used less strict semantics see $(LREF isWhite).
++/
+@safe pure nothrow @nogc
+bool isSpace(dchar c)
+{
+ import std.internal.unicode_tables : isSpaceGen; // generated file
+ return isSpaceGen(c);
+}
+
+@safe unittest
+{
+ assert(isSpace('\u0020'));
+ auto space = unicode.Zs;
+ foreach (ch; space.byCodepoint)
+ assert(isSpace(ch));
+ foreach (ch; 0 .. 0x1000)
+ assert(isSpace(ch) == space[ch]);
+}
+
+
+/++
+ Returns whether $(D c) is a Unicode graphical $(CHARACTER)
+ (general Unicode category: L, M, N, P, S, Zs).
+
++/
+@safe pure nothrow @nogc
+bool isGraphical(dchar c)
+{
+ return graphicalTrie[c];
+}
+
+
+@safe unittest
+{
+ auto set = unicode("Graphical");
+ import std.format : format;
+ foreach (ch; set.byCodepoint)
+ assert(isGraphical(ch), format("%4x", ch));
+ foreach (ch; 0 .. 0x4000)
+ assert((ch in set) == isGraphical(ch));
+}
+
+
+/++
+ Returns whether $(D c) is a Unicode control $(CHARACTER)
+ (general Unicode category: Cc).
++/
+@safe pure nothrow @nogc
+bool isControl(dchar c)
+{
+ import std.internal.unicode_tables : isControlGen; // generated file
+ return isControlGen(c);
+}
+
+@safe unittest
+{
+ assert(isControl('\u0000'));
+ assert(isControl('\u0081'));
+ assert(!isControl('\u0100'));
+ auto cc = unicode.Cc;
+ foreach (ch; cc.byCodepoint)
+ assert(isControl(ch));
+ foreach (ch; 0 .. 0x1000)
+ assert(isControl(ch) == cc[ch]);
+}
+
+
+/++
+ Returns whether $(D c) is a Unicode formatting $(CHARACTER)
+ (general Unicode category: Cf).
++/
+@safe pure nothrow @nogc
+bool isFormat(dchar c)
+{
+ import std.internal.unicode_tables : isFormatGen; // generated file
+ return isFormatGen(c);
+}
+
+
+@safe unittest
+{
+ assert(isFormat('\u00AD'));
+ foreach (ch; unicode("Format").byCodepoint)
+ assert(isFormat(ch));
+}
+
+// code points for private use, surrogates are not likely to change in near feature
+// if need be they can be generated from unicode data as well
+
+/++
+ Returns whether $(D c) is a Unicode Private Use $(CODEPOINT)
+ (general Unicode category: Co).
++/
+@safe pure nothrow @nogc
+bool isPrivateUse(dchar c)
+{
+ return (0x00_E000 <= c && c <= 0x00_F8FF)
+ || (0x0F_0000 <= c && c <= 0x0F_FFFD)
+ || (0x10_0000 <= c && c <= 0x10_FFFD);
+}
+
+/++
+ Returns whether $(D c) is a Unicode surrogate $(CODEPOINT)
+ (general Unicode category: Cs).
++/
+@safe pure nothrow @nogc
+bool isSurrogate(dchar c)
+{
+ return (0xD800 <= c && c <= 0xDFFF);
+}
+
+/++
+ Returns whether $(D c) is a Unicode high surrogate (lead surrogate).
++/
+@safe pure nothrow @nogc
+bool isSurrogateHi(dchar c)
+{
+ return (0xD800 <= c && c <= 0xDBFF);
+}
+
+/++
+ Returns whether $(D c) is a Unicode low surrogate (trail surrogate).
++/
+@safe pure nothrow @nogc
+bool isSurrogateLo(dchar c)
+{
+ return (0xDC00 <= c && c <= 0xDFFF);
+}
+
+/++
+ Returns whether $(D c) is a Unicode non-character i.e.
+ a $(CODEPOINT) with no assigned abstract character.
+ (general Unicode category: Cn)
++/
+@safe pure nothrow @nogc
+bool isNonCharacter(dchar c)
+{
+ return nonCharacterTrie[c];
+}
+
+@safe unittest
+{
+ auto set = unicode("Cn");
+ foreach (ch; set.byCodepoint)
+ assert(isNonCharacter(ch));
+}
+
+private:
+// load static data from pre-generated tables into usable datastructures
+
+
+@safe auto asSet(const (ubyte)[] compressed) pure
+{
+ return CodepointSet.fromIntervals(decompressIntervals(compressed));
+}
+
+@safe pure nothrow auto asTrie(T...)(in TrieEntry!T e)
+{
+ return const(CodepointTrie!T)(e.offsets, e.sizes, e.data);
+}
+
+@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
+ // increase the object size of the caller.
+ auto lowerCaseTrie() { static immutable res = asTrie(lowerCaseTrieEntries); return res; }
+ auto upperCaseTrie() { static immutable res = asTrie(upperCaseTrieEntries); return res; }
+ auto simpleCaseTrie() { static immutable res = asTrie(simpleCaseTrieEntries); return res; }
+ auto fullCaseTrie() { static immutable res = asTrie(fullCaseTrieEntries); return res; }
+ auto alphaTrie() { static immutable res = asTrie(alphaTrieEntries); return res; }
+ auto markTrie() { static immutable res = asTrie(markTrieEntries); return res; }
+ auto numberTrie() { static immutable res = asTrie(numberTrieEntries); return res; }
+ auto punctuationTrie() { static immutable res = asTrie(punctuationTrieEntries); return res; }
+ auto symbolTrie() { static immutable res = asTrie(symbolTrieEntries); return res; }
+ auto graphicalTrie() { static immutable res = asTrie(graphicalTrieEntries); return res; }
+ auto nonCharacterTrie() { static immutable res = asTrie(nonCharacterTrieEntries); return res; }
+
+ //normalization quick-check tables
+ auto nfcQCTrie()
+ {
+ import std.internal.unicode_norm : nfcQCTrieEntries;
+ static immutable res = asTrie(nfcQCTrieEntries);
+ return res;
+ }
+
+ auto nfdQCTrie()
+ {
+ import std.internal.unicode_norm : nfdQCTrieEntries;
+ static immutable res = asTrie(nfdQCTrieEntries);
+ return res;
+ }
+
+ auto nfkcQCTrie()
+ {
+ import std.internal.unicode_norm : nfkcQCTrieEntries;
+ static immutable res = asTrie(nfkcQCTrieEntries);
+ return res;
+ }
+
+ auto nfkdQCTrie()
+ {
+ import std.internal.unicode_norm : nfkdQCTrieEntries;
+ static immutable res = asTrie(nfkdQCTrieEntries);
+ return res;
+ }
+
+ //grapheme breaking algorithm tables
+ auto mcTrie()
+ {
+ import std.internal.unicode_grapheme : mcTrieEntries;
+ static immutable res = asTrie(mcTrieEntries);
+ return res;
+ }
+
+ auto graphemeExtendTrie()
+ {
+ import std.internal.unicode_grapheme : graphemeExtendTrieEntries;
+ static immutable res = asTrie(graphemeExtendTrieEntries);
+ return res;
+ }
+
+ auto hangLV()
+ {
+ import std.internal.unicode_grapheme : hangulLVTrieEntries;
+ static immutable res = asTrie(hangulLVTrieEntries);
+ return res;
+ }
+
+ auto hangLVT()
+ {
+ import std.internal.unicode_grapheme : hangulLVTTrieEntries;
+ static immutable res = asTrie(hangulLVTTrieEntries);
+ return res;
+ }
+
+ // tables below are used for composition/decomposition
+ auto combiningClassTrie()
+ {
+ import std.internal.unicode_comp : combiningClassTrieEntries;
+ static immutable res = asTrie(combiningClassTrieEntries);
+ return res;
+ }
+
+ auto compatMappingTrie()
+ {
+ import std.internal.unicode_decomp : compatMappingTrieEntries;
+ static immutable res = asTrie(compatMappingTrieEntries);
+ return res;
+ }
+
+ auto canonMappingTrie()
+ {
+ import std.internal.unicode_decomp : canonMappingTrieEntries;
+ static immutable res = asTrie(canonMappingTrieEntries);
+ return res;
+ }
+
+ auto compositionJumpTrie()
+ {
+ import std.internal.unicode_comp : compositionJumpTrieEntries;
+ static immutable res = asTrie(compositionJumpTrieEntries);
+ return res;
+ }
+
+ //case conversion tables
+ auto toUpperIndexTrie() { static immutable res = asTrie(toUpperIndexTrieEntries); return res; }
+ auto toLowerIndexTrie() { static immutable res = asTrie(toLowerIndexTrieEntries); return res; }
+ auto toTitleIndexTrie() { static immutable res = asTrie(toTitleIndexTrieEntries); return res; }
+ //simple case conversion tables
+ auto toUpperSimpleIndexTrie() { static immutable res = asTrie(toUpperSimpleIndexTrieEntries); return res; }
+ auto toLowerSimpleIndexTrie() { static immutable res = asTrie(toLowerSimpleIndexTrieEntries); return res; }
+ auto toTitleSimpleIndexTrie() { static immutable res = asTrie(toTitleSimpleIndexTrieEntries); return res; }
+
+}
+
+}// version (!std_uni_bootstrap)
diff --git a/libphobos/src/std/uri.d b/libphobos/src/std/uri.d
new file mode 100644
index 0000000..0852955
--- /dev/null
+++ b/libphobos/src/std/uri.d
@@ -0,0 +1,592 @@
+// Written in the D programming language.
+
+/**
+ * Encode and decode Uniform Resource Identifiers (URIs).
+ * URIs are used in internet transfer protocols.
+ * Valid URI characters consist of letters, digits,
+ * and the characters $(B ;/?:@&amp;=+$,-_.!~*'())
+ * Reserved URI characters are $(B ;/?:@&amp;=+$,)
+ * Escape sequences consist of $(B %) followed by two hex digits.
+ *
+ * See_Also:
+ * $(LINK2 http://www.ietf.org/rfc/rfc3986.txt, RFC 3986)<br>
+ * $(LINK2 http://en.wikipedia.org/wiki/Uniform_resource_identifier, Wikipedia)
+ * Copyright: Copyright Digital Mars 2000 - 2009.
+ * 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.
+ * 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.uri;
+
+//debug=uri; // uncomment to turn on debugging writefln's
+debug(uri) import std.stdio;
+import std.traits : isSomeChar;
+
+/** This Exception is thrown if something goes wrong when encoding or
+decoding a URI.
+*/
+class URIException : Exception
+{
+ import std.exception : basicExceptionCtors;
+ mixin basicExceptionCtors;
+}
+
+private enum
+{
+ URI_Alpha = 1,
+ URI_Reserved = 2,
+ URI_Mark = 4,
+ URI_Digit = 8,
+ URI_Hash = 0x10, // '#'
+}
+
+private immutable char[16] hex2ascii = "0123456789ABCDEF";
+
+private immutable ubyte[128] uri_flags = // indexed by character
+ ({
+ ubyte[128] uflags;
+
+ // 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 str, uint unescapedSet)
+{
+ import core.exception : OutOfMemoryError;
+ import core.stdc.stdlib : alloca;
+
+ uint j;
+ uint k;
+ dchar V;
+ dchar C;
+
+ // result buffer
+ char[50] buffer = void;
+ char* R;
+ uint Rlen;
+ uint Rsize; // alloc'd size
+
+ immutable len = str.length;
+
+ R = buffer.ptr;
+ Rsize = buffer.length;
+ Rlen = 0;
+
+ for (k = 0; k != len; k++)
+ {
+ C = str[k];
+ // if (C in unescapedSet)
+ if (C < uri_flags.length && uri_flags[C] & unescapedSet)
+ {
+ if (Rlen == Rsize)
+ {
+ char* R2;
+
+ Rsize *= 2;
+ if (Rsize > 1024)
+ {
+ R2 = (new char[Rsize]).ptr;
+ }
+ else
+ {
+ R2 = cast(char *) alloca(Rsize * char.sizeof);
+ if (!R2)
+ throw new OutOfMemoryError("Alloca failure");
+ }
+ R2[0 .. Rlen] = R[0 .. Rlen];
+ R = R2;
+ }
+ R[Rlen] = cast(char) C;
+ Rlen++;
+ }
+ else
+ {
+ char[6] Octet;
+ uint L;
+
+ V = C;
+
+ // Transform V into octets
+ if (V <= 0x7F)
+ {
+ Octet[0] = cast(char) V;
+ L = 1;
+ }
+ else if (V <= 0x7FF)
+ {
+ Octet[0] = cast(char)(0xC0 | (V >> 6));
+ Octet[1] = cast(char)(0x80 | (V & 0x3F));
+ L = 2;
+ }
+ else if (V <= 0xFFFF)
+ {
+ Octet[0] = cast(char)(0xE0 | (V >> 12));
+ Octet[1] = cast(char)(0x80 | ((V >> 6) & 0x3F));
+ Octet[2] = cast(char)(0x80 | (V & 0x3F));
+ L = 3;
+ }
+ else if (V <= 0x1FFFFF)
+ {
+ Octet[0] = cast(char)(0xF0 | (V >> 18));
+ Octet[1] = cast(char)(0x80 | ((V >> 12) & 0x3F));
+ Octet[2] = cast(char)(0x80 | ((V >> 6) & 0x3F));
+ Octet[3] = cast(char)(0x80 | (V & 0x3F));
+ L = 4;
+ }
+ else
+ {
+ throw new URIException("Undefined UTF-32 code point");
+ }
+
+ if (Rlen + L * 3 > Rsize)
+ {
+ char *R2;
+
+ Rsize = 2 * (Rlen + L * 3);
+ if (Rsize > 1024)
+ {
+ R2 = (new char[Rsize]).ptr;
+ }
+ else
+ {
+ R2 = cast(char *) alloca(Rsize * char.sizeof);
+ if (!R2)
+ throw new OutOfMemoryError("Alloca failure");
+ }
+ R2[0 .. Rlen] = R[0 .. Rlen];
+ R = R2;
+ }
+
+ for (j = 0; j < L; j++)
+ {
+ R[Rlen] = '%';
+ R[Rlen + 1] = hex2ascii[Octet[j] >> 4];
+ R[Rlen + 2] = hex2ascii[Octet[j] & 15];
+
+ Rlen += 3;
+ }
+ }
+ }
+
+ return R[0 .. Rlen].idup;
+}
+
+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)
+{
+ import core.exception : OutOfMemoryError;
+ import core.stdc.stdlib : alloca;
+ import std.ascii : isHexDigit;
+
+ uint j;
+ uint k;
+ uint V;
+ dchar C;
+
+ // Result array, allocated on stack
+ dchar* R;
+ uint Rlen;
+
+ 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)
+ {
+ R = (new dchar[Rsize]).ptr;
+ }
+ else
+ {
+ R = cast(dchar *) alloca(Rsize * dchar.sizeof);
+ if (!R)
+ throw new OutOfMemoryError("Alloca failure");
+ }
+ Rlen = 0;
+
+ for (k = 0; k != len; k++)
+ {
+ char B;
+ uint start;
+
+ C = s[k];
+ if (C != '%')
+ {
+ R[Rlen] = C;
+ Rlen++;
+ continue;
+ }
+ start = k;
+ if (k + 2 >= len)
+ throw new URIException("Unexpected end of URI");
+ if (!isHexDigit(s[k + 1]) || !isHexDigit(s[k + 2]))
+ throw new URIException("Expected two hexadecimal digits after '%'");
+ B = cast(char)((ascii2hex(s[k + 1]) << 4) + ascii2hex(s[k + 2]));
+ k += 2;
+ if ((B & 0x80) == 0)
+ {
+ C = B;
+ }
+ else
+ {
+ uint n;
+
+ for (n = 1; ; n++)
+ {
+ if (n > 4)
+ throw new URIException("UTF-32 code point size too large");
+ if (((B << n) & 0x80) == 0)
+ {
+ if (n == 1)
+ throw new URIException("UTF-32 code point size too small");
+ break;
+ }
+ }
+
+ // Pick off (7 - n) significant bits of B from first byte of octet
+ V = B & ((1 << (7 - n)) - 1); // (!!!)
+
+ if (k + (3 * (n - 1)) >= len)
+ throw new URIException("UTF-32 unaligned String");
+ for (j = 1; j != n; j++)
+ {
+ k++;
+ if (s[k] != '%')
+ throw new URIException("Expected: '%'");
+ if (!isHexDigit(s[k + 1]) || !isHexDigit(s[k + 2]))
+ throw new URIException("Expected two hexadecimal digits after '%'");
+ B = cast(char)((ascii2hex(s[k + 1]) << 4) + ascii2hex(s[k + 2]));
+ if ((B & 0xC0) != 0x80)
+ throw new URIException("Incorrect UTF-32 multi-byte sequence");
+ k += 2;
+ V = (V << 6) | (B & 0x3F);
+ }
+ if (V > 0x10FFFF)
+ throw new URIException("Unknown UTF-32 code point");
+ C = V;
+ }
+ if (C < uri_flags.length && uri_flags[C] & reservedSet)
+ {
+ // R ~= s[start .. k + 1];
+ immutable width = (k + 1) - start;
+ for (int ii = 0; ii < width; ii++)
+ R[Rlen + ii] = s[start + ii];
+ Rlen += width;
+ }
+ else
+ {
+ R[Rlen] = C;
+ Rlen++;
+ }
+ }
+ assert(Rlen <= Rsize); // enforce our preallocation size guarantee
+
+ // Copy array on stack to array in memory
+ return R[0 .. Rlen].idup;
+}
+
+/*************************************
+ * Decodes the URI string encodedURI into a UTF-8 string and returns it.
+ * Escape sequences that resolve to reserved URI characters are not replaced.
+ * Escape sequences that resolve to the '#' character are not replaced.
+ */
+
+string decode(Char)(in Char[] encodedURI)
+if (isSomeChar!Char)
+{
+ import std.algorithm.iteration : each;
+ import std.utf : encode;
+ auto s = URI_Decode(encodedURI, URI_Reserved | URI_Hash);
+ char[] r;
+ s.each!(c => encode(r, c));
+ return r;
+}
+
+/*******************************
+ * Decodes the URI string encodedURI into a UTF-8 string and returns it. All
+ * escape sequences are decoded.
+ */
+
+string decodeComponent(Char)(in Char[] encodedURIComponent)
+if (isSomeChar!Char)
+{
+ import std.algorithm.iteration : each;
+ import std.utf : encode;
+ auto s = URI_Decode(encodedURIComponent, 0);
+ char[] r;
+ s.each!(c => encode(r, c));
+ return r;
+}
+
+/*****************************
+ * Encodes the UTF-8 string uri into a URI and returns that URI. Any character
+ * not a valid URI character is escaped. The '#' character is not escaped.
+ */
+
+string encode(Char)(in Char[] uri)
+if (isSomeChar!Char)
+{
+ import std.utf : toUTF32;
+ auto s = toUTF32(uri);
+ return URI_Encode(s, URI_Reserved | URI_Hash | URI_Alpha | URI_Digit | URI_Mark);
+}
+
+/********************************
+ * Encodes the UTF-8 string uriComponent into a URI and returns that URI.
+ * Any character not a letter, digit, or one of -_.!~*'() is escaped.
+ */
+
+string encodeComponent(Char)(in Char[] uriComponent)
+if (isSomeChar!Char)
+{
+ 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
+ */
+
+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;
+
+ ptrdiff_t i;
+
+ if (s.length <= 4)
+ return -1;
+
+ if (s.length > 7 && icmp(s[0 .. 7], "http://") == 0)
+ {
+ i = 7;
+ }
+ else
+ {
+ if (s.length > 8 && icmp(s[0 .. 8], "https://") == 0)
+ i = 8;
+ else
+ return -1;
+ }
+
+ ptrdiff_t lastdot;
+ for (; i < s.length; i++)
+ {
+ auto c = s[i];
+ if (isAlphaNum(c))
+ continue;
+ if (c == '-' || c == '_' || c == '?' ||
+ c == '=' || c == '%' || c == '&' ||
+ c == '/' || c == '+' || c == '#' ||
+ c == '~' || c == '$')
+ continue;
+ if (c == '.')
+ {
+ lastdot = i;
+ continue;
+ }
+ break;
+ }
+ if (!lastdot)
+ return -1;
+
+ return i;
+}
+
+///
+@safe unittest
+{
+ string s1 = "http://www.digitalmars.com/~fred/fredsRX.html#foo end!";
+ assert(uriLength(s1) == 49);
+ string s2 = "no uri here";
+ assert(uriLength(s2) == -1);
+ assert(uriLength("issue 14924") < 0);
+}
+
+
+/***************************
+ * 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
+ * References:
+ * RFC2822
+ */
+ptrdiff_t emailLength(Char)(in Char[] s)
+if (isSomeChar!Char)
+{
+ import std.ascii : isAlpha, isAlphaNum;
+
+ ptrdiff_t i;
+
+ if (!isAlpha(s[0]))
+ return -1;
+
+ for (i = 1; 1; i++)
+ {
+ if (i == s.length)
+ return -1;
+ auto c = s[i];
+ if (isAlphaNum(c))
+ continue;
+ if (c == '-' || c == '_' || c == '.')
+ continue;
+ if (c != '@')
+ return -1;
+ i++;
+ break;
+ }
+
+ /* Now do the part past the '@'
+ */
+ ptrdiff_t lastdot;
+ for (; i < s.length; i++)
+ {
+ auto c = s[i];
+ if (isAlphaNum(c))
+ continue;
+ if (c == '-' || c == '_')
+ continue;
+ if (c == '.')
+ {
+ lastdot = i;
+ continue;
+ }
+ break;
+ }
+ if (!lastdot || (i - lastdot != 3 && i - lastdot != 4))
+ return -1;
+
+ return i;
+}
+
+///
+@safe unittest
+{
+ string s1 = "my.e-mail@www.example-domain.com with garbage added";
+ assert(emailLength(s1) == 32);
+ string s2 = "no email address here";
+ assert(emailLength(s2) == -1);
+ assert(emailLength("issue 14924") < 0);
+}
+
+
+@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";
+ string target = "http://www.digitalmars.com/~fred/fred's%20RX.html#foo";
+
+ auto result = encode(source);
+ debug(uri) writefln("result = '%s'", result);
+ assert(result == target);
+ result = decode(target);
+ debug(uri) writefln("result = '%s'", result);
+ assert(result == source);
+
+ result = encode(decode("%E3%81%82%E3%81%82"));
+ assert(result == "%E3%81%82%E3%81%82");
+
+ result = encodeComponent("c++");
+ assert(result == "c%2B%2B");
+
+ auto str = new char[10_000_000];
+ str[] = 'A';
+ result = encodeComponent(str);
+ foreach (char c; result)
+ assert(c == 'A');
+
+ result = decode("%41%42%43");
+ debug(uri) writeln(result);
+
+ import std.meta : AliasSeq;
+ foreach (StringType; AliasSeq!(char[], wchar[], dchar[], string, wstring, dstring))
+ {
+ import std.conv : to;
+ StringType decoded1 = source.to!StringType;
+ string encoded1 = encode(decoded1);
+ assert(decoded1 == source.to!StringType); // check that `decoded1` wasn't changed
+ assert(encoded1 == target);
+ assert(decoded1 == decode(encoded1).to!StringType);
+
+ StringType encoded2 = target.to!StringType;
+ string decoded2 = decode(encoded2);
+ assert(encoded2 == target.to!StringType); // check that `encoded2` wasn't changed
+ assert(decoded2 == source);
+ assert(encoded2 == encode(decoded2).to!StringType);
+ }
+}
diff --git a/libphobos/src/std/utf.d b/libphobos/src/std/utf.d
new file mode 100644
index 0000000..63ae736
--- /dev/null
+++ b/libphobos/src/std/utf.d
@@ -0,0 +1,4058 @@
+// Written in the D programming language.
+
+/++
+ Encode and decode UTF-8, UTF-16 and UTF-32 strings.
+
+ UTF character support is restricted to
+ $(D '\u0000' &lt;= character &lt;= '\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)
+ Copyright: Copyright Digital Mars 2000 - 2012.
+ 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.exception; // basicExceptionCtors
+import std.meta; // AliasSeq
+import std.range.primitives;
+import std.traits; // isSomeChar, isSomeString
+import std.typecons; // Flag, Yes, No
+
+
+/++
+ Exception thrown on errors in std.utf functions.
+ +/
+class UTFException : Exception
+{
+ import core.internal.string : unsignedToTempString, UnsignedStringBuf;
+
+ uint[4] sequence;
+ size_t len;
+
+ @safe pure nothrow @nogc
+ UTFException setSequence(scope uint[] data...)
+ {
+ assert(data.length <= 4);
+
+ len = data.length < 4 ? data.length : 4;
+ sequence[0 .. len] = data[0 .. len];
+
+ return this;
+ }
+
+ // 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);
+ }
+
+ this(string msg, size_t index, string file = __FILE__,
+ size_t line = __LINE__, Throwable next = null) @safe pure nothrow
+ {
+ UnsignedStringBuf buf = void;
+ msg ~= " (at index " ~ unsignedToTempString(index, buf, 10) ~ ")";
+ super(msg, file, line, next);
+ }
+
+
+ override string toString() const
+ {
+ if (len == 0)
+ {
+ /* 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])
+ {
+ 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)
+ {
+ result ~= " - ";
+ result ~= super.msg;
+ }
+
+ return result;
+ }
+}
+
+/*
+ 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);
+}
+
+/++
+ 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.
+ +/
+bool isValidDchar(dchar c) pure nothrow @safe @nogc
+{
+ return c < 0xD800 || (c > 0xDFFF && c <= 0x10FFFF);
+}
+
+pure nothrow @safe @nogc unittest
+{
+ import std.exception;
+
+ 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));
+ });
+}
+
+
+/++
+ Calculate the length of the UTF sequence starting at $(D index)
+ in $(D str).
+
+ 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 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 sequence.
+
+ Note:
+ $(D stride) will only analyze the first $(D str[index]) 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 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)))
+{
+ static if (is(typeof(str.length) : ulong))
+ assert(index < str.length, "Past the end of the UTF-8 sequence");
+ immutable c = str[index];
+
+ if (c < 0x80)
+ return 1;
+ else
+ return strideImpl(c, index);
+}
+
+/// Ditto
+uint stride(S)(auto ref S str)
+if (is(S : const char[]) ||
+ (isInputRange!S && is(Unqual!(ElementType!S) == char)))
+{
+ static if (is(S : const char[]))
+ immutable c = str[0];
+ else
+ immutable c = str.front;
+
+ if (c < 0x80)
+ return 1;
+ else
+ return strideImpl(c, 0);
+}
+
+private uint strideImpl(char c, size_t index) @trusted pure
+in { assert(c & 0x80); }
+body
+{
+ import core.bitop : bsr;
+ immutable msbs = 7 - bsr((~uint(c)) & 0xFF);
+ if (c == 0xFF || msbs < 2 || msbs > 4)
+ throw new UTFException("Invalid UTF-8 sequence", index);
+ return msbs;
+}
+
+@system unittest
+{
+ import core.exception : AssertError;
+ import std.conv : to;
+ import std.exception;
+ import std.string : format;
+ static void test(string s, dchar c, size_t i = 0, size_t line = __LINE__)
+ {
+ enforce(stride(s, i) == codeLength!char(c),
+ new AssertError(format("Unit test failure string: %s", s), __FILE__, line));
+
+ enforce(stride(RandomCU!char(s), i) == codeLength!char(c),
+ new AssertError(format("Unit test failure range: %s", s), __FILE__, line));
+
+ auto refRandom = new RefRandomCU!char(s);
+ immutable randLen = refRandom.length;
+ enforce(stride(refRandom, i) == codeLength!char(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!char(c),
+ new AssertError(format("Unit test failure string 0: %s", s), __FILE__, line));
+
+ enforce(stride(InputCU!char(s)) == codeLength!char(c),
+ new AssertError(format("Unit test failure range 0: %s", s), __FILE__, line));
+
+ auto refBidir = new RefBidirCU!char(s);
+ immutable bidirLen = refBidir.length;
+ enforce(stride(refBidir) == codeLength!char(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', 9);
+ test("hello\U00010143\u0100\U00010143", '\U00010143', 11);
+
+ foreach (S; AliasSeq!(char[], const char[], string))
+ {
+ 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);
+ }
+ });
+}
+
+@safe unittest // invalid start bytes
+{
+ import std.exception : assertThrown;
+ immutable char[] invalidStartBytes = [
+ 0b1111_1000, // indicating a sequence length of 5
+ 0b1111_1100, // 6
+ 0b1111_1110, // 7
+ 0b1111_1111, // 8
+ 0b1000_0000, // continuation byte
+ ];
+ 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);
+}
+
+/// 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 core.exception : AssertError;
+ import std.conv : to;
+ import std.exception;
+ import std.string : format;
+ 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 core.exception : AssertError;
+ import std.conv : to;
+ import std.exception;
+ import std.string : format;
+ 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);
+ }
+ });
+}
+
+/++
+ Calculate the length of the UTF sequence ending one code unit before
+ $(D index) in $(D str).
+
+ 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 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 sequence.
+
+ 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)))
+{
+ static if (is(typeof(str.length) : ulong))
+ assert(index <= str.length, "Past the end of the UTF-8 sequence");
+ assert(index > 0, "Not the end of the UTF-8 sequence");
+
+ if ((str[index-1] & 0b1100_0000) != 0b1000_0000)
+ return 1;
+
+ if (index >= 4) //single verification for most common case
+ {
+ foreach (i; AliasSeq!(2, 3, 4))
+ {
+ if ((str[index-i] & 0b1100_0000) != 0b1000_0000)
+ return i;
+ }
+ }
+ else
+ {
+ foreach (i; AliasSeq!(2, 3))
+ {
+ if (index >= i && (str[index-i] & 0b1100_0000) != 0b1000_0000)
+ return i;
+ }
+ }
+ throw new UTFException("Not the end of the UTF sequence", index);
+}
+
+/// Ditto
+uint strideBack(S)(auto ref S str)
+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)
+{
+ assert(!str.empty, "Past the end of the UTF-8 sequence");
+ auto temp = str.save;
+ foreach (i; AliasSeq!(1, 2, 3, 4))
+ {
+ if ((temp.back & 0b1100_0000) != 0b1000_0000)
+ return i;
+ temp.popBack();
+ if (temp.empty)
+ break;
+ }
+ throw new UTFException("The last code unit is not the end of the UTF-8 sequence");
+}
+
+@system unittest
+{
+ import core.exception : AssertError;
+ import std.conv : to;
+ import std.exception;
+ import std.string : format;
+ static void test(string s, dchar c, size_t i = size_t.max, size_t line = __LINE__)
+ {
+ enforce(strideBack(s, i == size_t.max ? s.length : i) == codeLength!char(c),
+ new AssertError(format("Unit test failure string: %s", s), __FILE__, line));
+
+ enforce(strideBack(RandomCU!char(s), i == size_t.max ? s.length : i) == codeLength!char(c),
+ new AssertError(format("Unit test failure range: %s", s), __FILE__, line));
+
+ auto refRandom = new RefRandomCU!char(s);
+ immutable randLen = refRandom.length;
+ enforce(strideBack(refRandom, i == size_t.max ? s.length : i) == codeLength!char(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 == size_t.max)
+ {
+ enforce(strideBack(s) == codeLength!char(c),
+ new AssertError(format("Unit test failure string code length: %s", s), __FILE__, line));
+
+ enforce(strideBack(BidirCU!char(s)) == codeLength!char(c),
+ new AssertError(format("Unit test failure range code length: %s", s), __FILE__, line));
+
+ auto refBidir = new RefBidirCU!char(s);
+ immutable bidirLen = refBidir.length;
+ enforce(strideBack(refBidir) == codeLength!char(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", 'f');
+ test("\U00010143\u0100\U00010143hello", 'o', 15);
+ test("\U00010143\u0100\U00010143hello", 'l', 14);
+ test("\U00010143\u0100\U00010143hello", 'l', 13);
+ test("\U00010143\u0100\U00010143hello", 'e', 12);
+ test("\U00010143\u0100\U00010143hello", 'h', 11);
+ test("\U00010143\u0100\U00010143hello", '\U00010143', 10);
+ test("\U00010143\u0100\U00010143hello", '\u0100', 6);
+ test("\U00010143\u0100\U00010143hello", '\U00010143', 4);
+
+ foreach (S; AliasSeq!(char[], const char[], string))
+ {
+ enum str = to!S("hello world");
+ static assert(isSafe!({ strideBack(str, 0); }));
+ static assert(isSafe!({ strideBack(str); }));
+ static assert((functionAttributes!({ strideBack(str, 0); }) & FunctionAttribute.pure_) != 0);
+ static assert((functionAttributes!({ strideBack(str); }) & FunctionAttribute.pure_) != 0);
+ }
+ });
+}
+
+//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");
+ 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);
+}
+
+@system unittest
+{
+ import core.exception : AssertError;
+ import std.conv : to;
+ import std.exception;
+ import std.string : format;
+ 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),
+ new AssertError(format("Unit test failure range: %s", s), __FILE__, line));
+
+ auto refRandom = new RefRandomCU!wchar(s);
+ immutable randLen = refRandom.length;
+ enforce(strideBack(refRandom, i == size_t.max ? s.length : 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 == size_t.max)
+ {
+ enforce(strideBack(s) == codeLength!wchar(c),
+ new AssertError(format("Unit test failure string code length: %s", s), __FILE__, line));
+
+ enforce(strideBack(BidirCU!wchar(s)) == codeLength!wchar(c),
+ new AssertError(format("Unit test failure range code length: %s", s), __FILE__, line));
+
+ auto refBidir = new RefBidirCU!wchar(s);
+ immutable bidirLen = refBidir.length;
+ enforce(strideBack(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", 'f');
+ test("\U00010143\u0100\U00010143hello", 'o', 10);
+ test("\U00010143\u0100\U00010143hello", 'l', 9);
+ test("\U00010143\u0100\U00010143hello", 'l', 8);
+ test("\U00010143\u0100\U00010143hello", 'e', 7);
+ test("\U00010143\u0100\U00010143hello", 'h', 6);
+ test("\U00010143\u0100\U00010143hello", '\U00010143', 5);
+ test("\U00010143\u0100\U00010143hello", '\u0100', 3);
+ test("\U00010143\u0100\U00010143hello", '\U00010143', 2);
+
+ foreach (S; AliasSeq!(wchar[], const wchar[], wstring))
+ {
+ enum str = to!S("hello world");
+ static assert(isSafe!(() => strideBack(str, 0)));
+ static assert(isSafe!(() => strideBack(str) ));
+ static assert((functionAttributes!(() => strideBack(str, 0)) & FunctionAttribute.pure_) != 0);
+ static assert((functionAttributes!(() => strideBack(str) ) & FunctionAttribute.pure_) != 0);
+ }
+ });
+}
+
+/// Ditto
+uint strideBack(S)(auto ref S str, size_t index)
+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");
+ assert(index > 0, "Not the end of the UTF-32 sequence");
+ return 1;
+}
+
+/// Ditto
+uint strideBack(S)(auto ref S str)
+if (isBidirectionalRange!S && is(Unqual!(ElementEncodingType!S) == dchar))
+{
+ assert(!str.empty, "Empty UTF-32 sequence");
+ return 1;
+}
+
+@system unittest
+{
+ import core.exception : AssertError;
+ import std.conv : to;
+ import std.exception;
+ import std.string : format;
+ static void test(dstring s, dchar c, size_t i = size_t.max, size_t line = __LINE__)
+ {
+ enforce(strideBack(s, i == size_t.max ? s.length : i) == codeLength!dchar(c),
+ new AssertError(format("Unit test failure string: %s", s), __FILE__, line));
+
+ enforce(strideBack(RandomCU!dchar(s), i == size_t.max ? s.length : 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(strideBack(refRandom, i == size_t.max ? s.length : 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 == size_t.max)
+ {
+ enforce(strideBack(s) == codeLength!dchar(c),
+ new AssertError(format("Unit test failure string code length: %s", s), __FILE__, line));
+
+ enforce(strideBack(BidirCU!dchar(s)) == codeLength!dchar(c),
+ new AssertError(format("Unit test failure range code length: %s", s), __FILE__, line));
+
+ auto refBidir = new RefBidirCU!dchar(s);
+ immutable bidirLen = refBidir.length;
+ enforce(strideBack(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", 'f');
+ test("\U00010143\u0100\U00010143hello", 'o', 8);
+ test("\U00010143\u0100\U00010143hello", 'l', 7);
+ test("\U00010143\u0100\U00010143hello", 'l', 6);
+ test("\U00010143\u0100\U00010143hello", 'e', 5);
+ test("\U00010143\u0100\U00010143hello", 'h', 4);
+ test("\U00010143\u0100\U00010143hello", '\U00010143', 3);
+ test("\U00010143\u0100\U00010143hello", '\u0100', 2);
+ test("\U00010143\u0100\U00010143hello", '\U00010143', 1);
+
+ foreach (S; AliasSeq!(dchar[], const dchar[], dstring))
+ {
+ enum str = to!S("hello world");
+ static assert(isSafe!(() => strideBack(str, 0)));
+ static assert(isSafe!(() => strideBack(str) ));
+ static assert((functionAttributes!(() => strideBack(str, 0)) & FunctionAttribute.pure_) != 0);
+ static assert((functionAttributes!(() => strideBack(str) ) & FunctionAttribute.pure_) != 0);
+ }
+ });
+}
+
+
+/++
+ Given $(D index) into $(D str) and assuming that $(D index) is at the start
+ of a UTF sequence, $(D toUCSindex) determines the number of UCS characters
+ up to $(D index). So, $(D index) is the index of a code unit at the
+ beginning of a code point, and the return value is how many code points into
+ the string that that code point is.
+ +/
+size_t toUCSindex(C)(const(C)[] str, size_t index) @safe pure
+if (isSomeChar!C)
+{
+ static if (is(Unqual!C == dchar))
+ return index;
+ else
+ {
+ size_t n = 0;
+ size_t j = 0;
+
+ for (; j < index; ++n)
+ j += stride(str, j);
+
+ if (j > index)
+ {
+ static if (is(Unqual!C == char))
+ throw new UTFException("Invalid UTF-8 sequence", index);
+ else
+ throw new UTFException("Invalid UTF-16 sequence", index);
+ }
+
+ return n;
+ }
+}
+
+///
+@safe unittest
+{
+ assert(toUCSindex(`hello world`, 7) == 7);
+ assert(toUCSindex(`hello world`w, 7) == 7);
+ assert(toUCSindex(`hello world`d, 7) == 7);
+
+ assert(toUCSindex(`Ma Chérie`, 7) == 6);
+ assert(toUCSindex(`Ma Chérie`w, 7) == 7);
+ assert(toUCSindex(`Ma Chérie`d, 7) == 7);
+
+ assert(toUCSindex(`さいごの果実 / ミツバチと科学者`, 9) == 3);
+ assert(toUCSindex(`さいごの果実 / ミツバチと科学者`w, 9) == 9);
+ assert(toUCSindex(`さいごの果実 / ミツバチと科学者`d, 9) == 9);
+}
+
+
+/++
+ Given a UCS index $(D n) into $(D str), returns the UTF index.
+ So, $(D n) is how many code points into the string the code point is, and
+ the array index of the code unit is returned.
+ +/
+size_t toUTFindex(C)(const(C)[] str, size_t n) @safe pure
+if (isSomeChar!C)
+{
+ static if (is(Unqual!C == dchar))
+ {
+ return n;
+ }
+ else
+ {
+ size_t i;
+ while (n--)
+ {
+ i += stride(str, i);
+ }
+ return i;
+ }
+}
+
+///
+@safe unittest
+{
+ assert(toUTFindex(`hello world`, 7) == 7);
+ assert(toUTFindex(`hello world`w, 7) == 7);
+ assert(toUTFindex(`hello world`d, 7) == 7);
+
+ assert(toUTFindex(`Ma Chérie`, 6) == 7);
+ assert(toUTFindex(`Ma Chérie`w, 7) == 7);
+ assert(toUTFindex(`Ma Chérie`d, 7) == 7);
+
+ assert(toUTFindex(`さいごの果実 / ミツバチと科学者`, 3) == 9);
+ assert(toUTFindex(`さいごの果実 / ミツバチと科学者`w, 9) == 9);
+ assert(toUTFindex(`さいごの果実 / ミツバチと科学者`d, 9) == 9);
+}
+
+
+/* =================== 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
+ well-formed, then a $(D UTFException) is thrown and $(D index) remains
+ unchanged.
+
+ decode will only work with strings and random access ranges of code units
+ 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 and useReplacementDchar is $(D No.useReplacementDchar)
+ +/
+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");
+}
+out (result)
+{
+ assert(isValidDchar(result));
+}
+body
+{
+ if (str[index] < codeUnitLimit!S)
+ return str[index++];
+ else
+ return decodeImpl!(true, useReplacementDchar)(str, index);
+}
+
+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");
+}
+out (result)
+{
+ assert(isValidDchar(result));
+}
+body
+{
+ if (str[index] < codeUnitLimit!S)
+ return str[index++];
+ else
+ return decodeImpl!(true, useReplacementDchar)(str, index);
+}
+
+/++
+ $(D decodeFront) is a variant of $(LREF decode) which specifically decodes
+ the first code point. Unlike $(LREF decode), $(D decodeFront) accepts any
+ input 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 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
+ the number of code units which were popped off, as it depends on the
+ 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(UseReplacementDchar useReplacementDchar = No.useReplacementDchar, S)(
+ref S str, out size_t numCodeUnits)
+if (!isSomeString!S && isInputRange!S && isSomeChar!(ElementType!S))
+in
+{
+ assert(!str.empty);
+}
+out (result)
+{
+ assert(isValidDchar(result));
+}
+body
+{
+ immutable fst = str.front;
+
+ if (fst < codeUnitLimit!S)
+ {
+ str.popFront();
+ numCodeUnits = 1;
+ return fst;
+ }
+ else
+ {
+ //@@@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, useReplacementDchar)(str, numCodeUnits);
+
+ // The other range types were already popped by decodeImpl.
+ static if (isRandomAccessRange!S && hasSlicing!S && hasLength!S)
+ str = str[numCodeUnits .. str.length];
+
+ return retval;
+ }
+}
+
+dchar decodeFront(UseReplacementDchar useReplacementDchar = No.useReplacementDchar, S)(
+ref S str, out size_t numCodeUnits) @trusted pure
+if (isSomeString!S)
+in
+{
+ assert(!str.empty);
+}
+out (result)
+{
+ assert(isValidDchar(result));
+}
+body
+{
+ if (str[0] < codeUnitLimit!S)
+ {
+ numCodeUnits = 1;
+ immutable retval = str[0];
+ str = str[1 .. $];
+ return retval;
+ }
+ else
+ {
+ immutable retval = decodeImpl!(true, useReplacementDchar)(str, numCodeUnits);
+ str = str[numCodeUnits .. $];
+ return retval;
+ }
+}
+
+/++ Ditto +/
+dchar decodeFront(UseReplacementDchar useReplacementDchar = No.useReplacementDchar, S)(ref S str)
+if (isInputRange!S && isSomeChar!(ElementType!S))
+{
+ size_t numCodeUnits;
+ return decodeFront!useReplacementDchar(str, numCodeUnits);
+}
+
+/++
+ $(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
+{
+ 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
+ {
+ 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;
+ }
+}
+
+/++ 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
+{
+ 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 = AliasSeq!((1 << 7) - 1, (1 << 11) - 1, (1 << 16) - 1, (1 << 21) - 1);
+
+ static if (is(S : const char[]))
+ 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@@@ 14447 forces this to be done outside of decodeImpl
+ //enum canIndex = is(S : const char[]) || (isRandomAccessRange!S && hasSlicing!S && hasLength!S);
+
+ static if (canIndex)
+ {
+ immutable length = str.length - index;
+ ubyte fst = pstr[0];
+ }
+ else
+ {
+ ubyte fst = pstr.front;
+ pstr.popFront();
+ }
+
+ static if (!useReplacementDchar)
+ {
+ static if (canIndex)
+ {
+ static UTFException exception(S)(S str, string msg)
+ {
+ uint[4] sequence = void;
+ size_t 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
+ {
+ //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()
+ {
+ 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");
+ }
+ }
+
+ if ((fst & 0b1100_0000) != 0b1100_0000)
+ {
+ static if (useReplacementDchar)
+ {
+ ++index; // always consume bad input to avoid infinite loops
+ return replacementDchar;
+ }
+ else
+ 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; AliasSeq!(1, 2, 3))
+ {
+
+ static if (canIndex)
+ {
+ if (i == length)
+ {
+ static if (useReplacementDchar)
+ {
+ index += i;
+ return replacementDchar;
+ }
+ else
+ throw outOfBounds();
+ }
+ }
+ else
+ {
+ if (pstr.empty)
+ {
+ static if (useReplacementDchar)
+ {
+ index += i;
+ return replacementDchar;
+ }
+ else
+ throw outOfBounds();
+ }
+ }
+
+ static if (canIndex)
+ tmp = pstr[i];
+ else
+ {
+ tmp = pstr.front;
+ pstr.popFront();
+ }
+
+ if ((tmp & 0xC0) != 0x80)
+ {
+ static if (useReplacementDchar)
+ {
+ index += i + 1;
+ return replacementDchar;
+ }
+ else
+ throw invalidUTF();
+ }
+
+ d = (d << 6) | (tmp & 0x3F);
+ fst <<= 1;
+
+ if (!(fst & 0x80)) // no more bytes
+ {
+ d &= bitMask[i]; // mask out control bits
+
+ // overlong, could have been encoded with i bytes
+ if ((d & ~bitMask[i - 1]) == 0)
+ {
+ 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))
+ {
+ static if (useReplacementDchar)
+ {
+ index += i + 1;
+ return replacementDchar;
+ }
+ else
+ throw invalidUTF();
+ }
+ }
+
+ index += i + 1;
+ static if (i == 3)
+ {
+ if (d > dchar.max)
+ {
+ static if (useReplacementDchar)
+ d = replacementDchar;
+ else
+ throw invalidUTF();
+ }
+ }
+ return d;
+ }
+ }
+
+ 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, 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;
+ else static if (isRandomAccessRange!S && hasSlicing!S && hasLength!S)
+ auto pstr = str[index .. str.length];
+ else
+ alias pstr = str;
+
+ //@@@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)
+ {
+ immutable length = str.length - index;
+ uint u = pstr[0];
+ }
+ else
+ {
+ uint u = pstr.front;
+ pstr.popFront();
+ }
+
+ static if (!useReplacementDchar)
+ {
+ UTFException exception(string msg)
+ {
+ static if (canIndex)
+ return new UTFException(msg).setSequence(pstr[0]);
+ else
+ return new UTFException(msg);
+ }
+ }
+
+ // The < case must be taken care of before decodeImpl is called.
+ assert(u >= 0xD800);
+
+ if (u <= 0xDBFF)
+ {
+ static if (canIndex)
+ immutable onlyOneCodeUnit = length == 1;
+ else
+ immutable onlyOneCodeUnit = pstr.empty;
+
+ if (onlyOneCodeUnit)
+ {
+ 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];
+ else
+ {
+ immutable uint u2 = pstr.front;
+ pstr.popFront();
+ }
+
+ if (u2 < 0xDC00 || u2 > 0xDFFF)
+ {
+ 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)
+ {
+ 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;
+}
+
+@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, 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)
+ {
+ 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
+ {
+ dchar dc = pstr.front;
+ if (!isValidDchar(dc))
+ {
+ static if (useReplacementDchar)
+ dc = replacementDchar;
+ else
+ throw new UTFException("Invalid UTF-32 value").setSequence(dc);
+ }
+ ++index;
+ pstr.popFront();
+ 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 core.exception : AssertError;
+ import std.string : format;
+
+ static if (hasLength!R)
+ immutable lenBefore = range.length;
+
+ static if (isRandomAccessRange!R)
+ {
+ {
+ immutable result = decode(range, index);
+ enforce(result == expectedChar,
+ new AssertError(format("decode: Wrong character: %s", result), __FILE__, line));
+ enforce(index == expectedIndex,
+ new AssertError(format("decode: Wrong index: %s", index), __FILE__, line));
+ static if (hasLength!R)
+ {
+ enforce(range.length == lenBefore,
+ new AssertError(format("decode: length changed: %s", range.length), __FILE__, line));
+ }
+ }
+ }
+}
+
+version (unittest) private void testDecodeFront(R)(ref R range,
+ dchar expectedChar,
+ size_t expectedNumCodeUnits,
+ size_t line = __LINE__)
+{
+ import core.exception : AssertError;
+ import std.string : format;
+
+ static if (hasLength!R)
+ immutable lenBefore = range.length;
+
+ size_t numCodeUnits;
+ immutable result = decodeFront(range, numCodeUnits);
+ enforce(result == expectedChar,
+ new AssertError(format("decodeFront: Wrong character: %s", result), __FILE__, line));
+ enforce(numCodeUnits == expectedNumCodeUnits,
+ new AssertError(format("decodeFront: Wrong numCodeUnits: %s", numCodeUnits), __FILE__, line));
+
+ static if (hasLength!R)
+ {
+ enforce(range.length == lenBefore - numCodeUnits,
+ new AssertError(format("decodeFront: wrong length: %s", range.length), __FILE__, line));
+ }
+}
+
+version (unittest) private void testDecodeBack(R)(ref R range,
+ dchar expectedChar,
+ 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 core.exception : AssertError;
+ import std.string : format;
+
+ 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 core.exception : AssertError;
+ import std.string : format;
+
+ immutable initialIndex = index;
+
+ static if (hasLength!R)
+ immutable lenBefore = range.length;
+
+ static if (isRandomAccessRange!R)
+ {
+ assertThrown!UTFException(decode(range, index), null, __FILE__, line);
+ enforce(index == initialIndex,
+ new AssertError(format("decode: Wrong index: %s", index), __FILE__, line));
+ static if (hasLength!R)
+ {
+ enforce(range.length == lenBefore,
+ new AssertError(format("decode: length changed:", range.length), __FILE__, line));
+ }
+ }
+
+ if (initialIndex == 0)
+ assertThrown!UTFException(decodeFront(range, index), null, __FILE__, line);
+}
+
+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 core.exception : AssertError;
+ import std.string : format;
+
+ 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;
+
+ assertCTFEable!(
+ {
+ 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")));
+
+ {
+ auto range = S("abcd");
+ testDecode(range, 0, 'a', 1);
+ testDecode(range, 1, 'b', 2);
+ testDecodeFront(range, 'a', 1);
+ testDecodeFront(range, 'b', 1);
+ assert(decodeFront(range) == 'c');
+ assert(decodeFront(range) == 'd');
+ }
+
+ {
+ auto range = S("ウェブサイト");
+ testDecode(range, 0, 'ウ', 3);
+ testDecode(range, 3, 'ェ', 6);
+ testDecodeFront(range, 'ウ', 3);
+ testDecodeFront(range, 'ェ', 3);
+ assert(decodeFront(range) == 'ブ');
+ assert(decodeFront(range) == 'サ');
+ }
+
+ {
+ 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",
+ "\xE0\x80\x8A",
+ "\xF0\x80\x80\x8A",
+ "\xF8\x80\x80\x80\x8A",
+ "\xFC\x80\x80\x80\x80\x8A"])
+ {
+ testBadDecode(S(str), 0);
+ testBadDecode(S(str), 1);
+ testBadDecodeBack(S(str));
+ }
+
+ //Invalid UTF-8 sequence where the first code unit is valid.
+ 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.
+ 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));
+ }
+ }
+ });
+}
+
+@system unittest
+{
+ import std.conv : to;
+ import std.exception;
+ assertCTFEable!(
+ {
+ foreach (S; AliasSeq!(to!wstring, InputCU!wchar, RandomCU!wchar,
+ (wstring s) => new RefBidirCU!wchar(s),
+ (wstring s) => new RefRandomCU!wchar(s)))
+ {
+ 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);
+
+ testBadDecodeBack(S([ cast(wchar) 0xD801 ]));
+ testBadDecodeBack(S([ cast(wchar) 0x0010, cast(wchar) 0xD800 ]));
+
+ {
+ auto range = S("ウェブサイト");
+ testDecode(range, 0, 'ウ', 1);
+ testDecode(range, 1, 'ェ', 2);
+ testDecodeFront(range, 'ウ', 1);
+ testDecodeFront(range, 'ェ', 1);
+ assert(decodeFront(range) == 'ブ');
+ assert(decodeFront(range) == 'サ');
+ }
+
+ {
+ auto range = S("ウェブサイト");
+ testDecodeBack(range, 'ト', 1);
+ testDecodeBack(range, 'イ', 1);
+ testDecodeBack(range, 'サ', 1);
+ testDecodeBack(range, 'ブ', 1);
+ }
+ }
+
+ 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);
+ }
+ });
+}
+
+@system unittest
+{
+ import std.conv : to;
+ import std.exception;
+ assertCTFEable!(
+ {
+ foreach (S; AliasSeq!(to!dstring, RandomCU!dchar, InputCU!dchar,
+ (dstring s) => new RefBidirCU!dchar(s),
+ (dstring s) => new RefRandomCU!dchar(s)))
+ {
+ 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);
+
+ testBadDecodeBack(S([cast(dchar) 0xD800]));
+ testBadDecodeBack(S([cast(dchar) 0xDFFE]));
+ testBadDecodeBack(S([cast(dchar) 0x110000]));
+
+ {
+ auto range = S("ウェブサイト");
+ testDecode(range, 0, 'ウ', 1);
+ testDecode(range, 1, 'ェ', 2);
+ testDecodeFront(range, 'ウ', 1);
+ testDecodeFront(range, 'ェ', 1);
+ assert(decodeFront(range) == 'ブ');
+ assert(decodeFront(range) == 'サ');
+ }
+
+ {
+ auto range = S("ウェブサイト");
+ testDecodeBack(range, 'ト', 1);
+ testDecodeBack(range, 'イ', 1);
+ testDecodeBack(range, 'サ', 1);
+ testDecodeBack(range, 'ブ', 1);
+ }
+ }
+
+ 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]);
+ 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);
+ }
+ });
+}
+
+@safe unittest
+{
+ import std.exception;
+ assertCTFEable!(
+ {
+ 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; 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);
+ }
+ });
+}
+
+@safe unittest
+{
+ import std.exception;
+ char[4] val;
+ val[0] = 0b1111_0111;
+ val[1] = 0b1011_1111;
+ val[2] = 0b1011_1111;
+ val[3] = 0b1011_1111;
+ size_t i = 0;
+ assertThrown!UTFException((){ dchar ch = decode(val[], i); }());
+}
+/* =================== Encode ======================= */
+
+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(UseReplacementDchar useReplacementDchar = No.useReplacementDchar)(
+ out char[4] buf, dchar c) @safe pure
+{
+ if (c <= 0x7F)
+ {
+ assert(isValidDchar(c));
+ buf[0] = cast(char) c;
+ return 1;
+ }
+ if (c <= 0x7FF)
+ {
+ assert(isValidDchar(c));
+ buf[0] = cast(char)(0xC0 | (c >> 6));
+ buf[1] = cast(char)(0x80 | (c & 0x3F));
+ return 2;
+ }
+ if (c <= 0xFFFF)
+ {
+ if (0xD800 <= c && c <= 0xDFFF)
+ 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));
+ return 3;
+ }
+ if (c <= 0x10FFFF)
+ {
+ assert(isValidDchar(c));
+ 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 4;
+ }
+
+ assert(!isValidDchar(c));
+ c = _utfException!useReplacementDchar("Encoding an invalid code point in UTF-8", c);
+ goto L3;
+}
+
+@safe unittest
+{
+ import std.exception;
+ assertCTFEable!(
+ {
+ char[4] buf;
+
+ assert(encode(buf, '\u0000') == 1 && buf[0 .. 1] == "\u0000");
+ assert(encode(buf, '\u007F') == 1 && buf[0 .. 1] == "\u007F");
+ assert(encode(buf, '\u0080') == 2 && buf[0 .. 2] == "\u0080");
+ assert(encode(buf, '\u07FF') == 2 && buf[0 .. 2] == "\u07FF");
+ assert(encode(buf, '\u0800') == 3 && buf[0 .. 3] == "\u0800");
+ assert(encode(buf, '\uD7FF') == 3 && buf[0 .. 3] == "\uD7FF");
+ assert(encode(buf, '\uE000') == 3 && buf[0 .. 3] == "\uE000");
+ assert(encode(buf, 0xFFFE) == 3 && buf[0 .. 3] == "\xEF\xBF\xBE");
+ assert(encode(buf, 0xFFFF) == 3 && buf[0 .. 3] == "\xEF\xBF\xBF");
+ 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));
+
+ assert(encode!(Yes.useReplacementDchar)(buf, cast(dchar) 0x110000) == buf.stride);
+ assert(buf.front == replacementDchar);
+ });
+}
+
+
+/// Ditto
+size_t encode(UseReplacementDchar useReplacementDchar = No.useReplacementDchar)(
+ out wchar[2] buf, dchar c) @safe pure
+{
+ if (c <= 0xFFFF)
+ {
+ if (0xD800 <= c && c <= 0xDFFF)
+ c = _utfException!useReplacementDchar("Encoding an isolated surrogate code point in UTF-16", c);
+
+ assert(isValidDchar(c));
+ L1:
+ buf[0] = cast(wchar) c;
+ return 1;
+ }
+ if (c <= 0x10FFFF)
+ {
+ assert(isValidDchar(c));
+ buf[0] = cast(wchar)((((c - 0x10000) >> 10) & 0x3FF) + 0xD800);
+ buf[1] = cast(wchar)(((c - 0x10000) & 0x3FF) + 0xDC00);
+ return 2;
+ }
+
+ c = _utfException!useReplacementDchar("Encoding an invalid code point in UTF-16", c);
+ goto L1;
+}
+
+@safe unittest
+{
+ import std.exception;
+ assertCTFEable!(
+ {
+ wchar[2] buf;
+
+ assert(encode(buf, '\u0000') == 1 && buf[0 .. 1] == "\u0000");
+ assert(encode(buf, '\uD7FF') == 1 && buf[0 .. 1] == "\uD7FF");
+ assert(encode(buf, '\uE000') == 1 && buf[0 .. 1] == "\uE000");
+ assert(encode(buf, 0xFFFE) == 1 && buf[0] == 0xFFFE);
+ assert(encode(buf, 0xFFFF) == 1 && buf[0] == 0xFFFF);
+ 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));
+
+ assert(encode!(Yes.useReplacementDchar)(buf, cast(dchar) 0x110000) == buf.stride);
+ assert(buf.front == replacementDchar);
+ });
+}
+
+
+/// Ditto
+size_t encode(UseReplacementDchar useReplacementDchar = No.useReplacementDchar)(
+ out 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);
+ });
+}
+
+
+/++
+ Encodes $(D c) in $(D str)'s encoding and appends it to $(D str).
+
+ Throws:
+ $(D UTFException) if $(D c) is not a valid UTF code point.
+ +/
+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;
+ }
+ else
+ {
+ char[4] buf;
+ uint L;
+
+ if (c <= 0x7FF)
+ {
+ assert(isValidDchar(c));
+ buf[0] = cast(char)(0xC0 | (c >> 6));
+ buf[1] = cast(char)(0x80 | (c & 0x3F));
+ L = 2;
+ }
+ else if (c <= 0xFFFF)
+ {
+ if (0xD800 <= c && c <= 0xDFFF)
+ 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));
+ L = 3;
+ }
+ else if (c <= 0x10FFFF)
+ {
+ assert(isValidDchar(c));
+ 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));
+ L = 4;
+ }
+ else
+ {
+ assert(!isValidDchar(c));
+ c = _utfException!useReplacementDchar("Encoding an invalid code point in UTF-8", c);
+ goto L3;
+ }
+ r ~= buf[0 .. L];
+ }
+ str = r;
+}
+
+@safe unittest
+{
+ import std.exception;
+
+ assertCTFEable!(
+ {
+ char[] s = "abcd".dup;
+ encode(s, cast(dchar)'a');
+ assert(s.length == 5);
+ assert(s == "abcda");
+
+ encode(s, cast(dchar)'\u00A9');
+ assert(s.length == 7);
+ assert(s == "abcda\xC2\xA9");
+ //assert(s == "abcda\u00A9"); // BUG: fix compiler
+
+ encode(s, cast(dchar)'\u2260');
+ assert(s.length == 10);
+ assert(s == "abcda\xC2\xA9\xE2\x89\xA0");
+ });
+}
+
+@safe unittest
+{
+ import std.exception;
+ assertCTFEable!(
+ {
+ char[] buf;
+
+ encode(buf, '\u0000'); assert(buf[0 .. $] == "\u0000");
+ encode(buf, '\u007F'); assert(buf[1 .. $] == "\u007F");
+ encode(buf, '\u0080'); assert(buf[2 .. $] == "\u0080");
+ encode(buf, '\u07FF'); assert(buf[4 .. $] == "\u07FF");
+ encode(buf, '\u0800'); assert(buf[6 .. $] == "\u0800");
+ encode(buf, '\uD7FF'); assert(buf[9 .. $] == "\uD7FF");
+ encode(buf, '\uE000'); assert(buf[12 .. $] == "\uE000");
+ encode(buf, 0xFFFE); assert(buf[15 .. $] == "\xEF\xBF\xBE");
+ encode(buf, 0xFFFF); assert(buf[18 .. $] == "\xEF\xBF\xBF");
+ 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));
+
+ assert(buf.back != replacementDchar);
+ encode!(Yes.useReplacementDchar)(buf, cast(dchar) 0x110000);
+ assert(buf.back == replacementDchar);
+ });
+}
+
+/// ditto
+void encode(UseReplacementDchar useReplacementDchar = No.useReplacementDchar)(
+ ref wchar[] str, dchar c) @safe pure
+{
+ wchar[] r = str;
+
+ if (c <= 0xFFFF)
+ {
+ if (0xD800 <= c && c <= 0xDFFF)
+ c = _utfException!useReplacementDchar("Encoding an isolated surrogate code point in UTF-16", c);
+
+ assert(isValidDchar(c));
+ L1:
+ r ~= cast(wchar) c;
+ }
+ else if (c <= 0x10FFFF)
+ {
+ wchar[2] buf;
+
+ assert(isValidDchar(c));
+ buf[0] = cast(wchar)((((c - 0x10000) >> 10) & 0x3FF) + 0xD800);
+ buf[1] = cast(wchar)(((c - 0x10000) & 0x3FF) + 0xDC00);
+ r ~= buf;
+ }
+ else
+ {
+ assert(!isValidDchar(c));
+ c = _utfException!useReplacementDchar("Encoding an invalid code point in UTF-16", c);
+ goto L1;
+ }
+
+ str = r;
+}
+
+@safe unittest
+{
+ import std.exception;
+ assertCTFEable!(
+ {
+ wchar[] buf;
+
+ encode(buf, '\u0000'); assert(buf[0] == '\u0000');
+ encode(buf, '\uD7FF'); assert(buf[1] == '\uD7FF');
+ encode(buf, '\uE000'); assert(buf[2] == '\uE000');
+ encode(buf, 0xFFFE); assert(buf[3] == 0xFFFE);
+ encode(buf, 0xFFFF); assert(buf[4] == 0xFFFF);
+ 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));
+
+ assert(buf.back != replacementDchar);
+ encode!(Yes.useReplacementDchar)(buf, cast(dchar) 0x110000);
+ assert(buf.back == replacementDchar);
+ });
+}
+
+/// ditto
+void encode(UseReplacementDchar useReplacementDchar = No.useReplacementDchar)(
+ ref dchar[] str, 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));
+ str ~= c;
+}
+
+@safe unittest
+{
+ import std.exception;
+ assertCTFEable!(
+ {
+ dchar[] buf;
+
+ encode(buf, '\u0000'); assert(buf[0] == '\u0000');
+ encode(buf, '\uD7FF'); assert(buf[1] == '\uD7FF');
+ encode(buf, '\uE000'); assert(buf[2] == '\uE000');
+ encode(buf, 0xFFFE ); assert(buf[3] == 0xFFFE);
+ 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));
+
+ assert(buf.back != replacementDchar);
+ encode!(Yes.useReplacementDchar)(buf, cast(dchar) 0x110000);
+ assert(buf.back == replacementDchar);
+ });
+}
+
+
+/++
+ Returns the number of code units that are required to encode the code point
+ $(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)
+{
+ static if (C.sizeof == 1)
+ {
+ if (c <= 0x7F) return 1;
+ if (c <= 0x7FF) return 2;
+ if (c <= 0xFFFF) return 3;
+ if (c <= 0x10FFFF) return 4;
+ assert(false);
+ }
+ else static if (C.sizeof == 2)
+ {
+ return c <= 0xFFFF ? 1 : 2;
+ }
+ else
+ {
+ static assert(C.sizeof == 4);
+ return 1;
+ }
+}
+
+///
+@safe pure nothrow @nogc unittest
+{
+ assert(codeLength!char('a') == 1);
+ assert(codeLength!wchar('a') == 1);
+ assert(codeLength!dchar('a') == 1);
+
+ assert(codeLength!char('\U0010FFFF') == 4);
+ assert(codeLength!wchar('\U0010FFFF') == 2);
+ assert(codeLength!dchar('\U0010FFFF') == 1);
+}
+
+
+/++
+ Returns the number of code units that are required to encode $(D str)
+ 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 && !isInfinite!InputRange && is(ElementType!InputRange : dchar))
+{
+ alias EncType = Unqual!(ElementEncodingType!InputRange);
+ static if (isSomeString!InputRange && is(EncType == C) && is(typeof(input.length)))
+ return input.length;
+ else
+ {
+ size_t total = 0;
+
+ foreach (dchar c; input)
+ total += codeLength!C(c);
+
+ return total;
+ }
+}
+
+///
+@safe unittest
+{
+ import std.conv : to;
+ assert(codeLength!char("hello world") ==
+ to!string("hello world").length);
+ assert(codeLength!wchar("hello world") ==
+ to!wstring("hello world").length);
+ assert(codeLength!dchar("hello world") ==
+ to!dstring("hello world").length);
+
+ assert(codeLength!char(`プログラミング`) ==
+ to!string(`プログラミング`).length);
+ assert(codeLength!wchar(`プログラミング`) ==
+ to!wstring(`プログラミング`).length);
+ assert(codeLength!dchar(`プログラミング`) ==
+ to!dstring(`プログラミング`).length);
+
+ string haystack = `Être sans la verité, ça, ce ne serait pas bien.`;
+ wstring needle = `Être sans la verité`;
+ assert(haystack[codeLength!char(needle) .. $] ==
+ `, ça, ce ne serait pas bien.`);
+}
+
+@safe unittest
+{
+ import std.algorithm.iteration : filter;
+ import std.conv : to;
+ import std.exception;
+
+ assertCTFEable!(
+ {
+ foreach (S; AliasSeq!( char[], const char[], string,
+ wchar[], const wchar[], wstring,
+ dchar[], const dchar[], dstring))
+ {
+ 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);
+ assert(codeLength!C(to!S(`ウェブサイト@La_Verité.com`)) ==
+ to!(C[])(`ウェブサイト@La_Verité.com`).length);
+ assert(codeLength!C(to!S(`ウェブサイト@La_Verité.com`).filter!(x => true)()) ==
+ to!(C[])(`ウェブサイト@La_Verité.com`).length);
+ }
+ }
+ });
+}
+
+/+
+Internal helper function:
+
+Returns true if it is safe to search for the Codepoint $(D c) inside
+code units, without decoding.
+
+This is a runtime check that is used an optimization in various functions,
+particularly, in $(D std.string).
+ +/
+package bool canSearchInCodeUnits(C)(dchar c)
+if (isSomeChar!C)
+{
+ static if (C.sizeof == 1)
+ return c <= 0x7F;
+ else static if (C.sizeof == 2)
+ return c <= 0xD7FF || (0xE000 <= c && c <= 0xFFFF);
+ else static if (C.sizeof == 4)
+ return true;
+ else
+ static assert(0);
+}
+@safe unittest
+{
+ assert( canSearchInCodeUnits! char('a'));
+ assert( canSearchInCodeUnits!wchar('a'));
+ assert( canSearchInCodeUnits!dchar('a'));
+ assert(!canSearchInCodeUnits! char('ö')); //Important test: ö <= 0xFF
+ assert(!canSearchInCodeUnits! char(cast(char)'ö')); //Important test: ö <= 0xFF
+ assert( canSearchInCodeUnits!wchar('ö'));
+ assert( canSearchInCodeUnits!dchar('ö'));
+ assert(!canSearchInCodeUnits! char('日'));
+ assert( canSearchInCodeUnits!wchar('日'));
+ assert( canSearchInCodeUnits!dchar('日'));
+ assert(!canSearchInCodeUnits!wchar(cast(wchar) 0xDA00));
+ assert( canSearchInCodeUnits!dchar(cast(dchar) 0xDA00));
+ assert(!canSearchInCodeUnits! char('\U00010001'));
+ assert(!canSearchInCodeUnits!wchar('\U00010001'));
+ assert( canSearchInCodeUnits!dchar('\U00010001'));
+}
+
+/* =================== Validation ======================= */
+
+/++
+ Checks to see if $(D str) is well-formed unicode or not.
+
+ Throws:
+ $(D UTFException) if $(D str) is not well-formed.
+ +/
+void validate(S)(in S str) @safe pure
+if (isSomeString!S)
+{
+ immutable len = str.length;
+ for (size_t i = 0; i < len; )
+ {
+ decode(str, i);
+ }
+}
+
+
+@safe unittest // bugzilla 12923
+{
+ import std.exception;
+ assertThrown((){
+ char[3]a=[167, 133, 175];
+ validate(a[]);
+ }());
+}
+
+//@@@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
+{
+ const sz = encode!(Yes.useReplacementDchar)(buf, c);
+ return buf[0 .. sz];
+}
+
+/**
+ * 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(S)(S s)
+if (isInputRange!S && !isInfinite!S && isSomeChar!(ElementEncodingType!S))
+{
+ return toUTFImpl!string(s);
+}
+
+///
+@safe pure unittest
+{
+ import std.algorithm.comparison : equal;
+
+ // The ö is represented by two UTF-8 code units
+ assert("Hellø"w.toUTF8.equal(['H', 'e', 'l', 'l', 0xC3, 0xB8]));
+
+ // 𐐷 is four code units in UTF-8
+ assert("𐐷"d.toUTF8.equal([0xF0, 0x90, 0x90, 0xB7]));
+}
+
+@system pure unittest
+{
+ import std.algorithm.comparison : equal;
+ import std.internal.test.dummyrange : ReferenceInputRange;
+
+ auto r1 = new ReferenceInputRange!dchar("Hellø");
+ auto r2 = new ReferenceInputRange!dchar("𐐷");
+
+ assert(r1.toUTF8.equal(['H', 'e', 'l', 'l', 0xC3, 0xB8]));
+ assert(r2.toUTF8.equal([0xF0, 0x90, 0x90, 0xB7]));
+}
+
+//@@@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
+{
+ const sz = encode!(Yes.useReplacementDchar)(buf, c);
+ return buf[0 .. sz];
+}
+
+/**
+ * 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(S)(S s)
+if (isInputRange!S && !isInfinite!S && isSomeChar!(ElementEncodingType!S))
+{
+ return toUTFImpl!wstring(s);
+}
+
+///
+@safe pure unittest
+{
+ 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]));
+}
+
+@system pure unittest
+{
+ import std.algorithm.comparison : equal;
+ import std.internal.test.dummyrange : ReferenceInputRange;
+
+ auto r1 = new ReferenceInputRange!dchar("𤭢");
+ auto r2 = new ReferenceInputRange!dchar("𐐷");
+
+ assert(r1.toUTF16.equal([0xD852, 0xDF62]));
+ assert(r2.toUTF16.equal([0xD801, 0xDC37]));
+}
+
+
+/**
+ * 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(S)(S s)
+if (isInputRange!S && !isInfinite!S && isSomeChar!(ElementEncodingType!S))
+{
+ return toUTFImpl!dstring(s);
+}
+
+private T toUTFImpl(T, S)(S s)
+{
+ static if (is(S : T))
+ {
+ return s.idup;
+ }
+ else
+ {
+ import std.array : appender;
+ auto app = appender!T();
+
+ static if (hasLength!S || isSomeString!S)
+ app.reserve(s.length);
+
+ foreach (c; s.byUTF!(Unqual!(ElementEncodingType!T)))
+ app.put(c);
+
+ return app.data;
+ }
+}
+
+/* =================== toUTFz ======================= */
+
+/++
+ Returns a C-style zero-terminated string equivalent to $(D str). $(D str)
+ 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 str.empty) is
+ $(D true), then a string containing only $(D '\0') is returned.
+
+ $(D toUTFz) accepts any type of string and is templated on the type of
+ character pointer that you wish to convert to. It will avoid allocating a
+ new string if it can, but there's a decent chance that it will end up having
+ to allocate a new string - particularly when dealing with character types
+ other than $(D char).
+
+ $(RED Warning 1:) If the result of $(D toUTFz) equals $(D str.ptr), then if
+ anything alters the character one past the end of $(D str) (which is the
+ $(D '\0') character terminating the string), then the string won't be
+ zero-terminated anymore. The most likely scenarios for that are if you
+ append to $(D str) and no reallocation takes place or when $(D str) is a
+ slice of a larger array, and you alter the character in the larger array
+ which is one character past the end of $(D str). Another case where it could
+ occur would be if you had a mutable character array immediately after
+ $(D str) in memory (for example, if they're member variables in a
+ user-defined type with one declared right after the other) and that
+ character array happened to start with $(D '\0'). Such scenarios will never
+ occur if you immediately use the zero-terminated string after calling
+ $(D toUTFz) and the C function using it doesn't keep a reference to it.
+ Also, they are unlikely to occur even if you save the zero-terminated string
+ (the cases above would be among the few examples of where it could happen).
+ However, if you save the zero-terminate string and want to be absolutely
+ certain that the string stays zero-terminated, then simply append a
+ $(D '\0') to the string and use its $(D ptr) property rather than calling
+ $(D toUTFz).
+
+ $(RED Warning 2:) When passing a character pointer 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.
+ +/
+template toUTFz(P)
+{
+ P toUTFz(S)(S str) @safe pure
+ {
+ return toUTFzImpl!(P, S)(str);
+ }
+}
+
+///
+@safe pure unittest
+{
+ auto p1 = toUTFz!(char*)("hello world");
+ auto p2 = toUTFz!(const(char)*)("hello world");
+ auto p3 = toUTFz!(immutable(char)*)("hello world");
+ auto p4 = toUTFz!(char*)("hello world"d);
+ auto p5 = toUTFz!(const(wchar)*)("hello world");
+ auto p6 = toUTFz!(immutable(dchar)*)("hello world"w);
+}
+
+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))
+//immutable(C)[] -> C*, const(C)*, or immutable(C)*
+{
+ if (str.empty)
+ {
+ typeof(*P.init)[] retval = ['\0'];
+
+ auto trustedPtr() @trusted { return retval.ptr; }
+ return trustedPtr();
+ }
+
+ alias C = Unqual!(ElementEncodingType!S);
+
+ //If the P is mutable, then we have to make a copy.
+ static if (is(Unqual!(typeof(*P.init)) == typeof(*P.init)))
+ {
+ return toUTFzImpl!(P, const(C)[])(cast(const(C)[])str);
+ }
+ else
+ {
+ if (!__ctfe)
+ {
+ auto trustedPtrAdd(S s) @trusted { return s.ptr + s.length; }
+ immutable p = trustedPtrAdd(str);
+
+ // Peek past end of str, if it's 0, no conversion necessary.
+ // Note that the compiler will put a 0 past the end of static
+ // strings, and the storage allocator will put a 0 past the end
+ // of newly allocated char[]'s.
+ // Is p dereferenceable? A simple test: if the p points to an
+ // address multiple of 4, then conservatively assume the pointer
+ // 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[0];
+ }
+
+ return toUTFzImpl!(P, const(C)[])(cast(const(C)[])str);
+ }
+}
+
+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))
+//C[] or const(C)[] -> C*, const(C)*, or immutable(C)*
+{
+ alias InChar = ElementEncodingType!S;
+ alias OutChar = typeof(*P.init);
+
+ //const(C)[] -> const(C)* or
+ //C[] -> C* or const(C)*
+ static if (( is(const(Unqual!InChar) == InChar) && is(const(Unqual!OutChar) == OutChar)) ||
+ (!is(const(Unqual!InChar) == InChar) && !is(immutable(Unqual!OutChar) == OutChar)))
+ {
+ if (!__ctfe)
+ {
+ auto trustedPtrAdd(S s) @trusted { return s.ptr + s.length; }
+ auto p = trustedPtrAdd(str);
+
+ if ((cast(size_t) p & 3) && *p == '\0')
+ return &str[0];
+ }
+
+ str ~= '\0';
+ return &str[0];
+ }
+ //const(C)[] -> C* or immutable(C)* or
+ //C[] -> immutable(C)*
+ else
+ {
+ import std.array : uninitializedArray;
+ auto copy = uninitializedArray!(Unqual!OutChar[])(str.length + 1);
+ copy[0 .. $ - 1] = str[];
+ copy[$ - 1] = '\0';
+
+ 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)))
+//C1[], const(C1)[], or immutable(C1)[] -> C2*, const(C2)*, or immutable(C2)*
+{
+ import std.array : appender;
+ auto retval = appender!(typeof(*P.init)[])();
+
+ foreach (dchar c; str)
+ retval.put(c);
+ retval.put('\0');
+
+ return () @trusted { return cast(P) retval.data.ptr; } ();
+}
+
+@safe pure unittest
+{
+ import core.exception : AssertError;
+ import std.algorithm;
+ import std.conv : to;
+ import std.exception;
+ import std.string : format;
+
+ assertCTFEable!(
+ {
+ foreach (S; AliasSeq!(string, wstring, dstring))
+ {
+ alias C = Unqual!(ElementEncodingType!S);
+
+ auto s1 = to!S("hello\U00010143\u0100\U00010143");
+ auto temp = new C[](s1.length + 1);
+ temp[0 .. $ - 1] = s1[0 .. $];
+ temp[$ - 1] = '\n';
+ --temp.length;
+ auto trustedAssumeUnique(T)(T t) @trusted { return assumeUnique(t); }
+ auto s2 = trustedAssumeUnique(temp);
+ assert(s1 == s2);
+
+ void trustedCStringAssert(P, S)(S s) @trusted
+ {
+ auto p = toUTFz!P(s);
+ assert(p[0 .. s.length] == s);
+ assert(p[s.length] == '\0');
+ }
+
+ foreach (P; AliasSeq!(C*, const(C)*, immutable(C)*))
+ {
+ trustedCStringAssert!P(s1);
+ trustedCStringAssert!P(s2);
+ }
+ }
+ });
+
+ static void test(P, S)(S s, size_t line = __LINE__) @trusted
+ {
+ static size_t zeroLen(C)(const(C)* ptr) @trusted
+ {
+ size_t len = 0;
+ while (*ptr != '\0') { ++ptr; ++len; }
+ return len;
+ }
+
+ auto p = toUTFz!P(s);
+ immutable len = zeroLen(p);
+ enforce(cmp(s, p[0 .. len]) == 0,
+ new AssertError(format("Unit test failed: %s %s", P.stringof, S.stringof),
+ __FILE__, line));
+ }
+
+ assertCTFEable!(
+ {
+ foreach (P; AliasSeq!(wchar*, const(wchar)*, immutable(wchar)*,
+ dchar*, const(dchar)*, immutable(dchar)*))
+ {
+ test!P("hello\U00010143\u0100\U00010143");
+ }
+ foreach (P; AliasSeq!( char*, const( char)*, immutable( char)*,
+ dchar*, const(dchar)*, immutable(dchar)*))
+ {
+ test!P("hello\U00010143\u0100\U00010143"w);
+ }
+ foreach (P; AliasSeq!( char*, const( char)*, immutable( char)*,
+ wchar*, const(wchar)*, immutable(wchar)*))
+ {
+ test!P("hello\U00010143\u0100\U00010143"d);
+ }
+ foreach (S; AliasSeq!( char[], const( char)[],
+ wchar[], const(wchar)[],
+ dchar[], const(dchar)[]))
+ {
+ auto s = to!S("hello\U00010143\u0100\U00010143");
+
+ foreach (P; AliasSeq!( char*, const( char)*, immutable( char)*,
+ wchar*, const(wchar)*, immutable(wchar)*,
+ dchar*, const(dchar)*, immutable(dchar)*))
+ {
+ test!P(s);
+ }
+ }
+ });
+}
+
+
+/++
+ $(D toUTF16z) is a convenience function for $(D toUTFz!(const(wchar)*)).
+
+ Encodes string $(D s) into UTF-16 and returns the encoded string.
+ $(D toUTF16z) is suitable for calling the 'W' functions in the Win32 API
+ that take an $(D LPWSTR) or $(D LPCWSTR) argument.
+ +/
+const(wchar)* toUTF16z(C)(const(C)[] str) @safe pure
+if (isSomeChar!C)
+{
+ return toUTFz!(const(wchar)*)(str);
+}
+
+@safe pure unittest
+{
+ 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; AliasSeq!(string, wstring, dstring))
+ assert(toUTF16z(to!S("hello world")) !is null);
+}
+
+
+/* ================================ tests ================================== */
+
+@safe pure unittest
+{
+ import std.exception;
+
+ assertCTFEable!(
+ {
+ assert(toUTF16("hello"c) == "hello");
+ assert(toUTF32("hello"c) == "hello");
+ assert(toUTF8 ("hello"w) == "hello");
+ assert(toUTF32("hello"w) == "hello");
+ assert(toUTF8 ("hello"d) == "hello");
+ assert(toUTF16("hello"d) == "hello");
+
+ assert(toUTF16("hel\u1234o"c) == "hel\u1234o");
+ assert(toUTF32("hel\u1234o"c) == "hel\u1234o");
+ assert(toUTF8 ("hel\u1234o"w) == "hel\u1234o");
+ assert(toUTF32("hel\u1234o"w) == "hel\u1234o");
+ assert(toUTF8 ("hel\u1234o"d) == "hel\u1234o");
+ assert(toUTF16("hel\u1234o"d) == "hel\u1234o");
+
+ assert(toUTF16("he\U0010AAAAllo"c) == "he\U0010AAAAllo");
+ assert(toUTF32("he\U0010AAAAllo"c) == "he\U0010AAAAllo");
+ assert(toUTF8 ("he\U0010AAAAllo"w) == "he\U0010AAAAllo");
+ assert(toUTF32("he\U0010AAAAllo"w) == "he\U0010AAAAllo");
+ assert(toUTF8 ("he\U0010AAAAllo"d) == "he\U0010AAAAllo");
+ assert(toUTF16("he\U0010AAAAllo"d) == "he\U0010AAAAllo");
+ });
+}
+
+
+/++
+ Returns the total number of code points encoded in $(D str).
+
+ Supercedes: This function supercedes $(LREF toUCSindex).
+
+ Standards: Unicode 5.0, ASCII, ISO-8859-1, WINDOWS-1252
+
+ Throws:
+ $(D UTFException) if $(D str) is not well-formed.
+ +/
+size_t count(C)(const(C)[] str) @trusted pure nothrow @nogc
+if (isSomeChar!C)
+{
+ return walkLength(str);
+}
+
+@safe pure nothrow @nogc unittest
+{
+ import std.exception;
+ assertCTFEable!(
+ {
+ assert(count("") == 0);
+ assert(count("a") == 1);
+ assert(count("abc") == 3);
+ assert(count("\u20AC100") == 4);
+ });
+}
+
+
+// Ranges of code units for testing.
+version (unittest)
+{
+ struct InputCU(C)
+ {
+ import std.conv : to;
+ @property bool empty() { return _str.empty; }
+ @property C front() { return _str[0]; }
+ void popFront() { _str = _str[1 .. $]; }
+
+ this(inout(C)[] str)
+ {
+ _str = to!(C[])(str);
+ }
+
+ C[] _str;
+ }
+
+ struct BidirCU(C)
+ {
+ import std.conv : to;
+ @property bool empty() { return _str.empty; }
+ @property C front() { return _str[0]; }
+ void popFront() { _str = _str[1 .. $]; }
+ @property C back() { return _str[$ - 1]; }
+ void popBack() { _str = _str[0 .. $ - 1]; }
+ @property auto save() { return BidirCU(_str); }
+ @property size_t length() { return _str.length; }
+
+ this(inout(C)[] str)
+ {
+ _str = to!(C[])(str);
+ }
+
+ C[] _str;
+ }
+
+ struct RandomCU(C)
+ {
+ import std.conv : to;
+ @property bool empty() { return _str.empty; }
+ @property C front() { return _str[0]; }
+ void popFront() { _str = _str[1 .. $]; }
+ @property C back() { return _str[$ - 1]; }
+ void popBack() { _str = _str[0 .. $ - 1]; }
+ @property auto save() { return RandomCU(_str); }
+ @property size_t length() { return _str.length; }
+ C opIndex(size_t i) { return _str[i]; }
+ auto opSlice(size_t i, size_t j) { return RandomCU(_str[i .. j]); }
+
+ this(inout(C)[] str)
+ {
+ _str = to!(C[])(str);
+ }
+
+ C[] _str;
+ }
+
+ class RefBidirCU(C)
+ {
+ import std.conv : to;
+ @property bool empty() { return _str.empty; }
+ @property C front() { return _str[0]; }
+ void popFront() { _str = _str[1 .. $]; }
+ @property C back() { return _str[$ - 1]; }
+ void popBack() { _str = _str[0 .. $ - 1]; }
+ @property auto save() { return new RefBidirCU(_str); }
+ @property size_t length() { return _str.length; }
+
+ this(inout(C)[] str)
+ {
+ _str = to!(C[])(str);
+ }
+
+ C[] _str;
+ }
+
+ class RefRandomCU(C)
+ {
+ import std.conv : to;
+ @property bool empty() { return _str.empty; }
+ @property C front() { return _str[0]; }
+ void popFront() { _str = _str[1 .. $]; }
+ @property C back() { return _str[$ - 1]; }
+ void popBack() { _str = _str[0 .. $ - 1]; }
+ @property auto save() { return new RefRandomCU(_str); }
+ @property size_t length() { return _str.length; }
+ C opIndex(size_t i) { return _str[i]; }
+ auto opSlice(size_t i, size_t j) { return new RefRandomCU(_str[i .. j]); }
+
+ this(inout(C)[] str)
+ {
+ _str = to!(C[])(str);
+ }
+
+ C[] _str;
+ }
+}
+
+
+/**
+ * Inserted in place of invalid UTF sequences.
+ *
+ * References:
+ * $(LINK http://en.wikipedia.org/wiki/Replacement_character#Replacement_character)
+ */
+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
+ * $(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 = an input range of characters (including strings) or a type that
+ * implicitly converts to a string type.
+ * Returns:
+ * 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 (isAutodecodableString!R ||
+ isInputRange!R && isSomeChar!(ElementEncodingType!R) ||
+ (is(R : const dchar[]) && !isStaticArray!R))
+{
+ 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")))
+ {
+ static struct ByCodeUnitImpl
+ {
+ @safe pure nothrow @nogc:
+
+ @property bool empty() const { return str.length == 0; }
+ @property auto ref front() inout { return str[0]; }
+ void popFront() { str = str[1 .. $]; }
+
+ @property auto save() { return ByCodeUnitImpl(str.save); }
+
+ @property auto ref back() inout { return str[$ - 1]; }
+ void popBack() { str = str[0 .. $-1]; }
+
+ 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 str.length; }
+ alias opDollar = length;
+
+ private:
+ StringTypeOf!R str;
+ }
+
+ 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;
+ }
+}
+
+///
+@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)));
+}
+
+/// `byCodeUnit` does no Unicode decoding
+@safe unittest
+{
+ 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] != 'ë');
+}
+
+@safe pure nothrow @nogc unittest
+{
+ import std.range;
+ {
+ enum testStr = "𐁄𐂌𐃯 hello ディラン";
+ char[testStr.length] s;
+ int i;
+ foreach (c; testStr.byCodeUnit().byCodeUnit())
+ {
+ s[i++] = c;
+ }
+ assert(s == testStr);
+ }
+ {
+ enum testStr = "𐁄𐂌𐃯 hello ディラン"w;
+ wchar[testStr.length] s;
+ int i;
+ foreach (c; testStr.byCodeUnit().byCodeUnit())
+ {
+ s[i++] = c;
+ }
+ assert(s == testStr);
+ }
+ {
+ enum testStr = "𐁄𐂌𐃯 hello ディラン"d;
+ dchar[testStr.length] s;
+ int i;
+ foreach (c; testStr.byCodeUnit().byCodeUnit())
+ {
+ s[i++] = c;
+ }
+ assert(s == testStr);
+ }
+ {
+ auto bcu = "hello".byCodeUnit();
+ assert(bcu.length == 5);
+ assert(bcu[3] == 'l');
+ assert(bcu[2 .. 4][1] == 'l');
+ }
+ {
+ char[5] orig = "hello";
+ auto bcu = orig[].byCodeUnit();
+ bcu.front = 'H';
+ assert(bcu.front == 'H');
+ bcu[1] = 'E';
+ assert(bcu[1] == 'E');
+ }
+ {
+ 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 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 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 == 'μ');
+ }
+ {
+ 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);
+ }
+ {
+ static struct WStringish
+ {
+ wstring s;
+ alias s this;
+ }
+
+ 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);
+ }
+ {
+ 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);
+ }
+ {
+ 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 .. $]; }
+ }
+
+ 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 .. $]; }
+ }
+
+ 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 .. $]; }
+
+ string data;
+ string s;
+ alias s this;
+ }
+
+ 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');
+ }
+ {
+ static struct WRangeAndStringish
+ {
+ 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;
+ }
+
+ 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
+ {
+ 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;
+ }
+
+ 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" }
+
+ 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 }
+
+ 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 }
+
+ 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');
+ }
+
+ static assert(!is(typeof(byCodeUnit("hello")) == string));
+ static assert(!is(typeof(byCodeUnit("hello"w)) == wstring));
+ static assert(is(typeof(byCodeUnit("hello"d)) == dstring));
+
+ static assert(!__traits(compiles, byCodeUnit((char[5]).init)));
+ static assert(!__traits(compiles, byCodeUnit((wchar[5]).init)));
+ static assert(!__traits(compiles, byCodeUnit((dchar[5]).init)));
+
+ enum SEnum : char[5] { a = "hello" }
+ enum WSEnum : wchar[5] { a = "hello"w }
+ enum DSEnum : dchar[5] { a = "hello"d }
+
+ static assert(!__traits(compiles, byCodeUnit(SEnum.a)));
+ static assert(!__traits(compiles, byCodeUnit(WSEnum.a)));
+ static assert(!__traits(compiles, byCodeUnit(DSEnum.a)));
+}
+
+/****************************
+ * 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;
+ int i;
+ foreach (c; "hello".byChar.byChar())
+ {
+ //writefln("[%d] '%c'", i, c);
+ s[i++] = c;
+ }
+ assert(s == "hello");
+ }
+ {
+ char[5+2+3+4+3+3] s;
+ int i;
+ dchar[10] a;
+ a[0 .. 8] = "hello\u07FF\uD7FF\U0010FFFF"d;
+ a[8] = 0xD800; // invalid
+ a[9] = cast(dchar) 0x110000; // invalid
+ foreach (c; a[].byChar())
+ {
+ //writefln("[%d] '%c'", i, c);
+ s[i++] = c;
+ }
+ assert(s == "hello\u07FF\uD7FF\U0010FFFF\uFFFD\uFFFD");
+ }
+ {
+ auto r = "hello"w.byChar();
+ r.popFront();
+ r.popFront();
+ assert(r.front == 'l');
+ }
+ {
+ auto r = "hello"d.byChar();
+ r.popFront();
+ r.popFront();
+ assert(r.front == 'l');
+ }
+ {
+ auto r = "hello"d.byChar();
+ assert(isForwardRange!(typeof(r)));
+ auto s = r.save;
+ r.popFront();
+ assert(s.front == 'h');
+ }
+}
+
+@safe pure nothrow @nogc unittest
+{
+ {
+ wchar[11] s;
+ int i;
+ dchar[10] a;
+ a[0 .. 8] = "hello\u07FF\uD7FF\U0010FFFF"d;
+ a[8] = 0xD800; // invalid
+ a[9] = cast(dchar) 0x110000; // invalid
+ foreach (c; a[].byWchar())
+ {
+ //writefln("[%d] '%c' x%x", i, c, c);
+ s[i++] = c;
+ }
+ foreach (j, wchar c; "hello\u07FF\uD7FF\U0010FFFF\uFFFD\uFFFD"w)
+ {
+ //writefln("[%d] '%c' x%x", j, c, c);
+ }
+ assert(s == "hello\u07FF\uD7FF\U0010FFFF\uFFFD\uFFFD"w);
+ }
+
+ {
+ auto r = "hello".byWchar();
+ r.popFront();
+ r.popFront();
+ assert(r.front == 'l');
+ }
+ {
+ auto r = "hello"d.byWchar();
+ r.popFront();
+ r.popFront();
+ assert(r.front == 'l');
+ }
+ {
+ auto r = "hello"d.byWchar();
+ assert(isForwardRange!(typeof(r)));
+ auto s = r.save;
+ r.popFront();
+ assert(s.front == 'h');
+ }
+}
+
+@safe pure nothrow @nogc unittest
+{
+ {
+ dchar[9] s;
+ int i;
+ string a = "hello\u07FF\uD7FF\U00010000\U0010FFFF"; // 1,2,3,4 byte sequences
+ foreach (c; a.byDchar())
+ {
+ s[i++] = c;
+ }
+ assert(s == "hello\u07FF\uD7FF\U00010000\U0010FFFF"d);
+ }
+ {
+ 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();
+ r.popFront();
+ r.popFront();
+ assert(r.front == 'l');
+ }
+
+ {
+ dchar[8] s;
+ int i;
+ wstring a = "hello\u07FF\uD7FF\U0010FFFF"w;
+ foreach (c; a.byDchar())
+ {
+ //writefln("[%d] '%c' x%x", i, c, c);
+ s[i++] = c;
+ }
+ assert(s == "hello\u07FF\uD7FF\U0010FFFF"d);
+ }
+ {
+ 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;
+ ws[0] = 0xD800;
+ ws[1] = 0xDD00; // correct surrogate pair
+ auto r = ws[].byDchar();
+ assert(!r.empty);
+ assert(r.front == r.front);
+ dchar c = r.front;
+ assert(c == '\U00010100');
+ }
+ {
+ auto r = "hello"w.byDchar();
+ r.popFront();
+ r.popFront();
+ assert(r.front == 'l');
+ }
+
+ {
+ dchar[5] s;
+ int i;
+ dstring a = "hello"d;
+ foreach (c; a.byDchar.byDchar())
+ {
+ //writefln("[%d] '%c' x%x", i, c, c);
+ s[i++] = c;
+ }
+ assert(s == "hello"d);
+ }
+ {
+ auto r = "hello".byDchar();
+ assert(isForwardRange!(typeof(r)));
+ auto s = r.save;
+ r.popFront();
+ assert(s.front == 'h');
+ }
+ {
+ auto r = "hello"w.byDchar();
+ assert(isForwardRange!(typeof(r)));
+ auto s = r.save;
+ r.popFront();
+ assert(s.front == 'h');
+ }
+}
+
+// test pure, @safe, nothrow, @nogc correctness of byChar/byWchar/byDchar,
+// which needs to support ranges with and without those attributes
+
+pure @safe nothrow @nogc unittest
+{
+ dchar[5] s = "hello"d;
+ foreach (c; s[].byChar()) { }
+ foreach (c; s[].byWchar()) { }
+ foreach (c; s[].byDchar()) { }
+}
+
+version (unittest)
+int impureVariable;
+
+@system unittest
+{
+ static struct ImpureThrowingSystemRange(Char)
+ {
+ @property bool empty() const { return true; }
+ @property Char front() const { return Char.init; }
+ void popFront()
+ {
+ impureVariable++;
+ throw new Exception("only for testing nothrow");
+ }
+ }
+
+ foreach (Char; AliasSeq!(char, wchar, dchar))
+ {
+ ImpureThrowingSystemRange!Char range;
+ foreach (c; range.byChar()) { }
+ foreach (c; range.byWchar()) { }
+ 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/libphobos/src/std/uuid.d b/libphobos/src/std/uuid.d
new file mode 100644
index 0000000..c804e8e
--- /dev/null
+++ b/libphobos/src/std/uuid.d
@@ -0,0 +1,1731 @@
+/**
+ * A $(LINK2 http://en.wikipedia.org/wiki/Universally_unique_identifier, UUID), or
+ * $(LINK2 http://en.wikipedia.org/wiki/Universally_unique_identifier, Universally unique identifier),
+ * is intended to uniquely identify information in a distributed environment
+ * without significant central coordination. It can be
+ * used to tag objects with very short lifetimes, or to reliably identify very
+ * persistent objects across a network.
+ *
+$(SCRIPT inhibitQuickIndex = 1;)
+
+$(DIVC quickindex,
+$(BOOKTABLE ,
+$(TR $(TH Category) $(TH Functions)
+)
+$(TR $(TDNW Parsing UUIDs)
+ $(TD $(MYREF parseUUID)
+ $(MYREF UUID)
+ $(MYREF UUIDParsingException)
+ $(MYREF uuidRegex)
+ )
+ )
+$(TR $(TDNW Generating UUIDs)
+ $(TD $(MYREF sha1UUID)
+ $(MYREF randomUUID)
+ $(MYREF md5UUID)
+ )
+ )
+$(TR $(TDNW Using UUIDs)
+ $(TD $(MYREF2 UUID.uuidVersion, uuidVersion)
+ $(MYREF2 UUID.variant, variant)
+ $(MYREF2 UUID.toString, toString)
+ $(MYREF2 UUID.data, data)
+ $(MYREF2 UUID.swap, swap)
+ $(MYREF2 UUID.opEquals, opEquals)
+ $(MYREF2 UUID.opCmp, opCmp)
+ $(MYREF2 UUID.toHash, toHash)
+ )
+ )
+$(TR $(TDNW UUID namespaces)
+ $(TD $(MYREF dnsNamespace)
+ $(MYREF urlNamespace)
+ $(MYREF oidNamespace)
+ $(MYREF x500Namespace)
+ )
+ )
+)
+)
+
+ * UUIDs have many applications. Some examples follow: Databases may use UUIDs to identify
+ * rows or records in order to ensure that they are unique across different
+ * databases, or for publication/subscription services. Network messages may be
+ * identified with a UUID to ensure that different parts of a message are put back together
+ * again. Distributed computing may use UUIDs to identify a remote procedure call.
+ * Transactions and classes involved in serialization may be identified by UUIDs.
+ * Microsoft's component object model (COM) uses UUIDs to distinguish different software
+ * component interfaces. UUIDs are inserted into documents from Microsoft Office programs.
+ * UUIDs identify audio or video streams in the Advanced Systems Format (ASF). UUIDs are
+ * also a basis for OIDs (object identifiers), and URNs (uniform resource name).
+ *
+ * An attractive feature of UUIDs when compared to alternatives is their relative small size,
+ * of 128 bits, or 16 bytes. Another is that the creation of UUIDs does not require
+ * a centralized authority.
+ *
+ * When UUIDs are generated by one of the defined mechanisms, they are either guaranteed
+ * to be unique, different from all other generated UUIDs (that is, it has never been
+ * generated before and it will never be generated again), or it is extremely likely
+ * to be unique (depending on the mechanism).
+ *
+ * For efficiency, UUID is implemented as a struct. UUIDs are therefore empty if not explicitly
+ * initialized. An UUID is empty if $(MYREF3 UUID.empty, empty) is true. Empty UUIDs are equal to
+ * $(D UUID.init), which is a UUID with all 16 bytes set to 0.
+ * Use UUID's constructors or the UUID generator functions to get an initialized UUID.
+ *
+ * This is a port of $(LINK2 http://www.boost.org/doc/libs/1_42_0/libs/uuid/uuid.html,
+ * boost._uuid) from the Boost project with some minor additions and API
+ * changes for a more D-like API.
+ *
+ * Standards:
+ * $(LINK2 http://www.ietf.org/rfc/rfc4122.txt, RFC 4122)
+ *
+ * See_Also:
+ * $(LINK http://en.wikipedia.org/wiki/Universally_unique_identifier)
+ *
+ * Copyright: Copyright Johannes Pfau 2011 - .
+ * License: $(HTTP www.boost.org/LICENSE_1_0.txt, Boost License 1.0).
+ * Authors: Johannes Pfau
+ * Source: $(PHOBOSSRC std/_uuid.d)
+ *
+ * Macros:
+ * MYREF2 = <a href="#$2">$(TT $1)</a>&nbsp;
+ * MYREF3 = <a href="#$2">$(D $1)</a>
+ */
+/* Copyright Johannes Pfau 2011 - 2012.
+ * 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.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;
+
+/**
+ *
+ */
+public struct UUID
+{
+ import std.meta : AliasSeq, allSatisfy;
+
+ private:
+ 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 @nogc Char toChar(Char)(size_t i) const
+ {
+ if (i <= 9)
+ return cast(Char)('0' + i);
+ else
+ return cast(Char)('a' + (i-10));
+ }
+
+ @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");
+ }
+
+ // Reinterpret the UUID as an array of some other primitive.
+ @trusted ref T[16 / T.sizeof] asArrayOf(T)() return
+ if (isIntegral!T)
+ {
+ return *cast(typeof(return)*)&data;
+ }
+
+ public:
+ /**
+ * RFC 4122 defines different internal data layouts for UUIDs. These are
+ * the UUID formats supported by this module. It's
+ * 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 $(REF _Variant, std,_variant).
+ */
+ enum Variant
+ {
+ ncs, /// NCS backward compatibility
+ rfc4122, /// Defined in RFC 4122 document
+ microsoft, /// Microsoft Corporation backward compatibility
+ future ///Reserved for future use
+ }
+
+ /**
+ * RFC 4122 defines different UUID versions. The version shows
+ * how a UUID was generated, e.g. a version 4 UUID was generated
+ * from a random number, a version 3 UUID from an MD5 hash of a name.
+ *
+ * Note:
+ * All of these UUID versions can be read and processed by
+ * $(D std.uuid), but only version 3, 4 and 5 UUIDs can be generated.
+ */
+ enum Version
+ {
+ ///Unknown version
+ unknown = -1,
+ ///Version 1
+ timeBased = 1,
+ ///Version 2
+ dceSecurity = 2,
+ ///Version 3 (Name based + MD5)
+ nameBasedMD5 = 3,
+ ///Version 4 (Random)
+ randomNumberBased = 4,
+ ///Version 5 (Name based + SHA-1)
+ nameBasedSHA1 = 5
+ }
+
+ 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
+ * fields specified in RFC 4122, but as we never have to access
+ * those (only necessary for version 1 (and maybe 2) UUIDs),
+ * that is not needed right now.
+ */
+
+ @safe pure unittest
+ {
+ UUID tmp;
+ tmp.data = cast(ubyte[16])[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]);
+ tmp.data[2] = 3;
+ 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;
+ assert(tmp2.data == cast(ubyte[16])[0,1,3,3,4,5,6,7,8,9,10,11,
+ 12,13,14,15]);
+ }
+
+ /**
+ * Construct a UUID struct from the 16 byte representation
+ * of a UUID.
+ */
+ @safe pure nothrow @nogc this(ref in ubyte[16] uuidData)
+ {
+ data = uuidData;
+ }
+ /// ditto
+ @safe pure nothrow @nogc this(in ubyte[16] uuidData)
+ {
+ data = uuidData;
+ }
+
+ ///
+ @safe pure unittest
+ {
+ enum ubyte[16] data = [0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15];
+ auto uuid = UUID(data);
+ enum ctfe = UUID(data);
+ assert(uuid.data == data);
+ assert(ctfe.data == data);
+ }
+
+ /**
+ * Construct a UUID struct from the 16 byte representation
+ * of a UUID. Variadic constructor to allow a simpler syntax, see examples.
+ * You need to pass exactly 16 ubytes.
+ */
+ @safe pure this(T...)(T uuidData)
+ if (uuidData.length == 16 && allSatisfy!(isIntegral, T))
+ {
+ import std.conv : to;
+
+ foreach (idx, it; uuidData)
+ {
+ this.data[idx] = to!ubyte(it);
+ }
+ }
+
+ ///
+ @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]);
+ }
+
+ @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,
+ 12,13,14,15]);
+
+ enum UUID ctfeID = UUID(0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15);
+ assert(ctfeID == tmp);
+
+ //Too few arguments
+ assert(!__traits(compiles, typeof(UUID(0,1,2,3,4,5,6,7,8,9,10,11,12,13,14))));
+
+ //Too many arguments
+ assert(!__traits(compiles, typeof(UUID(0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,1))));
+ }
+
+ /**
+ * <a name="UUID(string)"></a>
+ * Parse a UUID from its canonical string form. An UUID in its
+ * canonical form looks like this: 8ab3060e-2cba-4f23-b74c-b52db3bdfb46
+ *
+ * Throws:
+ * $(LREF UUIDParsingException) if the input is invalid
+ *
+ * CTFE:
+ * This function is supported in CTFE code. Note that error messages
+ * caused by a malformed UUID parsed at compile time can be cryptic,
+ * but errors are detected and reported at
+ * compile time.
+ *
+ * Note:
+ * This is a strict parser. It only accepts the pattern above.
+ * It doesn't support any leading or trailing characters. It only
+ * accepts characters used for hex numbers and the string must have
+ * hyphens exactly like above.
+ *
+ * For a less strict parser, see $(LREF parseUUID)
+ */
+ this(T)(in T[] uuid) if (isSomeChar!(Unqual!T))
+ {
+ import std.conv : to, parse;
+ if (uuid.length < 36)
+ {
+ throw new UUIDParsingException(to!string(uuid), 0,
+ UUIDParsingException.Reason.tooLittle, "Insufficient Input");
+ }
+ 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
+ uint pos = void;
+
+ foreach (i, p; byteSeq)
+ {
+ 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')
+ {
+ h |= 0x20; //poorman's tolower
+ if (h < 'a') goto Lerr;
+ if (h > 'f') goto Lerr;
+ h -= s;
+ }
+ if (l > '9')
+ {
+ l |= 0x20; //poorman's tolower
+ if (l < 'a') goto Lerr;
+ if (l > 'f') goto Lerr;
+ l -= s;
+ }
+ h -= '0';
+ l -= '0';
+
+ 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.conv : to;
+ import std.exception;
+ import std.meta;
+
+ foreach (S; AliasSeq!(char[], const(char)[], immutable(char)[],
+ wchar[], const(wchar)[], immutable(wchar)[],
+ dchar[], const(dchar)[], immutable(dchar)[],
+ immutable(char[]), immutable(wchar[]), immutable(dchar[])))
+ {
+ //Test valid, working cases
+ assert(UUID(to!S("00000000-0000-0000-0000-000000000000")).empty);
+
+ auto id = UUID(to!S("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");
+
+ enum UUID ctfe = UUID(to!S("8ab3060e-2cba-4f23-b74c-b52db3bdfb46"));
+ assert(ctfe == id);
+
+ assert(UUID(to!S("5668122d-9df0-49a4-ad0b-b9b0a57f886a")).data
+ == [86, 104, 18, 45, 157, 240, 73, 164, 173, 11, 185, 176, 165, 127, 136, 106]);
+
+ //Test too short UUIDS
+ auto except = collectException!UUIDParsingException(
+ UUID(to!S("5668122d-9df0-49a4-ad0b-b9b0a57f886")));
+ assert(except && except.reason == UUIDParsingException.Reason.tooLittle);
+
+ //Test too long UUIDS
+ except = collectException!UUIDParsingException(
+ UUID(to!S("5668122d-9df0-49a4-ad0b-b9b0a57f886aa")));
+ assert(except && except.reason == UUIDParsingException.Reason.tooMuch);
+
+ //Test dashes
+ except = collectException!UUIDParsingException(
+ UUID(to!S("8ab3060e2cba-4f23-b74c-b52db3bdfb-46")));
+ assert(except && except.reason == UUIDParsingException.Reason.invalidChar);
+
+ //Test dashes 2
+ except = collectException!UUIDParsingException(
+ UUID(to!S("8ab3-060e2cba-4f23-b74c-b52db3bdfb46")));
+ assert(except && except.reason == UUIDParsingException.Reason.invalidChar);
+
+ //Test invalid characters
+ //make sure 36 characters in total or we'll get a 'tooMuch' reason
+ except = collectException!UUIDParsingException(
+ UUID(to!S("{8ab3060e-2cba-4f23-b74c-b52db3bdf6}")));
+ assert(except && except.reason == UUIDParsingException.Reason.invalidChar);
+
+ //Boost test
+ assert(UUID(to!S("01234567-89ab-cdef-0123-456789ABCDEF"))
+ == UUID(cast(ubyte[16])[0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef,0x01,
+ 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef]));
+ }
+ }
+
+ /**
+ * Returns true if and only if the UUID is equal
+ * to {00000000-0000-0000-0000-000000000000}
+ */
+ @trusted pure nothrow @nogc @property bool empty() const
+ {
+ if (__ctfe)
+ return data == (ubyte[16]).init;
+
+ auto p = cast(const(size_t*))data.ptr;
+ 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)
+ return p[0] == 0 && p[1] == 0;
+ else
+ static assert(false, "nonsense, it's not 32 or 64 bit");
+ }
+
+ ///
+ @safe pure unittest
+ {
+ UUID id;
+ assert(id.empty);
+ id = UUID("00000000-0000-0000-0000-000000000001");
+ assert(!id.empty);
+ }
+
+ @safe pure unittest
+ {
+ ubyte[16] getData(size_t i)
+ {
+ ubyte[16] data;
+ data[i] = 1;
+ return data;
+ }
+
+ for (size_t i = 0; i < 16; i++)
+ {
+ assert(!UUID(getData(i)).empty);
+ }
+
+ enum ctfeEmpty = UUID.init.empty;
+ assert(ctfeEmpty);
+
+ bool ctfeTest()
+ {
+ for (size_t i = 0; i < 16; i++)
+ {
+ auto ctfeEmpty2 = UUID(getData(i)).empty;
+ assert(!ctfeEmpty2);
+ }
+ return true;
+ }
+ enum res = ctfeTest();
+ }
+
+ /**
+ * RFC 4122 defines different internal data layouts for UUIDs.
+ * Returns the format used by this UUID.
+ *
+ * Note: Do not confuse this with $(REF _Variant, std,_variant).
+ * The type of this property is $(MYREF3 std.uuid.UUID.Variant, _Variant).
+ *
+ * See_Also:
+ * $(MYREF3 UUID.Variant, Variant)
+ */
+ @safe pure nothrow @nogc @property Variant variant() const
+ {
+ //variant is stored in octet 7
+ //which is index 8, since indexes count backwards
+ immutable octet7 = data[8]; //octet 7 is array index 8
+
+ if ((octet7 & 0x80) == 0x00) //0b0xxxxxxx
+ return Variant.ncs;
+ else if ((octet7 & 0xC0) == 0x80) //0b10xxxxxx
+ return Variant.rfc4122;
+ else if ((octet7 & 0xE0) == 0xC0) //0b110xxxxx
+ return Variant.microsoft;
+ else
+ {
+ //assert((octet7 & 0xE0) == 0xE0, "Unknown UUID variant!") //0b111xxxx
+ return Variant.future;
+ }
+ }
+
+ ///
+ @safe pure unittest
+ {
+ assert(UUID("8ab3060e-2cba-4f23-b74c-b52db3bdfb46").variant
+ == UUID.Variant.rfc4122);
+ }
+ @system pure unittest
+ {
+ // @system due to Variant
+ Variant[ubyte] tests = cast(Variant[ubyte])[0x00 : Variant.ncs,
+ 0x10 : Variant.ncs,
+ 0x20 : Variant.ncs,
+ 0x30 : Variant.ncs,
+ 0x40 : Variant.ncs,
+ 0x50 : Variant.ncs,
+ 0x60 : Variant.ncs,
+ 0x70 : Variant.ncs,
+ 0x80 : Variant.rfc4122,
+ 0x90 : Variant.rfc4122,
+ 0xa0 : Variant.rfc4122,
+ 0xb0 : Variant.rfc4122,
+ 0xc0 : Variant.microsoft,
+ 0xd0 : Variant.microsoft,
+ 0xe0 : Variant.future,
+ 0xf0 : Variant.future];
+ foreach (key, value; tests)
+ {
+ UUID u;
+ u.data[8] = key;
+ assert(u.variant == value);
+ }
+ }
+
+ /**
+ * RFC 4122 defines different UUID versions. The version shows
+ * how a UUID was generated, e.g. a version 4 UUID was generated
+ * from a random number, a version 3 UUID from an MD5 hash of a name.
+ * Returns the version used by this UUID.
+ *
+ * See_Also:
+ * $(MYREF3 UUID.Version, Version)
+ */
+ @safe pure nothrow @nogc @property Version uuidVersion() const
+ {
+ //version is stored in octet 9
+ //which is index 6, since indexes count backwards
+ immutable octet9 = data[6];
+ if ((octet9 & 0xF0) == 0x10)
+ return Version.timeBased;
+ else if ((octet9 & 0xF0) == 0x20)
+ return Version.dceSecurity;
+ else if ((octet9 & 0xF0) == 0x30)
+ return Version.nameBasedMD5;
+ else if ((octet9 & 0xF0) == 0x40)
+ return Version.randomNumberBased;
+ else if ((octet9 & 0xF0) == 0x50)
+ return Version.nameBasedSHA1;
+ else
+ return Version.unknown;
+ }
+
+ ///
+ @safe unittest
+ {
+ assert(UUID("8ab3060e-2cba-4f23-b74c-b52db3bdfb46").uuidVersion
+ == UUID.Version.randomNumberBased);
+ }
+ @system unittest
+ {
+ // @system due to cast
+ Version[ubyte] tests = cast(Version[ubyte]) [
+ 0x00 : UUID.Version.unknown,
+ 0x10 : UUID.Version.timeBased,
+ 0x20 : UUID.Version.dceSecurity,
+ 0x30 : UUID.Version.nameBasedMD5,
+ 0x40 : UUID.Version.randomNumberBased,
+ 0x50 : UUID.Version.nameBasedSHA1,
+ 0x60 : UUID.Version.unknown,
+ 0x70 : UUID.Version.unknown,
+ 0x80 : UUID.Version.unknown,
+ 0x90 : UUID.Version.unknown,
+ 0xa0 : UUID.Version.unknown,
+ 0xb0 : UUID.Version.unknown,
+ 0xc0 : UUID.Version.unknown,
+ 0xd0 : UUID.Version.unknown,
+ 0xe0 : UUID.Version.unknown,
+ 0xf0 : UUID.Version.unknown];
+ foreach (key, value; tests)
+ {
+ UUID u;
+ u.data[6] = key;
+ assert(u.uuidVersion == value);
+ }
+ }
+
+ /**
+ * Swap the data of this UUID with the data of rhs.
+ */
+ @safe pure nothrow @nogc void swap(ref UUID rhs)
+ {
+ immutable bck = data;
+ data = rhs.data;
+ rhs.data = bck;
+ }
+
+ ///
+ @safe unittest
+ {
+ immutable ubyte[16] data = [0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15];
+ UUID u1;
+ UUID u2 = UUID(data);
+ u1.swap(u2);
+
+ assert(u1 == UUID(data));
+ assert(u2 == UUID.init);
+ }
+
+ /**
+ * All of the standard numeric operators are defined for
+ * the UUID struct.
+ */
+ @safe pure nothrow @nogc bool opEquals(in UUID s) const
+ {
+ return ulongs[0] == s.ulongs[0] && ulongs[1] == s.ulongs[1];
+ }
+
+ ///
+ @safe pure unittest
+ {
+ //compare UUIDs
+ assert(UUID("00000000-0000-0000-0000-000000000000") == UUID.init);
+
+ //UUIDs in associative arrays:
+ int[UUID] test = [UUID("8a94f585-d180-44f7-8929-6fca0189c7d0") : 1,
+ UUID("7c351fd4-b860-4ee3-bbdc-7f79f3dfb00a") : 2,
+ UUID("9ac0a4e5-10ee-493a-86fc-d29eeb82ecc1") : 3];
+
+ assert(test[UUID("9ac0a4e5-10ee-493a-86fc-d29eeb82ecc1")] == 3);
+
+ //UUIDS can be sorted:
+ import std.algorithm;
+ UUID[] ids = [UUID("8a94f585-d180-44f7-8929-6fca0189c7d0"),
+ UUID("7c351fd4-b860-4ee3-bbdc-7f79f3dfb00a"),
+ UUID("9ac0a4e5-10ee-493a-86fc-d29eeb82ecc1")];
+ sort(ids);
+ }
+
+ /**
+ * ditto
+ */
+ @safe pure nothrow @nogc bool opEquals(ref in UUID s) const
+ {
+ return ulongs[0] == s.ulongs[0] && ulongs[1] == s.ulongs[1];
+ }
+
+ /**
+ * ditto
+ */
+ @safe pure nothrow @nogc int opCmp(in UUID s) const
+ {
+ import std.algorithm.comparison : cmp;
+ return cmp(this.data[], s.data[]);
+ }
+
+ /**
+ * ditto
+ */
+ @safe pure nothrow @nogc int opCmp(ref in UUID s) const
+ {
+ import std.algorithm.comparison : cmp;
+ return cmp(this.data[], s.data[]);
+ }
+
+ /**
+ * ditto
+ */
+ @safe pure nothrow @nogc UUID opAssign(in UUID s)
+ {
+ ulongs[0] = s.ulongs[0];
+ ulongs[1] = s.ulongs[1];
+ return this;
+ }
+
+ /**
+ * ditto
+ */
+ @safe pure nothrow @nogc UUID opAssign(ref in UUID s)
+ {
+ ulongs[0] = s.ulongs[0];
+ ulongs[1] = s.ulongs[1];
+ return this;
+ }
+
+ /**
+ * 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,
+ UUID("7c351fd4-b860-4ee3-bbdc-7f79f3dfb00a") : 2,
+ UUID("9ac0a4e5-10ee-493a-86fc-d29eeb82ecc1") : 3];
+
+ assert(test[UUID("9ac0a4e5-10ee-493a-86fc-d29eeb82ecc1")] == 3);
+
+ import std.algorithm;
+ UUID[] ids = [UUID("8a94f585-d180-44f7-8929-6fca0189c7d0"),
+ UUID("7c351fd4-b860-4ee3-bbdc-7f79f3dfb00a"),
+ UUID("9ac0a4e5-10ee-493a-86fc-d29eeb82ecc1")];
+ sort(ids);
+ auto id2 = ids.dup;
+
+ ids = [UUID("7c351fd4-b860-4ee3-bbdc-7f79f3dfb00a"),
+ UUID("8a94f585-d180-44f7-8929-6fca0189c7d0"),
+ UUID("9ac0a4e5-10ee-493a-86fc-d29eeb82ecc1")];
+ sort(ids);
+ assert(ids == id2);
+
+ //test comparsion
+ UUID u1;
+ UUID u2 = UUID(cast(ubyte[16])[1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0]);
+ UUID u3 = UUID(cast(ubyte[16])[255,255,255,255,255,255,255,255,255,
+ 255,255,255,255,255,255,255]);
+
+ assert(u1 == u1);
+
+ assert(u1 != u2);
+
+ assert(u1 < u2);
+ assert(u2 < u3);
+
+ assert(u1 <= u1);
+ assert(u1 <= u2);
+ assert(u2 <= u3);
+
+ assert(u2 >= u2);
+ assert(u3 >= u2);
+
+ assert(u3 >= u3);
+ assert(u2 >= u1);
+ assert(u3 >= u1);
+
+ // test hash
+ assert(u1.toHash() != u2.toHash());
+ assert(u2.toHash() != u3.toHash());
+ assert(u3.toHash() != u1.toHash());
+ }
+
+
+ /**
+ * 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(Writer)(scope Writer sink) const
+ {
+ 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;
+ }
+ }
+
+ /**
+ * Return the UUID as a string in the canonical form.
+ */
+ @trusted pure nothrow string toString() const
+ {
+ import std.exception : assumeUnique;
+ auto result = new char[36];
+ toString(result);
+ return result.assumeUnique;
+ }
+
+ ///
+ @safe pure unittest
+ {
+ immutable str = "8ab3060e-2cba-4f23-b74c-b52db3bdfb46";
+ auto id = UUID(str);
+ assert(id.toString() == str);
+ }
+
+ @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]);
+ assert(u1.toString() == "8ab3060e-2cba-4f23-b74c-b52db3bdfb46");
+ u1 = UUID("8ab3060e-2cba-4f23-b74c-b52db3bdfb46");
+ assert(u1.toString() == "8ab3060e-2cba-4f23-b74c-b52db3bdfb46");
+
+ char[] buf;
+ void sink(const(char)[] data)
+ {
+ buf ~= data;
+ }
+ u1.toString(&sink);
+ assert(buf == "8ab3060e-2cba-4f23-b74c-b52db3bdfb46");
+ }
+}
+
+
+/**
+ * This function generates a name based (Version 3) UUID from a namespace UUID and a name.
+ * If no namespace UUID was passed, the empty UUID $(D UUID.init) is used.
+ *
+ * Note:
+ * The default namespaces ($(LREF dnsNamespace), ...) defined by
+ * this module should be used when appropriate.
+ *
+ * RFC 4122 recommends to use Version 5 UUIDs (SHA-1) instead of Version 3
+ * UUIDs (MD5) for new applications.
+ *
+ * CTFE:
+ * CTFE is not supported.
+ *
+ * Note:
+ * RFC 4122 isn't very clear on how UUIDs should be generated from names.
+ * It is possible that different implementations return different UUIDs
+ * for the same input, so be warned. The implementation for UTF-8 strings
+ * and byte arrays used by $(D std.uuid) is compatible with Boost's implementation.
+ * $(D std.uuid) guarantees that the same input to this function will generate
+ * the same output at any time, on any system (this especially means endianness
+ * doesn't matter).
+ *
+ * Note:
+ * This function does not provide overloads for wstring and dstring, as
+ * there's no clear answer on how that should be implemented. It could be
+ * argued, that string, wstring and dstring input should have the same output,
+ * but that wouldn't be compatible with Boost, which generates different output
+ * for strings and wstrings. It's always possible to pass wstrings and dstrings
+ * by using the ubyte[] function overload (but be aware of endianness issues!).
+ */
+@safe pure nothrow @nogc UUID md5UUID(const(char[]) name, const UUID namespace = UUID.init)
+{
+ return md5UUID(cast(const(ubyte[]))name, namespace);
+}
+
+/// ditto
+@safe pure nothrow @nogc UUID md5UUID(const(ubyte[]) data, const UUID namespace = UUID.init)
+{
+ import std.digest.md : MD5;
+
+ MD5 hash;
+ hash.start();
+
+ /*
+ * NOTE: RFC 4122 says namespace should be converted to big-endian.
+ * We always keep the UUID data in big-endian representation, so
+ * that's fine
+ */
+ hash.put(namespace.data[]);
+ hash.put(data[]);
+
+ UUID u;
+ u.data = hash.finish();
+
+ //set variant
+ //must be 0b10xxxxxx
+ u.data[8] &= 0b10111111;
+ u.data[8] |= 0b10000000;
+
+ //set version
+ //must be 0b0011xxxx
+ u.data[6] &= 0b00111111;
+ u.data[6] |= 0b00110000;
+
+ return u;
+}
+
+///
+@safe unittest
+{
+ //Use default UUID.init namespace
+ auto simpleID = md5UUID("test.uuid.any.string");
+
+ //use a name-based id as namespace
+ auto namespace = md5UUID("my.app");
+ auto id = md5UUID("some-description", namespace);
+}
+
+@safe pure unittest
+{
+ auto simpleID = md5UUID("test.uuid.any.string");
+ assert(simpleID.data == cast(ubyte[16])[126, 206, 86, 72, 29, 233, 62, 213, 178, 139, 198, 136,
+ 188, 135, 153, 123]);
+ auto namespace = md5UUID("my.app");
+ auto id = md5UUID("some-description", namespace);
+ assert(id.data == cast(ubyte[16])[166, 138, 167, 79, 48, 219, 55, 166, 170, 103, 39, 73, 216,
+ 150, 144, 164]);
+
+ auto constTest = md5UUID(cast(const(char)[])"test");
+ constTest = md5UUID(cast(const(char[]))"test");
+
+ char[] mutable = "test".dup;
+ id = md5UUID(mutable, namespace);
+
+ const(ubyte)[] data = cast(ubyte[])[0,1,2,244,165,222];
+ id = md5UUID(data);
+ assert(id.data == cast(ubyte[16])[16, 50, 29, 247, 243, 185, 61, 178, 157, 100, 253, 236, 73,
+ 76, 51, 47]);
+
+ assert(id.variant == UUID.Variant.rfc4122);
+ assert(id.uuidVersion == UUID.Version.nameBasedMD5);
+
+ auto correct = UUID("3d813cbb-47fb-32ba-91df-831e1593ac29");
+
+ auto u = md5UUID("www.widgets.com", dnsNamespace);
+ //enum ctfeId = md5UUID("www.widgets.com", dnsNamespace);
+ //assert(ctfeId == u);
+ assert(u == correct);
+ assert(u.variant == UUID.Variant.rfc4122);
+ assert(u.uuidVersion == UUID.Version.nameBasedMD5);
+}
+
+ /**
+ * This function generates a name based (Version 5) UUID from a namespace
+ * UUID and a name.
+ * If no namespace UUID was passed, the empty UUID $(D UUID.init) is used.
+ *
+ * Note:
+ * The default namespaces ($(LREF dnsNamespace), ...) defined by
+ * this module should be used when appropriate.
+ *
+ * CTFE:
+ * CTFE is not supported.
+ *
+ * Note:
+ * RFC 4122 isn't very clear on how UUIDs should be generated from names.
+ * It is possible that different implementations return different UUIDs
+ * for the same input, so be warned. The implementation for UTF-8 strings
+ * and byte arrays used by $(D std.uuid) is compatible with Boost's implementation.
+ * $(D std.uuid) guarantees that the same input to this function will generate
+ * the same output at any time, on any system (this especially means endianness
+ * doesn't matter).
+ *
+ * Note:
+ * This function does not provide overloads for wstring and dstring, as
+ * there's no clear answer on how that should be implemented. It could be
+ * argued, that string, wstring and dstring input should have the same output,
+ * but that wouldn't be compatible with Boost, which generates different output
+ * for strings and wstrings. It's always possible to pass wstrings and dstrings
+ * by using the ubyte[] function overload (but be aware of endianness issues!).
+ */
+@safe pure nothrow @nogc UUID sha1UUID(in char[] name, const UUID namespace = UUID.init)
+{
+ return sha1UUID(cast(const(ubyte[]))name, namespace);
+}
+
+/// ditto
+@safe pure nothrow @nogc UUID sha1UUID(in ubyte[] data, const UUID namespace = UUID.init)
+{
+ import std.digest.sha : SHA1;
+
+ SHA1 sha;
+ sha.start();
+
+ /*
+ * NOTE: RFC 4122 says namespace should be converted to big-endian.
+ * We always keep the UUID data in big-endian representation, so
+ * that's fine
+ */
+ sha.put(namespace.data[]);
+ sha.put(data[]);
+
+ auto hash = sha.finish();
+ auto u = UUID();
+ u.data[] = hash[0 .. 16];
+
+ //set variant
+ //must be 0b10xxxxxx
+ u.data[8] &= 0b10111111;
+ u.data[8] |= 0b10000000;
+
+ //set version
+ //must be 0b0101xxxx
+ u.data[6] &= 0b01011111;
+ u.data[6] |= 0b01010000;
+
+ return u;
+}
+
+///
+@safe unittest
+{
+ //Use default UUID.init namespace
+ auto simpleID = sha1UUID("test.uuid.any.string");
+
+ //use a name-based id as namespace
+ auto namespace = sha1UUID("my.app");
+ auto id = sha1UUID("some-description", namespace);
+}
+
+@safe pure unittest
+{
+ auto simpleID = sha1UUID("test.uuid.any.string");
+ assert(simpleID.data == cast(ubyte[16])[16, 209, 239, 61, 99, 12, 94, 70, 159, 79, 255, 250,
+ 131, 79, 14, 147]);
+ auto namespace = sha1UUID("my.app");
+ auto id = sha1UUID("some-description", namespace);
+ assert(id.data == cast(ubyte[16])[225, 94, 195, 219, 126, 75, 83, 71, 157, 52, 247, 43, 238, 248,
+ 148, 46]);
+
+ auto constTest = sha1UUID(cast(const(char)[])"test");
+ constTest = sha1UUID(cast(const(char[]))"test");
+
+ char[] mutable = "test".dup;
+ id = sha1UUID(mutable, namespace);
+
+ const(ubyte)[] data = cast(ubyte[])[0,1,2,244,165,222];
+ id = sha1UUID(data);
+ assert(id.data == cast(ubyte[16])[60, 65, 92, 240, 96, 46, 95, 238, 149, 100, 12, 64, 199, 194,
+ 243, 12]);
+
+ auto correct = UUID("21f7f8de-8051-5b89-8680-0195ef798b6a");
+
+ auto u = sha1UUID("www.widgets.com", dnsNamespace);
+ assert(u == correct);
+ assert(u.variant == UUID.Variant.rfc4122);
+ assert(u.uuidVersion == UUID.Version.nameBasedSHA1);
+}
+
+/**
+ * This function generates a random number based UUID from a random
+ * number generator.
+ *
+ * This function is not supported at compile time.
+ *
+ * Params:
+ * randomGen = uniform RNG
+ * See_Also: $(REF isUniformRNG, std,random)
+ */
+@safe UUID randomUUID()
+{
+ import std.random : rndGen;
+ return randomUUID(rndGen);
+}
+
+/// 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");
+
+ alias E = ElementEncodingType!RNG;
+ enum size_t elemSize = E.sizeof;
+ static assert(elemSize <= 16);
+ static assert(16 % elemSize == 0);
+
+ UUID u;
+ foreach (ref E e ; u.asArrayOf!E())
+ {
+ e = randomGen.front;
+ randomGen.popFront();
+ }
+
+ //set variant
+ //must be 0b10xxxxxx
+ u.data[8] &= 0b10111111;
+ u.data[8] |= 0b10000000;
+
+ //set version
+ //must be 0b0100xxxx
+ u.data[6] &= 0b01001111;
+ u.data[6] |= 0b01000000;
+
+ return u;
+}
+
+///
+@safe unittest
+{
+ import std.random : Xorshift192, unpredictableSeed;
+
+ //simple call
+ auto uuid = randomUUID();
+
+ //provide a custom RNG. Must be seeded manually.
+ Xorshift192 gen;
+
+ gen.seed(unpredictableSeed);
+ auto uuid3 = randomUUID(gen);
+}
+
+/*
+ * Original boost.uuid used Mt19937, we don't want
+ * to use anything worse than that. If Random is changed
+ * to something else, this assert and the randomUUID function
+ * have to be updated.
+ */
+@safe unittest
+{
+ import std.random : rndGen, Mt19937;
+ static assert(is(typeof(rndGen) == Mt19937));
+}
+
+@safe unittest
+{
+ import std.random : Xorshift192, unpredictableSeed;
+ //simple call
+ auto uuid = randomUUID();
+
+ //provide a custom RNG. Must be seeded manually.
+ Xorshift192 gen;
+ gen.seed(unpredictableSeed);
+ auto uuid3 = randomUUID(gen);
+
+ auto u1 = randomUUID();
+ auto u2 = randomUUID();
+ assert(u1 != u2);
+ assert(u1.variant == UUID.Variant.rfc4122);
+ assert(u1.uuidVersion == UUID.Version.randomNumberBased);
+}
+
+/**
+ * This is a less strict parser compared to the parser used in the
+ * UUID constructor. It enforces the following rules:
+ *
+ * $(UL
+ * $(LI hex numbers are always two hexdigits([0-9a-fA-F]))
+ * $(LI there must be exactly 16 such pairs in the input, not less, not more)
+ * $(LI there can be exactly one dash between two hex-pairs, but not more)
+ * $(LI there can be multiple characters enclosing the 16 hex pairs,
+ * as long as these characters do not contain [0-9a-fA-F])
+ * )
+ *
+ * Note:
+ * Like most parsers, it consumes its argument. This means:
+ * -------------------------
+ * string s = "8AB3060E-2CBA-4F23-b74c-B52Db3BDFB46";
+ * parseUUID(s);
+ * assert(s == "");
+ * -------------------------
+ *
+ * Throws:
+ * $(LREF UUIDParsingException) if the input is invalid
+ *
+ * CTFE:
+ * This function is supported in CTFE code. Note that error messages
+ * 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)
+{
+ return parseUUID(uuidString);
+}
+
+///ditto
+UUID parseUUID(Range)(ref Range uuidRange)
+if (isInputRange!Range
+ && is(Unqual!(ElementType!Range) == dchar))
+{
+ import std.ascii : isHexDigit;
+ import std.conv : ConvException, parse;
+
+ 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)
+ {
+ import std.conv : to;
+ static if (isInfinite!Range)
+ {
+ throw new UUIDParsingException(to!string(take(errorCopy, pos)), pos, reason, message,
+ next, file, line);
+ }
+ else
+ {
+ throw new UUIDParsingException(to!string(errorCopy), pos, reason, message, next, file,
+ line);
+ }
+ }
+ else
+ {
+ throw new UUIDParsingException("", pos, reason, message, next, file, line);
+ }
+ }
+
+ static if (hasLength!Range)
+ {
+ import std.conv : to;
+ if (uuidRange.length < 32)
+ {
+ throw new UUIDParsingException(to!string(uuidRange), 0, UUIDParsingException.Reason.tooLittle,
+ "Insufficient Input");
+ }
+ }
+
+ UUID result;
+ size_t consumed;
+ size_t element = 0;
+
+ //skip garbage
+ size_t skip()()
+ {
+ size_t skipped;
+ while (!uuidRange.empty && !isHexDigit(uuidRange.front))
+ {
+ skipped++;
+ uuidRange.popFront();
+ }
+ return skipped;
+ }
+
+ consumed += skip();
+
+ if (uuidRange.empty)
+ parserError(consumed, UUIDParsingException.Reason.tooLittle, "Insufficient Input");
+
+ bool dashAllowed = false;
+
+ parseLoop: while (!uuidRange.empty)
+ {
+ immutable character = uuidRange.front;
+
+ if (character == '-')
+ {
+ if (!dashAllowed)
+ parserError(consumed, UUIDParsingException.Reason.invalidChar, "Unexpected '-'");
+ else
+ dashAllowed = false;
+
+ consumed++;
+ }
+ else if (!isHexDigit(character))
+ {
+ parserError(consumed, UUIDParsingException.Reason.invalidChar,
+ "Unexpected character (wanted a hexDigit)");
+ }
+ else
+ {
+ try
+ {
+ consumed += 2;
+ static if (isSomeString!Range)
+ {
+ if (uuidRange.length < 2)
+ {
+ parserError(consumed, UUIDParsingException.Reason.tooLittle,
+ "Insufficient Input");
+ }
+ auto part = uuidRange[0 .. 2];
+ result.data[element++] = parse!ubyte(part, 16);
+ uuidRange.popFront();
+ }
+ else
+ {
+ dchar[2] copyBuf;
+ copyBuf[0] = character;
+ uuidRange.popFront();
+ if (uuidRange.empty)
+ {
+ parserError(consumed, UUIDParsingException.Reason.tooLittle,
+ "Insufficient Input");
+ }
+ copyBuf[1] = uuidRange.front;
+ auto part = copyBuf[];
+ result.data[element++] = parse!ubyte(part, 16);
+ }
+
+ if (element == 16)
+ {
+ uuidRange.popFront();
+ break parseLoop;
+ }
+
+ dashAllowed = true;
+ }
+ catch (ConvException e)
+ {
+ parserError(consumed, UUIDParsingException.Reason.invalidChar,
+ "Couldn't parse ubyte", e);
+ }
+ }
+ uuidRange.popFront();
+ }
+ assert(element <= 16);
+
+ if (element < 16)
+ parserError(consumed, UUIDParsingException.Reason.tooLittle, "Insufficient Input");
+
+ consumed += skip();
+ if (!uuidRange.empty)
+ parserError(consumed, UUIDParsingException.Reason.invalidChar, "Unexpected character");
+
+ return result;
+}
+
+///
+@safe unittest
+{
+ auto id = parseUUID("8AB3060E-2CBA-4F23-b74c-B52Db3BDFB46");
+ //no dashes
+ id = parseUUID("8ab3060e2cba4f23b74cb52db3bdfb46");
+ //dashes at different positions
+ id = parseUUID("8a-b3-06-0e2cba4f23b74c-b52db3bdfb-46");
+ //leading / trailing characters
+ id = parseUUID("{8ab3060e-2cba-4f23-b74c-b52db3bdfb46}");
+ //unicode
+ id = parseUUID("ü8ab3060e2cba4f23b74cb52db3bdfb46ü");
+ //multiple trailing/leading characters
+ id = parseUUID("///8ab3060e2cba4f23b74cb52db3bdfb46||");
+
+ //Can also be used in CTFE, for example as UUID literals:
+ enum ctfeID = parseUUID("8ab3060e-2cba-4f23-b74c-b52db3bdfb46");
+ //here parsing is done at compile time, no runtime overhead!
+}
+
+@safe pure unittest
+{
+ import std.conv : to;
+ import std.exception;
+ import std.meta;
+
+ struct TestRange(bool forward)
+ {
+ dstring input;
+
+ @property dchar front()
+ {
+ return input.front;
+ }
+
+ void popFront()
+ {
+ input.popFront();
+ }
+
+ @property bool empty()
+ {
+ return input.empty;
+ }
+
+ static if (forward)
+ {
+ @property TestRange!true save()
+ {
+ return this;
+ }
+ }
+ }
+ alias TestInputRange = TestRange!false;
+ alias TestForwardRange = TestRange!true;
+
+ assert(isInputRange!TestInputRange);
+ assert(is(ElementType!TestInputRange == dchar));
+ assert(isInputRange!TestForwardRange);
+ assert(isForwardRange!TestForwardRange);
+ assert(is(ElementType!TestForwardRange == dchar));
+
+ //Helper function for unittests - Need to pass ranges by ref
+ UUID parseHelper(T)(string input)
+ {
+ static if (is(T == TestInputRange) || is(T == TestForwardRange))
+ {
+ T range = T(to!dstring(input));
+ return parseUUID(range);
+ }
+ else
+ return parseUUID(to!T(input));
+ }
+
+ foreach (S; AliasSeq!(char[], const(char)[], immutable(char)[],
+ wchar[], const(wchar)[], immutable(wchar)[],
+ dchar[], const(dchar)[], immutable(dchar)[],
+ immutable(char[]), immutable(wchar[]), immutable(dchar[]),
+ TestForwardRange, TestInputRange))
+ {
+ //Verify examples.
+ auto id = parseHelper!S("8AB3060E-2CBA-4F23-b74c-B52Db3BDFB46");
+ //no dashes
+ id = parseHelper!S("8ab3060e2cba4f23b74cb52db3bdfb46");
+ //dashes at different positions
+ id = parseHelper!S("8a-b3-06-0e2cba4f23b74c-b52db3bdfb-46");
+ //leading / trailing characters
+ id = parseHelper!S("{8ab3060e-2cba-4f23-b74c-b52db3bdfb46}");
+ //unicode
+ id = parseHelper!S("ü8ab3060e2cba4f23b74cb52db3bdfb46ü");
+ //multiple trailing/leading characters
+ id = parseHelper!S("///8ab3060e2cba4f23b74cb52db3bdfb46||");
+ enum ctfeId = parseHelper!S("8ab3060e-2cba-4f23-b74c-b52db3bdfb46");
+ assert(parseHelper!S("8AB3060E-2cba-4f23-b74c-b52db3bdfb46") == ctfeId);
+
+ //Test valid, working cases
+ assert(parseHelper!S("00000000-0000-0000-0000-000000000000").empty);
+ assert(parseHelper!S("8AB3060E-2CBA-4F23-b74c-B52Db3BDFB46").data
+ == [138, 179, 6, 14, 44, 186, 79, 35, 183, 76, 181, 45, 179, 189, 251, 70]);
+
+ assert(parseHelper!S("5668122d-9df0-49a4-ad0b-b9b0a57f886a").data
+ == [86, 104, 18, 45, 157, 240, 73, 164, 173, 11, 185, 176, 165, 127, 136, 106]);
+
+ //wstring / dstring
+ assert(parseHelper!S("5668122d-9df0-49a4-ad0b-b9b0a57f886a").data
+ == [86, 104, 18, 45, 157, 240, 73, 164, 173, 11, 185, 176, 165, 127, 136, 106]);
+ assert(parseHelper!S("5668122d-9df0-49a4-ad0b-b9b0a57f886a").data
+ == [86, 104, 18, 45, 157, 240, 73, 164, 173, 11, 185, 176, 165, 127, 136, 106]);
+
+ //Test too short UUIDS
+ auto except = collectException!UUIDParsingException(
+ parseHelper!S("5668122d-9df0-49a4-ad0b-b9b0a57f886"));
+ assert(except && except.reason == UUIDParsingException.Reason.tooLittle);
+
+ //Test too long UUIDS
+ except = collectException!UUIDParsingException(
+ parseHelper!S("5668122d-9df0-49a4-ad0b-b9b0a57f886aa"));
+ assert(except && except.reason == UUIDParsingException.Reason.invalidChar);
+
+ //Test too long UUIDS 2
+ except = collectException!UUIDParsingException(
+ parseHelper!S("5668122d-9df0-49a4-ad0b-b9b0a57f886a-aa"));
+ assert(except && except.reason == UUIDParsingException.Reason.invalidChar);
+
+ //Test dashes
+ assert(parseHelper!S("8ab3060e2cba-4f23-b74c-b52db3bdfb46")
+ == parseUUID("8ab3060e-2cba-4f23-b74c-b52db3bdfb46"));
+ assert(parseHelper!S("8ab3-060e2cba-4f23-b74c-b52db3bdfb46")
+ == parseUUID("8ab3060e-2cba-4f23-b74c-b52db3bdfb46"));
+ assert(parseHelper!S("8ab3060e2cba4f23b74cb52db3bdfb46")
+ == parseUUID("8ab3060e-2cba-4f23-b74c-b52db3bdfb46"));
+
+ except = collectException!UUIDParsingException(
+ parseHelper!S("8-ab3060e2cba-4f23-b74c-b52db3bdfb46"));
+ assert(except && except.reason == UUIDParsingException.Reason.invalidChar);
+
+ //Test leading/trailing characters
+ assert(parseHelper!S("{8ab3060e-2cba-4f23-b74c-b52db3bdfb46}")
+ == parseUUID("8ab3060e-2cba-4f23-b74c-b52db3bdfb46"));
+ assert(parseHelper!S("{8ab3060e2cba4f23b74cb52db3bdfb46}")
+ == parseUUID("8ab3060e-2cba-4f23-b74c-b52db3bdfb46"));
+
+ //Boost test
+ auto u_increasing = UUID(cast(ubyte[16])[0x01, 0x23, 0x45, 0x67, 0x89, 0xab,
+ 0xcd, 0xef,0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef]);
+ assert(parseHelper!S("0123456789abcdef0123456789ABCDEF") == UUID(cast(ubyte[16])[0x01,
+ 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef,0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef]));
+
+ //unicode
+ assert(parseHelper!S("ü8ab3060e2cba4f23b74cb52db3bdfb46ü")
+ == parseUUID("8ab3060e-2cba-4f23-b74c-b52db3bdfb46"));
+
+ //multiple trailing/leading characters
+ assert(parseHelper!S("///8ab3060e2cba4f23b74cb52db3bdfb46||")
+ == parseUUID("8ab3060e-2cba-4f23-b74c-b52db3bdfb46"));
+ }
+}
+
+/**
+ * Default namespace from RFC 4122
+ *
+ * Name string is a fully-qualified domain name
+ */
+enum dnsNamespace = UUID("6ba7b810-9dad-11d1-80b4-00c04fd430c8");
+
+/**
+ * Default namespace from RFC 4122
+ *
+ * Name string is a URL
+ */
+enum urlNamespace = UUID("6ba7b811-9dad-11d1-80b4-00c04fd430c8");
+
+/**
+ * Default namespace from RFC 4122
+ *
+ * Name string is an ISO OID
+ */
+enum oidNamespace = UUID("6ba7b812-9dad-11d1-80b4-00c04fd430c8");
+
+/**
+ * Default namespace from RFC 4122
+ *
+ * Name string is an X.500 DN (in DER or a text output format)
+ */
+enum x500Namespace = UUID("6ba7b814-9dad-11d1-80b4-00c04fd430c8");
+
+/**
+ * Regex string to extract UUIDs from text.
+ */
+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}";
+
+///
+@safe unittest
+{
+ import std.algorithm;
+ import std.regex;
+
+ string test = "Lorem ipsum dolor sit amet, consetetur "~
+ "6ba7b814-9dad-11d1-80b4-00c04fd430c8 sadipscing \n"~
+ "elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore \r\n"~
+ "magna aliquyam erat, sed diam voluptua. "~
+ "8ab3060e-2cba-4f23-b74c-b52db3bdfb46 At vero eos et accusam et "~
+ "justo duo dolores et ea rebum.";
+
+ auto r = regex(uuidRegex, "g");
+ UUID[] found;
+ foreach (c; match(test, r))
+ {
+ found ~= UUID(c.hit);
+ }
+ assert(found == [
+ UUID("6ba7b814-9dad-11d1-80b4-00c04fd430c8"),
+ UUID("8ab3060e-2cba-4f23-b74c-b52db3bdfb46"),
+ ]);
+}
+
+/**
+ * This exception is thrown if an error occurs when parsing a UUID
+ * from a string.
+ */
+public class UUIDParsingException : Exception
+{
+ /**
+ * The reason why parsing the UUID string failed (if known)
+ */
+ enum Reason
+ {
+ unknown, ///
+ tooLittle, ///The passed in input was correct, but more input was expected.
+ tooMuch, ///The input data is too long (There's no guarantee the first part of the data is valid)
+ invalidChar, ///Encountered an invalid character
+
+ }
+ ///ditto
+ Reason reason;
+ ///The original input string which should have been parsed.
+ string input;
+ ///The position in the input string where the error occurred.
+ size_t position;
+
+ private this(string input, size_t pos, Reason why = Reason.unknown, string msg = "",
+ Throwable next = null, string file = __FILE__, size_t line = __LINE__) pure @trusted
+ {
+ import std.array : replace;
+ import std.format : format;
+ this.input = input;
+ this.position = pos;
+ this.reason = why;
+ string message = format("An error occured in the UUID parser: %s\n" ~
+ " * Input:\t'%s'\n * Position:\t%s", msg, replace(replace(input,
+ "\r", "\\r"), "\n", "\\n"), pos);
+ super(message, file, line, next);
+ }
+}
+
+///
+@safe unittest
+{
+ import std.exception : collectException;
+
+ const inputUUID = "this-is-an-invalid-uuid";
+ auto ex = collectException!UUIDParsingException(UUID(inputUUID));
+ assert(ex !is null); // check that exception was thrown
+ assert(ex.input == inputUUID);
+ assert(ex.position == 0);
+ assert(ex.reason == UUIDParsingException.Reason.tooLittle);
+}
+
+@safe unittest
+{
+ auto ex = new UUIDParsingException("foo", 10, UUIDParsingException.Reason.tooMuch);
+ assert(ex.input == "foo");
+ assert(ex.position == 10);
+ assert(ex.reason == UUIDParsingException.Reason.tooMuch);
+}
diff --git a/libphobos/src/std/variant.d b/libphobos/src/std/variant.d
new file mode 100644
index 0000000..574e2c5
--- /dev/null
+++ b/libphobos/src/std/variant.d
@@ -0,0 +1,2771 @@
+// Written in the D programming language.
+
+/**
+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.
+
+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 std.meta, std.traits, std.typecons;
+
+///
+@system unittest
+{
+ 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");
+
+ // can also assign arrays
+ a = new int[42];
+ 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
+}
+
+/++
+ Gives the $(D sizeof) the largest type given.
+ +/
+template maxSize(T...)
+{
+ static if (T.length == 1)
+ {
+ enum size_t maxSize = T[0].sizeof;
+ }
+ else
+ {
+ import std.algorithm.comparison : max;
+ enum size_t maxSize = max(T[0].sizeof, maxSize!(T[1 .. $]));
+ }
+}
+
+///
+@safe unittest
+{
+ static assert(maxSize!(int, long) == 8);
+ static assert(maxSize!(bool, byte) == 1);
+
+ struct Cat { int a, b, c; }
+ static assert(maxSize!(bool, Cat) == 12);
+}
+
+struct This;
+
+private alias This2Variant(V, T...) = AliasSeq!(ReplaceType!(This, V, T));
+
+/**
+ * Back-end type seldom used directly by user
+ * code. Two commonly-used types using $(D VariantN) are:
+ *
+ * $(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 $(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 Algebraic) and $(D Variant) share $(D
+ * VariantN)'s interface. (See their respective documentations below.)
+ *
+ * $(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 VariantN) object without being boxed (types larger
+ * than this will be boxed).
+ *
+ */
+struct VariantN(size_t maxDataSize, AllowedTypesParam...)
+{
+ /**
+ The list of allowed types. If empty, any type is allowed.
+ */
+ alias AllowedTypes = This2Variant!(VariantN, AllowedTypesParam);
+
+private:
+ // Compute the largest practical size from maxDataSize
+ struct SizeChecker
+ {
+ int function() fptr;
+ ubyte[maxDataSize] data;
+ }
+ enum size = SizeChecker.sizeof - (int function()).sizeof;
+
+ /** 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)
+ {
+ enum bool allowed
+ = is(T == VariantN)
+ ||
+ //T.sizeof <= size &&
+ (AllowedTypes.length == 0 || staticIndexOf!(T, AllowedTypes) >= 0);
+ }
+
+ // Each internal operation is encoded with an identifier. See
+ // the "handler" function below.
+ enum OpID { getTypeInfo, get, compare, equals, testConversion, toString,
+ index, indexAssign, catAssign, copyOut, length,
+ apply, postblit, destruct }
+
+ // state
+ ptrdiff_t function(OpID selector, ubyte[size]* store, void* data) fptr
+ = &handler!(void);
+ union
+ {
+ ubyte[size] store;
+ // conservatively mark the region as pointers
+ static if (size >= (void*).sizeof)
+ void*[size / (void*).sizeof] p;
+ }
+
+ // internals
+ // Handler for an uninitialized value
+ static ptrdiff_t handler(A : void)(OpID selector, ubyte[size]*, void* parm)
+ {
+ switch (selector)
+ {
+ case OpID.getTypeInfo:
+ *cast(TypeInfo *) parm = typeid(A);
+ break;
+ case OpID.copyOut:
+ auto target = cast(VariantN *) parm;
+ target.fptr = &handler!(A);
+ // no need to copy the data (it's garbage)
+ break;
+ case OpID.compare:
+ case OpID.equals:
+ auto rhs = cast(const VariantN *) parm;
+ return rhs.peek!(A)
+ ? 0 // all uninitialized are equal
+ : ptrdiff_t.min; // uninitialized variant is not comparable otherwise
+ case OpID.toString:
+ string * target = cast(string*) parm;
+ *target = "<Uninitialized VariantN>";
+ break;
+ case OpID.postblit:
+ case OpID.destruct:
+ break;
+ case OpID.get:
+ case OpID.testConversion:
+ case OpID.index:
+ case OpID.indexAssign:
+ case OpID.catAssign:
+ case OpID.length:
+ throw new VariantException(
+ "Attempt to use an uninitialized VariantN");
+ default: assert(false, "Invalid OpID");
+ }
+ return 0;
+ }
+
+ // 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)
+ {
+ static if (A.sizeof <= size)
+ return cast(A*) untyped;
+ else
+ return *cast(A**) untyped;
+ }
+ return null;
+ }
+
+ static ptrdiff_t compare(A* rhsPA, A* zis, OpID selector)
+ {
+ static if (is(typeof(*rhsPA == *zis)))
+ {
+ if (*rhsPA == *zis)
+ {
+ return 0;
+ }
+ static if (is(typeof(*zis < *rhsPA)))
+ {
+ // Many types (such as any using the default Object opCmp)
+ // will throw on an invalid opCmp, so do it only
+ // if the caller requests it.
+ if (selector == OpID.compare)
+ return *zis < *rhsPA ? -1 : 1;
+ else
+ return ptrdiff_t.min;
+ }
+ else
+ {
+ // Not equal, and type does not support ordering
+ // comparisons.
+ return ptrdiff_t.min;
+ }
+ }
+ else
+ {
+ // Type does not support comparisons at all.
+ return ptrdiff_t.min;
+ }
+ }
+
+ auto zis = getPtr(pStore);
+ // Input: TypeInfo object
+ // Output: target points to a copy of *me, if me was not null
+ // Returns: true iff the A can be converted to the type represented
+ // by the incoming TypeInfo
+ static bool tryPutting(A* src, TypeInfo targetType, void* target)
+ {
+ alias UA = Unqual!A;
+ 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 = AliasSeq!(ImmuTypes, ConstTypes, SharedConstTypes);
+ else static if (is(A == shared))
+ {
+ static if (is(A == const))
+ alias AllTypes = SharedConstTypes;
+ else
+ alias AllTypes = AliasSeq!(SharedTypes, SharedConstTypes);
+ }
+ else
+ {
+ static if (is(A == const))
+ alias AllTypes = ConstTypes;
+ else
+ alias AllTypes = AliasSeq!(MutaTypes, ConstTypes);
+ }
+
+ foreach (T ; AllTypes)
+ {
+ if (targetType != typeid(T))
+ continue;
+
+ 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)
+ {
+ static if (T.sizeof > 0)
+ assert(target, "target must be non-null");
+
+ emplaceRef(*cast(Unqual!T*) zat, *cast(UA*) src);
+ }
+ }
+ else
+ {
+ // type T is not constructible from A
+ if (src)
+ assert(false, A.stringof);
+ }
+ return true;
+ }
+ return false;
+ }
+
+ switch (selector)
+ {
+ case OpID.getTypeInfo:
+ *cast(TypeInfo *) parm = typeid(A);
+ break;
+ case OpID.copyOut:
+ auto target = cast(VariantN *) parm;
+ assert(target);
+
+ static if (target.size < A.sizeof)
+ {
+ if (target.type.tsize < A.sizeof)
+ *cast(A**)&target.store = new A;
+ }
+ tryPutting(zis, typeid(A), cast(void*) getPtr(&target.store))
+ || assert(false);
+ target.fptr = &handler!(A);
+ break;
+ case OpID.get:
+ 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:
+ case OpID.equals:
+ auto rhsP = cast(VariantN *) parm;
+ auto rhsType = rhsP.type;
+ // Are we the same?
+ if (rhsType == typeid(A))
+ {
+ // cool! Same type!
+ auto rhsPA = getPtr(&rhsP.store);
+ return compare(rhsPA, zis, selector);
+ }
+ else if (rhsType == typeid(void))
+ {
+ // No support for ordering comparisons with
+ // uninitialized vars
+ return ptrdiff_t.min;
+ }
+ VariantN temp;
+ // Do I convert to rhs?
+ if (tryPutting(zis, rhsType, &temp.store))
+ {
+ // cool, I do; temp's store contains my data in rhs's type!
+ // also fix up its fptr
+ temp.fptr = rhsP.fptr;
+ // now lhsWithRhsType is a full-blown VariantN of rhs's type
+ if (selector == OpID.compare)
+ return temp.opCmp(*rhsP);
+ else
+ return temp.opEquals(*rhsP) ? 0 : 1;
+ }
+ // Does rhs convert to zis?
+ 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);
+ return compare(rhsPA, zis, selector);
+ }
+ return ptrdiff_t.min; // dunno
+ case OpID.toString:
+ auto target = cast(string*) parm;
+ static if (is(typeof(to!(string)(*zis))))
+ {
+ *target = to!(string)(*zis);
+ break;
+ }
+ // TODO: The following test evaluates to true for shared objects.
+ // Use __traits for now until this is sorted out.
+ // else static if (is(typeof((*zis).toString)))
+ else static if (__traits(compiles, {(*zis).toString();}))
+ {
+ *target = (*zis).toString();
+ break;
+ }
+ else
+ {
+ throw new VariantException(typeid(A), typeid(string));
+ }
+
+ case OpID.index:
+ auto result = cast(Variant*) parm;
+ static if (isArray!(A) && !is(Unqual!(typeof(A.init[0])) == void))
+ {
+ // array type; input and output are the same VariantN
+ size_t index = result.convertsTo!(int)
+ ? result.get!(int) : result.get!(size_t);
+ *result = (*zis)[index];
+ break;
+ }
+ else static if (isAssociativeArray!(A))
+ {
+ *result = (*zis)[result.get!(typeof(A.init.keys[0]))];
+ break;
+ }
+ else
+ {
+ throw new VariantException(typeid(A), result[0].type);
+ }
+
+ case OpID.indexAssign:
+ // array type; result comes first, index comes second
+ auto args = cast(Variant*) parm;
+ static if (isArray!(A) && is(typeof((*zis)[0] = (*zis)[0])))
+ {
+ size_t index = args[1].convertsTo!(int)
+ ? args[1].get!(int) : args[1].get!(size_t);
+ (*zis)[index] = args[0].get!(typeof((*zis)[0]));
+ break;
+ }
+ else static if (isAssociativeArray!(A))
+ {
+ (*zis)[args[1].get!(typeof(A.init.keys[0]))]
+ = args[0].get!(typeof(A.init.values[0]));
+ break;
+ }
+ else
+ {
+ throw new VariantException(typeid(A), args[0].type);
+ }
+
+ case OpID.catAssign:
+ static if (!is(Unqual!(typeof((*zis)[0])) == void) && is(typeof((*zis)[0])) && is(typeof((*zis) ~= *zis)))
+ {
+ // array type; parm is the element to append
+ auto arg = cast(Variant*) parm;
+ alias E = typeof((*zis)[0]);
+ if (arg[0].convertsTo!(E))
+ {
+ // append one element to the array
+ (*zis) ~= [ arg[0].get!(E) ];
+ }
+ else
+ {
+ // append a whole array to the array
+ (*zis) ~= arg[0].get!(A);
+ }
+ break;
+ }
+ else
+ {
+ throw new VariantException(typeid(A), typeid(void[]));
+ }
+
+ case OpID.length:
+ static if (isArray!(A) || isAssociativeArray!(A))
+ {
+ return zis.length;
+ }
+ else
+ {
+ throw new VariantException(typeid(A), typeid(void[]));
+ }
+
+ 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
+ {
+ 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,
+ // otherwise we run into issues such as with const values.
+ // We still get the actual type from the Variant though
+ // to ensure that we retain const correctness.
+ Tuple!(staticMap!(Unqual, ParamTypes)) t;
+ enforce(t.length == argCount,
+ text("Argument count mismatch: ",
+ A.stringof, " expects ", t.length,
+ " argument(s), not ", argCount, "."));
+ auto variantArgs = p[1 .. argCount + 1];
+ foreach (i, T; ParamTypes)
+ {
+ t[i] = cast() variantArgs[i].get!T;
+ }
+
+ auto args = cast(Tuple!(ParamTypes))t;
+ static if (is(ReturnType!A == void))
+ {
+ (*zis)(args.expand);
+ *p = Variant.init; // void returns uninitialized Variant.
+ }
+ else
+ {
+ *p = (*zis)(args.expand);
+ }
+ }
+ break;
+
+ case OpID.postblit:
+ static if (hasElaborateCopyConstructor!A)
+ {
+ typeid(A).postblit(zis);
+ }
+ break;
+
+ case OpID.destruct:
+ static if (hasElaborateDestructor!A)
+ {
+ typeid(A).destroy(zis);
+ }
+ break;
+
+ default: assert(false);
+ }
+ return 0;
+ }
+
+ enum doUnittest = is(VariantN == Variant);
+
+public:
+ /** Constructs a $(D VariantN) value given an argument of a
+ * generic type. Statically rejects disallowed types.
+ */
+
+ this(T)(T value)
+ {
+ static assert(allowed!(T), "Cannot store a " ~ T.stringof
+ ~ " in a " ~ VariantN.stringof);
+ 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)
+ {
+ fptr(OpID.postblit, &store, null);
+ }
+ }
+
+ static if (!AllowedTypes.length || anySatisfy!(hasElaborateDestructor, AllowedTypes))
+ {
+ ~this()
+ {
+ fptr(OpID.destruct, &store, null);
+ }
+ }
+
+ /** Assigns a $(D VariantN) from a generic
+ * argument. Statically rejects disallowed types. */
+
+ VariantN opAssign(T)(T rhs)
+ {
+ //writeln(typeid(rhs));
+ static assert(allowed!(T), "Cannot store a " ~ T.stringof
+ ~ " in a " ~ VariantN.stringof ~ ". Valid types are "
+ ~ AllowedTypes.stringof);
+
+ static if (is(T : VariantN))
+ {
+ rhs.fptr(OpID.copyOut, &rhs.store, &this);
+ }
+ else static if (is(T : const(VariantN)))
+ {
+ static assert(false,
+ "Assigning Variant objects from const Variant"~
+ " objects is currently not supported.");
+ }
+ else
+ {
+ static if (!AllowedTypes.length || anySatisfy!(hasElaborateDestructor, AllowedTypes))
+ {
+ // Assignment should destruct previous value
+ fptr(OpID.destruct, &store, null);
+ }
+
+ 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.
+ //
+ // TODO: If a shared class has an atomic reference then using
+ // an atomic load may be more correct. Just make sure
+ // to use the fastest approach for the load op.
+ static if (is(T == class) && is(T == shared))
+ memcpy(&store, cast(const(void*)) &rhs, rhs.sizeof);
+ else
+ memcpy(&store, &rhs, rhs.sizeof);
+ static if (hasElaborateCopyConstructor!T)
+ {
+ typeid(T).postblit(&store);
+ }
+ }
+ else
+ {
+ import core.stdc.string : memcpy;
+ static if (__traits(compiles, {new T(T.init);}))
+ {
+ auto p = new T(rhs);
+ }
+ else
+ {
+ auto p = new T;
+ *p = rhs;
+ }
+ memcpy(&store, &p, p.sizeof);
+ }
+ fptr = &handler!(T);
+ }
+ 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;
+ pack[0] = P.length;
+ foreach (i, _; params)
+ {
+ pack[i + 1] = params[i];
+ }
+ fptr(OpID.apply, &store, &pack);
+ return pack[0];
+ }
+
+ /** Returns true if and only if the $(D VariantN) object
+ * holds a valid value (has been initialized with, or assigned
+ * from, a valid value).
+ */
+ @property bool hasValue() const pure nothrow
+ {
+ // @@@BUG@@@ in compiler, the cast shouldn't be needed
+ return cast(typeof(&handler!(void))) fptr != &handler!(void);
+ }
+
+ ///
+ static if (doUnittest)
+ @system unittest
+ {
+ Variant a;
+ assert(!a.hasValue);
+ Variant b;
+ a = b;
+ assert(!a.hasValue); // still no value
+ a = 5;
+ assert(a.hasValue);
+ }
+
+ /**
+ * 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
+ {
+ static if (!is(T == void))
+ static assert(allowed!(T), "Cannot store a " ~ T.stringof
+ ~ " in a " ~ VariantN.stringof);
+ if (type != typeid(T))
+ return null;
+ static if (T.sizeof <= size)
+ return cast(inout T*)&store;
+ else
+ return *cast(inout T**)&store;
+ }
+
+ ///
+ static if (doUnittest)
+ @system unittest
+ {
+ Variant a = 5;
+ auto b = a.peek!(int);
+ assert(b !is null);
+ *b = 6;
+ assert(a == 6);
+ }
+
+ /**
+ * Returns the $(D typeid) of the currently held value.
+ */
+
+ @property TypeInfo type() const nothrow @trusted
+ {
+ scope(failure) assert(0);
+
+ TypeInfo result;
+ fptr(OpID.getTypeInfo, null, &result);
+ return result;
+ }
+
+ /**
+ * 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
+ {
+ TypeInfo info = typeid(T);
+ return fptr(OpID.testConversion, null, &info) == 0;
+ }
+
+ /**
+ 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 inout(T) get(T)() inout
+ {
+ 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, cast(ubyte[size]*) &store, &buf))
+ {
+ throw new VariantException(type, typeid(T));
+ }
+ return result;
+ }
+
+ /// Ditto
+ @property auto get(uint index)() inout
+ if (index < AllowedTypes.length)
+ {
+ foreach (i, T; AllowedTypes)
+ {
+ static if (index == i) return get!T;
+ }
+ assert(0);
+ }
+
+ /**
+ * 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)
+ {
+ // maybe optimize this fella; handle ints separately
+ return to!T(get!real);
+ }
+ else if (convertsTo!(const(char)[]))
+ {
+ return to!T(get!(const(char)[]));
+ }
+ // I'm not sure why this doesn't convert to const(char),
+ // but apparently it doesn't (probably a deeper bug).
+ //
+ // Until that is fixed, this quick addition keeps a common
+ // function working. "10".coerce!int ought to work.
+ else if (convertsTo!(immutable(char)[]))
+ {
+ return to!T(get!(immutable(char)[]));
+ }
+ else
+ {
+ import std.exception : enforce;
+ enforce(false, text("Type ", type, " does not convert to ",
+ typeid(T)));
+ assert(0);
+ }
+ }
+ else static if (is(T : Object))
+ {
+ return to!(T)(get!(Object));
+ }
+ else static if (isSomeString!(T))
+ {
+ return to!(T)(toString());
+ }
+ else
+ {
+ // Fix for bug 1649
+ static assert(false, "unsupported type for coercion");
+ }
+ }
+
+ /**
+ * Formats the stored value as a string.
+ */
+
+ string toString()
+ {
+ string result;
+ fptr(OpID.toString, &store, &result) == 0 || assert(false);
+ return result;
+ }
+
+ /**
+ * Comparison for equality used by the "==" and "!=" operators.
+ */
+
+ // 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;
+ else
+ auto temp = VariantN(rhs);
+ return !fptr(OpID.equals, cast(ubyte[size]*) &store,
+ cast(void*) &temp);
+ }
+
+ // workaround for bug 10567 fix
+ int opCmp(ref const VariantN rhs) const
+ {
+ 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 rhs), an exception is thrown.
+ */
+
+ int opCmp(T)(T rhs)
+ if (allowed!T) // includes T == VariantN
+ {
+ static if (is(T == VariantN))
+ alias temp = rhs;
+ else
+ auto temp = VariantN(rhs);
+ auto result = fptr(OpID.compare, &store, &temp);
+ if (result == ptrdiff_t.min)
+ {
+ throw new VariantException(type, temp.type);
+ }
+
+ assert(result >= -1 && result <= 1); // Should be true for opCmp.
+ return cast(int) result;
+ }
+
+ /**
+ * Computes the hash of the held value.
+ */
+
+ size_t toHash() const nothrow @safe
+ {
+ return type.getHash(&store);
+ }
+
+ private VariantN opArithmetic(T, string op)(T other)
+ {
+ static if (isInstanceOf!(.VariantN, T))
+ {
+ string tryUseType(string tp)
+ {
+ import std.format : format;
+ return q{
+ static if (allowed!%1$s && T.allowed!%1$s)
+ if (convertsTo!%1$s && other.convertsTo!%1$s)
+ return VariantN(get!%1$s %2$s other.get!%1$s);
+ }.format(tp, op);
+ }
+
+ mixin(tryUseType("uint"));
+ mixin(tryUseType("int"));
+ mixin(tryUseType("ulong"));
+ mixin(tryUseType("long"));
+ mixin(tryUseType("float"));
+ mixin(tryUseType("double"));
+ mixin(tryUseType("real"));
+ }
+ else
+ {
+ static if (allowed!T)
+ if (auto pv = peek!T) return VariantN(mixin("*pv " ~ op ~ " other"));
+ static if (allowed!uint && is(typeof(T.max) : uint) && isUnsigned!T)
+ if (convertsTo!uint) return VariantN(mixin("get!(uint) " ~ op ~ " other"));
+ static if (allowed!int && is(typeof(T.max) : int) && !isUnsigned!T)
+ if (convertsTo!int) return VariantN(mixin("get!(int) " ~ op ~ " other"));
+ static if (allowed!ulong && is(typeof(T.max) : ulong) && isUnsigned!T)
+ if (convertsTo!ulong) return VariantN(mixin("get!(ulong) " ~ op ~ " other"));
+ static if (allowed!long && is(typeof(T.max) : long) && !isUnsigned!T)
+ if (convertsTo!long) return VariantN(mixin("get!(long) " ~ op ~ " other"));
+ static if (allowed!float && is(T : float))
+ if (convertsTo!float) return VariantN(mixin("get!(float) " ~ op ~ " other"));
+ static if (allowed!double && is(T : double))
+ if (convertsTo!double) return VariantN(mixin("get!(double) " ~ op ~ " other"));
+ static if (allowed!real && is (T : real))
+ if (convertsTo!real) return VariantN(mixin("get!(real) " ~ op ~ " other"));
+ }
+
+ throw new VariantException("No possible match found for VariantN "~op~" "~T.stringof);
+ }
+
+ private VariantN opLogic(T, string op)(T other)
+ {
+ VariantN result;
+ static if (is(T == VariantN))
+ {
+ if (convertsTo!(uint) && other.convertsTo!(uint))
+ result = mixin("get!(uint) " ~ op ~ " other.get!(uint)");
+ else if (convertsTo!(int) && other.convertsTo!(int))
+ result = mixin("get!(int) " ~ op ~ " other.get!(int)");
+ else if (convertsTo!(ulong) && other.convertsTo!(ulong))
+ result = mixin("get!(ulong) " ~ op ~ " other.get!(ulong)");
+ else
+ result = mixin("get!(long) " ~ op ~ " other.get!(long)");
+ }
+ else
+ {
+ if (is(typeof(T.max) : uint) && T.min == 0 && convertsTo!(uint))
+ result = mixin("get!(uint) " ~ op ~ " other");
+ else if (is(typeof(T.max) : int) && T.min < 0 && convertsTo!(int))
+ result = mixin("get!(int) " ~ op ~ " other");
+ else if (is(typeof(T.max) : ulong) && T.min == 0
+ && convertsTo!(ulong))
+ result = mixin("get!(ulong) " ~ op ~ " other");
+ else
+ result = mixin("get!(long) " ~ op ~ " other");
+ }
+ return result;
+ }
+
+ /**
+ * 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.
+ */
+
+ // Adapted from http://www.prowiki.org/wiki4d/wiki.cgi?DanielKeep/Variant
+ // arithmetic
+ VariantN opAdd(T)(T rhs) { return opArithmetic!(T, "+")(rhs); }
+ ///ditto
+ VariantN opSub(T)(T rhs) { return opArithmetic!(T, "-")(rhs); }
+
+ // Commenteed all _r versions for now because of ambiguities
+ // arising when two Variants are used
+
+ // ///ditto
+ // VariantN opSub_r(T)(T lhs)
+ // {
+ // return VariantN(lhs).opArithmetic!(VariantN, "-")(this);
+ // }
+ ///ditto
+ VariantN opMul(T)(T rhs) { return opArithmetic!(T, "*")(rhs); }
+ ///ditto
+ VariantN opDiv(T)(T rhs) { return opArithmetic!(T, "/")(rhs); }
+ // ///ditto
+ // VariantN opDiv_r(T)(T lhs)
+ // {
+ // return VariantN(lhs).opArithmetic!(VariantN, "/")(this);
+ // }
+ ///ditto
+ VariantN opMod(T)(T rhs) { return opArithmetic!(T, "%")(rhs); }
+ // ///ditto
+ // VariantN opMod_r(T)(T lhs)
+ // {
+ // return VariantN(lhs).opArithmetic!(VariantN, "%")(this);
+ // }
+ ///ditto
+ VariantN opAnd(T)(T rhs) { return opLogic!(T, "&")(rhs); }
+ ///ditto
+ VariantN opOr(T)(T rhs) { return opLogic!(T, "|")(rhs); }
+ ///ditto
+ VariantN opXor(T)(T rhs) { return opLogic!(T, "^")(rhs); }
+ ///ditto
+ VariantN opShl(T)(T rhs) { return opLogic!(T, "<<")(rhs); }
+ // ///ditto
+ // VariantN opShl_r(T)(T lhs)
+ // {
+ // return VariantN(lhs).opLogic!(VariantN, "<<")(this);
+ // }
+ ///ditto
+ VariantN opShr(T)(T rhs) { return opLogic!(T, ">>")(rhs); }
+ // ///ditto
+ // VariantN opShr_r(T)(T lhs)
+ // {
+ // return VariantN(lhs).opLogic!(VariantN, ">>")(this);
+ // }
+ ///ditto
+ VariantN opUShr(T)(T rhs) { return opLogic!(T, ">>>")(rhs); }
+ // ///ditto
+ // VariantN opUShr_r(T)(T lhs)
+ // {
+ // return VariantN(lhs).opLogic!(VariantN, ">>>")(this);
+ // }
+ ///ditto
+ VariantN opCat(T)(T rhs)
+ {
+ auto temp = this;
+ temp ~= rhs;
+ return temp;
+ }
+ // ///ditto
+ // VariantN opCat_r(T)(T rhs)
+ // {
+ // VariantN temp = rhs;
+ // temp ~= this;
+ // return temp;
+ // }
+
+ ///ditto
+ VariantN opAddAssign(T)(T rhs) { return this = this + rhs; }
+ ///ditto
+ VariantN opSubAssign(T)(T rhs) { return this = this - rhs; }
+ ///ditto
+ VariantN opMulAssign(T)(T rhs) { return this = this * rhs; }
+ ///ditto
+ VariantN opDivAssign(T)(T rhs) { return this = this / rhs; }
+ ///ditto
+ VariantN opModAssign(T)(T rhs) { return this = this % rhs; }
+ ///ditto
+ VariantN opAndAssign(T)(T rhs) { return this = this & rhs; }
+ ///ditto
+ VariantN opOrAssign(T)(T rhs) { return this = this | rhs; }
+ ///ditto
+ VariantN opXorAssign(T)(T rhs) { return this = this ^ rhs; }
+ ///ditto
+ VariantN opShlAssign(T)(T rhs) { return this = this << rhs; }
+ ///ditto
+ VariantN opShrAssign(T)(T rhs) { return this = this >> rhs; }
+ ///ditto
+ VariantN opUShrAssign(T)(T rhs) { return this = this >>> rhs; }
+ ///ditto
+ VariantN opCatAssign(T)(T rhs)
+ {
+ auto toAppend = Variant(rhs);
+ fptr(OpID.catAssign, &store, &toAppend) == 0 || assert(false);
+ return this;
+ }
+
+ /**
+ * Array and associative array operations. If a $(D
+ * VariantN) contains an (associative) array, it can be indexed
+ * into. Otherwise, an exception is thrown.
+ */
+ inout(Variant) opIndex(K)(K i) inout
+ {
+ auto result = Variant(i);
+ fptr(OpID.index, cast(ubyte[size]*) &store, &result) == 0 || assert(false);
+ return result;
+ }
+
+ ///
+ static if (doUnittest)
+ @system unittest
+ {
+ Variant a = new int[10];
+ a[5] = 42;
+ assert(a[5] == 42);
+ a[5] += 8;
+ assert(a[5] == 50);
+
+ int[int] hash = [ 42:24 ];
+ 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];
+ }
+
+ /// 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()
+ {
+ return cast(size_t) fptr(OpID.length, &store, null);
+ }
+
+ /**
+ If the $(D VariantN) contains an array, applies $(D dg) to each
+ element of the array in turn. Otherwise, throws an exception.
+ */
+ int opApply(Delegate)(scope Delegate dg) if (is(Delegate == delegate))
+ {
+ alias A = Parameters!(Delegate)[0];
+ if (type == typeid(A[]))
+ {
+ auto arr = get!(A[]);
+ foreach (ref e; arr)
+ {
+ if (dg(e)) return 1;
+ }
+ }
+ else static if (is(A == VariantN))
+ {
+ foreach (i; 0 .. length)
+ {
+ // @@@TODO@@@: find a better way to not confuse
+ // clients who think they change values stored in the
+ // Variant when in fact they are only changing tmp.
+ auto tmp = this[i];
+ debug scope(exit) assert(tmp == this[i]);
+ if (dg(tmp)) return 1;
+ }
+ }
+ else
+ {
+ import std.conv : text;
+ import std.exception : enforce;
+ enforce(false, text("Variant type ", type,
+ " not iterable with values of type ",
+ A.stringof));
+ }
+ return 0;
+ }
+}
+
+@system unittest
+{
+ import std.conv : to;
+ Variant v;
+ int foo() { return 42; }
+ v = &foo;
+ assert(v() == 42);
+
+ static int bar(string s) { return to!int(s); }
+ v = &bar;
+ 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
+@system unittest
+{
+ int[4] elements = [0, 1, 2, 3];
+ Variant v = elements;
+ assert(v == elements);
+ assert(v[2] == 2);
+ assert(v[3] == 3);
+ v[2] = 6;
+ assert(v[2] == 6);
+ 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
+@system unittest
+{
+ struct S
+ {
+ int a;
+ long b;
+ string c;
+ real d = 0.0;
+ bool e;
+ }
+
+ static assert(S.sizeof >= Variant.sizeof);
+ alias Types = AliasSeq!(string, int, S);
+ alias MyVariant = VariantN!(maxSize!Types, Types);
+
+ auto v = MyVariant(S.init);
+ assert(v == S.init);
+}
+
+// Issue #10961
+@system unittest
+{
+ // Primarily test that we can assign a void[] to a Variant.
+ void[] elements = cast(void[])[1, 2, 3];
+ Variant v = elements;
+ void[] returned = v.get!(void[]);
+ assert(returned == elements);
+}
+
+// Issue #13352
+@system unittest
+{
+ alias TP = Algebraic!(long);
+ auto a = TP(1L);
+ auto b = TP(2L);
+ assert(!TP.allowed!ulong);
+ assert(a + b == 3L);
+ assert(a + 2 == 3L);
+ assert(1 + b == 3L);
+
+ alias TP2 = Algebraic!(long, string);
+ auto c = TP2(3L);
+ assert(a + c == 4L);
+}
+
+// Issue #13354
+@system unittest
+{
+ alias A = Algebraic!(string[]);
+ A a = ["a", "b"];
+ assert(a[0] == "a");
+ assert(a[1] == "b");
+ a[1] = "c";
+ assert(a[1] == "c");
+
+ alias AA = Algebraic!(int[string]);
+ AA aa = ["a": 1, "b": 2];
+ assert(aa["a"] == 1);
+ assert(aa["b"] == 2);
+ aa["b"] = 3;
+ assert(aa["b"] == 3);
+}
+
+// Issue #14198
+@system unittest
+{
+ Variant a = true;
+ assert(a.type == typeid(bool));
+}
+
+// Issue #14233
+@system unittest
+{
+ alias Atom = Algebraic!(string, This[]);
+
+ Atom[] values = [];
+ auto a = Atom(values);
+}
+
+pure nothrow @nogc
+@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 $(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);
+}
+
+///
+@system unittest
+{
+ auto v = Algebraic!(int, double, string)(5);
+ assert(v.peek!(int));
+ v = 3.14;
+ assert(v.peek!(double));
+ // auto x = v.peek!(long); // won't compile, type long not allowed
+ // v = '1'; // won't compile, type char not allowed
+}
+
+/**
+$(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 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 args).
+ *
+ * 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.
+ */
+Variant[] variantArray(T...)(T args)
+{
+ Variant[] result;
+ foreach (arg; args)
+ {
+ result ~= Variant(arg);
+ }
+ return result;
+}
+
+///
+@system unittest
+{
+ auto a = variantArray(1, 3.14, "Hi!");
+ assert(a[1] == 3.14);
+ auto b = Variant(a); // variant array as variant
+ assert(b[1] == 3.14);
+}
+
+/**
+ * Thrown in three cases:
+ *
+ * $(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.))
+ *
+ */
+
+// @@@ BUG IN COMPILER. THE 'STATIC' BELOW SHOULD NOT COMPILE
+static class VariantException : Exception
+{
+ /// The source type in the conversion or comparison
+ TypeInfo source;
+ /// The target type in the conversion or comparison
+ TypeInfo target;
+ this(string s)
+ {
+ super(s);
+ }
+ this(TypeInfo source, TypeInfo target)
+ {
+ super("Variant: attempting to use incompatible types "
+ ~ source.toString()
+ ~ " and " ~ target.toString());
+ this.source = source;
+ this.target = target;
+ }
+}
+
+@system unittest
+{
+ alias W1 = This2Variant!(char, int, This[int]);
+ alias W2 = AliasSeq!(int, char[int]);
+ static assert(is(W1 == W2));
+
+ alias var_t = Algebraic!(void, string);
+ var_t foo = "quux";
+}
+
+@system unittest
+{
+ 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);
+}
+
+@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);
+
+ // variantArray tests
+ auto heterogeneous = variantArray(1, 4.5, "hi");
+ assert(heterogeneous.length == 3);
+ auto variantArrayAsVariant = Variant(heterogeneous);
+ assert(variantArrayAsVariant[0] == 1);
+ assert(variantArrayAsVariant.length == 3);
+
+ // array tests
+ auto arr = Variant([1.2].dup);
+ auto e = arr[0];
+ assert(e == 1.2);
+ arr[0] = 2.0;
+ assert(arr[0] == 2);
+ arr ~= 4.5;
+ assert(arr[1] == 4.5);
+
+ // general tests
+ Variant a;
+ auto b = Variant(5);
+ assert(!b.peek!(real) && b.peek!(int));
+ // assign
+ a = *b.peek!(int);
+ // comparison
+ assert(a == b, a.type.toString() ~ " " ~ b.type.toString());
+ auto c = Variant("this is a string");
+ assert(a != c);
+ // comparison via implicit conversions
+ a = 42; b = 42.0; assert(a == b);
+
+ // try failing conversions
+ bool failed = false;
+ try
+ {
+ auto d = c.get!(int);
+ }
+ catch (Exception e)
+ {
+ //writeln(stderr, e.toString);
+ failed = true;
+ }
+ assert(failed); // :o)
+
+ // toString tests
+ a = Variant(42); assert(a.toString() == "42");
+ a = Variant(42.22); assert(a.toString() == "42.22");
+
+ // coerce tests
+ a = Variant(42.22); assert(a.coerce!(int) == 42);
+ a = cast(short) 5; assert(a.coerce!(double) == 5);
+ a = Variant("10"); assert(a.coerce!int == 10);
+
+ a = Variant(1);
+ assert(a.coerce!bool);
+ a = Variant(0);
+ assert(!a.coerce!bool);
+
+ a = Variant(1.0);
+ assert(a.coerce!bool);
+ a = Variant(0.0);
+ assert(!a.coerce!bool);
+ a = Variant(float.init);
+ assertThrown!ConvException(a.coerce!bool);
+
+ a = Variant("true");
+ assert(a.coerce!bool);
+ a = Variant("false");
+ assert(!a.coerce!bool);
+ a = Variant("");
+ assertThrown!ConvException(a.coerce!bool);
+
+ // Object tests
+ class B1 {}
+ class B2 : B1 {}
+ a = new B2;
+ assert(a.coerce!(B1) !is null);
+ a = new B1;
+ assert(collectException(a.coerce!(B2) is null));
+ a = cast(Object) new B2; // lose static type info; should still work
+ assert(a.coerce!(B2) !is null);
+
+// struct Big { int a[45]; }
+// a = Big.init;
+
+ // hash
+ assert(a.toHash() != 0);
+}
+
+// tests adapted from
+// http://www.dsource.org/projects/tango/browser/trunk/tango/core/Variant.d?rev=2601
+@system unittest
+{
+ Variant v;
+
+ assert(!v.hasValue);
+ v = 42;
+ assert( v.peek!(int) );
+ assert( v.convertsTo!(long) );
+ assert( v.get!(int) == 42 );
+ assert( v.get!(long) == 42L );
+ assert( v.get!(ulong) == 42uL );
+
+ v = "Hello, World!";
+ assert( v.peek!(string) );
+
+ assert( v.get!(string) == "Hello, World!" );
+ assert(!is(char[] : wchar[]));
+ assert( !v.convertsTo!(wchar[]) );
+ assert( v.get!(string) == "Hello, World!" );
+
+ // Literal arrays are dynamically-typed
+ v = cast(int[4]) [1,2,3,4];
+ assert( v.peek!(int[4]) );
+ assert( v.get!(int[4]) == [1,2,3,4] );
+
+ {
+ v = [1,2,3,4,5];
+ assert( v.peek!(int[]) );
+ assert( v.get!(int[]) == [1,2,3,4,5] );
+ }
+
+ v = 3.1413;
+ assert( v.peek!(double) );
+ assert( v.convertsTo!(real) );
+ //@@@ BUG IN COMPILER: DOUBLE SHOULD NOT IMPLICITLY CONVERT TO FLOAT
+ assert( !v.convertsTo!(float) );
+ assert( *v.peek!(double) == 3.1413 );
+
+ auto u = Variant(v);
+ assert( u.peek!(double) );
+ assert( *u.peek!(double) == 3.1413 );
+
+ // operators
+ v = 38;
+ assert( v + 4 == 42 );
+ assert( 4 + v == 42 );
+ assert( v - 4 == 34 );
+ assert( Variant(4) - v == -34 );
+ assert( v * 2 == 76 );
+ assert( 2 * v == 76 );
+ assert( v / 2 == 19 );
+ assert( Variant(2) / v == 0 );
+ assert( v % 2 == 0 );
+ assert( Variant(2) % v == 2 );
+ assert( (v & 6) == 6 );
+ assert( (6 & v) == 6 );
+ assert( (v | 9) == 47 );
+ assert( (9 | v) == 47 );
+ assert( (v ^ 5) == 35 );
+ assert( (5 ^ v) == 35 );
+ assert( v << 1 == 76 );
+ assert( Variant(1) << Variant(2) == 4 );
+ assert( v >> 1 == 19 );
+ assert( Variant(4) >> Variant(2) == 1 );
+ assert( Variant("abc") ~ "def" == "abcdef" );
+ assert( Variant("abc") ~ Variant("def") == "abcdef" );
+
+ v = 38;
+ v += 4;
+ assert( v == 42 );
+ v = 38; v -= 4; assert( v == 34 );
+ v = 38; v *= 2; assert( v == 76 );
+ v = 38; v /= 2; assert( v == 19 );
+ v = 38; v %= 2; assert( v == 0 );
+ v = 38; v &= 6; assert( v == 6 );
+ v = 38; v |= 9; assert( v == 47 );
+ v = 38; v ^= 5; assert( v == 35 );
+ v = 38; v <<= 1; assert( v == 76 );
+ v = 38; v >>= 1; assert( v == 19 );
+ v = 38; v += 1; assert( v < 40 );
+
+ v = "abc";
+ v ~= "def";
+ assert( v == "abcdef", *v.peek!(char[]) );
+ assert( Variant(0) < Variant(42) );
+ assert( Variant(42) > Variant(0) );
+ assert( Variant(42) > Variant(0.1) );
+ assert( Variant(42.1) > Variant(1) );
+ assert( Variant(21) == Variant(21) );
+ assert( Variant(0) != Variant(42) );
+ assert( Variant("bar") == Variant("bar") );
+ assert( Variant("foo") != Variant("bar") );
+
+ {
+ auto v1 = Variant(42);
+ auto v2 = Variant("foo");
+ auto v3 = Variant(1+2.0i);
+
+ int[Variant] hash;
+ hash[v1] = 0;
+ hash[v2] = 1;
+ hash[v3] = 2;
+
+ assert( hash[v1] == 0 );
+ assert( hash[v2] == 1 );
+ assert( hash[v3] == 2 );
+ }
+
+ {
+ int[char[]] hash;
+ hash["a"] = 1;
+ hash["b"] = 2;
+ hash["c"] = 3;
+ Variant vhash = hash;
+
+ assert( vhash.get!(int[char[]])["a"] == 1 );
+ assert( vhash.get!(int[char[]])["b"] == 2 );
+ assert( vhash.get!(int[char[]])["c"] == 3 );
+ }
+}
+
+@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;
+ Variant vb=-2;
+ assert((va+vb).get!(int) == -1);
+ assert((va-vb).get!(int) == 3);
+}
+
+@system unittest
+{
+ Variant a;
+ a=5;
+ Variant b;
+ b=a;
+ Variant[] c;
+ c = variantArray(1, 2, 3.0, "hello", 4);
+ assert(c[3] == "hello");
+}
+
+@system unittest
+{
+ Variant v = 5;
+ assert(!__traits(compiles, v.coerce!(bool delegate())));
+}
+
+
+@system unittest
+{
+ struct Huge {
+ real a, b, c, d, e, f, g;
+ }
+
+ Huge huge;
+ huge.e = 42;
+ Variant v;
+ v = huge; // Compile time error.
+ assert(v.get!(Huge).e == 42);
+}
+
+@system unittest
+{
+ const x = Variant(42);
+ auto y1 = x.get!(const int);
+ // @@@BUG@@@
+ //auto y2 = x.get!(immutable int)();
+}
+
+// test iteration
+@system unittest
+{
+ auto v = Variant([ 1, 2, 3, 4 ][]);
+ auto j = 0;
+ foreach (int i; v)
+ {
+ assert(i == ++j);
+ }
+ assert(j == 4);
+}
+
+// test convertibility
+@system unittest
+{
+ auto v = Variant("abc".dup);
+ assert(v.convertsTo!(char[]));
+}
+
+// http://d.puremagic.com/issues/show_bug.cgi?id=5424
+@system unittest
+{
+ interface A {
+ void func1();
+ }
+ static class AC: A {
+ void func1() {
+ }
+ }
+
+ A a = new AC();
+ a.func1();
+ Variant b = Variant(a);
+}
+
+@system unittest
+{
+ // bug 7070
+ Variant v;
+ v = null;
+}
+
+// Class and interface opEquals, issue 12157
+@system unittest
+{
+ class Foo { }
+
+ class DerivedFoo : Foo { }
+
+ Foo f1 = new Foo();
+ Foo f2 = new DerivedFoo();
+
+ Variant v1 = f1, v2 = f2;
+ assert(v1 == f1);
+ assert(v1 != new Foo());
+ assert(v1 != f2);
+ assert(v2 != v1);
+ assert(v2 == f2);
+}
+
+// Const parameters with opCall, issue 11361.
+@system unittest
+{
+ static string t1(string c) {
+ return c ~ "a";
+ }
+
+ static const(char)[] t2(const(char)[] p) {
+ return p ~ "b";
+ }
+
+ static char[] t3(int p) {
+ import std.conv : text;
+ return p.text.dup;
+ }
+
+ Variant v1 = &t1;
+ Variant v2 = &t2;
+ Variant v3 = &t3;
+
+ assert(v1("abc") == "abca");
+ assert(v1("abc").type == typeid(string));
+ assert(v2("abc") == "abcb");
+
+ assert(v2(cast(char[])("abc".dup)) == "abcb");
+ assert(v2("abc").type == typeid(const(char)[]));
+
+ assert(v3(4) == ['4']);
+ assert(v3(4).type == typeid(char[]));
+}
+
+// issue 12071
+@system unittest
+{
+ static struct Structure { int data; }
+ alias VariantTest = Algebraic!(Structure delegate() pure nothrow @nogc @safe);
+
+ bool called = false;
+ Structure example() pure nothrow @nogc @safe
+ {
+ called = true;
+ return Structure.init;
+ }
+ auto m = VariantTest(&example);
+ m();
+ assert(called);
+}
+
+// Ordering comparisons of incompatible types, e.g. issue 7990.
+@system unittest
+{
+ import std.exception : assertThrown;
+ assertThrown!VariantException(Variant(3) < "a");
+ assertThrown!VariantException("a" < Variant(3));
+ assertThrown!VariantException(Variant(3) < Variant("a"));
+
+ assertThrown!VariantException(Variant.init < Variant(3));
+ assertThrown!VariantException(Variant(3) < Variant.init);
+}
+
+// Handling of unordered types, e.g. issue 9043.
+@system unittest
+{
+ import std.exception : assertThrown;
+ static struct A { int a; }
+
+ assert(Variant(A(3)) != A(4));
+
+ assertThrown!VariantException(Variant(A(3)) < A(4));
+ assertThrown!VariantException(A(3) < Variant(A(4)));
+ assertThrown!VariantException(Variant(A(3)) < Variant(A(4)));
+}
+
+// Handling of empty types and arrays, e.g. issue 10958
+@system unittest
+{
+ class EmptyClass { }
+ struct EmptyStruct { }
+ alias EmptyArray = void[0];
+ alias Alg = Algebraic!(EmptyClass, EmptyStruct, EmptyArray);
+
+ Variant testEmpty(T)()
+ {
+ T inst;
+ Variant v = inst;
+ assert(v.get!T == inst);
+ assert(v.peek!T !is null);
+ assert(*v.peek!T == inst);
+ Alg alg = inst;
+ assert(alg.get!T == inst);
+ return v;
+ }
+
+ testEmpty!EmptyClass();
+ testEmpty!EmptyStruct();
+ testEmpty!EmptyArray();
+
+ // EmptyClass/EmptyStruct sizeof is 1, so we have this to test just size 0.
+ EmptyArray arr = EmptyArray.init;
+ Algebraic!(EmptyArray) a = arr;
+ assert(a.length == 0);
+ assert(a.get!EmptyArray == arr);
+}
+
+// Handling of void function pointers / delegates, e.g. issue 11360
+@system unittest
+{
+ static void t1() { }
+ Variant v = &t1;
+ assert(v() == Variant.init);
+
+ static int t2() { return 3; }
+ Variant v2 = &t2;
+ assert(v2() == 3);
+}
+
+// Using peek for large structs, issue 8580
+@system unittest
+{
+ struct TestStruct(bool pad)
+ {
+ int val1;
+ static if (pad)
+ ubyte[Variant.size] padding;
+ int val2;
+ }
+
+ void testPeekWith(T)()
+ {
+ T inst;
+ inst.val1 = 3;
+ inst.val2 = 4;
+ Variant v = inst;
+ T* original = v.peek!T;
+ assert(original.val1 == 3);
+ assert(original.val2 == 4);
+ original.val1 = 6;
+ original.val2 = 8;
+ T modified = v.get!T;
+ assert(modified.val1 == 6);
+ assert(modified.val2 == 8);
+ }
+
+ testPeekWith!(TestStruct!false)();
+ testPeekWith!(TestStruct!true)();
+}
+
+/**
+ * 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 variant)'s current value. Visiting handlers are passed
+ * in the template parameter list.
+ * 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
+ * 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: $(LREF VariantException) if `variant` doesn't hold a value and no
+ * parameter-less fallback function is specified.
+ */
+template visit(Handlers...)
+if (Handlers.length > 0)
+{
+ ///
+ auto visit(VariantType)(VariantType variant)
+ if (isAlgebraic!VariantType)
+ {
+ return visitImpl!(true, VariantType, Handlers)(variant);
+ }
+}
+
+///
+@system unittest
+{
+ Algebraic!(int, string) variant;
+
+ variant = 10;
+ 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)()
+ == 6);
+
+ // Error function usage
+ Algebraic!(int, string) emptyVar;
+ 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);
+}
+
+@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;
+ auto which = 0;
+ variant.visit!( (string s) => which = 1,
+ (size_t i) => which = 0
+ )();
+
+ // integer overload was called
+ assert(which == 0);
+
+ // mustn't compile as generic Variant not supported
+ Variant v;
+ static assert(!__traits(compiles, v.visit!((string s) => which = 1,
+ (size_t i) => which = 0
+ )()
+ ));
+
+ static size_t func(string s) {
+ return s.length;
+ }
+
+ variant = "test";
+ assert( 4 == variant.visit!(func,
+ (size_t i) => i
+ )());
+
+ Algebraic!(int, float, string) variant2 = 5.0f;
+ // Shouldn' t compile as float not handled by visitor.
+ static assert(!__traits(compiles, variant2.visit!(
+ (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; },
+ func,
+ (size_t i) { return i; }
+ )() == 10);
+ assert(floatVisited == true);
+
+ Algebraic!(float, string) variant4;
+
+ 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))
+ );
+}
+
+// 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 $(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 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: $(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(Handlers...)
+if (Handlers.length > 0)
+{
+ ///
+ auto tryVisit(VariantType)(VariantType variant)
+ if (isAlgebraic!VariantType)
+ {
+ return visitImpl!(false, VariantType, Handlers)(variant);
+ }
+}
+
+///
+@system unittest
+{
+ Algebraic!(int, string) variant;
+
+ variant = 10;
+ auto which = -1;
+ variant.tryVisit!((int i) { which = 0; })();
+ assert(which == 0);
+
+ // Error function usage
+ variant = "test";
+ variant.tryVisit!((int i) { which = 0; },
+ () { which = -100; })();
+ assert(which == -100);
+}
+
+@system unittest
+{
+ import std.exception : assertThrown;
+ Algebraic!(int, string) variant;
+
+ variant = 10;
+ auto which = -1;
+ variant.tryVisit!((int i){ which = 0; })();
+
+ assert(which == 0);
+
+ variant = "test";
+
+ assertThrown!VariantException(variant.tryVisit!((int i) { which = 0; })());
+
+ void errorfunc()
+ {
+ which = -1;
+ }
+
+ variant.tryVisit!((int i) { which = 0; }, errorfunc)();
+
+ assert(which == -1);
+}
+
+private template isAlgebraic(Type)
+{
+ static if (is(Type _ == VariantN!T, T...))
+ enum isAlgebraic = T.length >= 2; // T[0] == maxDataSize, T[1..$] == AllowedTypesParam
+ else
+ enum isAlgebraic = false;
+}
+
+@system unittest
+{
+ static assert(!isAlgebraic!(Variant));
+ static assert( isAlgebraic!(Algebraic!(string)));
+ static assert( isAlgebraic!(Algebraic!(int, int[])));
+}
+
+private auto visitImpl(bool Strict, VariantType, Handler...)(VariantType variant)
+if (isAlgebraic!VariantType && Handler.length > 0)
+{
+ alias AllowedTypes = VariantType.AllowedTypes;
+
+
+ /**
+ * 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 exceptionFuncIdx) is set;
+ * otherwise it's -1.
+ */
+ auto visitGetOverloadMap()
+ {
+ struct Result {
+ int[AllowedTypes.length] indices;
+ int exceptionFuncIdx = -1;
+ int generalFuncIdx = -1;
+ }
+
+ Result result;
+
+ foreach (tidx, T; AllowedTypes)
+ {
+ bool added = false;
+ foreach (dgidx, dg; Handler)
+ {
+ // Handle normal function objects
+ static if (isSomeFunction!dg)
+ {
+ alias Params = Parameters!dg;
+ static if (Params.length == 0)
+ {
+ // Just check exception functions in the first
+ // inner iteration (over delegates)
+ if (tidx > 0)
+ continue;
+ else
+ {
+ if (result.exceptionFuncIdx != -1)
+ assert(false, "duplicate parameter-less (error-)function specified");
+ result.exceptionFuncIdx = dgidx;
+ }
+ }
+ else static if (is(Params[0] == T) || is(Unqual!(Params[0]) == T))
+ {
+ if (added)
+ assert(false, "duplicate overload specified for type '" ~ T.stringof ~ "'");
+
+ added = true;
+ 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
+ {
+ static assert(false, dg.stringof ~ " is not a function or delegate");
+ }
+ }
+
+ if (!added)
+ result.indices[tidx] = -1;
+ }
+
+ return result;
+ }
+
+ enum HandlerOverloadMap = visitGetOverloadMap();
+
+ if (!variant.hasValue)
+ {
+ // Call the exception function. The HandlerOverloadMap
+ // will have its exceptionFuncIdx field set to value != -1 if an
+ // exception function has been specified; otherwise we just through an exception.
+ static if (HandlerOverloadMap.exceptionFuncIdx != -1)
+ return Handler[ HandlerOverloadMap.exceptionFuncIdx ]();
+ else
+ throw new VariantException("variant must hold a value before being visited.");
+ }
+
+ foreach (idx, T; AllowedTypes)
+ {
+ if (auto ptr = variant.peek!T)
+ {
+ enum dgIdx = HandlerOverloadMap.indices[idx];
+
+ static if (dgIdx == -1)
+ {
+ 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
+ throw new VariantException(
+ "variant holds value of type '"
+ ~ T.stringof ~
+ "' but no visitor has been provided"
+ );
+ }
+ else
+ {
+ return Handler[ dgIdx ](*ptr);
+ }
+ }
+ }
+
+ assert(false);
+}
+
+@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;
+ assert(a == a);
+ Variant b;
+ assert(a == b);
+ assert(b == a);
+}
+
+@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
+ {
+ ubyte[Variant.size + 1] s;
+ }
+
+ Variant v1, v2;
+ v1 = S(); // the payload is allocated on the heap
+ v2 = v1; // AssertError: target must be non-null
+ assert(v1 == v2);
+}
+@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; AliasSeq!(MutableOf, ConstOf))
+ {
+ assert(v.get!(qual!int) == 10);
+ assert(v.get!(qual!float) == 10.0f);
+ }
+ foreach (qual; AliasSeq!(ImmutableOf, SharedOf, SharedConstOf))
+ {
+ assertThrown!VariantException(v.get!(qual!int));
+ }
+
+ const(int) ci = 20;
+ v = ci;
+ foreach (qual; AliasSeq!(ConstOf))
+ {
+ assert(v.get!(qual!int) == 20);
+ assert(v.get!(qual!float) == 20.0f);
+ }
+ foreach (qual; AliasSeq!(MutableOf, ImmutableOf, SharedOf, SharedConstOf))
+ {
+ assertThrown!VariantException(v.get!(qual!int));
+ assertThrown!VariantException(v.get!(qual!float));
+ }
+
+ immutable(int) ii = ci;
+ v = ii;
+ foreach (qual; AliasSeq!(ImmutableOf, ConstOf, SharedConstOf))
+ {
+ assert(v.get!(qual!int) == 20);
+ assert(v.get!(qual!float) == 20.0f);
+ }
+ foreach (qual; AliasSeq!(MutableOf, SharedOf))
+ {
+ assertThrown!VariantException(v.get!(qual!int));
+ assertThrown!VariantException(v.get!(qual!float));
+ }
+
+ int[] ai = [1,2,3];
+ v = ai;
+ foreach (qual; AliasSeq!(MutableOf, ConstOf))
+ {
+ assert(v.get!(qual!(int[])) == [1,2,3]);
+ assert(v.get!(qual!(int)[]) == [1,2,3]);
+ }
+ foreach (qual; AliasSeq!(ImmutableOf, SharedOf, SharedConstOf))
+ {
+ assertThrown!VariantException(v.get!(qual!(int[])));
+ assertThrown!VariantException(v.get!(qual!(int)[]));
+ }
+
+ const(int[]) cai = [4,5,6];
+ v = cai;
+ foreach (qual; AliasSeq!(ConstOf))
+ {
+ assert(v.get!(qual!(int[])) == [4,5,6]);
+ assert(v.get!(qual!(int)[]) == [4,5,6]);
+ }
+ foreach (qual; AliasSeq!(MutableOf, ImmutableOf, SharedOf, SharedConstOf))
+ {
+ assertThrown!VariantException(v.get!(qual!(int[])));
+ assertThrown!VariantException(v.get!(qual!(int)[]));
+ }
+
+ immutable(int[]) iai = [7,8,9];
+ v = iai;
+ //assert(v.get!(immutable(int[])) == [7,8,9]); // Bug ??? runtime error
+ assert(v.get!(immutable(int)[]) == [7,8,9]);
+ assert(v.get!(const(int[])) == [7,8,9]);
+ 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; AliasSeq!(MutableOf))
+ {
+ assertThrown!VariantException(v.get!(qual!(int[])));
+ assertThrown!VariantException(v.get!(qual!(int)[]));
+ }
+
+ class A {}
+ class B : A {}
+ B b = new B();
+ v = b;
+ 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; AliasSeq!(ImmutableOf, SharedOf, SharedConstOf))
+ {
+ assertThrown!VariantException(v.get!(qual!B));
+ assertThrown!VariantException(v.get!(qual!A));
+ assertThrown!VariantException(v.get!(qual!Object));
+ }
+
+ const(B) cb = new B();
+ v = cb;
+ 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; AliasSeq!(MutableOf, ImmutableOf, SharedOf, SharedConstOf))
+ {
+ assertThrown!VariantException(v.get!(qual!B));
+ assertThrown!VariantException(v.get!(qual!A));
+ assertThrown!VariantException(v.get!(qual!Object));
+ }
+
+ immutable(B) ib = new immutable(B)();
+ v = ib;
+ 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; AliasSeq!(MutableOf, SharedOf))
+ {
+ assertThrown!VariantException(v.get!(qual!B));
+ assertThrown!VariantException(v.get!(qual!A));
+ assertThrown!VariantException(v.get!(qual!Object));
+ }
+
+ shared(B) sb = new shared B();
+ v = sb;
+ 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; AliasSeq!(MutableOf, ImmutableOf, ConstOf))
+ {
+ assertThrown!VariantException(v.get!(qual!B));
+ assertThrown!VariantException(v.get!(qual!A));
+ assertThrown!VariantException(v.get!(qual!Object));
+ }
+
+ shared(const(B)) scb = new shared const B();
+ v = scb;
+ 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; AliasSeq!(MutableOf, ConstOf, ImmutableOf, SharedOf))
+ {
+ assertThrown!VariantException(v.get!(qual!B));
+ assertThrown!VariantException(v.get!(qual!A));
+ assertThrown!VariantException(v.get!(qual!Object));
+ }
+}
+
+@system unittest
+{
+ static struct DummyScope
+ {
+ // https://d.puremagic.com/issues/show_bug.cgi?id=12540
+ alias Alias12540 = Algebraic!Class12540;
+
+ static class Class12540
+ {
+ Alias12540 entity;
+ }
+ }
+}
+
+@system unittest
+{
+ // https://issues.dlang.org/show_bug.cgi?id=10194
+ // Also test for elaborate copying
+ static struct S
+ {
+ @disable this();
+ this(int dummy)
+ {
+ ++cnt;
+ }
+
+ this(this)
+ {
+ ++cnt;
+ }
+
+ @disable S opAssign();
+
+ ~this()
+ {
+ --cnt;
+ assert(cnt >= 0);
+ }
+ static int cnt = 0;
+ }
+
+ {
+ Variant v;
+ {
+ v = S(0);
+ assert(S.cnt == 1);
+ }
+ assert(S.cnt == 1);
+
+ // assigning a new value should destroy the existing one
+ v = 0;
+ assert(S.cnt == 0);
+
+ // destroying the variant should destroy it's current value
+ v = S(0);
+ assert(S.cnt == 1);
+ }
+ assert(S.cnt == 0);
+}
+
+@system unittest
+{
+ // Bugzilla 13300
+ static struct S
+ {
+ this(this) {}
+ ~this() {}
+ }
+
+ static assert( hasElaborateCopyConstructor!(Variant));
+ static assert(!hasElaborateCopyConstructor!(Algebraic!bool));
+ static assert( hasElaborateCopyConstructor!(Algebraic!S));
+ static assert( hasElaborateCopyConstructor!(Algebraic!(bool, S)));
+
+ static assert( hasElaborateDestructor!(Variant));
+ static assert(!hasElaborateDestructor!(Algebraic!bool));
+ static assert( hasElaborateDestructor!(Algebraic!S));
+ static assert( hasElaborateDestructor!(Algebraic!(bool, S)));
+
+ import std.array;
+ alias Value = Algebraic!bool;
+
+ static struct T
+ {
+ Value value;
+ @disable this();
+ }
+ auto a = appender!(T[]);
+}
+
+@system unittest
+{
+ // Bugzilla 13871
+ alias A = Algebraic!(int, typeof(null));
+ static struct B { A value; }
+ alias C = std.variant.Algebraic!B;
+
+ C var;
+ var = C(B());
+}
+
+@system unittest
+{
+ import std.exception : assertThrown, assertNotThrown;
+ // Make sure Variant can handle types with opDispatch but no length field.
+ struct SWithNoLength
+ {
+ void opDispatch(string s)() { }
+ }
+
+ struct SWithLength
+ {
+ @property int opDispatch(string s)()
+ {
+ // Assume that s == "length"
+ return 5; // Any value is OK for test.
+ }
+ }
+
+ SWithNoLength sWithNoLength;
+ Variant v = sWithNoLength;
+ assertThrown!VariantException(v.length);
+
+ SWithLength sWithLength;
+ v = sWithLength;
+ assertNotThrown!VariantException(v.get!SWithLength.length);
+ 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.typecons;
+ import std.variant;
+
+ 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/libphobos/src/std/windows/charset.d b/libphobos/src/std/windows/charset.d
new file mode 100644
index 0000000..ee7211d
--- /dev/null
+++ b/libphobos/src/std/windows/charset.d
@@ -0,0 +1,122 @@
+// Written in the D programming language.
+
+/**
+ * Support UTF-8 on Windows 95, 98 and ME systems.
+ *
+ * Copyright: Copyright Digital Mars 2005 - 2009.
+ * 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.
+ * (See accompanying file LICENSE_1_0.txt or copy at
+ * 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):
+
+import core.sys.windows.windows;
+import std.conv;
+import std.string;
+import std.windows.syserror;
+
+import std.internal.cstring;
+
+const(char)* toMBSz(in char[] s, uint codePage = 0)
+{
+ // Only need to do this if any chars have the high bit set
+ foreach (char c; s)
+ {
+ if (c >= 0x80)
+ {
+ char[] result;
+ int readLen;
+ auto wsTmp = s.tempCStringW();
+ result.length = WideCharToMultiByte(codePage, 0, wsTmp, -1, null, 0,
+ null, null);
+
+ if (result.length)
+ {
+ readLen = WideCharToMultiByte(codePage, 0, wsTmp, -1, result.ptr,
+ to!int(result.length), null, null);
+ }
+
+ if (!readLen || readLen != result.length)
+ {
+ throw new Exception("Couldn't convert string: " ~
+ sysErrorString(GetLastError()));
+ }
+
+ return result.ptr;
+ }
+ }
+ return std.string.toStringz(s);
+}
+
+string fromMBSz(immutable(char)* s, int codePage = 0)
+{
+ const(char)* c;
+
+ for (c = s; *c != 0; c++)
+ {
+ if (*c >= 0x80)
+ {
+ wchar[] result;
+ int readLen;
+
+ result.length = MultiByteToWideChar(codePage, 0, s, -1, null, 0);
+
+ if (result.length)
+ {
+ readLen = MultiByteToWideChar(codePage, 0, s, -1, result.ptr,
+ to!int(result.length));
+ }
+
+ if (!readLen || readLen != result.length)
+ {
+ throw new Exception("Couldn't convert string: " ~
+ sysErrorString(GetLastError()));
+ }
+
+ return result[0 .. result.length-1].to!string; // omit trailing null
+ }
+ }
+ return s[0 .. c-s]; // string is ASCII, no conversion necessary
+}
+
+
diff --git a/libphobos/src/std/windows/registry.d b/libphobos/src/std/windows/registry.d
new file mode 100644
index 0000000..7293d2d
--- /dev/null
+++ b/libphobos/src/std/windows/registry.d
@@ -0,0 +1,1868 @@
+/**
+ This library provides Win32 Registry facilities.
+
+ Copyright: Copyright 2003-2004 by Matthew Wilson and Synesis Software
+ Written by Matthew Wilson
+
+ License: $(HTTP www.boost.org/LICENSE_1_0.txt, Boost License 1.0).
+
+ Author: Matthew Wilson, Kenji Hara
+
+ History:
+ Created 15th March 2003,
+ Updated 25th April 2004,
+
+ Source: $(PHOBOSSRC std/windows/_registry.d)
+*/
+/* /////////////////////////////////////////////////////////////////////////////
+ *
+ * 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, in both source and binary form, subject to the following
+ * restrictions:
+ *
+ * - 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.
+ * - Altered source versions must be plainly marked as such, and must not
+ * be misrepresented as being the original software.
+ * - This notice may not be removed or altered from any source
+ * distribution.
+ *
+ * ////////////////////////////////////////////////////////////////////////// */
+module std.windows.registry;
+version (Windows):
+
+import core.sys.windows.windows;
+import std.array;
+import std.conv;
+import std.exception;
+import std.internal.cstring;
+import std.internal.windows.advapi32;
+import std.system : Endian, endian;
+import std.windows.syserror;
+
+//debug = winreg;
+debug(winreg) import std.stdio;
+
+private
+{
+ import core.sys.windows.winbase : lstrlenW;
+
+ void enforceSucc(LONG res, lazy string message, string fn = __FILE__, size_t ln = __LINE__)
+ {
+ if (res != ERROR_SUCCESS)
+ throw new RegistryException(message, res, fn, ln);
+ }
+}
+
+/* ************* Exceptions *************** */
+
+// Do not use. Left for compatibility.
+class Win32Exception : WindowsException
+{
+ @safe
+ this(string message, string fn = __FILE__, size_t ln = __LINE__, Throwable next = null)
+ {
+ super(0, message, fn, ln);
+ }
+
+ @safe
+ this(string message, int errnum, string fn = __FILE__, size_t ln = __LINE__, Throwable next = null)
+ {
+ super(errnum, message, fn, ln);
+ }
+
+ @property int error() { return super.code; }
+}
+
+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.startsWith(message));
+}
+
+@system unittest
+{
+ // ditto
+ string message = "Test W2";
+ int code = 5;
+
+ auto e = collectException!Win32Exception(
+ enforce(false, new Win32Exception(message, code)));
+ assert(e.error == code);
+ assert(e.msg.startsWith(message));
+}
+
+/**
+ Exception class thrown by the std.windows.registry classes.
+ */
+class RegistryException
+ : Win32Exception
+{
+public:
+ /**
+ Creates an instance of the exception.
+
+ Params:
+ message = The message associated with the exception.
+ */
+ @safe
+ this(string message, string fn = __FILE__, size_t ln = __LINE__, Throwable next = null)
+ {
+ super(message, fn, ln, next);
+ }
+
+ /**
+ Creates an instance of the exception, with the given.
+
+ Params:
+ message = The message associated with the exception.
+ error = The Win32 error number associated with the exception.
+ */
+ @safe
+ this(string message, int error, string fn = __FILE__, size_t ln = __LINE__, Throwable next = null)
+ {
+ super(message, error, fn, ln, next);
+ }
+}
+
+@system unittest
+{
+ // (i) Test that we can throw and catch one by its own type
+ string message = "Test 1";
+ int code = 3;
+
+ auto e = collectException!RegistryException(
+ enforce(false, new RegistryException(message, code)));
+ assert(e.error == code);
+ assert(e.msg.startsWith(message));
+}
+
+@safe unittest
+{
+ // ditto
+ string message = "Test 2";
+
+ auto e = collectException!RegistryException(
+ enforce(false, new RegistryException(message)));
+ assert(e.msg.startsWith(message));
+}
+
+/* ************* public enumerations *************** */
+
+/**
+ Enumeration of the recognised registry access modes.
+ */
+enum REGSAM
+{
+ KEY_QUERY_VALUE = 0x0001, /// Permission to query subkey data
+ KEY_SET_VALUE = 0x0002, /// Permission to set subkey data
+ KEY_CREATE_SUB_KEY = 0x0004, /// Permission to create subkeys
+ KEY_ENUMERATE_SUB_KEYS = 0x0008, /// Permission to enumerate subkeys
+ KEY_NOTIFY = 0x0010, /// Permission for change notification
+ KEY_CREATE_LINK = 0x0020, /// Permission to create a symbolic link
+ KEY_WOW64_32KEY = 0x0200, /// Enables a 64- or 32-bit application to open a 32-bit key
+ KEY_WOW64_64KEY = 0x0100, /// Enables a 64- or 32-bit application to open a 64-bit key
+ KEY_WOW64_RES = 0x0300, ///
+ KEY_READ = (STANDARD_RIGHTS_READ
+ | KEY_QUERY_VALUE | KEY_ENUMERATE_SUB_KEYS | KEY_NOTIFY)
+ & ~(SYNCHRONIZE),
+ /// Combines the STANDARD_RIGHTS_READ, KEY_QUERY_VALUE,
+ /// KEY_ENUMERATE_SUB_KEYS, and KEY_NOTIFY access rights
+ KEY_WRITE = (STANDARD_RIGHTS_WRITE
+ | KEY_SET_VALUE | KEY_CREATE_SUB_KEY)
+ & ~(SYNCHRONIZE),
+ /// Combines the STANDARD_RIGHTS_WRITE, KEY_SET_VALUE,
+ /// and KEY_CREATE_SUB_KEY access rights
+ KEY_EXECUTE = KEY_READ & ~(SYNCHRONIZE),
+ /// Permission for read access
+ KEY_ALL_ACCESS = (STANDARD_RIGHTS_ALL
+ | KEY_QUERY_VALUE | KEY_SET_VALUE | KEY_CREATE_SUB_KEY
+ | KEY_ENUMERATE_SUB_KEYS | KEY_NOTIFY | KEY_CREATE_LINK)
+ & ~(SYNCHRONIZE),
+ /// Combines the KEY_QUERY_VALUE, KEY_ENUMERATE_SUB_KEYS,
+ /// KEY_NOTIFY, KEY_CREATE_SUB_KEY, KEY_CREATE_LINK, and
+ /// KEY_SET_VALUE access rights, plus all the standard
+ /// access rights except SYNCHRONIZE
+}
+
+/**
+ Enumeration of the recognised registry value types.
+ */
+enum REG_VALUE_TYPE : DWORD
+{
+ REG_UNKNOWN = -1, ///
+ REG_NONE = 0, /// The null value type. (In practise this is treated as a zero-length binary array by the Win32 registry)
+ REG_SZ = 1, /// A zero-terminated string
+ REG_EXPAND_SZ = 2, /// A zero-terminated string containing expandable environment variable references
+ REG_BINARY = 3, /// A binary blob
+ REG_DWORD = 4, /// A 32-bit unsigned integer
+ REG_DWORD_LITTLE_ENDIAN = 4, /// A 32-bit unsigned integer, stored in little-endian byte order
+ REG_DWORD_BIG_ENDIAN = 5, /// A 32-bit unsigned integer, stored in big-endian byte order
+ REG_LINK = 6, /// A registry link
+ REG_MULTI_SZ = 7, /// A set of zero-terminated strings
+ REG_RESOURCE_LIST = 8, /// A hardware resource list
+ REG_FULL_RESOURCE_DESCRIPTOR = 9, /// A hardware resource descriptor
+ REG_RESOURCE_REQUIREMENTS_LIST = 10, /// A hardware resource requirements list
+ REG_QWORD = 11, /// A 64-bit unsigned integer
+ REG_QWORD_LITTLE_ENDIAN = 11, /// A 64-bit unsigned integer, stored in little-endian byte order
+}
+
+
+/* ************* private *************** */
+
+import core.sys.windows.winnt :
+ DELETE ,
+ READ_CONTROL ,
+ WRITE_DAC ,
+ WRITE_OWNER ,
+ SYNCHRONIZE ,
+
+ STANDARD_RIGHTS_REQUIRED,
+
+ STANDARD_RIGHTS_READ ,
+ STANDARD_RIGHTS_WRITE ,
+ STANDARD_RIGHTS_EXECUTE ,
+
+ STANDARD_RIGHTS_ALL ,
+
+ SPECIFIC_RIGHTS_ALL ;
+
+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
+private REGSAM compatibleRegsam(in REGSAM samDesired)
+{
+ return isWow64 ? samDesired : cast(REGSAM)(samDesired & ~REGSAM.KEY_WOW64_RES);
+}
+
+///Returns true, if we are in WoW64 mode and have WoW64 flags
+private bool haveWoW64Job(in REGSAM samDesired)
+{
+ return isWow64 && (samDesired & REGSAM.KEY_WOW64_RES);
+}
+
+private REG_VALUE_TYPE _RVT_from_Endian(Endian endian)
+{
+ final switch (endian)
+ {
+ case Endian.bigEndian:
+ return REG_VALUE_TYPE.REG_DWORD_BIG_ENDIAN;
+
+ case Endian.littleEndian:
+ return REG_VALUE_TYPE.REG_DWORD_LITTLE_ENDIAN;
+ }
+}
+
+private LONG regCloseKey(in HKEY hkey)
+in
+{
+ assert(hkey !is null);
+}
+body
+{
+ /* No need to attempt to close any of the standard hive keys.
+ * Although it's documented that calling RegCloseKey() on any of
+ * these hive keys is ignored, we'd rather not trust the Win32
+ * API.
+ */
+ if (cast(uint) hkey & 0x80000000)
+ {
+ switch (cast(uint) hkey)
+ {
+ case HKEY_CLASSES_ROOT:
+ case HKEY_CURRENT_USER:
+ case HKEY_LOCAL_MACHINE:
+ case HKEY_USERS:
+ case HKEY_PERFORMANCE_DATA:
+ case HKEY_PERFORMANCE_TEXT:
+ case HKEY_PERFORMANCE_NLSTEXT:
+ case HKEY_CURRENT_CONFIG:
+ case HKEY_DYN_DATA:
+ return ERROR_SUCCESS;
+ default:
+ /* Do nothing */
+ break;
+ }
+ }
+
+ return RegCloseKey(hkey);
+}
+
+private void regFlushKey(in HKEY hkey)
+in
+{
+ assert(hkey !is null);
+}
+body
+{
+ immutable res = RegFlushKey(hkey);
+ enforceSucc(res, "Key cannot be flushed");
+}
+
+private HKEY regCreateKey(in HKEY hkey, in string subKey, in DWORD dwOptions, in REGSAM samDesired,
+ in LPSECURITY_ATTRIBUTES lpsa, out DWORD disposition)
+in
+{
+ assert(hkey !is null);
+ assert(subKey !is null);
+}
+body
+{
+ HKEY hkeyResult;
+ enforceSucc(RegCreateKeyExW(
+ hkey, subKey.tempCStringW(), 0, null, dwOptions,
+ compatibleRegsam(samDesired), cast(LPSECURITY_ATTRIBUTES) lpsa,
+ &hkeyResult, &disposition),
+ "Failed to create requested key: \"" ~ subKey ~ "\"");
+
+ return hkeyResult;
+}
+
+private void regDeleteKey(in HKEY hkey, in string subKey, in REGSAM samDesired)
+in
+{
+ assert(hkey !is null);
+ assert(subKey !is null);
+}
+body
+{
+ LONG res;
+ if (haveWoW64Job(samDesired))
+ {
+ loadAdvapi32();
+ res = pRegDeleteKeyExW(hkey, subKey.tempCStringW(), samDesired, 0);
+ }
+ else
+ {
+ res = RegDeleteKeyW(hkey, subKey.tempCStringW());
+ }
+ enforceSucc(res, "Key cannot be deleted: \"" ~ subKey ~ "\"");
+}
+
+private void regDeleteValue(in HKEY hkey, in string valueName)
+in
+{
+ assert(hkey !is null);
+ assert(valueName !is null);
+}
+body
+{
+ enforceSucc(RegDeleteValueW(hkey, valueName.tempCStringW()),
+ "Value cannot be deleted: \"" ~ valueName ~ "\"");
+}
+
+private HKEY regDup(HKEY hkey)
+in
+{
+ assert(hkey !is null);
+}
+body
+{
+ /* Can't duplicate standard keys, but don't need to, so can just return */
+ if (cast(uint) hkey & 0x80000000)
+ {
+ switch (cast(uint) hkey)
+ {
+ case HKEY_CLASSES_ROOT:
+ case HKEY_CURRENT_USER:
+ case HKEY_LOCAL_MACHINE:
+ case HKEY_USERS:
+ case HKEY_PERFORMANCE_DATA:
+ case HKEY_PERFORMANCE_TEXT:
+ case HKEY_PERFORMANCE_NLSTEXT:
+ case HKEY_CURRENT_CONFIG:
+ case HKEY_DYN_DATA:
+ return hkey;
+ default:
+ /* Do nothing */
+ break;
+ }
+ }
+
+ HKEY hkeyDup;
+ immutable res = RegOpenKeyW(hkey, null, &hkeyDup);
+
+ debug(winreg)
+ {
+ if (res != ERROR_SUCCESS)
+ {
+ writefln("regDup() failed: 0x%08x 0x%08x %d", hkey, hkeyDup, res);
+ }
+
+ assert(res == ERROR_SUCCESS);
+ }
+
+ return (res == ERROR_SUCCESS) ? hkeyDup : null;
+}
+
+private LONG regEnumKeyName(in HKEY hkey, in DWORD index, ref wchar[] name, out DWORD cchName)
+in
+{
+ assert(hkey !is null);
+ assert(name !is null);
+ assert(name.length > 0);
+}
+out(res)
+{
+ assert(res != ERROR_MORE_DATA);
+}
+body
+{
+ // The Registry API lies about the lengths of a very few sub-key lengths
+ // so we have to test to see if it whinges about more data, and provide
+ // more if it does.
+ for (;;)
+ {
+ cchName = to!DWORD(name.length);
+ immutable res = RegEnumKeyExW(hkey, index, name.ptr, &cchName, null, null, null, null);
+ if (res != ERROR_MORE_DATA)
+ return res;
+
+ // Now need to increase the size of the buffer and try again
+ name.length *= 2;
+ }
+
+ assert(0);
+}
+
+
+private LONG regEnumValueName(in HKEY hkey, in DWORD dwIndex, ref wchar[] name, out DWORD cchName)
+in
+{
+ assert(hkey !is null);
+}
+body
+{
+ for (;;)
+ {
+ cchName = to!DWORD(name.length);
+ immutable res = RegEnumValueW(hkey, dwIndex, name.ptr, &cchName, null, null, null, null);
+ if (res != ERROR_MORE_DATA)
+ return res;
+
+ name.length *= 2;
+ }
+
+ assert(0);
+}
+
+private LONG regGetNumSubKeys(in HKEY hkey, out DWORD cSubKeys, out DWORD cchSubKeyMaxLen)
+in
+{
+ assert(hkey !is null);
+}
+body
+{
+ return RegQueryInfoKeyW(hkey, null, null, null, &cSubKeys,
+ &cchSubKeyMaxLen, null, null, null, null, null, null);
+}
+
+private LONG regGetNumValues(in HKEY hkey, out DWORD cValues, out DWORD cchValueMaxLen)
+in
+{
+ assert(hkey !is null);
+}
+body
+{
+ return RegQueryInfoKeyW(hkey, null, null, null, null, null, null,
+ &cValues, &cchValueMaxLen, null, null, null);
+}
+
+private REG_VALUE_TYPE regGetValueType(in HKEY hkey, in string name)
+in
+{
+ assert(hkey !is null);
+}
+body
+{
+ REG_VALUE_TYPE type;
+ enforceSucc(RegQueryValueExW(hkey, name.tempCStringW(), null, cast(LPDWORD) &type, null, null),
+ "Value cannot be opened: \"" ~ name ~ "\"");
+
+ return type;
+}
+
+private HKEY regOpenKey(in HKEY hkey, in string subKey, in REGSAM samDesired)
+in
+{
+ assert(hkey !is null);
+ assert(subKey !is null);
+}
+body
+{
+ HKEY hkeyResult;
+ enforceSucc(RegOpenKeyExW(hkey, subKey.tempCStringW(), 0, compatibleRegsam(samDesired), &hkeyResult),
+ "Failed to open requested key: \"" ~ subKey ~ "\"");
+
+ return hkeyResult;
+}
+
+private void regQueryValue(in HKEY hkey, string name, out string value, REG_VALUE_TYPE reqType)
+in
+{
+ assert(hkey !is null);
+}
+body
+{
+ import core.bitop : bswap;
+
+ REG_VALUE_TYPE type;
+
+ // See bugzilla 961 on this
+ union U
+ {
+ uint dw;
+ ulong qw;
+ }
+ U u;
+ void* data = &u.qw;
+ DWORD cbData = u.qw.sizeof;
+
+ auto keynameTmp = name.tempCStringW();
+ LONG res = RegQueryValueExW(hkey, keynameTmp, null, cast(LPDWORD) &type, data, &cbData);
+ if (res == ERROR_MORE_DATA)
+ {
+ data = (new ubyte[cbData]).ptr;
+ res = RegQueryValueExW(hkey, keynameTmp, null, cast(LPDWORD) &type, data, &cbData);
+ }
+
+ enforceSucc(res,
+ "Cannot read the requested value");
+ enforce(type == reqType,
+ new RegistryException("Value type has been changed since the value was acquired"));
+
+ switch (type)
+ {
+ case REG_VALUE_TYPE.REG_SZ:
+ case REG_VALUE_TYPE.REG_EXPAND_SZ:
+ auto wstr = (cast(immutable(wchar)*)data)[0 .. cbData / wchar.sizeof];
+ assert(wstr.length > 0 && wstr[$-1] == '\0');
+ if (wstr.length && wstr[$-1] == '\0')
+ wstr.length = wstr.length - 1;
+ assert(wstr.length == 0 || wstr[$-1] != '\0');
+ value = wstr.to!string;
+ break;
+
+ case REG_VALUE_TYPE.REG_DWORD_LITTLE_ENDIAN:
+ version (LittleEndian)
+ value = to!string(u.dw);
+ else
+ value = to!string(bswap(u.dw));
+ break;
+
+ case REG_VALUE_TYPE.REG_DWORD_BIG_ENDIAN:
+ version (LittleEndian)
+ value = to!string(bswap(u.dw));
+ else
+ value = to!string(u.dw);
+ break;
+
+ case REG_VALUE_TYPE.REG_QWORD_LITTLE_ENDIAN:
+ value = to!string(u.qw);
+ break;
+
+ case REG_VALUE_TYPE.REG_BINARY:
+ case REG_VALUE_TYPE.REG_MULTI_SZ:
+ default:
+ throw new RegistryException("Cannot read the given value as a string");
+ }
+}
+
+private void regQueryValue(in HKEY hkey, in string name, out string[] value, REG_VALUE_TYPE reqType)
+in
+{
+ assert(hkey !is null);
+}
+body
+{
+ REG_VALUE_TYPE type;
+
+ auto keynameTmp = name.tempCStringW();
+ wchar[] data = new wchar[256];
+ DWORD cbData = to!DWORD(data.length * wchar.sizeof);
+ LONG res = RegQueryValueExW(hkey, keynameTmp, null, cast(LPDWORD) &type, data.ptr, &cbData);
+ if (res == ERROR_MORE_DATA)
+ {
+ data.length = cbData / wchar.sizeof;
+ res = RegQueryValueExW(hkey, keynameTmp, null, cast(LPDWORD) &type, data.ptr, &cbData);
+ }
+ else if (res == ERROR_SUCCESS)
+ {
+ data.length = cbData / wchar.sizeof;
+ }
+ enforceSucc(res, "Cannot read the requested value");
+ enforce(type == REG_VALUE_TYPE.REG_MULTI_SZ,
+ new RegistryException("Cannot read the given value as a string"));
+ enforce(type == reqType,
+ new RegistryException("Value type has been changed since the value was acquired"));
+
+ // Remove last two (or one) null terminator
+ assert(data.length > 0 && data[$-1] == '\0');
+ data.length = data.length - 1;
+ if (data.length > 0 && data[$-1] == '\0')
+ data.length = data.length - 1;
+
+ auto list = std.array.split(data[], "\0");
+ value.length = list.length;
+ foreach (i, ref v; value)
+ {
+ v = list[i].to!string;
+ }
+}
+
+private void regQueryValue(in HKEY hkey, in string name, out uint value, REG_VALUE_TYPE reqType)
+in
+{
+ assert(hkey !is null);
+}
+body
+{
+ import core.bitop : bswap;
+
+ REG_VALUE_TYPE type;
+
+ DWORD cbData = value.sizeof;
+ enforceSucc(RegQueryValueExW(hkey, name.tempCStringW(), null, cast(LPDWORD) &type, &value, &cbData),
+ "Cannot read the requested value");
+ enforce(type == reqType,
+ new RegistryException("Value type has been changed since the value was acquired"));
+
+ switch (type)
+ {
+ case REG_VALUE_TYPE.REG_DWORD_LITTLE_ENDIAN:
+ version (LittleEndian)
+ static assert(REG_VALUE_TYPE.REG_DWORD == REG_VALUE_TYPE.REG_DWORD_LITTLE_ENDIAN);
+ else
+ value = bswap(value);
+ break;
+
+ case REG_VALUE_TYPE.REG_DWORD_BIG_ENDIAN:
+ version (LittleEndian)
+ value = bswap(value);
+ else
+ static assert(REG_VALUE_TYPE.REG_DWORD == REG_VALUE_TYPE.REG_DWORD_BIG_ENDIAN);
+ break;
+
+ default:
+ throw new RegistryException("Cannot read the given value as a 32-bit integer");
+ }
+}
+
+private void regQueryValue(in HKEY hkey, in string name, out ulong value, REG_VALUE_TYPE reqType)
+in
+{
+ assert(hkey !is null);
+}
+body
+{
+ REG_VALUE_TYPE type;
+
+ DWORD cbData = value.sizeof;
+ enforceSucc(RegQueryValueExW(hkey, name.tempCStringW(), null, cast(LPDWORD) &type, &value, &cbData),
+ "Cannot read the requested value");
+ enforce(type == reqType,
+ new RegistryException("Value type has been changed since the value was acquired"));
+
+ switch (type)
+ {
+ case REG_VALUE_TYPE.REG_QWORD_LITTLE_ENDIAN:
+ break;
+
+ default:
+ throw new RegistryException("Cannot read the given value as a 64-bit integer");
+ }
+}
+
+private void regQueryValue(in HKEY hkey, in string name, out byte[] value, REG_VALUE_TYPE reqType)
+in
+{
+ assert(hkey !is null);
+}
+body
+{
+ REG_VALUE_TYPE type;
+
+ byte[] data = new byte[100];
+ DWORD cbData = to!DWORD(data.length);
+ LONG res;
+ auto keynameTmp = name.tempCStringW();
+ res = RegQueryValueExW(hkey, keynameTmp, null, cast(LPDWORD) &type, data.ptr, &cbData);
+ if (res == ERROR_MORE_DATA)
+ {
+ data.length = cbData;
+ res = RegQueryValueExW(hkey, keynameTmp, null, cast(LPDWORD) &type, data.ptr, &cbData);
+ }
+ enforceSucc(res, "Cannot read the requested value");
+ enforce(type == reqType,
+ new RegistryException("Value type has been changed since the value was acquired"));
+
+ switch (type)
+ {
+ case REG_VALUE_TYPE.REG_BINARY:
+ data.length = cbData;
+ value = data;
+ break;
+
+ default:
+ throw new RegistryException("Cannot read the given value as a string");
+ }
+}
+
+private void regSetValue(in HKEY hkey, in string subKey, in REG_VALUE_TYPE type, in LPCVOID lpData, in DWORD cbData)
+in
+{
+ assert(hkey !is null);
+}
+body
+{
+ enforceSucc(RegSetValueExW(hkey, subKey.tempCStringW(), 0, type, cast(BYTE*) lpData, cbData),
+ "Value cannot be set: \"" ~ subKey ~ "\"");
+}
+
+private void regProcessNthKey(Key key, scope void delegate(scope LONG delegate(DWORD, out string)) dg)
+{
+ DWORD cSubKeys;
+ DWORD cchSubKeyMaxLen;
+
+ immutable res = regGetNumSubKeys(key.m_hkey, cSubKeys, cchSubKeyMaxLen);
+ assert(res == ERROR_SUCCESS);
+
+ wchar[] sName = new wchar[cchSubKeyMaxLen + 1];
+
+ // Capture `key` in the lambda to keep the object alive (and so its HKEY handle open).
+ dg((DWORD index, out string name)
+ {
+ DWORD cchName;
+ immutable res = regEnumKeyName(key.m_hkey, index, sName, cchName);
+ if (res == ERROR_SUCCESS)
+ {
+ name = sName[0 .. cchName].to!string;
+ }
+ return res;
+ });
+}
+
+private void regProcessNthValue(Key key, scope void delegate(scope LONG delegate(DWORD, out string)) dg)
+{
+ DWORD cValues;
+ DWORD cchValueMaxLen;
+
+ immutable res = regGetNumValues(key.m_hkey, cValues, cchValueMaxLen);
+ assert(res == ERROR_SUCCESS);
+
+ wchar[] sName = new wchar[cchValueMaxLen + 1];
+
+ // Capture `key` in the lambda to keep the object alive (and so its HKEY handle open).
+ dg((DWORD index, out string name)
+ {
+ DWORD cchName;
+ immutable res = regEnumValueName(key.m_hkey, index, sName, cchName);
+ if (res == ERROR_SUCCESS)
+ {
+ name = sName[0 .. cchName].to!string;
+ }
+ return res;
+ });
+}
+
+/* ************* public classes *************** */
+
+/**
+ This class represents a registry key.
+ */
+class Key
+{
+ @safe pure nothrow
+ invariant()
+ {
+ assert(m_hkey !is null);
+ }
+
+private:
+ @safe pure nothrow
+ this(HKEY hkey, string name, bool created)
+ in
+ {
+ assert(hkey !is null);
+ }
+ body
+ {
+ m_hkey = hkey;
+ m_name = name;
+ }
+
+ ~this()
+ {
+ regCloseKey(m_hkey);
+
+ // Even though this is horried waste-of-cycles programming
+ // we're doing it here so that the
+ m_hkey = null;
+ }
+
+public:
+ /// The name of the key
+ @property string name() @safe pure nothrow const
+ {
+ return m_name;
+ }
+
+ /**
+ The number of sub keys.
+ */
+ @property size_t keyCount() const
+ {
+ uint cSubKeys;
+ uint cchSubKeyMaxLen;
+ enforceSucc(regGetNumSubKeys(m_hkey, cSubKeys, cchSubKeyMaxLen),
+ "Number of sub-keys cannot be determined");
+
+ return cSubKeys;
+ }
+
+ /**
+ An enumerable sequence of all the sub-keys of this key.
+ */
+ @property KeySequence keys() @safe pure
+ {
+ return new KeySequence(this);
+ }
+
+ /**
+ An enumerable sequence of the names of all the sub-keys of this key.
+ */
+ @property KeyNameSequence keyNames() @safe pure
+ {
+ return new KeyNameSequence(this);
+ }
+
+ /**
+ The number of values.
+ */
+ @property size_t valueCount() const
+ {
+ uint cValues;
+ uint cchValueMaxLen;
+ enforceSucc(regGetNumValues(m_hkey, cValues, cchValueMaxLen),
+ "Number of values cannot be determined");
+
+ return cValues;
+ }
+
+ /**
+ An enumerable sequence of all the values of this key.
+ */
+ @property ValueSequence values() @safe pure
+ {
+ return new ValueSequence(this);
+ }
+
+ /**
+ An enumerable sequence of the names of all the values of this key.
+ */
+ @property ValueNameSequence valueNames() @safe pure
+ {
+ return new ValueNameSequence(this);
+ }
+
+public:
+ /**
+ Returns the named sub-key of this key.
+
+ Params:
+ name = The name of the subkey to create. May not be $(D null).
+ Returns:
+ The created key.
+ Throws:
+ $(D RegistryException) is thrown if the key cannot be created.
+ */
+ Key createKey(string name, REGSAM access = REGSAM.KEY_ALL_ACCESS)
+ {
+ enforce(!name.empty, new RegistryException("Key name is invalid"));
+
+ DWORD disposition;
+ HKEY hkey = regCreateKey(m_hkey, name, 0, access, null, disposition);
+ assert(hkey !is null);
+
+ // Potential resource leak here!!
+ //
+ // If the allocation of the memory for Key fails, the HKEY could be
+ // lost. Hence, we catch such a failure by the finally, and release
+ // the HKEY there. If the creation of
+ try
+ {
+ Key key = new Key(hkey, name, disposition == REG_CREATED_NEW_KEY);
+ hkey = null;
+ return key;
+ }
+ finally
+ {
+ if (hkey !is null)
+ {
+ regCloseKey(hkey);
+ }
+ }
+ }
+
+ /**
+ Returns the named sub-key of this key.
+
+ Params:
+ name = The name of the subkey to aquire. If name is the empty
+ string, then the called key is duplicated.
+ access = The desired access; one of the $(D REGSAM) enumeration.
+ Returns:
+ The aquired key.
+ Throws:
+ This function never returns $(D null). If a key corresponding to
+ the requested name is not found, $(D RegistryException) is thrown.
+ */
+ Key getKey(string name, REGSAM access = REGSAM.KEY_READ)
+ {
+ if (name.empty)
+ return new Key(regDup(m_hkey), m_name, false);
+
+ HKEY hkey = regOpenKey(m_hkey, name, access);
+ assert(hkey !is null);
+
+ // Potential resource leak here!!
+ //
+ // If the allocation of the memory for Key fails, the HKEY could be
+ // lost. Hence, we catch such a failure by the finally, and release
+ // the HKEY there. If the creation of
+ try
+ {
+ Key key = new Key(hkey, name, false);
+ hkey = null;
+ return key;
+ }
+ finally
+ {
+ if (hkey != null)
+ {
+ regCloseKey(hkey);
+ }
+ }
+ }
+
+ /**
+ Deletes the named key.
+
+ Params:
+ name = The name of the key to delete. May not be $(D null).
+ */
+ void deleteKey(string name, REGSAM access = cast(REGSAM) 0)
+ {
+ enforce(!name.empty, new RegistryException("Key name is invalid"));
+
+ regDeleteKey(m_hkey, name, access);
+ }
+
+ /**
+ Returns the named value.
+ If $(D name) is the empty string, then the default value is returned.
+
+ Returns:
+ This function never returns $(D null). If a value corresponding
+ to the requested name is not found, $(D RegistryException) is thrown.
+ */
+ Value getValue(string name)
+ {
+ return new Value(this, name, regGetValueType(m_hkey, name));
+ }
+
+ /**
+ Sets the named value with the given 32-bit unsigned integer value.
+
+ Params:
+ name = The name of the value to set. If it is the empty string,
+ sets the default value.
+ value = The 32-bit unsigned value to set.
+ Throws:
+ If a value corresponding to the requested name is not found,
+ $(D RegistryException) is thrown.
+ */
+ void setValue(string name, uint value)
+ {
+ setValue(name, value, endian);
+ }
+
+ /**
+ Sets the named value with the given 32-bit unsigned integer value,
+ according to the desired byte-ordering.
+
+ Params:
+ name = The name of the value to set. If it is the empty string,
+ sets the default value.
+ value = The 32-bit unsigned value to set.
+ endian = Can be $(D Endian.BigEndian) or $(D Endian.LittleEndian).
+ Throws:
+ If a value corresponding to the requested name is not found,
+ $(D RegistryException) is thrown.
+ */
+ void setValue(string name, uint value, Endian endian)
+ {
+ REG_VALUE_TYPE type = _RVT_from_Endian(endian);
+
+ assert(type == REG_VALUE_TYPE.REG_DWORD_BIG_ENDIAN ||
+ type == REG_VALUE_TYPE.REG_DWORD_LITTLE_ENDIAN);
+
+ regSetValue(m_hkey, name, type, &value, value.sizeof);
+ }
+
+ /**
+ Sets the named value with the given 64-bit unsigned integer value.
+
+ Params:
+ name = The name of the value to set. If it is the empty string,
+ sets the default value.
+ value = The 64-bit unsigned value to set.
+ Throws:
+ If a value corresponding to the requested name is not found,
+ $(D RegistryException) is thrown.
+ */
+ void setValue(string name, ulong value)
+ {
+ regSetValue(m_hkey, name, REG_VALUE_TYPE.REG_QWORD, &value, value.sizeof);
+ }
+
+ /**
+ Sets the named value with the given string value.
+
+ Params:
+ name = The name of the value to set. If it is the empty string,
+ sets the default value.
+ value = The string value to set.
+ Throws:
+ If a value corresponding to the requested name is not found,
+ $(D RegistryException) is thrown.
+ */
+ void setValue(string name, string value)
+ {
+ setValue(name, value, false);
+ }
+
+ /**
+ Sets the named value with the given string value.
+
+ Params:
+ name = The name of the value to set. If it is the empty string,
+ sets the default value.
+ value = The string value to set.
+ asEXPAND_SZ = If $(D true), the value will be stored as an
+ expandable environment string, otherwise as a normal string.
+ Throws:
+ If a value corresponding to the requested name is not found,
+ $(D RegistryException) is thrown.
+ */
+ void setValue(string name, string value, bool asEXPAND_SZ)
+ {
+ auto pszTmp = value.tempCStringW();
+ const(void)* data = pszTmp;
+ DWORD len = to!DWORD(lstrlenW(pszTmp) * wchar.sizeof);
+
+ regSetValue(m_hkey, name,
+ asEXPAND_SZ ? REG_VALUE_TYPE.REG_EXPAND_SZ
+ : REG_VALUE_TYPE.REG_SZ,
+ data, len);
+ }
+
+ /**
+ Sets the named value with the given multiple-strings value.
+
+ Params:
+ name = The name of the value to set. If it is the empty string,
+ sets the default value.
+ value = The multiple-strings value to set.
+ Throws:
+ If a value corresponding to the requested name is not found,
+ $(D RegistryException) is thrown.
+ */
+ void setValue(string name, string[] value)
+ {
+ wstring[] data = new wstring[value.length+1];
+ foreach (i, ref s; data[0..$-1])
+ {
+ s = value[i].to!wstring;
+ }
+ data[$-1] = "\0";
+ auto ws = std.array.join(data, "\0"w);
+
+ regSetValue(m_hkey, name, REG_VALUE_TYPE.REG_MULTI_SZ, ws.ptr, to!uint(ws.length * wchar.sizeof));
+ }
+
+ /**
+ Sets the named value with the given binary value.
+
+ Params:
+ name = The name of the value to set. If it is the empty string,
+ sets the default value.
+ value = The binary value to set.
+ Throws:
+ If a value corresponding to the requested name is not found,
+ $(D RegistryException) is thrown.
+ */
+ void setValue(string name, byte[] value)
+ {
+ regSetValue(m_hkey, name, REG_VALUE_TYPE.REG_BINARY, value.ptr, to!DWORD(value.length));
+ }
+
+ /**
+ Deletes the named value.
+
+ Params:
+ name = The name of the value to delete. May not be $(D null).
+ Throws:
+ If a value of the requested name is not found,
+ $(D RegistryException) is thrown.
+ */
+ void deleteValue(string name)
+ {
+ regDeleteValue(m_hkey, name);
+ }
+
+ /**
+ Flushes any changes to the key to disk.
+ */
+ void flush()
+ {
+ regFlushKey(m_hkey);
+ }
+
+private:
+ HKEY m_hkey;
+ string m_name;
+}
+
+/**
+ This class represents a value of a registry key.
+ */
+class Value
+{
+ @safe pure nothrow
+ invariant()
+ {
+ assert(m_key !is null);
+ }
+
+private:
+ @safe pure nothrow
+ this(Key key, string name, REG_VALUE_TYPE type)
+ in
+ {
+ assert(null !is key);
+ }
+ body
+ {
+ m_key = key;
+ m_type = type;
+ m_name = name;
+ }
+
+public:
+ /**
+ The name of the value.
+ If the value represents a default value of a key, which has no name,
+ the returned string will be of zero length.
+ */
+ @property string name() @safe pure nothrow const
+ {
+ return m_name;
+ }
+
+ /**
+ The type of value.
+ */
+ @property REG_VALUE_TYPE type() @safe pure nothrow const
+ {
+ return m_type;
+ }
+
+ /**
+ Obtains the current value of the value as a string.
+ If the value's type is REG_EXPAND_SZ the returned value is <b>not</b>
+ expanded; $(D value_EXPAND_SZ) should be called
+
+ Returns:
+ The contents of the value.
+ Throws:
+ $(D RegistryException) if the type of the value is not REG_SZ,
+ REG_EXPAND_SZ, REG_DWORD, or REG_QWORD.
+ */
+ @property string value_SZ() const
+ {
+ string value;
+
+ regQueryValue(m_key.m_hkey, m_name, value, m_type);
+
+ return value;
+ }
+
+ /**
+ Obtains the current value as a string, within which any environment
+ variables have undergone expansion.
+ This function works with the same value-types as $(D value_SZ).
+
+ Returns:
+ The contents of the value.
+ */
+ @property string value_EXPAND_SZ() const
+ {
+ string value = value_SZ;
+
+ // ExpandEnvironemntStrings():
+ // http://msdn2.microsoft.com/en-us/library/ms724265.aspx
+ const srcTmp = value.tempCStringW();
+ DWORD cchRequired = ExpandEnvironmentStringsW(srcTmp, null, 0);
+ wchar[] newValue = new wchar[cchRequired];
+
+ immutable DWORD count = enforce!Win32Exception(
+ ExpandEnvironmentStringsW(srcTmp, newValue.ptr, to!DWORD(newValue.length)),
+ "Failed to expand environment variables");
+
+ return newValue[0 .. count-1].to!string; // remove trailing 0
+ }
+
+ /**
+ Obtains the current value as an array of strings.
+
+ Returns:
+ The contents of the value.
+ Throws:
+ $(D RegistryException) if the type of the value is not REG_MULTI_SZ.
+ */
+ @property string[] value_MULTI_SZ() const
+ {
+ string[] value;
+
+ regQueryValue(m_key.m_hkey, m_name, value, m_type);
+
+ return value;
+ }
+
+ /**
+ Obtains the current value as a 32-bit unsigned integer, ordered
+ correctly according to the current architecture.
+
+ Returns:
+ The contents of the value.
+ Throws:
+ $(D RegistryException) is thrown for all types other than
+ REG_DWORD, REG_DWORD_LITTLE_ENDIAN and REG_DWORD_BIG_ENDIAN.
+ */
+ @property uint value_DWORD() const
+ {
+ uint value;
+
+ regQueryValue(m_key.m_hkey, m_name, value, m_type);
+
+ return value;
+ }
+
+ /**
+ Obtains the value as a 64-bit unsigned integer, ordered correctly
+ according to the current architecture.
+
+ Returns:
+ The contents of the value.
+ Throws:
+ $(D RegistryException) if the type of the value is not REG_QWORD.
+ */
+ @property ulong value_QWORD() const
+ {
+ ulong value;
+
+ regQueryValue(m_key.m_hkey, m_name, value, m_type);
+
+ return value;
+ }
+
+ /**
+ Obtains the value as a binary blob.
+
+ Returns:
+ The contents of the value.
+ Throws:
+ $(D RegistryException) if the type of the value is not REG_BINARY.
+ */
+ @property byte[] value_BINARY() const
+ {
+ byte[] value;
+
+ regQueryValue(m_key.m_hkey, m_name, value, m_type);
+
+ return value;
+ }
+
+private:
+ Key m_key;
+ REG_VALUE_TYPE m_type;
+ string m_name;
+}
+
+/**
+ Represents the local system registry.
+ */
+final class Registry
+{
+private:
+ @disable this() { }
+
+public:
+ /// Returns the root key for the HKEY_CLASSES_ROOT hive
+ static @property Key classesRoot() { return new Key(HKEY_CLASSES_ROOT, "HKEY_CLASSES_ROOT", false); }
+ /// Returns the root key for the HKEY_CURRENT_USER hive
+ static @property Key currentUser() { return new Key(HKEY_CURRENT_USER, "HKEY_CURRENT_USER", false); }
+ /// Returns the root key for the HKEY_LOCAL_MACHINE hive
+ static @property Key localMachine() { return new Key(HKEY_LOCAL_MACHINE, "HKEY_LOCAL_MACHINE", false); }
+ /// Returns the root key for the HKEY_USERS hive
+ static @property Key users() { return new Key(HKEY_USERS, "HKEY_USERS", false); }
+ /// Returns the root key for the HKEY_PERFORMANCE_DATA hive
+ static @property Key performanceData() { return new Key(HKEY_PERFORMANCE_DATA, "HKEY_PERFORMANCE_DATA", false); }
+ /// Returns the root key for the HKEY_CURRENT_CONFIG hive
+ static @property Key currentConfig() { return new Key(HKEY_CURRENT_CONFIG, "HKEY_CURRENT_CONFIG", false); }
+ /// Returns the root key for the HKEY_DYN_DATA hive
+ static @property Key dynData() { return new Key(HKEY_DYN_DATA, "HKEY_DYN_DATA", false); }
+}
+
+/**
+ An enumerable sequence representing the names of the sub-keys of a registry Key.
+
+Example:
+----
+Key key = ...
+foreach (string subkeyName; key.keyNames)
+{
+ // using subkeyName
+}
+----
+ */
+class KeyNameSequence
+{
+ @safe pure nothrow
+ invariant()
+ {
+ assert(m_key !is null);
+ }
+
+private:
+ @safe pure nothrow
+ this(Key key)
+ {
+ m_key = key;
+ }
+
+public:
+ /**
+ The number of keys.
+ */
+ @property size_t count() const
+ {
+ return m_key.keyCount;
+ }
+
+ /**
+ The name of the key at the given index.
+
+ Params:
+ index = The 0-based index of the key to retrieve.
+ Returns:
+ The name of the key corresponding to the given index.
+ Throws:
+ RegistryException if no corresponding key is retrieved.
+ */
+ string getKeyName(size_t index)
+ {
+ string name;
+ regProcessNthKey(m_key, (scope LONG delegate(DWORD, out string) getName)
+ {
+ enforceSucc(getName(to!DWORD(index), name), "Invalid key");
+ });
+ return name;
+ }
+
+ /**
+ The name of the key at the given index.
+
+ Params:
+ index = The 0-based index of the key to retrieve.
+ Returns:
+ The name of the key corresponding to the given index.
+ Throws:
+ $(D RegistryException) if no corresponding key is retrieved.
+ */
+ string opIndex(size_t index)
+ {
+ return getKeyName(index);
+ }
+
+public:
+ ///
+ int opApply(scope int delegate(ref string name) dg)
+ {
+ int result;
+ regProcessNthKey(m_key, (scope LONG delegate(DWORD, out string) getName)
+ {
+ for (DWORD index = 0; !result; ++index)
+ {
+ string name;
+ immutable res = getName(index, name);
+ if (res == ERROR_NO_MORE_ITEMS) // Enumeration complete
+ break;
+ enforceSucc(res, "Key name enumeration incomplete");
+
+ result = dg(name);
+ }
+ });
+ return result;
+ }
+
+private:
+ Key m_key;
+}
+
+
+/**
+ An enumerable sequence representing the sub-keys of a registry Key.
+
+Example:
+----
+Key key = ...
+foreach (Key subkey; key.keys)
+{
+ // using subkey
+}
+----
+ */
+class KeySequence
+{
+ @safe pure nothrow
+ invariant()
+ {
+ assert(m_key !is null);
+ }
+
+private:
+ @safe pure nothrow
+ this(Key key)
+ {
+ m_key = key;
+ }
+
+public:
+ /**
+ The number of keys.
+ */
+ @property size_t count() const
+ {
+ return m_key.keyCount;
+ }
+
+ /**
+ The key at the given index.
+
+ Params:
+ index = The 0-based index of the key to retrieve.
+ Returns:
+ The key corresponding to the given index.
+ Throws:
+ $(D RegistryException) if no corresponding key is retrieved.
+ */
+ Key getKey(size_t index)
+ {
+ string name;
+ regProcessNthKey(m_key, (scope LONG delegate(DWORD, out string) getName)
+ {
+ enforceSucc(getName(to!DWORD(index), name), "Invalid key");
+ });
+ return m_key.getKey(name);
+ }
+
+ /**
+ The key at the given index.
+
+ Params:
+ index = The 0-based index of the key to retrieve.
+ Returns:
+ The key corresponding to the given index.
+ Throws:
+ $(D RegistryException) if no corresponding key is retrieved.
+ */
+ Key opIndex(size_t index)
+ {
+ return getKey(index);
+ }
+
+public:
+ ///
+ int opApply(scope int delegate(ref Key key) dg)
+ {
+ int result = 0;
+ regProcessNthKey(m_key, (scope LONG delegate(DWORD, out string) getName)
+ {
+ for (DWORD index = 0; !result; ++index)
+ {
+ string name;
+ immutable res = getName(index, name);
+ if (res == ERROR_NO_MORE_ITEMS) // Enumeration complete
+ break;
+ enforceSucc(res, "Key enumeration incomplete");
+
+ try
+ {
+ Key key = m_key.getKey(name);
+ result = dg(key);
+ }
+ catch (RegistryException e)
+ {
+ // Skip inaccessible keys; they are
+ // accessible via the KeyNameSequence
+ if (e.error == ERROR_ACCESS_DENIED)
+ continue;
+
+ throw e;
+ }
+ }
+ });
+ return result;
+ }
+
+private:
+ Key m_key;
+}
+
+/**
+ An enumerable sequence representing the names of the values of a registry Key.
+
+Example:
+----
+Key key = ...
+foreach (string valueName; key.valueNames)
+{
+ // using valueName
+}
+----
+ */
+class ValueNameSequence
+{
+ @safe pure nothrow
+ invariant()
+ {
+ assert(m_key !is null);
+ }
+
+private:
+ @safe pure nothrow
+ this(Key key)
+ {
+ m_key = key;
+ }
+
+public:
+ /**
+ The number of values.
+ */
+ @property size_t count() const
+ {
+ return m_key.valueCount;
+ }
+
+ /**
+ The name of the value at the given index.
+
+ Params:
+ index = The 0-based index of the value to retrieve.
+ Returns:
+ The name of the value corresponding to the given index.
+ Throws:
+ $(D RegistryException) if no corresponding value is retrieved.
+ */
+ string getValueName(size_t index)
+ {
+ string name;
+ regProcessNthValue(m_key, (scope LONG delegate(DWORD, out string) getName)
+ {
+ enforceSucc(getName(to!DWORD(index), name), "Invalid value");
+ });
+ return name;
+ }
+
+ /**
+ The name of the value at the given index.
+
+ Params:
+ index = The 0-based index of the value to retrieve.
+ Returns:
+ The name of the value corresponding to the given index.
+ Throws:
+ $(D RegistryException) if no corresponding value is retrieved.
+ */
+ string opIndex(size_t index)
+ {
+ return getValueName(index);
+ }
+
+public:
+ ///
+ int opApply(scope int delegate(ref string name) dg)
+ {
+ int result = 0;
+ regProcessNthValue(m_key, (scope LONG delegate(DWORD, out string) getName)
+ {
+ for (DWORD index = 0; !result; ++index)
+ {
+ string name;
+ immutable res = getName(index, name);
+ if (res == ERROR_NO_MORE_ITEMS) // Enumeration complete
+ break;
+ enforceSucc(res, "Value name enumeration incomplete");
+
+ result = dg(name);
+ }
+ });
+ return result;
+ }
+
+private:
+ Key m_key;
+}
+
+/**
+ An enumerable sequence representing the values of a registry Key.
+
+Example:
+----
+Key key = ...
+foreach (Value value; key.values)
+{
+ // using value
+}
+----
+ */
+class ValueSequence
+{
+ @safe pure nothrow
+ invariant()
+ {
+ assert(m_key !is null);
+ }
+
+private:
+ @safe pure nothrow
+ this(Key key)
+ {
+ m_key = key;
+ }
+
+public:
+ /// The number of values
+ @property size_t count() const
+ {
+ return m_key.valueCount;
+ }
+
+ /**
+ The value at the given $(D index).
+
+ Params:
+ index = The 0-based index of the value to retrieve
+ Returns:
+ The value corresponding to the given index.
+ Throws:
+ $(D RegistryException) if no corresponding value is retrieved
+ */
+ Value getValue(size_t index)
+ {
+ string name;
+ regProcessNthValue(m_key, (scope LONG delegate(DWORD, out string) getName)
+ {
+ enforceSucc(getName(to!DWORD(index), name), "Invalid value");
+ });
+ return m_key.getValue(name);
+ }
+
+ /**
+ The value at the given $(D index).
+
+ Params:
+ index = The 0-based index of the value to retrieve.
+ Returns:
+ The value corresponding to the given index.
+ Throws:
+ $(D RegistryException) if no corresponding value is retrieved.
+ */
+ Value opIndex(size_t index)
+ {
+ return getValue(index);
+ }
+
+public:
+ ///
+ int opApply(scope int delegate(ref Value value) dg)
+ {
+ int result = 0;
+ regProcessNthValue(m_key, (scope LONG delegate(DWORD, out string) getName)
+ {
+ for (DWORD index = 0; !result; ++index)
+ {
+ string name;
+ immutable res = getName(index, name);
+ if (res == ERROR_NO_MORE_ITEMS) // Enumeration complete
+ break;
+ enforceSucc(res, "Value enumeration incomplete");
+
+ Value value = m_key.getValue(name);
+ result = dg(value);
+ }
+ });
+ return result;
+ }
+
+private:
+ Key m_key;
+}
+
+
+@system unittest
+{
+ debug(winreg) scope(success) writeln("unittest @", __FILE__, ":", __LINE__, " succeeded.");
+ debug(winreg) writefln("std.windows.registry.unittest read");
+
+/+
+ // Mask for test speed up
+
+ Key HKCR = Registry.classesRoot;
+ Key CLSID = HKCR.getKey("CLSID");
+
+ foreach (Key key; CLSID.keys)
+ {
+ foreach (Value val; key.values)
+ {
+ }
+ }
++/
+ Key HKCU = Registry.currentUser;
+ assert(HKCU);
+
+ // Enumerate all subkeys of key Software
+ Key softwareKey = HKCU.getKey("Software");
+ assert(softwareKey);
+ foreach (Key key; softwareKey.keys)
+ {
+ //writefln("Key %s", key.name);
+ foreach (Value val; key.values)
+ {
+ }
+ }
+}
+
+@system unittest
+{
+ debug(winreg) scope(success) writeln("unittest @", __FILE__, ":", __LINE__, " succeeded.");
+ debug(winreg) writefln("std.windows.registry.unittest write");
+
+ // Warning: This unit test writes to the registry.
+ // The test can fail if you don't have sufficient rights
+
+ Key HKCU = Registry.currentUser;
+ assert(HKCU);
+
+ // Create a new key
+ 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"
+ );
+ cityKey.setValue("K\u00f6ln", "Germany"); // Cologne
+ cityKey.setValue("\u041c\u0438\u043d\u0441\u043a", "Belarus"); // Minsk
+ cityKey.setValue("\u5317\u4eac", "China"); // Bejing
+ bool foundCologne, foundMinsk, foundBeijing;
+ foreach (Value v; cityKey.values)
+ {
+ auto vname = v.name;
+ auto vvalue_SZ = v.value_SZ;
+ if (v.name == "K\u00f6ln")
+ {
+ foundCologne = true;
+ assert(v.value_SZ == "Germany");
+ }
+ if (v.name == "\u041c\u0438\u043d\u0441\u043a")
+ {
+ foundMinsk = true;
+ assert(v.value_SZ == "Belarus");
+ }
+ if (v.name == "\u5317\u4eac")
+ {
+ foundBeijing = true;
+ assert(v.value_SZ == "China");
+ }
+ }
+ assert(foundCologne);
+ assert(foundMinsk);
+ assert(foundBeijing);
+
+ Key stateKey = unittestKey.createKey("StateCollection");
+ stateKey.setValue("Germany", ["D\u00fcsseldorf", "K\u00f6ln", "Hamburg"]);
+ Value v = stateKey.getValue("Germany");
+ string[] actual = v.value_MULTI_SZ;
+ assert(actual.length == 3);
+ assert(actual[0] == "D\u00fcsseldorf");
+ assert(actual[1] == "K\u00f6ln");
+ assert(actual[2] == "Hamburg");
+
+ Key numberKey = unittestKey.createKey("Number");
+ numberKey.setValue("One", 1);
+ Value one = numberKey.getValue("One");
+ assert(one.value_SZ == "1");
+ assert(one.value_DWORD == 1);
+
+ unittestKey.deleteKey(numberKey.name);
+ unittestKey.deleteKey(stateKey.name);
+ unittestKey.deleteKey(cityKey.name);
+ HKCU.deleteKey(unittestKeyName);
+
+ auto e = collectException!RegistryException(HKCU.getKey("cDhmxsX9K23a8Uf869uB"));
+ assert(e.msg.endsWith(" (error 2)"));
+}
+
+@system unittest
+{
+ Key HKCU = Registry.currentUser;
+ assert(HKCU);
+
+ Key key = HKCU.getKey("Control Panel");
+ assert(key);
+ assert(key.keyCount >= 2);
+
+ // Make sure `key` isn't garbage-collected while iterating over it.
+ // Trigger a collection in the first iteration and check whether we
+ // make it successfully to the second iteration.
+ int i = 0;
+ foreach (name; key.keyNames)
+ {
+ if (i++ > 0)
+ break;
+
+ import core.memory;
+ GC.collect();
+ }
+ assert(i == 2);
+}
diff --git a/libphobos/src/std/windows/syserror.d b/libphobos/src/std/windows/syserror.d
new file mode 100644
index 0000000..7386360
--- /dev/null
+++ b/libphobos/src/std/windows/syserror.d
@@ -0,0 +1,201 @@
+// Written in the D programming language.
+
+/**
+ * Convert Win32 error code to string.
+ *
+ * Copyright: Copyright Digital Mars 2006 - 2013.
+ * 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.
+ * 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.windows.syserror;
+import std.traits : isSomeString;
+
+version (StdDdoc)
+{
+ private
+ {
+ alias DWORD = uint;
+ 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.
+ */
+ string sysErrorString(
+ DWORD errCode,
+ // MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT) is the user's default language
+ int langId = LANG_NEUTRAL,
+ 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.
+ */
+ class WindowsException : Exception
+ {
+ private alias DWORD = int;
+ final @property DWORD code(); /// $(D GetLastError)'s return value.
+ this(DWORD code, string str=null, string file = null, size_t line = 0) @trusted;
+ }
+
+ /++
+ If $(D !!value) is true, $(D value) is returned. Otherwise,
+ $(D new WindowsException(GetLastError(), msg)) is thrown.
+ $(D WindowsException) assumes that the last operation set
+ $(D GetLastError()) appropriately.
+
+ Example:
+ --------------------
+ wenforce(DeleteFileA("junk.tmp"), "DeleteFile failed");
+ --------------------
+ +/
+ T wenforce(T, S)(T value, lazy S msg = null,
+ string file = __FILE__, size_t line = __LINE__) @safe
+ if (isSomeString!S);
+}
+else:
+
+version (Windows):
+
+import core.sys.windows.windows;
+import std.array : appender;
+import std.conv : to;
+import std.format : formattedWrite;
+import std.windows.charset;
+
+string sysErrorString(
+ DWORD errCode,
+ // MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT) is the user's default language
+ int langId = LANG_NEUTRAL,
+ int subLangId = SUBLANG_DEFAULT) @trusted
+{
+ auto buf = appender!string();
+
+ if (!putSysError(errCode, buf, MAKELANGID(langId, subLangId)))
+ {
+ throw new Exception(
+ "failed getting error string for WinAPI error code: " ~
+ sysErrorString(GetLastError()));
+ }
+
+ return buf.data;
+}
+
+bool putSysError(Writer)(DWORD code, Writer w, /*WORD*/int langId = 0)
+{
+ wchar *lpMsgBuf = null;
+ auto res = FormatMessageW(
+ FORMAT_MESSAGE_ALLOCATE_BUFFER |
+ FORMAT_MESSAGE_FROM_SYSTEM |
+ FORMAT_MESSAGE_IGNORE_INSERTS,
+ null,
+ code,
+ langId,
+ cast(LPWSTR)&lpMsgBuf,
+ 0,
+ null);
+ scope(exit) if (lpMsgBuf) LocalFree(lpMsgBuf);
+
+ if (lpMsgBuf)
+ {
+ import std.string : strip;
+ w.put(lpMsgBuf[0 .. res].strip());
+ return true;
+ }
+ else
+ return false;
+}
+
+
+class WindowsException : Exception
+{
+ import core.sys.windows.windows : DWORD;
+
+ final @property DWORD code() { return _code; } /// $(D GetLastError)'s return value.
+ private DWORD _code;
+
+ this(DWORD code, string str=null, string file = null, size_t line = 0) @trusted
+ {
+ _code = code;
+
+ auto buf = appender!string();
+
+ if (str != null)
+ {
+ buf.put(str);
+ if (code)
+ buf.put(": ");
+ }
+
+ if (code)
+ {
+ auto success = putSysError(code, buf);
+ formattedWrite(buf, success ? " (error %d)" : "Error %d", code);
+ }
+
+ super(buf.data, file, line);
+ }
+}
+
+
+T wenforce(T, S)(T value, lazy S msg = null,
+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 core.stdc.wchar_ : wcslen;
+ import std.conv : to;
+ 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)
+@system unittest
+{
+ import std.algorithm.searching : startsWith, endsWith;
+ import std.exception;
+ import std.string;
+
+ auto e = collectException!WindowsException(
+ DeleteFileA("unexisting.txt").wenforce("DeleteFile")
+ );
+ assert(e.code == ERROR_FILE_NOT_FOUND);
+ 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/libphobos/src/std/xml.d b/libphobos/src/std/xml.d
new file mode 100644
index 0000000..770c56f
--- /dev/null
+++ b/libphobos/src/std/xml.d
@@ -0,0 +1,3103 @@
+// 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.)
+
+Classes and functions for creating and parsing XML
+
+The basic architecture of this module is that there are standalone functions,
+classes for constructing an XML document from scratch (Tag, Element and
+Document), and also classes for parsing a pre-existing XML file (ElementParser
+and DocumentParser). The parsing classes <i>may</i> be used to build a
+Document, but that is not their primary purpose. The handling capabilities of
+DocumentParser and ElementParser are sufficiently customizable that you can
+make them do pretty much whatever you want.
+
+Example: This example creates a DOM (Document Object Model) tree
+ from an XML file.
+------------------------------------------------------------------------------
+import std.xml;
+import std.stdio;
+import std.string;
+import std.file;
+
+// books.xml is used in various samples throughout the Microsoft XML Core
+// Services (MSXML) SDK.
+//
+// See http://msdn2.microsoft.com/en-us/library/ms762271(VS.85).aspx
+
+void main()
+{
+ string s = cast(string) std.file.read("books.xml");
+
+ // Check for well-formedness
+ check(s);
+
+ // Make a DOM tree
+ auto doc = new Document(s);
+
+ // Plain-print it
+ writeln(doc);
+}
+------------------------------------------------------------------------------
+
+Example: This example does much the same thing, except that the file is
+ deconstructed and reconstructed by hand. This is more work, but the
+ techniques involved offer vastly more power.
+------------------------------------------------------------------------------
+import std.xml;
+import std.stdio;
+import std.string;
+
+struct Book
+{
+ string id;
+ string author;
+ string title;
+ string genre;
+ string price;
+ string pubDate;
+ string description;
+}
+
+void main()
+{
+ string s = cast(string) std.file.read("books.xml");
+
+ // Check for well-formedness
+ check(s);
+
+ // Take it apart
+ Book[] books;
+
+ auto xml = new DocumentParser(s);
+ xml.onStartTag["book"] = (ElementParser xml)
+ {
+ Book book;
+ book.id = xml.tag.attr["id"];
+
+ xml.onEndTag["author"] = (in Element e) { book.author = e.text(); };
+ xml.onEndTag["title"] = (in Element e) { book.title = e.text(); };
+ xml.onEndTag["genre"] = (in Element e) { book.genre = e.text(); };
+ xml.onEndTag["price"] = (in Element e) { book.price = e.text(); };
+ xml.onEndTag["publish-date"] = (in Element e) { book.pubDate = e.text(); };
+ xml.onEndTag["description"] = (in Element e) { book.description = e.text(); };
+
+ xml.parse();
+
+ books ~= book;
+ };
+ xml.parse();
+
+ // Put it back together again;
+ auto doc = new Document(new Tag("catalog"));
+ foreach (book;books)
+ {
+ auto element = new Element("book");
+ element.tag.attr["id"] = book.id;
+
+ element ~= new Element("author", book.author);
+ element ~= new Element("title", book.title);
+ element ~= new Element("genre", book.genre);
+ element ~= new Element("price", book.price);
+ element ~= new Element("publish-date",book.pubDate);
+ element ~= new Element("description", book.description);
+
+ doc ~= element;
+ }
+
+ // Pretty-print it
+ writefln(join(doc.pretty(3),"\n"));
+}
+-------------------------------------------------------------------------------
+Copyright: Copyright Janice Caron 2008 - 2009.
+License: $(HTTP www.boost.org/LICENSE_1_0.txt, Boost License 1.0).
+Authors: Janice Caron
+Source: $(PHOBOSSRC std/_xml.d)
+*/
+/*
+ Copyright Janice Caron 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.xml;
+
+enum cdata = "<![CDATA[";
+
+/**
+ * Returns true if the character is a character according to the XML standard
+ *
+ * Standards: $(LINK2 http://www.w3.org/TR/1998/REC-xml-19980210, XML 1.0)
+ *
+ * Params:
+ * c = the character to be tested
+ */
+bool isChar(dchar c) @safe @nogc pure nothrow // rule 2
+{
+ if (c <= 0xD7FF)
+ {
+ if (c >= 0x20)
+ return true;
+ switch (c)
+ {
+ case 0xA:
+ case 0x9:
+ case 0xD:
+ return true;
+ default:
+ return false;
+ }
+ }
+ else if (0xE000 <= c && c <= 0x10FFFF)
+ {
+ if ((c & 0x1FFFFE) != 0xFFFE) // U+FFFE and U+FFFF
+ return true;
+ }
+ return false;
+}
+
+@safe @nogc nothrow pure unittest
+{
+ 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));
+
+ debug (stdxml_TestHardcodedChecks)
+ {
+ foreach (c; 0 .. dchar.max + 1)
+ assert(isChar(c) == lookup(CharTable, c));
+ }
+}
+
+/**
+ * Returns true if the character is whitespace according to the XML standard
+ *
+ * Only the following characters are considered whitespace in XML - space, tab,
+ * carriage return and linefeed
+ *
+ * Standards: $(LINK2 http://www.w3.org/TR/1998/REC-xml-19980210, XML 1.0)
+ *
+ * Params:
+ * c = the character to be tested
+ */
+bool isSpace(dchar c) @safe @nogc pure nothrow
+{
+ return c == '\u0020' || c == '\u0009' || c == '\u000A' || c == '\u000D';
+}
+
+/**
+ * Returns true if the character is a digit according to the XML standard
+ *
+ * Standards: $(LINK2 http://www.w3.org/TR/1998/REC-xml-19980210, XML 1.0)
+ *
+ * Params:
+ * c = the character to be tested
+ */
+bool isDigit(dchar c) @safe @nogc pure nothrow
+{
+ if (c <= 0x0039 && c >= 0x0030)
+ return true;
+ else
+ return lookup(DigitTable,c);
+}
+
+@safe @nogc nothrow pure unittest
+{
+ debug (stdxml_TestHardcodedChecks)
+ {
+ foreach (c; 0 .. dchar.max + 1)
+ assert(isDigit(c) == lookup(DigitTable, c));
+ }
+}
+
+/**
+ * Returns true if the character is a letter according to the XML standard
+ *
+ * Standards: $(LINK2 http://www.w3.org/TR/1998/REC-xml-19980210, XML 1.0)
+ *
+ * Params:
+ * c = the character to be tested
+ */
+bool isLetter(dchar c) @safe @nogc nothrow pure // rule 84
+{
+ return isIdeographic(c) || isBaseChar(c);
+}
+
+/**
+ * Returns true if the character is an ideographic character according to the
+ * XML standard
+ *
+ * Standards: $(LINK2 http://www.w3.org/TR/1998/REC-xml-19980210, XML 1.0)
+ *
+ * Params:
+ * c = the character to be tested
+ */
+bool isIdeographic(dchar c) @safe @nogc nothrow pure
+{
+ if (c == 0x3007)
+ return true;
+ if (c <= 0x3029 && c >= 0x3021 )
+ return true;
+ if (c <= 0x9FA5 && c >= 0x4E00)
+ return true;
+ return false;
+}
+
+@safe @nogc nothrow pure unittest
+{
+ assert(isIdeographic('\u4E00'));
+ assert(isIdeographic('\u9FA5'));
+ assert(isIdeographic('\u3007'));
+ assert(isIdeographic('\u3021'));
+ assert(isIdeographic('\u3029'));
+
+ debug (stdxml_TestHardcodedChecks)
+ {
+ foreach (c; 0 .. dchar.max + 1)
+ assert(isIdeographic(c) == lookup(IdeographicTable, c));
+ }
+}
+
+/**
+ * Returns true if the character is a base character according to the XML
+ * standard
+ *
+ * Standards: $(LINK2 http://www.w3.org/TR/1998/REC-xml-19980210, XML 1.0)
+ *
+ * Params:
+ * c = the character to be tested
+ */
+bool isBaseChar(dchar c) @safe @nogc nothrow pure
+{
+ return lookup(BaseCharTable,c);
+}
+
+/**
+ * Returns true if the character is a combining character according to the
+ * XML standard
+ *
+ * Standards: $(LINK2 http://www.w3.org/TR/1998/REC-xml-19980210, XML 1.0)
+ *
+ * Params:
+ * c = the character to be tested
+ */
+bool isCombiningChar(dchar c) @safe @nogc nothrow pure
+{
+ return lookup(CombiningCharTable,c);
+}
+
+/**
+ * Returns true if the character is an extender according to the XML standard
+ *
+ * Standards: $(LINK2 http://www.w3.org/TR/1998/REC-xml-19980210, XML 1.0)
+ *
+ * Params:
+ * c = the character to be tested
+ */
+bool isExtender(dchar c) @safe @nogc nothrow pure
+{
+ return lookup(ExtenderTable,c);
+}
+
+/**
+ * Encodes a string by replacing all characters which need to be escaped with
+ * appropriate predefined XML entities.
+ *
+ * encode() escapes certain characters (ampersand, quote, apostrophe, less-than
+ * and greater-than), and similarly, decode() unescapes them. These functions
+ * are provided for convenience only. You do not need to use them when using
+ * the std.xml classes, because then all the encoding and decoding will be done
+ * for you automatically.
+ *
+ * If the string is not modified, the original will be returned.
+ *
+ * Standards: $(LINK2 http://www.w3.org/TR/1998/REC-xml-19980210, XML 1.0)
+ *
+ * Params:
+ * s = The string to be encoded
+ *
+ * Returns: The encoded string
+ *
+ * Example:
+ * --------------
+ * writefln(encode("a > b")); // writes "a &gt; b"
+ * --------------
+ */
+S encode(S)(S s)
+{
+ import std.array : appender;
+
+ string r;
+ size_t lastI;
+ auto result = appender!S();
+
+ foreach (i, c; s)
+ {
+ switch (c)
+ {
+ case '&': r = "&amp;"; break;
+ case '"': r = "&quot;"; break;
+ case '\'': r = "&apos;"; break;
+ case '<': r = "&lt;"; break;
+ case '>': r = "&gt;"; break;
+ default: continue;
+ }
+ // Replace with r
+ result.put(s[lastI .. i]);
+ result.put(r);
+ lastI = i + 1;
+ }
+
+ if (!result.data.ptr) return s;
+ result.put(s[lastI .. $]);
+ return result.data;
+}
+
+@safe pure unittest
+{
+ auto s = "hello";
+ assert(encode(s) is s);
+ assert(encode("a > b") == "a &gt; b", encode("a > b"));
+ assert(encode("a < b") == "a &lt; b");
+ assert(encode("don't") == "don&apos;t");
+ assert(encode("\"hi\"") == "&quot;hi&quot;", encode("\"hi\""));
+ assert(encode("cat & dog") == "cat &amp; dog");
+}
+
+/**
+ * Mode to use for decoding.
+ *
+ * $(DDOC_ENUM_MEMBERS NONE) Do not decode
+ * $(DDOC_ENUM_MEMBERS LOOSE) Decode, but ignore errors
+ * $(DDOC_ENUM_MEMBERS STRICT) Decode, and throw exception on error
+ */
+enum DecodeMode
+{
+ NONE, LOOSE, STRICT
+}
+
+/**
+ * Decodes a string by unescaping all predefined XML entities.
+ *
+ * encode() escapes certain characters (ampersand, quote, apostrophe, less-than
+ * and greater-than), and similarly, decode() unescapes them. These functions
+ * are provided for convenience only. You do not need to use them when using
+ * the std.xml classes, because then all the encoding and decoding will be done
+ * for you automatically.
+ *
+ * This function decodes the entities &amp;amp;, &amp;quot;, &amp;apos;,
+ * &amp;lt; and &amp;gt,
+ * as well as decimal and hexadecimal entities such as &amp;#x20AC;
+ *
+ * If the string does not contain an ampersand, the original will be returned.
+ *
+ * Note that the "mode" parameter can be one of DecodeMode.NONE (do not
+ * decode), DecodeMode.LOOSE (decode, but ignore errors), or DecodeMode.STRICT
+ * (decode, and throw a DecodeException in the event of an error).
+ *
+ * Standards: $(LINK2 http://www.w3.org/TR/1998/REC-xml-19980210, XML 1.0)
+ *
+ * Params:
+ * s = The string to be decoded
+ * mode = (optional) Mode to use for decoding. (Defaults to LOOSE).
+ *
+ * Throws: DecodeException if mode == DecodeMode.STRICT and decode fails
+ *
+ * Returns: The decoded string
+ *
+ * Example:
+ * --------------
+ * writefln(decode("a &gt; b")); // writes "a > b"
+ * --------------
+ */
+string decode(string s, DecodeMode mode=DecodeMode.LOOSE) @safe pure
+{
+ import std.algorithm.searching : startsWith;
+
+ if (mode == DecodeMode.NONE) return s;
+
+ string buffer;
+ foreach (ref i; 0 .. s.length)
+ {
+ char c = s[i];
+ if (c != '&')
+ {
+ if (buffer.length != 0) buffer ~= c;
+ }
+ else
+ {
+ if (buffer.length == 0)
+ {
+ buffer = s[0 .. i].dup;
+ }
+ if (startsWith(s[i..$],"&#"))
+ {
+ try
+ {
+ dchar d;
+ string t = s[i..$];
+ checkCharRef(t, d);
+ char[4] temp;
+ import std.utf : encode;
+ buffer ~= temp[0 .. encode(temp, d)];
+ i = s.length - t.length - 1;
+ }
+ catch (Err e)
+ {
+ if (mode == DecodeMode.STRICT)
+ throw new DecodeException("Unescaped &");
+ buffer ~= '&';
+ }
+ }
+ else if (startsWith(s[i..$],"&amp;" )) { buffer ~= '&'; i += 4; }
+ else if (startsWith(s[i..$],"&quot;")) { buffer ~= '"'; i += 5; }
+ else if (startsWith(s[i..$],"&apos;")) { buffer ~= '\''; i += 5; }
+ else if (startsWith(s[i..$],"&lt;" )) { buffer ~= '<'; i += 3; }
+ else if (startsWith(s[i..$],"&gt;" )) { buffer ~= '>'; i += 3; }
+ else
+ {
+ if (mode == DecodeMode.STRICT)
+ throw new DecodeException("Unescaped &");
+ buffer ~= '&';
+ }
+ }
+ }
+ return (buffer.length == 0) ? s : buffer;
+}
+
+@safe pure unittest
+{
+ void assertNot(string s) pure
+ {
+ bool b = false;
+ try { decode(s,DecodeMode.STRICT); }
+ catch (DecodeException e) { b = true; }
+ assert(b,s);
+ }
+
+ // Assert that things that should work, do
+ auto s = "hello";
+ assert(decode(s, DecodeMode.STRICT) is s);
+ assert(decode("a &gt; b", DecodeMode.STRICT) == "a > b");
+ assert(decode("a &lt; b", DecodeMode.STRICT) == "a < b");
+ assert(decode("don&apos;t", DecodeMode.STRICT) == "don't");
+ assert(decode("&quot;hi&quot;", DecodeMode.STRICT) == "\"hi\"");
+ assert(decode("cat &amp; dog", DecodeMode.STRICT) == "cat & dog");
+ assert(decode("&#42;", DecodeMode.STRICT) == "*");
+ assert(decode("&#x2A;", DecodeMode.STRICT) == "*");
+ assert(decode("cat & dog", DecodeMode.LOOSE) == "cat & dog");
+ assert(decode("a &gt b", DecodeMode.LOOSE) == "a &gt b");
+ assert(decode("&#;", DecodeMode.LOOSE) == "&#;");
+ assert(decode("&#x;", DecodeMode.LOOSE) == "&#x;");
+ assert(decode("&#2G;", DecodeMode.LOOSE) == "&#2G;");
+ assert(decode("&#x2G;", DecodeMode.LOOSE) == "&#x2G;");
+
+ // Assert that things that shouldn't work, don't
+ assertNot("cat & dog");
+ assertNot("a &gt b");
+ assertNot("&#;");
+ assertNot("&#x;");
+ assertNot("&#2G;");
+ assertNot("&#x2G;");
+}
+
+/**
+ * Class representing an XML document.
+ *
+ * Standards: $(LINK2 http://www.w3.org/TR/1998/REC-xml-19980210, XML 1.0)
+ *
+ */
+class Document : Element
+{
+ /**
+ * Contains all text which occurs before the root element.
+ * Defaults to &lt;?xml version="1.0"?&gt;
+ */
+ string prolog = "<?xml version=\"1.0\"?>";
+ /**
+ * Contains all text which occurs after the root element.
+ * Defaults to the empty string
+ */
+ string epilog;
+
+ /**
+ * Constructs a Document by parsing XML text.
+ *
+ * This function creates a complete DOM (Document Object Model) tree.
+ *
+ * The input to this function MUST be valid XML.
+ * This is enforced by DocumentParser's in contract.
+ *
+ * Params:
+ * s = the complete XML text.
+ */
+ this(string s)
+ in
+ {
+ assert(s.length != 0);
+ }
+ body
+ {
+ auto xml = new DocumentParser(s);
+ string tagString = xml.tag.tagString;
+
+ this(xml.tag);
+ prolog = s[0 .. tagString.ptr - s.ptr];
+ parse(xml);
+ epilog = *xml.s;
+ }
+
+ /**
+ * Constructs a Document from a Tag.
+ *
+ * Params:
+ * tag = the start tag of the document.
+ */
+ this(const(Tag) tag)
+ {
+ super(tag);
+ }
+
+ const
+ {
+ /**
+ * Compares two Documents for equality
+ *
+ * Example:
+ * --------------
+ * Document d1,d2;
+ * if (d1 == d2) { }
+ * --------------
+ */
+ override bool opEquals(scope const Object o) const
+ {
+ const doc = toType!(const Document)(o);
+ return prolog == doc.prolog
+ && (cast(const) this).Element.opEquals(cast(const) doc)
+ && epilog == doc.epilog;
+ }
+
+ /**
+ * Compares two Documents
+ *
+ * You should rarely need to call this function. It exists so that
+ * Documents can be used as associative array keys.
+ *
+ * Example:
+ * --------------
+ * Document d1,d2;
+ * if (d1 < d2) { }
+ * --------------
+ */
+ override int opCmp(scope const Object o) scope const
+ {
+ const doc = toType!(const Document)(o);
+ if (prolog != doc.prolog)
+ return prolog < doc.prolog ? -1 : 1;
+ if (int cmp = this.Element.opCmp(doc))
+ return cmp;
+ if (epilog != doc.epilog)
+ return epilog < doc.epilog ? -1 : 1;
+ return 0;
+ }
+
+ /**
+ * Returns the hash of a Document
+ *
+ * You should rarely need to call this function. It exists so that
+ * Documents can be used as associative array keys.
+ */
+ override size_t toHash() scope const @trusted
+ {
+ 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() scope const @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.
+ *
+ * Standards: $(LINK2 http://www.w3.org/TR/1998/REC-xml-19980210, XML 1.0)
+ */
+class Element : Item
+{
+ Tag tag; /// The start tag of the element
+ Item[] items; /// The element's items
+ Text[] texts; /// The element's text items
+ CData[] cdatas; /// The element's CData items
+ Comment[] comments; /// The element's comments
+ ProcessingInstruction[] pis; /// The element's processing instructions
+ Element[] elements; /// The element's child elements
+
+ /**
+ * Constructs an Element given a name and a string to be used as a Text
+ * interior.
+ *
+ * Params:
+ * name = the name of the element.
+ * interior = (optional) the string interior.
+ *
+ * Example:
+ * -------------------------------------------------------
+ * auto element = new Element("title","Serenity")
+ * // constructs the element <title>Serenity</title>
+ * -------------------------------------------------------
+ */
+ this(string name, string interior=null) @safe pure
+ {
+ this(new Tag(name));
+ if (interior.length != 0) opCatAssign(new Text(interior));
+ }
+
+ /**
+ * Constructs an Element from a Tag.
+ *
+ * Params:
+ * tag_ = the start or empty tag of the element.
+ */
+ 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;
+ tag.tagString = tag_.tagString;
+ }
+
+ /**
+ * Append a text item to the interior of this element
+ *
+ * Params:
+ * item = the item you wish to append.
+ *
+ * Example:
+ * --------------
+ * Element element;
+ * element ~= new Text("hello");
+ * --------------
+ */
+ void opCatAssign(Text item) @safe pure
+ {
+ texts ~= item;
+ appendItem(item);
+ }
+
+ /**
+ * Append a CData item to the interior of this element
+ *
+ * Params:
+ * item = the item you wish to append.
+ *
+ * Example:
+ * --------------
+ * Element element;
+ * element ~= new CData("hello");
+ * --------------
+ */
+ void opCatAssign(CData item) @safe pure
+ {
+ cdatas ~= item;
+ appendItem(item);
+ }
+
+ /**
+ * Append a comment to the interior of this element
+ *
+ * Params:
+ * item = the item you wish to append.
+ *
+ * Example:
+ * --------------
+ * Element element;
+ * element ~= new Comment("hello");
+ * --------------
+ */
+ void opCatAssign(Comment item) @safe pure
+ {
+ comments ~= item;
+ appendItem(item);
+ }
+
+ /**
+ * Append a processing instruction to the interior of this element
+ *
+ * Params:
+ * item = the item you wish to append.
+ *
+ * Example:
+ * --------------
+ * Element element;
+ * element ~= new ProcessingInstruction("hello");
+ * --------------
+ */
+ void opCatAssign(ProcessingInstruction item) @safe pure
+ {
+ pis ~= item;
+ appendItem(item);
+ }
+
+ /**
+ * Append a complete element to the interior of this element
+ *
+ * Params:
+ * item = the item you wish to append.
+ *
+ * Example:
+ * --------------
+ * Element element;
+ * Element other = new Element("br");
+ * element ~= other;
+ * // appends element representing <br />
+ * --------------
+ */
+ void opCatAssign(Element item) @safe pure
+ {
+ elements ~= item;
+ appendItem(item);
+ }
+
+ private void appendItem(Item item) @safe pure
+ {
+ items ~= item;
+ if (tag.type == TagType.EMPTY && !item.isEmptyXML)
+ tag.type = TagType.START;
+ }
+
+ private void parse(ElementParser xml)
+ {
+ xml.onText = (string s) { opCatAssign(new Text(s)); };
+ xml.onCData = (string s) { opCatAssign(new CData(s)); };
+ xml.onComment = (string s) { opCatAssign(new Comment(s)); };
+ xml.onPI = (string s) { opCatAssign(new ProcessingInstruction(s)); };
+
+ xml.onStartTag[null] = (ElementParser xml)
+ {
+ auto e = new Element(xml.tag);
+ e.parse(xml);
+ opCatAssign(e);
+ };
+
+ xml.parse();
+ }
+
+ /**
+ * Compares two Elements for equality
+ *
+ * Example:
+ * --------------
+ * Element e1,e2;
+ * if (e1 == e2) { }
+ * --------------
+ */
+ override bool opEquals(scope const Object o) const
+ {
+ const element = toType!(const Element)(o);
+ immutable len = items.length;
+ if (len != element.items.length) return false;
+ foreach (i; 0 .. len)
+ {
+ if (!items[i].opEquals(element.items[i])) return false;
+ }
+ return true;
+ }
+
+ /**
+ * Compares two Elements
+ *
+ * You should rarely need to call this function. It exists so that Elements
+ * can be used as associative array keys.
+ *
+ * Example:
+ * --------------
+ * Element e1,e2;
+ * if (e1 < e2) { }
+ * --------------
+ */
+ override int opCmp(scope const Object o) @safe const
+ {
+ const element = toType!(const Element)(o);
+ for (uint i=0; ; ++i)
+ {
+ if (i == items.length && i == element.items.length) return 0;
+ if (i == items.length) return -1;
+ if (i == element.items.length) return 1;
+ if (!items[i].opEquals(element.items[i]))
+ return items[i].opCmp(element.items[i]);
+ }
+ }
+
+ /**
+ * Returns the hash of an Element
+ *
+ * You should rarely need to call this function. It exists so that Elements
+ * can be used as associative array keys.
+ */
+ override size_t toHash() scope const @safe
+ {
+ size_t hash = tag.toHash();
+ foreach (item;items) hash += item.toHash();
+ return hash;
+ }
+
+ const
+ {
+ /**
+ * Returns the decoded interior of an element.
+ *
+ * The element is assumed to contain text <i>only</i>. So, for
+ * example, given XML such as "&lt;title&gt;Good &amp;amp;
+ * Bad&lt;/title&gt;", will return "Good &amp; Bad".
+ *
+ * Params:
+ * mode = (optional) Mode to use for decoding. (Defaults to LOOSE).
+ *
+ * Throws: DecodeException if decode fails
+ */
+ string text(DecodeMode mode=DecodeMode.LOOSE)
+ {
+ string buffer;
+ foreach (item;items)
+ {
+ Text t = cast(Text) item;
+ if (t is null) throw new DecodeException(item.toString());
+ buffer ~= decode(t.toString(),mode);
+ }
+ return buffer;
+ }
+
+ /**
+ * Returns an indented string representation of this item
+ *
+ * Params:
+ * indent = (optional) number of spaces by which to indent this
+ * element. Defaults to 2.
+ */
+ override string[] pretty(uint indent=2) scope
+ {
+ import std.algorithm.searching : count;
+ import std.string : rightJustify;
+
+ if (isEmptyXML) return [ tag.toEmptyString() ];
+
+ if (items.length == 1)
+ {
+ auto t = cast(const(Text))(items[0]);
+ if (t !is null)
+ {
+ return [tag.toStartString() ~ t.toString() ~ tag.toEndString()];
+ }
+ }
+
+ string[] a = [ tag.toStartString() ];
+ foreach (item;items)
+ {
+ string[] b = item.pretty(indent);
+ foreach (s;b)
+ {
+ a ~= rightJustify(s,count(s) + indent);
+ }
+ }
+ a ~= tag.toEndString();
+ return a;
+ }
+
+ /**
+ * Returns the string representation of an Element
+ *
+ * Example:
+ * --------------
+ * auto element = new Element("br");
+ * writefln(element.toString()); // writes "<br />"
+ * --------------
+ */
+ override string toString() scope @safe
+ {
+ if (isEmptyXML) return tag.toEmptyString();
+
+ string buffer = tag.toStartString();
+ foreach (item;items) { buffer ~= item.toString(); }
+ buffer ~= tag.toEndString();
+ return buffer;
+ }
+
+ override @property @safe pure @nogc nothrow bool isEmptyXML() const scope { return items.length == 0; }
+ }
+}
+
+/**
+ * Tag types.
+ *
+ * $(DDOC_ENUM_MEMBERS START) Used for start tags
+ * $(DDOC_ENUM_MEMBERS END) Used for end tags
+ * $(DDOC_ENUM_MEMBERS EMPTY) Used for empty tags
+ *
+ */
+enum TagType { START, END, EMPTY }
+
+/**
+ * Class representing an XML tag.
+ *
+ * Standards: $(LINK2 http://www.w3.org/TR/1998/REC-xml-19980210, XML 1.0)
+ *
+ * The class invariant guarantees
+ * <ul>
+ * <li> that $(B type) is a valid enum TagType value</li>
+ * <li> that $(B name) consists of valid characters</li>
+ * <li> that each attribute name consists of valid characters</li>
+ * </ul>
+ */
+class Tag
+{
+ TagType type = TagType.START; /// Type of tag
+ string name; /// Tag name
+ string[string] attr; /// Associative array of attributes
+ private string tagString;
+
+ invariant()
+ {
+ string s;
+ string t;
+
+ assert(type == TagType.START
+ || type == TagType.END
+ || type == TagType.EMPTY);
+
+ s = name;
+ try { checkName(s,t); }
+ catch (Err e) { assert(false,"Invalid tag name:" ~ e.toString()); }
+
+ foreach (k,v;attr)
+ {
+ s = k;
+ try { checkName(s,t); }
+ catch (Err e)
+ { assert(false,"Invalid atrribute name:" ~ e.toString()); }
+ }
+ }
+
+ /**
+ * Constructs an instance of Tag with a specified name and type
+ *
+ * The constructor does not initialize the attributes. To initialize the
+ * attributes, you access the $(B attr) member variable.
+ *
+ * Params:
+ * name = the Tag's name
+ * type = (optional) the Tag's type. If omitted, defaults to
+ * TagType.START.
+ *
+ * Example:
+ * --------------
+ * auto tag = new Tag("img",Tag.EMPTY);
+ * tag.attr["src"] = "http://example.com/example.jpg";
+ * --------------
+ */
+ this(string name, TagType type=TagType.START) @safe pure
+ {
+ this.name = name;
+ this.type = type;
+ }
+
+ /* Private constructor (so don't ddoc this!)
+ *
+ * Constructs a Tag by parsing the string representation, e.g. "<html>".
+ *
+ * The string is passed by reference, and is advanced over all characters
+ * consumed.
+ *
+ * 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) @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;
+ 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] != '/')
+ {
+ 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,'=');
+ 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,'/'))
+ {
+ if (type == TagType.END) throw new TagException("");
+ type = TagType.EMPTY;
+ }
+ reqc(s,'>');
+ tagString.length = tagString.length - s.length;
+ }
+ catch (XMLException e)
+ {
+ tagString.length = tagString.length - s.length;
+ throw new TagException(tagString);
+ }
+ }
+
+ const
+ {
+ /**
+ * Compares two Tags for equality
+ *
+ * You should rarely need to call this function. It exists so that Tags
+ * can be used as associative array keys.
+ *
+ * Example:
+ * --------------
+ * Tag tag1,tag2
+ * if (tag1 == tag2) { }
+ * --------------
+ */
+ override bool opEquals(scope Object o)
+ {
+ const tag = toType!(const Tag)(o);
+ return
+ (name != tag.name) ? false : (
+ (attr != tag.attr) ? false : (
+ (type != tag.type) ? false : (
+ true )));
+ }
+
+ /**
+ * Compares two Tags
+ *
+ * Example:
+ * --------------
+ * Tag tag1,tag2
+ * if (tag1 < tag2) { }
+ * --------------
+ */
+ override int opCmp(Object o)
+ {
+ const tag = toType!(const Tag)(o);
+ // 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 ) :
+ ((type != tag.type) ? ( type < tag.type ? -1 : 1 ) :
+ 0 )));
+ }
+
+ /**
+ * Returns the hash of a Tag
+ *
+ * You should rarely need to call this function. It exists so that Tags
+ * can be used as associative array keys.
+ */
+ override size_t toHash()
+ {
+ return typeid(name).getHash(&name);
+ }
+
+ /**
+ * Returns the string representation of a Tag
+ *
+ * Example:
+ * --------------
+ * auto tag = new Tag("book",TagType.START);
+ * writefln(tag.toString()); // writes "<book>"
+ * --------------
+ */
+ override string toString() @safe
+ {
+ if (isEmpty) return toEmptyString();
+ return (isEnd) ? toEndString() : toStartString();
+ }
+
+ private
+ {
+ string toNonEndString() @safe
+ {
+ import std.format : format;
+
+ string s = "<" ~ name;
+ foreach (key,val;attr)
+ s ~= format(" %s=\"%s\"",key,encode(val));
+ return s;
+ }
+
+ string toStartString() @safe { return toNonEndString() ~ ">"; }
+
+ string toEndString() @safe { return "</" ~ name ~ ">"; }
+
+ string toEmptyString() @safe { return toNonEndString() ~ " />"; }
+ }
+
+ /**
+ * Returns true if the Tag is a start tag
+ *
+ * Example:
+ * --------------
+ * if (tag.isStart) { }
+ * --------------
+ */
+ @property bool isStart() @safe @nogc pure nothrow { return type == TagType.START; }
+
+ /**
+ * Returns true if the Tag is an end tag
+ *
+ * Example:
+ * --------------
+ * if (tag.isEnd) { }
+ * --------------
+ */
+ @property bool isEnd() @safe @nogc pure nothrow { return type == TagType.END; }
+
+ /**
+ * Returns true if the Tag is an empty tag
+ *
+ * Example:
+ * --------------
+ * if (tag.isEmpty) { }
+ * --------------
+ */
+ @property bool isEmpty() @safe @nogc pure nothrow { return type == TagType.EMPTY; }
+ }
+}
+
+/**
+ * Class representing a comment
+ */
+class Comment : Item
+{
+ private string content;
+
+ /**
+ * Construct a comment
+ *
+ * Params:
+ * content = the body of the comment
+ *
+ * Throws: CommentException if the comment body is illegal (contains "--"
+ * or exactly equals "-")
+ *
+ * Example:
+ * --------------
+ * auto item = new Comment("This is a comment");
+ * // constructs <!--This is a comment-->
+ * --------------
+ */
+ this(string content) @safe pure
+ {
+ import std.string : indexOf;
+
+ if (content == "-" || content.indexOf("--") != -1)
+ throw new CommentException(content);
+ this.content = content;
+ }
+
+ /**
+ * Compares two comments for equality
+ *
+ * Example:
+ * --------------
+ * Comment item1,item2;
+ * if (item1 == item2) { }
+ * --------------
+ */
+ override bool opEquals(scope const Object o) const
+ {
+ const item = toType!(const Item)(o);
+ const t = cast(const Comment) item;
+ return t !is null && content == t.content;
+ }
+
+ /**
+ * Compares two comments
+ *
+ * You should rarely need to call this function. It exists so that Comments
+ * can be used as associative array keys.
+ *
+ * Example:
+ * --------------
+ * Comment item1,item2;
+ * if (item1 < item2) { }
+ * --------------
+ */
+ override int opCmp(scope const Object o) scope const
+ {
+ const item = toType!(const Item)(o);
+ const t = cast(const Comment) item;
+ return t !is null && (content != t.content
+ ? (content < t.content ? -1 : 1 ) : 0 );
+ }
+
+ /**
+ * Returns the hash of a Comment
+ *
+ * You should rarely need to call this function. It exists so that Comments
+ * can be used as associative array keys.
+ */
+ override size_t toHash() scope const nothrow { return hash(content); }
+
+ /**
+ * Returns a string representation of this comment
+ */
+ override string toString() scope const @safe pure nothrow { return "<!--" ~ content ~ "-->"; }
+
+ override @property @safe @nogc pure nothrow scope 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("--"));
+}
+
+/**
+ * Class representing a Character Data section
+ */
+class CData : Item
+{
+ private string content;
+
+ /**
+ * Construct a character data section
+ *
+ * Params:
+ * content = the body of the character data segment
+ *
+ * Throws: CDataException if the segment body is illegal (contains "]]>")
+ *
+ * Example:
+ * --------------
+ * auto item = new CData("<b>hello</b>");
+ * // constructs <![CDATA[<b>hello</b>]]>
+ * --------------
+ */
+ this(string content) @safe pure
+ {
+ import std.string : indexOf;
+ if (content.indexOf("]]>") != -1) throw new CDataException(content);
+ this.content = content;
+ }
+
+ /**
+ * Compares two CDatas for equality
+ *
+ * Example:
+ * --------------
+ * CData item1,item2;
+ * if (item1 == item2) { }
+ * --------------
+ */
+ override bool opEquals(scope const Object o) const
+ {
+ const item = toType!(const Item)(o);
+ const t = cast(const CData) item;
+ return t !is null && content == t.content;
+ }
+
+ /**
+ * Compares two CDatas
+ *
+ * You should rarely need to call this function. It exists so that CDatas
+ * can be used as associative array keys.
+ *
+ * Example:
+ * --------------
+ * CData item1,item2;
+ * if (item1 < item2) { }
+ * --------------
+ */
+ override int opCmp(scope const Object o) scope const
+ {
+ const item = toType!(const Item)(o);
+ const t = cast(const CData) item;
+ return t !is null && (content != t.content
+ ? (content < t.content ? -1 : 1 ) : 0 );
+ }
+
+ /**
+ * Returns the hash of a CData
+ *
+ * You should rarely need to call this function. It exists so that CDatas
+ * can be used as associative array keys.
+ */
+ override size_t toHash() scope const nothrow { return hash(content); }
+
+ /**
+ * Returns a string representation of this CData section
+ */
+ override string toString() scope const @safe pure nothrow { return cdata ~ content ~ "]]>"; }
+
+ override @property @safe @nogc pure nothrow scope bool isEmptyXML() const { return false; } /// Returns false always
+}
+
+/**
+ * Class representing a text (aka Parsed Character Data) section
+ */
+class Text : Item
+{
+ private string content;
+
+ /**
+ * Construct a text (aka PCData) section
+ *
+ * Params:
+ * content = the text. This function encodes the text before
+ * insertion, so it is safe to insert any text
+ *
+ * Example:
+ * --------------
+ * auto Text = new CData("a < b");
+ * // constructs a &lt; b
+ * --------------
+ */
+ this(string content) @safe pure
+ {
+ this.content = encode(content);
+ }
+
+ /**
+ * Compares two text sections for equality
+ *
+ * Example:
+ * --------------
+ * Text item1,item2;
+ * if (item1 == item2) { }
+ * --------------
+ */
+ override bool opEquals(scope const Object o) const
+ {
+ const item = toType!(const Item)(o);
+ const t = cast(const Text) item;
+ return t !is null && content == t.content;
+ }
+
+ /**
+ * Compares two text sections
+ *
+ * You should rarely need to call this function. It exists so that Texts
+ * can be used as associative array keys.
+ *
+ * Example:
+ * --------------
+ * Text item1,item2;
+ * if (item1 < item2) { }
+ * --------------
+ */
+ override int opCmp(scope const Object o) scope const
+ {
+ const item = toType!(const Item)(o);
+ const t = cast(const Text) item;
+ return t !is null
+ && (content != t.content ? (content < t.content ? -1 : 1 ) : 0 );
+ }
+
+ /**
+ * Returns the hash of a text section
+ *
+ * You should rarely need to call this function. It exists so that Texts
+ * can be used as associative array keys.
+ */
+ override size_t toHash() scope const nothrow { return hash(content); }
+
+ /**
+ * Returns a string representation of this Text section
+ */
+ override string toString() scope const @safe @nogc pure nothrow { return content; }
+
+ /**
+ * Returns true if the content is the empty string
+ */
+ override @property @safe @nogc pure nothrow scope bool isEmptyXML() const { return content.length == 0; }
+}
+
+/**
+ * Class representing an XML Instruction section
+ */
+class XMLInstruction : Item
+{
+ private string content;
+
+ /**
+ * Construct an XML Instruction section
+ *
+ * Params:
+ * content = the body of the instruction segment
+ *
+ * Throws: XIException if the segment body is illegal (contains ">")
+ *
+ * Example:
+ * --------------
+ * auto item = new XMLInstruction("ATTLIST");
+ * // constructs <!ATTLIST>
+ * --------------
+ */
+ this(string content) @safe pure
+ {
+ import std.string : indexOf;
+ if (content.indexOf(">") != -1) throw new XIException(content);
+ this.content = content;
+ }
+
+ /**
+ * Compares two XML instructions for equality
+ *
+ * Example:
+ * --------------
+ * XMLInstruction item1,item2;
+ * if (item1 == item2) { }
+ * --------------
+ */
+ override bool opEquals(scope const Object o) const
+ {
+ const item = toType!(const Item)(o);
+ const t = cast(const XMLInstruction) item;
+ return t !is null && content == t.content;
+ }
+
+ /**
+ * Compares two XML instructions
+ *
+ * You should rarely need to call this function. It exists so that
+ * XmlInstructions can be used as associative array keys.
+ *
+ * Example:
+ * --------------
+ * XMLInstruction item1,item2;
+ * if (item1 < item2) { }
+ * --------------
+ */
+ override int opCmp(scope const Object o) scope const
+ {
+ const item = toType!(const Item)(o);
+ const t = cast(const XMLInstruction) item;
+ return t !is null
+ && (content != t.content ? (content < t.content ? -1 : 1 ) : 0 );
+ }
+
+ /**
+ * Returns the hash of an XMLInstruction
+ *
+ * You should rarely need to call this function. It exists so that
+ * XmlInstructions can be used as associative array keys.
+ */
+ override size_t toHash() scope const nothrow { return hash(content); }
+
+ /**
+ * Returns a string representation of this XmlInstruction
+ */
+ override string toString() scope const @safe pure nothrow { return "<!" ~ content ~ ">"; }
+
+ override @property @safe @nogc pure nothrow scope bool isEmptyXML() const { return false; } /// Returns false always
+}
+
+/**
+ * Class representing a Processing Instruction section
+ */
+class ProcessingInstruction : Item
+{
+ private string content;
+
+ /**
+ * Construct a Processing Instruction section
+ *
+ * Params:
+ * content = the body of the instruction segment
+ *
+ * Throws: PIException if the segment body is illegal (contains "?>")
+ *
+ * Example:
+ * --------------
+ * auto item = new ProcessingInstruction("php");
+ * // constructs <?php?>
+ * --------------
+ */
+ this(string content) @safe pure
+ {
+ import std.string : indexOf;
+ if (content.indexOf("?>") != -1) throw new PIException(content);
+ this.content = content;
+ }
+
+ /**
+ * Compares two processing instructions for equality
+ *
+ * Example:
+ * --------------
+ * ProcessingInstruction item1,item2;
+ * if (item1 == item2) { }
+ * --------------
+ */
+ override bool opEquals(scope const Object o) const
+ {
+ const item = toType!(const Item)(o);
+ const t = cast(const ProcessingInstruction) item;
+ return t !is null && content == t.content;
+ }
+
+ /**
+ * Compares two processing instructions
+ *
+ * You should rarely need to call this function. It exists so that
+ * ProcessingInstructions can be used as associative array keys.
+ *
+ * Example:
+ * --------------
+ * ProcessingInstruction item1,item2;
+ * if (item1 < item2) { }
+ * --------------
+ */
+ override int opCmp(scope const Object o) scope const
+ {
+ const item = toType!(const Item)(o);
+ const t = cast(const ProcessingInstruction) item;
+ return t !is null
+ && (content != t.content ? (content < t.content ? -1 : 1 ) : 0 );
+ }
+
+ /**
+ * Returns the hash of a ProcessingInstruction
+ *
+ * You should rarely need to call this function. It exists so that
+ * ProcessingInstructions can be used as associative array keys.
+ */
+ override size_t toHash() scope const nothrow { return hash(content); }
+
+ /**
+ * Returns a string representation of this ProcessingInstruction
+ */
+ override string toString() scope const @safe pure nothrow { return "<?" ~ content ~ "?>"; }
+
+ override @property @safe @nogc pure nothrow bool isEmptyXML() scope const { return false; } /// Returns false always
+}
+
+/**
+ * Abstract base class for XML items
+ */
+abstract class Item
+{
+ /// Compares with another Item of same type for equality
+ abstract override bool opEquals(scope const Object o) @safe const;
+
+ /// Compares with another Item of same type
+ abstract override int opCmp(scope const Object o) @safe const;
+
+ /// Returns the hash of this item
+ abstract override size_t toHash() @safe scope const;
+
+ /// Returns a string representation of this item
+ abstract override string toString() @safe scope const;
+
+ /**
+ * Returns an indented string representation of this item
+ *
+ * Params:
+ * indent = number of spaces by which to indent child elements
+ */
+ string[] pretty(uint indent) @safe scope 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 @safe @nogc pure nothrow bool isEmptyXML() scope const;
+}
+
+/**
+ * Class for parsing an XML Document.
+ *
+ * This is a subclass of ElementParser. Most of the useful functions are
+ * documented there.
+ *
+ * Standards: $(LINK2 http://www.w3.org/TR/1998/REC-xml-19980210, XML 1.0)
+ *
+ * Bugs:
+ * Currently only supports UTF documents.
+ *
+ * If there is an encoding attribute in the prolog, it is ignored.
+ *
+ */
+class DocumentParser : ElementParser
+{
+ string xmlText;
+
+ /**
+ * Constructs a DocumentParser.
+ *
+ * The input to this function MUST be valid XML.
+ * This is enforced by the function's in contract.
+ *
+ * Params:
+ * xmlText_ = the entire XML document as text
+ *
+ */
+ this(string xmlText_)
+ in
+ {
+ assert(xmlText_.length != 0);
+ try
+ {
+ // Confirm that the input is valid XML
+ check(xmlText_);
+ }
+ catch (CheckException e)
+ {
+ // And if it's not, tell the user why not
+ assert(false, "\n" ~ e.toString());
+ }
+ }
+ body
+ {
+ xmlText = xmlText_;
+ s = &xmlText;
+ super(); // Initialize everything
+ parse(); // Parse through the root tag (but not beyond)
+ }
+}
+
+@system unittest
+{
+ auto doc = new Document("<root><child><grandchild/></child></root>");
+ assert(doc.elements.length == 1);
+ assert(doc.elements[0].tag.name == "child");
+ assert(doc.items == doc.elements);
+}
+
+/**
+ * Class for parsing an XML element.
+ *
+ * Standards: $(LINK2 http://www.w3.org/TR/1998/REC-xml-19980210, XML 1.0)
+ *
+ * Note that you cannot construct instances of this class directly. You can
+ * construct a DocumentParser (which is a subclass of ElementParser), but
+ * otherwise, Instances of ElementParser will be created for you by the
+ * library, and passed your way via onStartTag handlers.
+ *
+ */
+class ElementParser
+{
+ alias Handler = void delegate(string);
+ alias ElementHandler = void delegate(in Element element);
+ alias ParserHandler = void delegate(ElementParser parser);
+
+ private
+ {
+ Tag tag_;
+ string elementStart;
+ string* s;
+
+ Handler commentHandler = null;
+ Handler cdataHandler = null;
+ Handler xiHandler = null;
+ Handler piHandler = null;
+ Handler rawTextHandler = null;
+ Handler textHandler = null;
+
+ // Private constructor for start tags
+ this(ElementParser parent) @safe @nogc pure nothrow
+ {
+ s = parent.s;
+ this();
+ tag_ = parent.tag_;
+ }
+
+ // Private constructor for empty tags
+ this(Tag tag, string* t) @safe @nogc pure nothrow
+ {
+ s = t;
+ this();
+ tag_ = tag;
+ }
+ }
+
+ /**
+ * The Tag at the start of the element being parsed. You can read this to
+ * determine the tag's name and attributes.
+ */
+ @property @safe @nogc pure nothrow const(Tag) tag() const { return tag_; }
+
+ /**
+ * Register a handler which will be called whenever a start tag is
+ * encountered which matches the specified name. You can also pass null as
+ * the name, in which case the handler will be called for any unmatched
+ * start tag.
+ *
+ * Example:
+ * --------------
+ * // Call this function whenever a <podcast> start tag is encountered
+ * onStartTag["podcast"] = (ElementParser xml)
+ * {
+ * // Your code here
+ * //
+ * // This is a a closure, so code here may reference
+ * // variables which are outside of this scope
+ * };
+ *
+ * // call myEpisodeStartHandler (defined elsewhere) whenever an <episode>
+ * // start tag is encountered
+ * onStartTag["episode"] = &myEpisodeStartHandler;
+ *
+ * // call delegate dg for all other start tags
+ * onStartTag[null] = dg;
+ * --------------
+ *
+ * This library will supply your function with a new instance of
+ * ElementHandler, which may be used to parse inside the element whose
+ * start tag was just found, or to identify the tag attributes of the
+ * element, etc.
+ *
+ * Note that your function will be called for both start tags and empty
+ * tags. That is, we make no distinction between &lt;br&gt;&lt;/br&gt;
+ * and &lt;br/&gt;.
+ */
+ ParserHandler[string] onStartTag;
+
+ /**
+ * Register a handler which will be called whenever an end tag is
+ * encountered which matches the specified name. You can also pass null as
+ * the name, in which case the handler will be called for any unmatched
+ * end tag.
+ *
+ * Example:
+ * --------------
+ * // Call this function whenever a </podcast> end tag is encountered
+ * onEndTag["podcast"] = (in Element e)
+ * {
+ * // Your code here
+ * //
+ * // This is a a closure, so code here may reference
+ * // variables which are outside of this scope
+ * };
+ *
+ * // call myEpisodeEndHandler (defined elsewhere) whenever an </episode>
+ * // end tag is encountered
+ * onEndTag["episode"] = &myEpisodeEndHandler;
+ *
+ * // call delegate dg for all other end tags
+ * onEndTag[null] = dg;
+ * --------------
+ *
+ * Note that your function will be called for both start tags and empty
+ * tags. That is, we make no distinction between &lt;br&gt;&lt;/br&gt;
+ * and &lt;br/&gt;.
+ */
+ ElementHandler[string] onEndTag;
+
+ protected this() @safe @nogc pure nothrow
+ {
+ elementStart = *s;
+ }
+
+ /**
+ * Register a handler which will be called whenever text is encountered.
+ *
+ * Example:
+ * --------------
+ * // Call this function whenever text is encountered
+ * onText = (string s)
+ * {
+ * // Your code here
+ *
+ * // The passed parameter s will have been decoded by the time you see
+ * // it, and so may contain any character.
+ * //
+ * // This is a a closure, so code here may reference
+ * // variables which are outside of this scope
+ * };
+ * --------------
+ */
+ @property @safe @nogc pure nothrow void onText(Handler handler) { textHandler = handler; }
+
+ /**
+ * Register an alternative handler which will be called whenever text
+ * is encountered. This differs from onText in that onText will decode
+ * the text, whereas onTextRaw will not. This allows you to make design
+ * choices, since onText will be more accurate, but slower, while
+ * onTextRaw will be faster, but less accurate. Of course, you can
+ * still call decode() within your handler, if you want, but you'd
+ * probably want to use onTextRaw only in circumstances where you
+ * know that decoding is unnecessary.
+ *
+ * Example:
+ * --------------
+ * // Call this function whenever text is encountered
+ * onText = (string s)
+ * {
+ * // Your code here
+ *
+ * // The passed parameter s will NOT have been decoded.
+ * //
+ * // This is a a closure, so code here may reference
+ * // variables which are outside of this scope
+ * };
+ * --------------
+ */
+ @safe @nogc pure nothrow void onTextRaw(Handler handler) { rawTextHandler = handler; }
+
+ /**
+ * Register a handler which will be called whenever a character data
+ * segment is encountered.
+ *
+ * Example:
+ * --------------
+ * // Call this function whenever a CData section is encountered
+ * onCData = (string s)
+ * {
+ * // Your code here
+ *
+ * // The passed parameter s does not include the opening <![CDATA[
+ * // nor closing ]]>
+ * //
+ * // This is a a closure, so code here may reference
+ * // variables which are outside of this scope
+ * };
+ * --------------
+ */
+ @property @safe @nogc pure nothrow void onCData(Handler handler) { cdataHandler = handler; }
+
+ /**
+ * Register a handler which will be called whenever a comment is
+ * encountered.
+ *
+ * Example:
+ * --------------
+ * // Call this function whenever a comment is encountered
+ * onComment = (string s)
+ * {
+ * // Your code here
+ *
+ * // The passed parameter s does not include the opening <!-- nor
+ * // closing -->
+ * //
+ * // This is a a closure, so code here may reference
+ * // variables which are outside of this scope
+ * };
+ * --------------
+ */
+ @property @safe @nogc pure nothrow void onComment(Handler handler) { commentHandler = handler; }
+
+ /**
+ * Register a handler which will be called whenever a processing
+ * instruction is encountered.
+ *
+ * Example:
+ * --------------
+ * // Call this function whenever a processing instruction is encountered
+ * onPI = (string s)
+ * {
+ * // Your code here
+ *
+ * // The passed parameter s does not include the opening <? nor
+ * // closing ?>
+ * //
+ * // This is a a closure, so code here may reference
+ * // variables which are outside of this scope
+ * };
+ * --------------
+ */
+ @property @safe @nogc pure nothrow void onPI(Handler handler) { piHandler = handler; }
+
+ /**
+ * Register a handler which will be called whenever an XML instruction is
+ * encountered.
+ *
+ * Example:
+ * --------------
+ * // Call this function whenever an XML instruction is encountered
+ * // (Note: XML instructions may only occur preceding the root tag of a
+ * // document).
+ * onPI = (string s)
+ * {
+ * // Your code here
+ *
+ * // The passed parameter s does not include the opening <! nor
+ * // closing >
+ * //
+ * // This is a a closure, so code here may reference
+ * // variables which are outside of this scope
+ * };
+ * --------------
+ */
+ @property @safe @nogc pure nothrow void onXI(Handler handler) { xiHandler = handler; }
+
+ /**
+ * Parse an XML element.
+ *
+ * Parsing will continue until the end of the current element. Any items
+ * encountered for which a handler has been registered will invoke that
+ * handler.
+ *
+ * Throws: various kinds of XMLException
+ */
+ void parse()
+ {
+ import std.algorithm.searching : startsWith;
+ import std.string : indexOf;
+
+ string t;
+ const Tag root = tag_;
+ Tag[string] startTags;
+ if (tag_ !is null) startTags[tag_.name] = tag_;
+
+ 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);
+ }
+ else if (startsWith(*s,"<![CDATA["))
+ {
+ chop(*s,9);
+ t = chop(*s,indexOf(*s,"]]>"));
+ if (cdataHandler.funcptr !is null) cdataHandler(t);
+ 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);
+ }
+ else if (startsWith(*s,"<?"))
+ {
+ chop(*s,2);
+ t = chop(*s,indexOf(*s,"?>"));
+ if (piHandler.funcptr !is null) piHandler(t);
+ chop(*s,2);
+ }
+ else if (startsWith(*s,"<"))
+ {
+ tag_ = new Tag(*s,true);
+ if (root is null)
+ return; // Return to constructor of derived class
+
+ if (tag_.isStart)
+ {
+ startTags[tag_.name] = tag_;
+
+ auto parser = new ElementParser(this);
+
+ auto handler = tag_.name in onStartTag;
+ if (handler !is null) (*handler)(parser);
+ else
+ {
+ handler = null in onStartTag;
+ if (handler !is null) (*handler)(parser);
+ }
+ }
+ else if (tag_.isEnd)
+ {
+ const startTag = startTags[tag_.name];
+ string text;
+
+ if (startTag.tagString.length == 0)
+ assert(0);
+
+ immutable(char)* p = startTag.tagString.ptr
+ + startTag.tagString.length;
+ immutable(char)* q = &tag_.tagString[0];
+ text = decode(p[0..(q-p)], DecodeMode.LOOSE);
+
+ auto element = new Element(startTag);
+ if (text.length != 0) element ~= new Text(text);
+
+ auto handler = tag_.name in onEndTag;
+ if (handler !is null) (*handler)(element);
+ else
+ {
+ handler = null in onEndTag;
+ if (handler !is null) (*handler)(element);
+ }
+
+ if (tag_.name == root.name) return;
+ }
+ else if (tag_.isEmpty)
+ {
+ Tag startTag = new Tag(tag_.name);
+
+ // 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;
+ // END FIX
+
+ // Handle the pretend start tag
+ string s2;
+ auto parser = new ElementParser(startTag,&s2);
+ auto handler1 = startTag.name in onStartTag;
+ if (handler1 !is null) (*handler1)(parser);
+ else
+ {
+ handler1 = null in onStartTag;
+ if (handler1 !is null) (*handler1)(parser);
+ }
+
+ // Handle the pretend end tag
+ auto element = new Element(startTag);
+ auto handler2 = tag_.name in onEndTag;
+ if (handler2 !is null) (*handler2)(element);
+ else
+ {
+ handler2 = null in onEndTag;
+ if (handler2 !is null) (*handler2)(element);
+ }
+ }
+ }
+ else
+ {
+ t = chop(*s,indexOf(*s,"<"));
+ if (rawTextHandler.funcptr !is null)
+ rawTextHandler(t);
+ else if (textHandler.funcptr !is null)
+ textHandler(decode(t,DecodeMode.LOOSE));
+ }
+ }
+ }
+
+ /**
+ * Returns that part of the element which has already been parsed
+ */
+ override string toString() const @nogc @safe pure nothrow
+ {
+ assert(elementStart.length >= s.length);
+ return elementStart[0 .. elementStart.length - s.length];
+ }
+
+}
+
+private
+{
+ template Check(string msg)
+ {
+ string old = s;
+
+ void fail() @safe pure
+ {
+ s = old;
+ throw new Err(s,msg);
+ }
+
+ void fail(Err e) @safe pure
+ {
+ s = old;
+ throw new Err(s,msg,e);
+ }
+
+ void fail(string msg2) @safe pure
+ {
+ fail(new Err(s,msg2));
+ }
+ }
+
+ void checkMisc(ref string s) @safe pure // rule 27
+ {
+ import std.algorithm.searching : startsWith;
+
+ mixin Check!("Misc");
+
+ try
+ {
+ if (s.startsWith("<!--")) { checkComment(s); }
+ else if (s.startsWith("<?")) { checkPI(s); }
+ else { checkSpace(s); }
+ }
+ catch (Err e) { fail(e); }
+ }
+
+ void checkDocument(ref string s) @safe pure // rule 1
+ {
+ mixin Check!("Document");
+ try
+ {
+ checkProlog(s);
+ checkElement(s);
+ star!(checkMisc)(s);
+ }
+ catch (Err e) { fail(e); }
+ }
+
+ void checkChars(ref string s) @safe pure // rule 2
+ {
+ // TO DO - Fix std.utf stride and decode functions, then use those
+ // instead
+ import std.format : format;
+
+ mixin Check!("Chars");
+
+ dchar c;
+ int n = -1;
+ foreach (int i,dchar d; s)
+ {
+ if (!isChar(d))
+ {
+ c = d;
+ n = i;
+ break;
+ }
+ }
+ if (n != -1)
+ {
+ s = s[n..$];
+ fail(format("invalid character: U+%04X",c));
+ }
+ }
+
+ void checkSpace(ref string s) @safe pure // rule 3
+ {
+ import std.algorithm.searching : countUntil;
+ import std.ascii : isWhite;
+ import std.utf : byCodeUnit;
+
+ mixin Check!("Whitespace");
+ ptrdiff_t i = s.byCodeUnit.countUntil!(a => !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) @safe pure // rule 5
+ {
+ mixin Check!("Name");
+
+ if (s.length == 0) fail();
+ int n;
+ foreach (int i,dchar c;s)
+ {
+ if (c == '_' || c == ':' || isLetter(c)) continue;
+ if (i == 0) fail();
+ if (c == '-' || c == '.' || isDigit(c)
+ || isCombiningChar(c) || isExtender(c)) continue;
+ n = i;
+ break;
+ }
+ name = s[0 .. n];
+ s = s[n..$];
+ }
+
+ 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();
+ char c = s[0];
+ if (c != '\u0022' && c != '\u0027')
+ fail("attribute value requires quotes");
+ s = s[1..$];
+ for (;;)
+ {
+ 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); }
+ }
+ s = s[1..$];
+ }
+
+ void checkCharData(ref string s) @safe pure // rule 14
+ {
+ import std.algorithm.searching : startsWith;
+
+ mixin Check!("CharData");
+
+ while (s.length != 0)
+ {
+ if (s.startsWith("&")) break;
+ if (s.startsWith("<")) break;
+ if (s.startsWith("]]>")) fail("]]> found within char data");
+ s = s[1..$];
+ }
+ }
+
+ void checkComment(ref string s) @safe pure // rule 15
+ {
+ import std.string : indexOf;
+
+ mixin Check!("Comment");
+
+ try { checkLiteral("<!--",s); } catch (Err e) { fail(e); }
+ ptrdiff_t n = s.indexOf("--");
+ if (n == -1) fail("unterminated comment");
+ s = s[n..$];
+ try { checkLiteral("-->",s); } catch (Err e) { fail(e); }
+ }
+
+ void checkPI(ref string s) @safe pure // rule 16
+ {
+ mixin Check!("PI");
+
+ try
+ {
+ checkLiteral("<?",s);
+ checkEnd("?>",s);
+ }
+ catch (Err e) { fail(e); }
+ }
+
+ void checkCDSect(ref string s) @safe pure // rule 18
+ {
+ mixin Check!("CDSect");
+
+ try
+ {
+ checkLiteral(cdata,s);
+ checkEnd("]]>",s);
+ }
+ catch (Err e) { fail(e); }
+ }
+
+ void checkProlog(ref string s) @safe pure // rule 22
+ {
+ mixin Check!("Prolog");
+
+ try
+ {
+ /* The XML declaration is optional
+ * http://www.w3.org/TR/2008/REC-xml-20081126/#NT-prolog
+ */
+ opt!(checkXMLDecl)(s);
+
+ star!(checkMisc)(s);
+ opt!(seq!(checkDocTypeDecl,star!(checkMisc)))(s);
+ }
+ catch (Err e) { fail(e); }
+ }
+
+ void checkXMLDecl(ref string s) @safe pure // rule 23
+ {
+ mixin Check!("XMLDecl");
+
+ try
+ {
+ checkLiteral("<?xml",s);
+ checkVersionInfo(s);
+ opt!(checkEncodingDecl)(s);
+ opt!(checkSDDecl)(s);
+ opt!(checkSpace)(s);
+ checkLiteral("?>",s);
+ }
+ catch (Err e) { fail(e); }
+ }
+
+ void checkVersionInfo(ref string s) @safe pure // rule 24
+ {
+ mixin Check!("VersionInfo");
+
+ try
+ {
+ checkSpace(s);
+ checkLiteral("version",s);
+ checkEq(s);
+ quoted!(checkVersionNum)(s);
+ }
+ catch (Err e) { fail(e); }
+ }
+
+ void checkEq(ref string s) @safe pure // rule 25
+ {
+ mixin Check!("Eq");
+
+ try
+ {
+ opt!(checkSpace)(s);
+ checkLiteral("=",s);
+ opt!(checkSpace)(s);
+ }
+ catch (Err e) { fail(e); }
+ }
+
+ void checkVersionNum(ref string s) @safe pure // rule 26
+ {
+ import std.algorithm.searching : countUntil;
+ import std.utf : byCodeUnit;
+
+ mixin Check!("VersionNum");
+
+ s = s[s.byCodeUnit.countUntil('\"') .. $];
+ if (s is old) fail();
+ }
+
+ void checkDocTypeDecl(ref string s) @safe pure // rule 28
+ {
+ mixin Check!("DocTypeDecl");
+
+ try
+ {
+ checkLiteral("<!DOCTYPE",s);
+ //
+ // TO DO -- ensure DOCTYPE is well formed
+ // (But not yet. That's one of our "future directions")
+ //
+ checkEnd(">",s);
+ }
+ catch (Err e) { fail(e); }
+ }
+
+ void checkSDDecl(ref string s) @safe pure // rule 32
+ {
+ import std.algorithm.searching : startsWith;
+
+ mixin Check!("SDDecl");
+
+ try
+ {
+ checkSpace(s);
+ checkLiteral("standalone",s);
+ checkEq(s);
+ }
+ catch (Err e) { fail(e); }
+
+ int n = 0;
+ if (s.startsWith("'yes'") || s.startsWith("\"yes\"")) n = 5;
+ else if (s.startsWith("'no'" ) || s.startsWith("\"no\"" )) n = 4;
+ else fail("standalone attribute value must be 'yes', \"yes\","~
+ " 'no' or \"no\"");
+ s = s[n..$];
+ }
+
+ 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); }
+
+ if (t == "STag")
+ {
+ try
+ {
+ checkContent(s);
+ t = s;
+ checkETag(s,ename);
+ }
+ catch (Err e) { fail(e); }
+
+ if (sname != ename)
+ {
+ s = t;
+ fail("end tag name \"" ~ ename
+ ~ "\" differs from start tag name \""~sname~"\"");
+ }
+ }
+ }
+
+ // rules 40 and 44
+ void checkTag(ref string s, out string type, out string name) @safe pure
+ {
+ mixin Check!("Tag");
+
+ try
+ {
+ type = "STag";
+ checkLiteral("<",s);
+ checkName(s,name);
+ star!(seq!(checkSpace,checkAttribute))(s);
+ opt!(checkSpace)(s);
+ if (s.length != 0 && s[0] == '/')
+ {
+ s = s[1..$];
+ type = "ETag";
+ }
+ checkLiteral(">",s);
+ }
+ catch (Err e) { fail(e); }
+ }
+
+ void checkAttribute(ref string s) @safe pure // rule 41
+ {
+ mixin Check!("Attribute");
+
+ try
+ {
+ string name;
+ checkName(s,name);
+ checkEq(s);
+ checkAttValue(s);
+ }
+ catch (Err e) { fail(e); }
+ }
+
+ void checkETag(ref string s, out string name) @safe pure // rule 42
+ {
+ mixin Check!("ETag");
+
+ try
+ {
+ checkLiteral("</",s);
+ checkName(s,name);
+ opt!(checkSpace)(s);
+ checkLiteral(">",s);
+ }
+ catch (Err e) { fail(e); }
+ }
+
+ void checkContent(ref string s) @safe pure // rule 43
+ {
+ import std.algorithm.searching : startsWith;
+
+ mixin Check!("Content");
+
+ try
+ {
+ while (s.length != 0)
+ {
+ old = s;
+ if (s.startsWith("&")) { checkReference(s); }
+ else if (s.startsWith("<!--")) { checkComment(s); }
+ else if (s.startsWith("<?")) { checkPI(s); }
+ else if (s.startsWith(cdata)) { checkCDSect(s); }
+ else if (s.startsWith("</")) { break; }
+ else if (s.startsWith("<")) { checkElement(s); }
+ else { checkCharData(s); }
+ }
+ }
+ catch (Err e) { fail(e); }
+ }
+
+ 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); }
+ int radix = 10;
+ if (s.length != 0 && s[0] == 'x')
+ {
+ s = s[1..$];
+ radix = 16;
+ }
+ if (s.length == 0) fail("unterminated character reference");
+ if (s[0] == ';')
+ fail("character reference must have at least one digit");
+ while (s.length != 0)
+ {
+ immutable char d = s[0];
+ int n = 0;
+ switch (d)
+ {
+ case 'F','f': ++n; goto case;
+ case 'E','e': ++n; goto case;
+ case 'D','d': ++n; goto case;
+ case 'C','c': ++n; goto case;
+ case 'B','b': ++n; goto case;
+ case 'A','a': ++n; goto case;
+ case '9': ++n; goto case;
+ case '8': ++n; goto case;
+ case '7': ++n; goto case;
+ case '6': ++n; goto case;
+ case '5': ++n; goto case;
+ case '4': ++n; goto case;
+ case '3': ++n; goto case;
+ case '2': ++n; goto case;
+ case '1': ++n; goto case;
+ case '0': break;
+ default: n = 100; break;
+ }
+ if (n >= radix) break;
+ c *= radix;
+ c += n;
+ s = s[1..$];
+ }
+ if (!isChar(c)) fail(format("U+%04X is not a legal character",c));
+ if (s.length == 0 || s[0] != ';') fail("expected ;");
+ else s = s[1..$];
+ }
+
+ void checkReference(ref string s) @safe pure // rule 67
+ {
+ import std.algorithm.searching : startsWith;
+
+ mixin Check!("Reference");
+
+ try
+ {
+ dchar c;
+ if (s.startsWith("&#")) checkCharRef(s,c);
+ else checkEntityRef(s);
+ }
+ catch (Err e) { fail(e); }
+ }
+
+ void checkEntityRef(ref string s) @safe pure // rule 68
+ {
+ mixin Check!("EntityRef");
+
+ try
+ {
+ string name;
+ checkLiteral("&",s);
+ checkName(s,name);
+ checkLiteral(";",s);
+ }
+ catch (Err e) { fail(e); }
+ }
+
+ 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");
+
+ s = s[s.byCodeUnit.countUntil!(a => !isAlpha(a)) .. $];
+ if (s is old) fail();
+ s = s[s.byCodeUnit.countUntil('\"', '\'') .. $];
+ }
+
+ void checkEncodingDecl(ref string s) @safe pure // rule 80
+ {
+ mixin Check!("EncodingDecl");
+
+ try
+ {
+ checkSpace(s);
+ checkLiteral("encoding",s);
+ checkEq(s);
+ quoted!(checkEncName)(s);
+ }
+ catch (Err e) { fail(e); }
+ }
+
+ // Helper functions
+
+ 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) @safe pure
+ {
+ import std.string : indexOf;
+ // Deliberately no mixin Check here.
+
+ auto n = s.indexOf(end);
+ if (n == -1) throw new Err(s,"Unable to find terminating \""~end~"\"");
+ s = s[n..$];
+ checkLiteral(end,s);
+ }
+
+ // Metafunctions -- none of these use mixin Check
+
+ void opt(alias f)(ref string s)
+ {
+ try { f(s); } catch (Err e) {}
+ }
+
+ void plus(alias f)(ref string s)
+ {
+ f(s);
+ star!(f)(s);
+ }
+
+ void star(alias f)(ref string s)
+ {
+ while (s.length != 0)
+ {
+ try { f(s); }
+ catch (Err e) { return; }
+ }
+ }
+
+ void quoted(alias f)(ref string s)
+ {
+ import std.string : startsWith;
+
+ if (s.startsWith("'"))
+ {
+ checkLiteral("'",s);
+ f(s);
+ checkLiteral("'",s);
+ }
+ else
+ {
+ checkLiteral("\"",s);
+ f(s);
+ checkLiteral("\"",s);
+ }
+ }
+
+ void seq(alias f,alias g)(ref string s)
+ {
+ f(s);
+ g(s);
+ }
+}
+
+/**
+ * Check an entire XML document for well-formedness
+ *
+ * Params:
+ * s = the document to be checked, passed as a string
+ *
+ * Throws: CheckException if the document is not well formed
+ *
+ * CheckException's toString() method will yield the complete hierarchy of
+ * 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) @safe pure
+{
+ try
+ {
+ checkChars(s);
+ checkDocument(s);
+ if (s.length != 0) throw new Err(s,"Junk found after document");
+ }
+ catch (Err e)
+ {
+ e.complete(s);
+ throw e;
+ }
+}
+
+@system pure unittest
+{
+ import std.string : indexOf;
+
+ try
+ {
+ check(q"[<?xml version="1.0"?>
+ <catalog>
+ <book id="bk101">
+ <author>Gambardella, Matthew</author>
+ <title>XML Developer's Guide</title>
+ <genre>Computer</genre>
+ <price>44.95</price>
+ <publish_date>2000-10-01</publish_date>
+ <description>An in-depth look at creating applications
+ with XML.</description>
+ </book>
+ <book id="bk102">
+ <author>Ralls, Kim</author>
+ <title>Midnight Rain</title>
+ <genre>Fantasy</genres>
+ <price>5.95</price>
+ <publish_date>2000-12-16</publish_date>
+ <description>A former architect battles corporate zombies,
+ an evil sorceress, and her own childhood to become queen
+ of the world.</description>
+ </book>
+ <book id="bk103">
+ <author>Corets, Eva</author>
+ <title>Maeve Ascendant</title>
+ <genre>Fantasy</genre>
+ <price>5.95</price>
+ <publish_date>2000-11-17</publish_date>
+ <description>After the collapse of a nanotechnology
+ society in England, the young survivors lay the
+ foundation for a new society.</description>
+ </book>
+ </catalog>
+ ]");
+ assert(false);
+ }
+ catch (CheckException e)
+ {
+ auto n = e.toString().indexOf("end tag name \"genres\" differs"~
+ " from start tag name \"genre\"");
+ assert(n != -1);
+ }
+}
+
+@system unittest
+{
+ string s = q"EOS
+<?xml version="1.0"?>
+<set>
+ <one>A</one>
+ <!-- comment -->
+ <two>B</two>
+</set>
+EOS";
+ try
+ {
+ check(s);
+ }
+ catch (CheckException e)
+ {
+ assert(0, e.toString());
+ }
+}
+
+@system unittest
+{
+ string test_xml = `<?xml version="1.0" encoding='UTF-8'?><r><stream:stream
+ xmlns:stream="http://etherx.'jabber'.org/streams"
+ xmlns="jabber:'client'" from='jid.pl' id="587a5767"
+ xml:lang="en" version="1.0" attr='a"b"c'>
+ </stream:stream></r>`;
+
+ 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
+<?xml version="1.0" encoding="utf-8"?> <Tests>
+ <Test thing="What &amp; Up">What &amp; Up Second</Test>
+</Tests>
+EOS";
+ auto xml = new DocumentParser(s);
+
+ xml.onStartTag["Test"] = (ElementParser xml) {
+ assert(xml.tag.attr["thing"] == "What & Up");
+ };
+
+ xml.onEndTag["Test"] = (in Element e) {
+ assert(e.text() == "What & Up Second");
+ };
+ xml.parse();
+}
+
+@system unittest
+{
+ string s = `<tag attr="&quot;value&gt;" />`;
+ auto doc = new Document(s);
+ assert(doc.toString() == s);
+}
+
+/** The base class for exceptions thrown by this module */
+class XMLException : Exception { this(string msg) @safe pure { super(msg); } }
+
+// Other exceptions
+
+/// Thrown during Comment constructor
+class CommentException : XMLException
+{ private this(string msg) @safe pure { super(msg); } }
+
+/// Thrown during CData constructor
+class CDataException : XMLException
+{ private this(string msg) @safe pure { super(msg); } }
+
+/// Thrown during XMLInstruction constructor
+class XIException : XMLException
+{ private this(string msg) @safe pure { super(msg); } }
+
+/// Thrown during ProcessingInstruction constructor
+class PIException : XMLException
+{ private this(string msg) @safe pure { super(msg); } }
+
+/// Thrown during Text constructor
+class TextException : XMLException
+{ private this(string msg) @safe pure { super(msg); } }
+
+/// Thrown during decode()
+class DecodeException : XMLException
+{ private this(string msg) @safe pure { super(msg); } }
+
+/// Thrown if comparing with wrong type
+class InvalidTypeException : XMLException
+{ private this(string msg) @safe pure { super(msg); } }
+
+/// Thrown when parsing for Tags
+class TagException : XMLException
+{ private this(string msg) @safe pure { super(msg); } }
+
+/**
+ * Thrown during check()
+ */
+class CheckException : XMLException
+{
+ CheckException err; /// Parent in hierarchy
+ private string tail;
+ /**
+ * Name of production rule which failed to parse,
+ * or specific error message
+ */
+ string msg;
+ 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) @safe pure
+ {
+ super(null);
+ this.tail = tail;
+ this.msg = msg;
+ this.err = err;
+ }
+
+ private void complete(string entire) @safe pure
+ {
+ import std.string : count, lastIndexOf;
+ import std.utf : toUTF32;
+
+ string head = entire[0..$-tail.length];
+ ptrdiff_t n = head.lastIndexOf('\n') + 1;
+ line = head.count("\n") + 1;
+ dstring t = toUTF32(head[n..$]);
+ column = t.length + 1;
+ if (err !is null) err.complete(entire);
+ }
+
+ 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;
+ s ~= '\n';
+ if (err !is null) s = err.toString() ~ s;
+ return s;
+ }
+}
+
+private alias Err = CheckException;
+
+// Private helper functions
+
+private
+{
+ inout(T) toType(T)(inout Object o)
+ {
+ T t = cast(T)(o);
+ if (t is null)
+ {
+ throw new InvalidTypeException("Attempt to compare a "
+ ~ T.stringof ~ " with an instance of another type");
+ }
+ return t;
+ }
+
+ string chop(ref string s, size_t n) @safe pure nothrow
+ {
+ if (n == -1) n = s.length;
+ string t = s[0 .. n];
+ s = s[n..$];
+ return t;
+ }
+
+ bool optc(ref string s, char c) @safe pure nothrow
+ {
+ immutable bool b = s.length != 0 && s[0] == c;
+ if (b) s = s[1..$];
+ return b;
+ }
+
+ 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;
+ }
+
+ // Definitions from the XML specification
+ immutable CharTable=[0x9,0x9,0xA,0xA,0xD,0xD,0x20,0xD7FF,0xE000,0xFFFD,
+ 0x10000,0x10FFFF];
+ immutable BaseCharTable=[0x0041,0x005A,0x0061,0x007A,0x00C0,0x00D6,0x00D8,
+ 0x00F6,0x00F8,0x00FF,0x0100,0x0131,0x0134,0x013E,0x0141,0x0148,0x014A,
+ 0x017E,0x0180,0x01C3,0x01CD,0x01F0,0x01F4,0x01F5,0x01FA,0x0217,0x0250,
+ 0x02A8,0x02BB,0x02C1,0x0386,0x0386,0x0388,0x038A,0x038C,0x038C,0x038E,
+ 0x03A1,0x03A3,0x03CE,0x03D0,0x03D6,0x03DA,0x03DA,0x03DC,0x03DC,0x03DE,
+ 0x03DE,0x03E0,0x03E0,0x03E2,0x03F3,0x0401,0x040C,0x040E,0x044F,0x0451,
+ 0x045C,0x045E,0x0481,0x0490,0x04C4,0x04C7,0x04C8,0x04CB,0x04CC,0x04D0,
+ 0x04EB,0x04EE,0x04F5,0x04F8,0x04F9,0x0531,0x0556,0x0559,0x0559,0x0561,
+ 0x0586,0x05D0,0x05EA,0x05F0,0x05F2,0x0621,0x063A,0x0641,0x064A,0x0671,
+ 0x06B7,0x06BA,0x06BE,0x06C0,0x06CE,0x06D0,0x06D3,0x06D5,0x06D5,0x06E5,
+ 0x06E6,0x0905,0x0939,0x093D,0x093D,0x0958,0x0961,0x0985,0x098C,0x098F,
+ 0x0990,0x0993,0x09A8,0x09AA,0x09B0,0x09B2,0x09B2,0x09B6,0x09B9,0x09DC,
+ 0x09DD,0x09DF,0x09E1,0x09F0,0x09F1,0x0A05,0x0A0A,0x0A0F,0x0A10,0x0A13,
+ 0x0A28,0x0A2A,0x0A30,0x0A32,0x0A33,0x0A35,0x0A36,0x0A38,0x0A39,0x0A59,
+ 0x0A5C,0x0A5E,0x0A5E,0x0A72,0x0A74,0x0A85,0x0A8B,0x0A8D,0x0A8D,0x0A8F,
+ 0x0A91,0x0A93,0x0AA8,0x0AAA,0x0AB0,0x0AB2,0x0AB3,0x0AB5,0x0AB9,0x0ABD,
+ 0x0ABD,0x0AE0,0x0AE0,0x0B05,0x0B0C,0x0B0F,0x0B10,0x0B13,0x0B28,0x0B2A,
+ 0x0B30,0x0B32,0x0B33,0x0B36,0x0B39,0x0B3D,0x0B3D,0x0B5C,0x0B5D,0x0B5F,
+ 0x0B61,0x0B85,0x0B8A,0x0B8E,0x0B90,0x0B92,0x0B95,0x0B99,0x0B9A,0x0B9C,
+ 0x0B9C,0x0B9E,0x0B9F,0x0BA3,0x0BA4,0x0BA8,0x0BAA,0x0BAE,0x0BB5,0x0BB7,
+ 0x0BB9,0x0C05,0x0C0C,0x0C0E,0x0C10,0x0C12,0x0C28,0x0C2A,0x0C33,0x0C35,
+ 0x0C39,0x0C60,0x0C61,0x0C85,0x0C8C,0x0C8E,0x0C90,0x0C92,0x0CA8,0x0CAA,
+ 0x0CB3,0x0CB5,0x0CB9,0x0CDE,0x0CDE,0x0CE0,0x0CE1,0x0D05,0x0D0C,0x0D0E,
+ 0x0D10,0x0D12,0x0D28,0x0D2A,0x0D39,0x0D60,0x0D61,0x0E01,0x0E2E,0x0E30,
+ 0x0E30,0x0E32,0x0E33,0x0E40,0x0E45,0x0E81,0x0E82,0x0E84,0x0E84,0x0E87,
+ 0x0E88,0x0E8A,0x0E8A,0x0E8D,0x0E8D,0x0E94,0x0E97,0x0E99,0x0E9F,0x0EA1,
+ 0x0EA3,0x0EA5,0x0EA5,0x0EA7,0x0EA7,0x0EAA,0x0EAB,0x0EAD,0x0EAE,0x0EB0,
+ 0x0EB0,0x0EB2,0x0EB3,0x0EBD,0x0EBD,0x0EC0,0x0EC4,0x0F40,0x0F47,0x0F49,
+ 0x0F69,0x10A0,0x10C5,0x10D0,0x10F6,0x1100,0x1100,0x1102,0x1103,0x1105,
+ 0x1107,0x1109,0x1109,0x110B,0x110C,0x110E,0x1112,0x113C,0x113C,0x113E,
+ 0x113E,0x1140,0x1140,0x114C,0x114C,0x114E,0x114E,0x1150,0x1150,0x1154,
+ 0x1155,0x1159,0x1159,0x115F,0x1161,0x1163,0x1163,0x1165,0x1165,0x1167,
+ 0x1167,0x1169,0x1169,0x116D,0x116E,0x1172,0x1173,0x1175,0x1175,0x119E,
+ 0x119E,0x11A8,0x11A8,0x11AB,0x11AB,0x11AE,0x11AF,0x11B7,0x11B8,0x11BA,
+ 0x11BA,0x11BC,0x11C2,0x11EB,0x11EB,0x11F0,0x11F0,0x11F9,0x11F9,0x1E00,
+ 0x1E9B,0x1EA0,0x1EF9,0x1F00,0x1F15,0x1F18,0x1F1D,0x1F20,0x1F45,0x1F48,
+ 0x1F4D,0x1F50,0x1F57,0x1F59,0x1F59,0x1F5B,0x1F5B,0x1F5D,0x1F5D,0x1F5F,
+ 0x1F7D,0x1F80,0x1FB4,0x1FB6,0x1FBC,0x1FBE,0x1FBE,0x1FC2,0x1FC4,0x1FC6,
+ 0x1FCC,0x1FD0,0x1FD3,0x1FD6,0x1FDB,0x1FE0,0x1FEC,0x1FF2,0x1FF4,0x1FF6,
+ 0x1FFC,0x2126,0x2126,0x212A,0x212B,0x212E,0x212E,0x2180,0x2182,0x3041,
+ 0x3094,0x30A1,0x30FA,0x3105,0x312C,0xAC00,0xD7A3];
+ immutable IdeographicTable=[0x3007,0x3007,0x3021,0x3029,0x4E00,0x9FA5];
+ immutable CombiningCharTable=[0x0300,0x0345,0x0360,0x0361,0x0483,0x0486,
+ 0x0591,0x05A1,0x05A3,0x05B9,0x05BB,0x05BD,0x05BF,0x05BF,0x05C1,0x05C2,
+ 0x05C4,0x05C4,0x064B,0x0652,0x0670,0x0670,0x06D6,0x06DC,0x06DD,0x06DF,
+ 0x06E0,0x06E4,0x06E7,0x06E8,0x06EA,0x06ED,0x0901,0x0903,0x093C,0x093C,
+ 0x093E,0x094C,0x094D,0x094D,0x0951,0x0954,0x0962,0x0963,0x0981,0x0983,
+ 0x09BC,0x09BC,0x09BE,0x09BE,0x09BF,0x09BF,0x09C0,0x09C4,0x09C7,0x09C8,
+ 0x09CB,0x09CD,0x09D7,0x09D7,0x09E2,0x09E3,0x0A02,0x0A02,0x0A3C,0x0A3C,
+ 0x0A3E,0x0A3E,0x0A3F,0x0A3F,0x0A40,0x0A42,0x0A47,0x0A48,0x0A4B,0x0A4D,
+ 0x0A70,0x0A71,0x0A81,0x0A83,0x0ABC,0x0ABC,0x0ABE,0x0AC5,0x0AC7,0x0AC9,
+ 0x0ACB,0x0ACD,0x0B01,0x0B03,0x0B3C,0x0B3C,0x0B3E,0x0B43,0x0B47,0x0B48,
+ 0x0B4B,0x0B4D,0x0B56,0x0B57,0x0B82,0x0B83,0x0BBE,0x0BC2,0x0BC6,0x0BC8,
+ 0x0BCA,0x0BCD,0x0BD7,0x0BD7,0x0C01,0x0C03,0x0C3E,0x0C44,0x0C46,0x0C48,
+ 0x0C4A,0x0C4D,0x0C55,0x0C56,0x0C82,0x0C83,0x0CBE,0x0CC4,0x0CC6,0x0CC8,
+ 0x0CCA,0x0CCD,0x0CD5,0x0CD6,0x0D02,0x0D03,0x0D3E,0x0D43,0x0D46,0x0D48,
+ 0x0D4A,0x0D4D,0x0D57,0x0D57,0x0E31,0x0E31,0x0E34,0x0E3A,0x0E47,0x0E4E,
+ 0x0EB1,0x0EB1,0x0EB4,0x0EB9,0x0EBB,0x0EBC,0x0EC8,0x0ECD,0x0F18,0x0F19,
+ 0x0F35,0x0F35,0x0F37,0x0F37,0x0F39,0x0F39,0x0F3E,0x0F3E,0x0F3F,0x0F3F,
+ 0x0F71,0x0F84,0x0F86,0x0F8B,0x0F90,0x0F95,0x0F97,0x0F97,0x0F99,0x0FAD,
+ 0x0FB1,0x0FB7,0x0FB9,0x0FB9,0x20D0,0x20DC,0x20E1,0x20E1,0x302A,0x302F,
+ 0x3099,0x3099,0x309A,0x309A];
+ immutable DigitTable=[0x0030,0x0039,0x0660,0x0669,0x06F0,0x06F9,0x0966,
+ 0x096F,0x09E6,0x09EF,0x0A66,0x0A6F,0x0AE6,0x0AEF,0x0B66,0x0B6F,0x0BE7,
+ 0x0BEF,0x0C66,0x0C6F,0x0CE6,0x0CEF,0x0D66,0x0D6F,0x0E50,0x0E59,0x0ED0,
+ 0x0ED9,0x0F20,0x0F29];
+ immutable ExtenderTable=[0x00B7,0x00B7,0x02D0,0x02D0,0x02D1,0x02D1,0x0387,
+ 0x0387,0x0640,0x0640,0x0E46,0x0E46,0x0EC6,0x0EC6,0x3005,0x3005,0x3031,
+ 0x3035,0x309D,0x309E,0x30FC,0x30FE];
+
+ 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];
+ }
+ else if (c > table[m+1])
+ {
+ table = table[m+2..$];
+ }
+ else return true;
+ }
+ return false;
+ }
+
+ string startOf(string s) @safe nothrow pure
+ {
+ string r;
+ foreach (char c;s)
+ {
+ r ~= (c < 0x20 || c > 0x7F) ? '.' : c;
+ if (r.length >= 40) { r ~= "___"; break; }
+ }
+ return r;
+ }
+
+ void exit(string s=null)
+ {
+ throw new XMLException(s);
+ }
+}
diff --git a/libphobos/src/std/zip.d b/libphobos/src/std/zip.d
new file mode 100644
index 0000000..db47dde
--- /dev/null
+++ b/libphobos/src/std/zip.d
@@ -0,0 +1,990 @@
+// Written in the D programming language.
+
+/**
+ * Read/write data in the $(LINK2 http://www.info-zip.org, _zip archive) format.
+ * Makes use of the etc.c.zlib compression library.
+ *
+ * Bugs:
+ * $(UL
+ * $(LI Multi-disk zips not supported.)
+ * $(LI Only Zip version 20 formats are supported.)
+ * $(LI Only supports compression modes 0 (no compression) and 8 (deflate).)
+ * $(LI Does not support encryption.)
+ * $(LI $(BUGZILLA 592))
+ * $(LI $(BUGZILLA 2137))
+ * )
+ *
+ * Example:
+ * ---
+// Read existing zip file.
+import std.digest.crc, std.file, std.stdio, std.zip;
+
+void main(string[] args)
+{
+ // read a zip file into memory
+ auto zip = new ZipArchive(read(args[1]));
+ writeln("Archive: ", args[1]);
+ writefln("%-10s %-8s Name", "Length", "CRC-32");
+ // iterate over all zip members
+ foreach (name, am; zip.directory)
+ {
+ // print some data about each member
+ writefln("%10s %08x %s", am.expandedSize, am.crc32, name);
+ assert(am.expandedData.length == 0);
+ // decompress the archive member
+ zip.expand(am);
+ 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: $(HTTP www.boost.org/LICENSE_1_0.txt, Boost License 1.0).
+ * Authors: $(HTTP digitalmars.com, Walter Bright)
+ * Source: $(PHOBOSSRC std/_zip.d)
+ */
+
+/* Copyright Digital Mars 2000 - 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.zip;
+
+//debug=print;
+
+/** Thrown on error.
+ */
+class ZipException : Exception
+{
+ this(string msg) @safe
+ {
+ super("ZipException: " ~ msg);
+ }
+}
+
+/**
+ * Compression method used by ArchiveMember
+ */
+enum CompressionMethod : ushort
+{
+ none = 0, /// No compression, just archiving
+ deflate = 8 /// Deflate algorithm. Use zlib library to compress
+}
+
+/**
+ * A member of the ZipArchive.
+ */
+final class ArchiveMember
+{
+ import std.conv : to, octal;
+ import std.datetime.systime : DosFileTime, SysTime, SysTimeToDosFileTime;
+
+ /**
+ * Read/Write: Usually the file name of the archive member; it is used to
+ * index the archive directory for the member. Each member must have a unique
+ * name[]. Do not change without removing member from the directory first.
+ */
+ string name;
+
+ ubyte[] extra; /// Read/Write: extra data for this member.
+ string comment; /// Read/Write: comment associated with this member.
+
+ private ubyte[] _compressedData;
+ private ubyte[] _expandedData;
+ private uint offset;
+ private uint _crc32;
+ private uint _compressedSize;
+ private uint _expandedSize;
+ private CompressionMethod _compressionMethod;
+ private ushort _madeVersion = 20;
+ private ushort _extractVersion = 20;
+ 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
+
+ @property ushort extractVersion() { return _extractVersion; } /// Read Only
+ @property uint crc32() { return _crc32; } /// Read Only: cyclic redundancy check (CRC) value
+
+ /// Read Only: size of data of member in compressed form.
+ @property uint compressedSize() { return _compressedSize; }
+
+ /// Read Only: size of data of member in expanded form.
+ @property uint expandedSize() { return _expandedSize; }
+ @property ushort diskNumber() { return _diskNumber; } /// Read Only: should be 0.
+
+ /// Read Only: data of member in compressed form.
+ @property ubyte[] compressedData() { return _compressedData; }
+
+ /// Read data of member in uncompressed form.
+ @property ubyte[] expandedData() { return _expandedData; }
+
+ /// Write data of member in uncompressed form.
+ @property @safe void expandedData(ubyte[] ed)
+ {
+ _expandedData = ed;
+ _expandedSize = to!uint(_expandedData.length);
+
+ // Clean old compressed data, if any
+ _compressedData.length = 0;
+ _compressedSize = 0;
+ }
+
+ /**
+ * Set the OS specific file attributes, as obtained by
+ * $(REF getAttributes, std,file) or $(REF DirEntry.attributes, std,file), for this archive member.
+ */
+ @property @safe void fileAttributes(uint attr)
+ {
+ version (Posix)
+ {
+ _externalAttributes = (attr & 0xFFFF) << 16;
+ _madeVersion &= 0x00FF;
+ _madeVersion |= 0x0300; // attributes are in UNIX format
+ }
+ else version (Windows)
+ {
+ _externalAttributes = attr;
+ _madeVersion &= 0x00FF; // attributes are in MS-DOS and OS/2 format
+ }
+ else
+ {
+ static assert(0, "Unimplemented platform");
+ }
+ }
+
+ version (Posix) @safe unittest
+ {
+ auto am = new ArchiveMember();
+ am.fileAttributes = octal!100644;
+ assert(am._externalAttributes == octal!100644 << 16);
+ assert((am._madeVersion & 0xFF00) == 0x0300);
+ }
+
+ /**
+ * Get the OS specific file attributes for the archive member.
+ *
+ * Returns: The file attributes or 0 if the file attributes were
+ * encoded for an incompatible OS (Windows vs. Posix).
+ *
+ */
+ @property uint fileAttributes() const
+ {
+ version (Posix)
+ {
+ if ((_madeVersion & 0xFF00) == 0x0300)
+ return _externalAttributes >> 16;
+ return 0;
+ }
+ else version (Windows)
+ {
+ if ((_madeVersion & 0xFF00) == 0x0000)
+ return _externalAttributes;
+ return 0;
+ }
+ else
+ {
+ static assert(0, "Unimplemented platform");
+ }
+ }
+
+ /// Set the last modification time for this member.
+ @property void time(SysTime time)
+ {
+ _time = SysTimeToDosFileTime(time);
+ }
+
+ /// ditto
+ @property void time(DosFileTime time)
+ {
+ _time = time;
+ }
+
+ /// Get the last modification time for this member.
+ @property DosFileTime time() const
+ {
+ return _time;
+ }
+
+ /**
+ * Read compression method used for this member
+ * See_Also:
+ * CompressionMethod
+ **/
+ @property @safe CompressionMethod compressionMethod() { return _compressionMethod; }
+
+ /**
+ * Write compression method used for this member
+ * See_Also:
+ * CompressionMethod
+ **/
+ @property void compressionMethod(CompressionMethod cm)
+ {
+ if (cm == _compressionMethod) return;
+
+ if (_compressedSize > 0)
+ throw new ZipException("Can't change compression method for a compressed element");
+
+ _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()
+ {
+ printf("name = '%.*s'\n", name.length, name.ptr);
+ printf("\tcomment = '%.*s'\n", comment.length, comment.ptr);
+ printf("\tmadeVersion = x%04x\n", _madeVersion);
+ printf("\textractVersion = x%04x\n", extractVersion);
+ printf("\tflags = x%04x\n", flags);
+ printf("\tcompressionMethod = %d\n", compressionMethod);
+ printf("\ttime = %d\n", time);
+ printf("\tcrc32 = x%08x\n", crc32);
+ printf("\texpandedSize = %d\n", expandedSize);
+ printf("\tcompressedSize = %d\n", compressedSize);
+ printf("\tinternalAttributes = x%04x\n", internalAttributes);
+ printf("\texternalAttributes = x%08x\n", externalAttributes);
+ printf("\tindex = x%08x\n", index);
+ }
+ }
+}
+
+/**
+ * Object representing the entire archive.
+ * ZipArchives are collections of ArchiveMembers.
+ */
+final class ZipArchive
+{
+ import std.algorithm.comparison : max;
+ import std.bitmanip : littleEndianToNative, nativeToLittleEndian;
+ import std.conv : to;
+ import std.datetime.systime : DosFileTime;
+
+ string comment; /// Read/Write: the archive comment. Must be less than 65536 bytes in length.
+
+ private ubyte[] _data;
+ private uint endrecOffset;
+
+ private uint _diskNumber;
+ private uint _diskStartDir;
+ private uint _numEntries;
+ private uint _totalEntries;
+ private bool _isZip64;
+ static const ushort zip64ExtractVersion = 45;
+ static const int digiSignLength = 6;
+ static const int eocd64LocLength = 20;
+ static const int eocd64Length = 56;
+
+ /// Read Only: array representing the entire contents of the archive.
+ @property @safe ubyte[] data() { return _data; }
+
+ /// Read Only: 0 since multi-disk zip archives are not supported.
+ @property @safe uint diskNumber() { return _diskNumber; }
+
+ /// Read Only: 0 since multi-disk zip archives are not supported
+ @property @safe uint diskStartDir() { return _diskStartDir; }
+
+ /// Read Only: number of ArchiveMembers in the directory.
+ @property @safe uint numEntries() { return _numEntries; }
+ @property @safe uint totalEntries() { return _totalEntries; } /// ditto
+
+ /// True when the archive is in Zip64 format.
+ @property @safe bool isZip64() { return _isZip64; }
+
+ /// Set this to true to force building a Zip64 archive.
+ @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:
+ * Example:
+ * --------------------
+ * ZipArchive archive = new ZipArchive(data);
+ * foreach (ArchiveMember am; archive.directory)
+ * {
+ * writefln("member name is '%s'", am.name);
+ * }
+ * --------------------
+ */
+ @property @safe ArchiveMember[string] directory() { return _directory; }
+
+ private ArchiveMember[string] _directory;
+
+ debug (print)
+ {
+ @safe void print()
+ {
+ printf("\tdiskNumber = %u\n", diskNumber);
+ printf("\tdiskStartDir = %u\n", diskStartDir);
+ printf("\tnumEntries = %u\n", numEntries);
+ printf("\ttotalEntries = %u\n", totalEntries);
+ printf("\tcomment = '%.*s'\n", comment.length, comment.ptr);
+ }
+ }
+
+ /* ============ Creating a new archive =================== */
+
+ /** Constructor to use when creating a new archive.
+ */
+ this() @safe
+ {
+ }
+
+ /** Add de to the archive. The file is compressed on the fly.
+ */
+ @safe void addMember(ArchiveMember de)
+ {
+ _directory[de.name] = de;
+ if (!de._compressedData.length)
+ {
+ switch (de.compressionMethod)
+ {
+ case CompressionMethod.none:
+ de._compressedData = de._expandedData;
+ break;
+
+ case CompressionMethod.deflate:
+ import std.zlib : compress;
+ () @trusted
+ {
+ de._compressedData = cast(ubyte[]) compress(cast(void[]) de._expandedData);
+ }();
+ de._compressedData = de._compressedData[2 .. de._compressedData.length - 4];
+ break;
+
+ default:
+ throw new ZipException("unsupported compression method");
+ }
+
+ de._compressedSize = to!uint(de._compressedData.length);
+ import std.zlib : crc32;
+ () @trusted { de._crc32 = crc32(0, cast(void[]) de._expandedData); }();
+ }
+ assert(de._compressedData.length == de._compressedSize);
+ }
+
+ /** Delete de from the archive.
+ */
+ @safe void deleteMember(ArchiveMember de)
+ {
+ _directory.remove(de.name);
+ }
+
+ /**
+ * Construct an archive out of the current members of the archive.
+ *
+ * Fills in the properties data[], diskNumber, diskStartDir, numEntries,
+ * totalEntries, and directory[].
+ * For each ArchiveMember, fills in properties crc32, compressedSize,
+ * compressedData[].
+ *
+ * Returns: array representing the entire archive.
+ */
+ void[] build()
+ {
+ import std.algorithm.sorting : sort;
+ uint i;
+ uint directoryOffset;
+
+ if (comment.length > 0xFFFF)
+ throw new ZipException("archive comment longer than 65535");
+
+ // Compress each member; compute size
+ uint archiveSize = 0;
+ uint directorySize = 0;
+ auto directory = _directory.values().sort!((x, y) => x.index < y.index).release;
+ foreach (ArchiveMember de; directory)
+ {
+ if (to!ulong(archiveSize) + 30 + de.name.length + de.extra.length + de.compressedSize
+ + directorySize + 46 + de.name.length + de.extra.length + de.comment.length
+ + 22 + comment.length + eocd64LocLength + eocd64Length > uint.max)
+ throw new ZipException("zip files bigger than 4 GB are unsupported");
+
+ archiveSize += 30 + de.name.length +
+ de.extra.length +
+ de.compressedSize;
+ directorySize += 46 + de.name.length +
+ de.extra.length +
+ de.comment.length;
+ }
+
+ if (!isZip64 && _directory.length > ushort.max)
+ _isZip64 = true;
+ uint dataSize = archiveSize + directorySize + 22 + cast(uint) comment.length;
+ if (isZip64)
+ dataSize += eocd64LocLength + eocd64Length;
+
+ _data = new ubyte[dataSize];
+
+ // Populate the data[]
+
+ // Store each archive member
+ i = 0;
+ 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 + 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);
+ i += 30;
+
+ _data[i .. i + de.name.length] = (cast(ubyte[]) de.name)[];
+ i += de.name.length;
+ _data[i .. i + de.extra.length] = (cast(ubyte[]) de.extra)[];
+ i += de.extra.length;
+ _data[i .. i + de.compressedSize] = de.compressedData[];
+ i += de.compressedSize;
+ }
+
+ // Write directory
+ directoryOffset = i;
+ _numEntries = 0;
+ 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 + 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 + 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)[];
+ i += de.name.length;
+ _data[i .. i + de.extra.length] = (cast(ubyte[]) de.extra)[];
+ i += de.extra.length;
+ _data[i .. i + de.comment.length] = (cast(ubyte[]) de.comment)[];
+ i += de.comment.length;
+ _numEntries++;
+ }
+ _totalEntries = numEntries;
+
+ if (isZip64)
+ {
+ // Write zip64 end of central directory record
+ uint eocd64Offset = i;
+ _data[i .. i + 4] = cast(ubyte[])"PK\x06\x06";
+ putUlong (i + 4, eocd64Length - 12);
+ putUshort(i + 12, zip64ExtractVersion);
+ putUshort(i + 14, zip64ExtractVersion);
+ putUint (i + 16, diskNumber);
+ putUint (i + 20, diskStartDir);
+ putUlong (i + 24, numEntries);
+ putUlong (i + 32, totalEntries);
+ putUlong (i + 40, directorySize);
+ putUlong (i + 48, directoryOffset);
+ i += eocd64Length;
+
+ // Write zip64 end of central directory record locator
+ _data[i .. i + 4] = cast(ubyte[])"PK\x06\x07";
+ putUint (i + 4, diskNumber);
+ putUlong (i + 8, eocd64Offset);
+ putUint (i + 16, 1);
+ i += eocd64LocLength;
+ }
+
+ // 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));
+ putUint (i + 12, directorySize);
+ putUint (i + 16, directoryOffset);
+ 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)[];
+
+ return cast(void[]) data;
+ }
+
+ /* ============ Reading an existing archive =================== */
+
+ /**
+ * Constructor to use when reading an existing archive.
+ *
+ * Fills in the properties data[], diskNumber, diskStartDir, numEntries,
+ * totalEntries, comment[], and directory[].
+ * For each ArchiveMember, fills in
+ * properties madeVersion, extractVersion, flags, compressionMethod, time,
+ * crc32, compressedSize, expandedSize, compressedData[], diskNumber,
+ * internalAttributes, externalAttributes, name[], extra[], comment[].
+ * Use expand() to get the expanded data for each ArchiveMember.
+ *
+ * Params:
+ * buffer = the entire contents of the archive.
+ */
+
+ this(void[] buffer)
+ { uint iend;
+ uint i;
+ int endcommentlength;
+ uint directorySize;
+ uint directoryOffset;
+
+ this._data = cast(ubyte[]) buffer;
+
+ if (data.length > uint.max - 2)
+ throw new ZipException("zip files bigger than 4 GB are unsupported");
+
+ // Find 'end record index' by searching backwards for signature
+ 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)
+ throw new ZipException("no end record");
+
+ if (_data[i .. i + 4] == cast(ubyte[])"PK\x05\x06")
+ {
+ endcommentlength = getUshort(i + 20);
+ if (i + 22 + endcommentlength > data.length
+ || i + 22 + endcommentlength < i)
+ continue;
+ comment = cast(string)(_data[i + 22 .. i + 22 + endcommentlength]);
+ endrecOffset = i;
+
+ uint k = i - eocd64LocLength;
+ if (k < i && _data[k .. k + 4] == cast(ubyte[])"PK\x06\x07")
+ {
+ _isZip64 = true;
+ i = k;
+ }
+
+ break;
+ }
+ }
+
+ if (isZip64)
+ {
+ // Read Zip64 record data
+ ulong eocdOffset = getUlong(i + 8);
+ if (eocdOffset + eocd64Length > _data.length)
+ throw new ZipException("corrupted directory");
+
+ i = to!uint(eocdOffset);
+ if (_data[i .. i + 4] != cast(ubyte[])"PK\x06\x06")
+ throw new ZipException("invalid Zip EOCD64 signature");
+
+ ulong eocd64Size = getUlong(i + 4);
+ if (eocd64Size + i - 12 > data.length)
+ throw new ZipException("invalid Zip EOCD64 size");
+
+ _diskNumber = getUint(i + 16);
+ _diskStartDir = getUint(i + 20);
+
+ ulong numEntriesUlong = getUlong(i + 24);
+ ulong totalEntriesUlong = getUlong(i + 32);
+ ulong directorySizeUlong = getUlong(i + 40);
+ ulong directoryOffsetUlong = getUlong(i + 48);
+
+ if (numEntriesUlong > uint.max)
+ throw new ZipException("supposedly more than 4294967296 files in archive");
+
+ if (numEntriesUlong != totalEntriesUlong)
+ throw new ZipException("multiple disk zips not supported");
+
+ if (directorySizeUlong > i || directoryOffsetUlong > i
+ || directorySizeUlong + directoryOffsetUlong > i)
+ throw new ZipException("corrupted directory");
+
+ _numEntries = to!uint(numEntriesUlong);
+ _totalEntries = to!uint(totalEntriesUlong);
+ directorySize = to!uint(directorySizeUlong);
+ directoryOffset = to!uint(directoryOffsetUlong);
+ }
+ else
+ {
+ // Read end record data
+ _diskNumber = getUshort(i + 4);
+ _diskStartDir = getUshort(i + 6);
+
+ _numEntries = getUshort(i + 8);
+ _totalEntries = getUshort(i + 10);
+
+ if (numEntries != totalEntries)
+ throw new ZipException("multiple disk zips not supported");
+
+ directorySize = getUint(i + 12);
+ directoryOffset = getUint(i + 16);
+
+ if (directoryOffset + directorySize > i)
+ throw new ZipException("corrupted directory");
+ }
+
+ i = directoryOffset;
+ for (int n = 0; n < numEntries; n++)
+ {
+ /* The format of an entry is:
+ * 'PK' 1, 2
+ * directory info
+ * path
+ * extra data
+ * comment
+ */
+
+ uint namelen;
+ uint extralen;
+ uint commentlen;
+
+ 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._crc32 = getUint(i + 16);
+ de._compressedSize = getUint(i + 20);
+ de._expandedSize = getUint(i + 24);
+ namelen = getUshort(i + 28);
+ extralen = getUshort(i + 30);
+ commentlen = getUshort(i + 32);
+ de._diskNumber = getUshort(i + 34);
+ de.internalAttributes = getUshort(i + 36);
+ de._externalAttributes = getUint(i + 38);
+ de.offset = getUint(i + 42);
+ i += 46;
+
+ if (i + namelen + extralen + commentlen > directoryOffset + directorySize)
+ throw new ZipException("invalid directory entry 2");
+
+ de.name = cast(string)(_data[i .. i + namelen]);
+ i += namelen;
+ de.extra = _data[i .. i + extralen];
+ i += extralen;
+ de.comment = cast(string)(_data[i .. i + commentlen]);
+ i += commentlen;
+
+ immutable uint dataOffset = de.offset + 30 + namelen + extralen;
+ if (dataOffset + de.compressedSize > endrecOffset)
+ throw new ZipException("Invalid directory entry offset or size.");
+ de._compressedData = _data[dataOffset .. dataOffset + de.compressedSize];
+
+ _directory[de.name] = de;
+
+ }
+ if (i != directoryOffset + directorySize)
+ throw new ZipException("invalid directory entry 3");
+ }
+
+ /*****
+ * Decompress the contents of archive member de and return the expanded
+ * data.
+ *
+ * Fills in properties extractVersion, flags, compressionMethod, time,
+ * crc32, compressedSize, expandedSize, expandedData[], name[], extra[].
+ */
+ ubyte[] expand(ArchiveMember de)
+ { uint namelen;
+ uint extralen;
+
+ if (_data[de.offset .. de.offset + 4] != cast(ubyte[])"PK\x03\x04")
+ throw new ZipException("invalid directory entry 4");
+
+ // 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._crc32 = getUint(de.offset + 14);
+ de._compressedSize = max(getUint(de.offset + 18), de.compressedSize);
+ de._expandedSize = max(getUint(de.offset + 22), de.expandedSize);
+ namelen = getUshort(de.offset + 26);
+ extralen = getUshort(de.offset + 28);
+
+ debug(print)
+ {
+ printf("\t\texpandedSize = %d\n", de.expandedSize);
+ printf("\t\tcompressedSize = %d\n", de.compressedSize);
+ printf("\t\tnamelen = %d\n", namelen);
+ printf("\t\textralen = %d\n", extralen);
+ }
+
+ if (de.flags & 1)
+ throw new ZipException("encryption not supported");
+
+ int i;
+ i = de.offset + 30 + namelen + extralen;
+ if (i + de.compressedSize > endrecOffset)
+ throw new ZipException("invalid directory entry 5");
+
+ de._compressedData = _data[i .. i + de.compressedSize];
+ debug(print) arrayPrint(de.compressedData);
+
+ switch (de.compressionMethod)
+ {
+ case CompressionMethod.none:
+ de._expandedData = de.compressedData;
+ return de.expandedData;
+
+ case CompressionMethod.deflate:
+ // -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.
+ import std.zlib : uncompress;
+ de._expandedData = cast(ubyte[]) uncompress(cast(void[]) de.compressedData, de.expandedSize, -15);
+ return de.expandedData;
+
+ default:
+ throw new ZipException("unsupported compression method");
+ }
+ }
+
+ /* ============ Utility =================== */
+
+ @safe ushort getUshort(int i)
+ {
+ ubyte[2] result = data[i .. i + 2];
+ return littleEndianToNative!ushort(result);
+ }
+
+ @safe uint getUint(int i)
+ {
+ ubyte[4] result = data[i .. i + 4];
+ return littleEndianToNative!uint(result);
+ }
+
+ @safe ulong getUlong(int i)
+ {
+ ubyte[8] result = data[i .. i + 8];
+ return littleEndianToNative!ulong(result);
+ }
+
+ @safe void putUshort(int i, ushort us)
+ {
+ data[i .. i + 2] = nativeToLittleEndian(us);
+ }
+
+ @safe void putUint(int i, uint ui)
+ {
+ data[i .. i + 4] = nativeToLittleEndian(ui);
+ }
+
+ @safe void putUlong(int i, ulong ul)
+ {
+ data[i .. i + 8] = nativeToLittleEndian(ul);
+ }
+}
+
+debug(print)
+{
+ @safe void arrayPrint(ubyte[] array)
+ {
+ printf("array %p,%d\n", cast(void*) array, array.length);
+ for (int i = 0; i < array.length; i++)
+ {
+ printf("%02x ", array[i]);
+ if (((i + 1) & 15) == 0)
+ printf("\n");
+ }
+ printf("\n");
+ }
+}
+
+@system unittest
+{
+ // @system due to (at least) ZipArchive.build
+ auto zip1 = new ZipArchive();
+ auto zip2 = new ZipArchive();
+ auto am1 = new ArchiveMember();
+ am1.name = "foo";
+ am1.expandedData = new ubyte[](1024);
+ zip1.addMember(am1);
+ auto data1 = zip1.build();
+ zip2.addMember(zip1.directory["foo"]);
+ zip2.build();
+ auto am2 = zip2.directory["foo"];
+ zip2.expand(am2);
+ assert(am1.expandedData == am2.expandedData);
+ auto zip3 = new ZipArchive(data1);
+ zip3.build();
+ assert(zip3.directory["foo"].compressedSize == am1.compressedSize);
+
+ // Test if packing and unpacking produces the original data
+ import std.conv, std.stdio;
+ import std.random : uniform, MinstdRand0;
+ MinstdRand0 gen;
+ const uint itemCount = 20, minSize = 10, maxSize = 500;
+ foreach (variant; 0 .. 2)
+ {
+ bool useZip64 = !!variant;
+ zip1 = new ZipArchive();
+ zip1.isZip64 = useZip64;
+ ArchiveMember[itemCount] ams;
+ foreach (i; 0 .. itemCount)
+ {
+ ams[i] = new ArchiveMember();
+ ams[i].name = to!string(i);
+ ams[i].expandedData = new ubyte[](uniform(minSize, maxSize));
+ foreach (ref ubyte c; ams[i].expandedData)
+ c = cast(ubyte)(uniform(0, 256));
+ ams[i].compressionMethod = CompressionMethod.deflate;
+ zip1.addMember(ams[i]);
+ }
+ auto zippedData = zip1.build();
+ zip2 = new ZipArchive(zippedData);
+ assert(zip2.isZip64 == useZip64);
+ foreach (am; ams)
+ {
+ am2 = zip2.directory[am.name];
+ zip2.expand(am2);
+ assert(am.crc32 == am2.crc32);
+ assert(am.expandedData == am2.expandedData);
+ }
+ }
+}
+
+@system unittest
+{
+ import std.conv : to;
+ import std.random : Mt19937, randomShuffle;
+ // 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/libphobos/src/std/zlib.d b/libphobos/src/std/zlib.d
new file mode 100644
index 0000000..e6cce24
--- /dev/null
+++ b/libphobos/src/std/zlib.d
@@ -0,0 +1,760 @@
+// Written in the D programming language.
+
+/**
+ * Compress/decompress data using the $(HTTP www._zlib.net, _zlib library).
+ *
+ * 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;
+ *
+ * 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: $(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.
+ * 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.zlib;
+
+//debug=zlib; // uncomment to turn on debugging printf's
+
+import etc.c.zlib;
+
+// Values for 'mode'
+
+enum
+{
+ Z_NO_FLUSH = 0,
+ Z_SYNC_FLUSH = 2,
+ Z_FULL_FLUSH = 3,
+ Z_FINISH = 4,
+}
+
+/*************************************
+ * Errors throw a ZlibException.
+ */
+
+class ZlibException : Exception
+{
+ this(int errnum)
+ { string msg;
+
+ switch (errnum)
+ {
+ case Z_STREAM_END: msg = "stream end"; break;
+ case Z_NEED_DICT: msg = "need dict"; break;
+ case Z_ERRNO: msg = "errno"; break;
+ case Z_STREAM_ERROR: msg = "stream error"; break;
+ case Z_DATA_ERROR: msg = "data error"; break;
+ case Z_MEM_ERROR: msg = "mem error"; break;
+ case Z_BUF_ERROR: msg = "buf error"; break;
+ case Z_VERSION_ERROR: msg = "version error"; break;
+ default: msg = "unknown error"; break;
+ }
+ super(msg);
+ }
+}
+
+/**
+ * $(P Compute the Adler-32 checksum of a buffer's worth of data.)
+ *
+ * Params:
+ * 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
+ *
+ * Returns:
+ * A $(D uint) checksum for the provided input data and starting checksum
+ *
+ * See_Also:
+ * $(LINK http://en.wikipedia.org/wiki/Adler-32)
+ */
+
+uint adler32(uint adler, const(void)[] buf)
+{
+ import std.range : chunks;
+ foreach (chunk; (cast(ubyte[]) buf).chunks(0xFFFF0000))
+ {
+ adler = etc.c.zlib.adler32(adler, chunk.ptr, cast(uint) chunk.length);
+ }
+ return adler;
+}
+
+///
+@system unittest
+{
+ static ubyte[] data = [1,2,3,4,5,6,7,8,9,10];
+
+ 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.)
+ *
+ * Params:
+ * crc = the starting checksum for the computation. Use 0
+ * for a new checksum. Use the output of this function
+ * for a cumulative checksum.
+ * buf = buffer containing input data
+ *
+ * Returns:
+ * A $(D uint) checksum for the provided input data and starting checksum
+ *
+ * See_Also:
+ * $(LINK http://en.wikipedia.org/wiki/Cyclic_redundancy_check)
+ */
+
+uint crc32(uint crc, const(void)[] buf)
+{
+ import std.range : chunks;
+ foreach (chunk; (cast(ubyte[]) buf).chunks(0xFFFF0000))
+ {
+ crc = etc.c.zlib.crc32(crc, chunk.ptr, cast(uint) chunk.length);
+ }
+ return crc;
+}
+
+@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);
+ debug(zlib) printf("crc = %x\n", crc);
+ assert(crc == 0x2520577b);
+}
+
+/**
+ * $(P Compress data)
+ *
+ * Params:
+ * srcbuf = buffer containing the data to compress
+ * 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
+ */
+
+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);
+ if (err)
+ {
+ GC.free(destbuf.ptr);
+ throw new ZlibException(err);
+ }
+
+ destbuf.length = destlen;
+ return destbuf;
+}
+
+/*********************************************
+ * ditto
+ */
+
+ubyte[] compress(const(void)[] srcbuf)
+{
+ return compress(srcbuf, Z_DEFAULT_COMPRESSION);
+}
+
+/*********************************************
+ * Decompresses the data in srcbuf[].
+ * Params:
+ * srcbuf = buffer containing the compressed data.
+ * destlen = size of the uncompressed data.
+ * It need not be accurate, but the decompression will be faster
+ * if the exact size is supplied.
+ * winbits = the base two logarithm of the maximum window size.
+ * Returns: the decompressed data.
+ */
+
+void[] uncompress(const(void)[] srcbuf, size_t destlen = 0u, int winbits = 15)
+{
+ import std.conv : to;
+ int err;
+ ubyte[] destbuf;
+
+ if (!destlen)
+ destlen = srcbuf.length * 2 + 1;
+
+ etc.c.zlib.z_stream zs;
+ 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)
+ {
+ throw new ZlibException(err);
+ }
+
+ size_t olddestlen = 0u;
+
+ loop:
+ while (true)
+ {
+ destbuf.length = destlen;
+ zs.next_out = cast(typeof(zs.next_out)) &destbuf[olddestlen];
+ zs.avail_out = to!uint(destlen - olddestlen);
+ olddestlen = destlen;
+
+ err = etc.c.zlib.inflate(&zs, Z_NO_FLUSH);
+ switch (err)
+ {
+ case Z_OK:
+ destlen = destbuf.length * 2;
+ continue loop;
+
+ case Z_STREAM_END:
+ destbuf.length = zs.total_out;
+ err = etc.c.zlib.inflateEnd(&zs);
+ if (err != Z_OK)
+ throw new ZlibException(err);
+ return destbuf;
+
+ default:
+ etc.c.zlib.inflateEnd(&zs);
+ throw new ZlibException(err);
+ }
+ }
+ assert(0);
+}
+
+@system unittest
+{
+ 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;
+
+ //arrayPrint(src);
+ dst = compress(src);
+ //arrayPrint(dst);
+ result = cast(ubyte[]) uncompress(dst);
+ //arrayPrint(result);
+ assert(result == src);
+}
+
+@system unittest
+{
+ ubyte[] src = new ubyte[1000000];
+ ubyte[] dst;
+ ubyte[] result;
+
+ src[] = 0x80;
+ dst = compress(src);
+ assert(dst.length*2 + 1 < src.length);
+ result = cast(ubyte[]) uncompress(dst);
+ assert(result == src);
+}
+
+/+
+void arrayPrint(ubyte[] array)
+{
+ //printf("array %p,%d\n", cast(void*) array, array.length);
+ for (size_t i = 0; i < array.length; i++)
+ {
+ printf("%02x ", array[i]);
+ if (((i + 1) & 15) == 0)
+ printf("\n");
+ }
+ printf("\n\n");
+}
++/
+
+/// the header format the compressed stream is wrapped in
+enum HeaderFormat {
+ deflate, /// a standard zlib header
+ gzip, /// a gzip file format header
+ determineFromData /// used when decompressing. Try to automatically detect the stream format by looking at the data
+}
+
+/*********************************************
+ * Used when the data to be compressed is not all in one buffer.
+ */
+
+class Compress
+{
+ import std.conv : to;
+
+ private:
+ z_stream zs;
+ int level = Z_DEFAULT_COMPRESSION;
+ int inited;
+ immutable bool gzip;
+
+ void error(int err)
+ {
+ if (inited)
+ { deflateEnd(&zs);
+ inited = 0;
+ }
+ throw new ZlibException(err);
+ }
+
+ public:
+
+ /**
+ * Constructor.
+ *
+ * Params:
+ * 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.
+ *
+ * See_Also:
+ * $(LREF compress), $(LREF HeaderFormat)
+ */
+ this(int level, HeaderFormat header = HeaderFormat.deflate)
+ in
+ {
+ assert(1 <= level && level <= 9);
+ }
+ body
+ {
+ this.level = level;
+ this.gzip = header == HeaderFormat.gzip;
+ }
+
+ /// ditto
+ this(HeaderFormat header = HeaderFormat.deflate)
+ {
+ this.gzip = header == HeaderFormat.gzip;
+ }
+
+ ~this()
+ { int err;
+
+ if (inited)
+ {
+ inited = 0;
+ deflateEnd(&zs);
+ }
+ }
+
+ /**
+ * Compress the data in buf and return the compressed data.
+ * Params:
+ * buf = data to compress
+ *
+ * Returns:
+ * the compressed data. The buffers returned from successive calls to this should be concatenated together.
+ *
+ */
+ const(void)[] compress(const(void)[] buf)
+ {
+ import core.memory : GC;
+ int err;
+ ubyte[] destbuf;
+
+ if (buf.length == 0)
+ return null;
+
+ if (!inited)
+ {
+ err = deflateInit2(&zs, level, Z_DEFLATED, 15 + (gzip ? 16 : 0), 8, Z_DEFAULT_STRATEGY);
+ if (err)
+ error(err);
+ inited = 1;
+ }
+
+ destbuf = new ubyte[zs.avail_in + buf.length];
+ zs.next_out = destbuf.ptr;
+ zs.avail_out = to!uint(destbuf.length);
+
+ if (zs.avail_in)
+ buf = zs.next_in[0 .. zs.avail_in] ~ cast(ubyte[]) buf;
+
+ zs.next_in = cast(typeof(zs.next_in)) buf.ptr;
+ zs.avail_in = to!uint(buf.length);
+
+ err = deflate(&zs, Z_NO_FLUSH);
+ if (err != Z_STREAM_END && err != Z_OK)
+ {
+ GC.free(destbuf.ptr);
+ error(err);
+ }
+ destbuf.length = destbuf.length - zs.avail_out;
+ return destbuf;
+ }
+
+ /***
+ * Compress and return any remaining data.
+ * The returned data should be appended to that returned by compress().
+ * Params:
+ * mode = one of the following:
+ * $(DL
+ $(DT Z_SYNC_FLUSH )
+ $(DD Syncs up flushing to the next byte boundary.
+ Used when more data is to be compressed later on.)
+ $(DT Z_FULL_FLUSH )
+ $(DD Syncs up flushing to the next byte boundary.
+ Used when more data is to be compressed later on,
+ and the decompressor needs to be restartable at this
+ point.)
+ $(DT Z_FINISH)
+ $(DD (default) Used when finished compressing the data. )
+ )
+ */
+ void[] flush(int mode = Z_FINISH)
+ in
+ {
+ assert(mode == Z_FINISH || mode == Z_SYNC_FLUSH || mode == Z_FULL_FLUSH);
+ }
+ body
+ {
+ import core.memory : GC;
+ ubyte[] destbuf;
+ ubyte[512] tmpbuf = void;
+ int err;
+
+ if (!inited)
+ return null;
+
+ /* may be zs.avail_out+<some constant>
+ * zs.avail_out is set nonzero by deflate in previous compress()
+ */
+ //tmpbuf = new void[zs.avail_out];
+ zs.next_out = tmpbuf.ptr;
+ zs.avail_out = tmpbuf.length;
+
+ 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)
+ {
+ destbuf ~= tmpbuf;
+ zs.next_out = tmpbuf.ptr;
+ zs.avail_out = tmpbuf.length;
+ continue;
+ }
+ err = Z_BUF_ERROR;
+ }
+ GC.free(destbuf.ptr);
+ error(err);
+ }
+ destbuf ~= tmpbuf[0 .. (tmpbuf.length - zs.avail_out)];
+
+ if (mode == Z_FINISH)
+ {
+ err = deflateEnd(&zs);
+ inited = 0;
+ if (err)
+ error(err);
+ }
+ return destbuf;
+ }
+}
+
+/******
+ * Used when the data to be decompressed is not all in one buffer.
+ */
+
+class UnCompress
+{
+ import std.conv : to;
+
+ private:
+ z_stream zs;
+ int inited;
+ int done;
+ size_t destbufsize;
+
+ HeaderFormat format;
+
+ void error(int err)
+ {
+ if (inited)
+ { inflateEnd(&zs);
+ inited = 0;
+ }
+ throw new ZlibException(err);
+ }
+
+ public:
+
+ /**
+ * Construct. destbufsize is the same as for D.zlib.uncompress().
+ */
+ this(uint destbufsize)
+ {
+ this.destbufsize = destbufsize;
+ }
+
+ /** ditto */
+ this(HeaderFormat format = HeaderFormat.determineFromData)
+ {
+ this.format = format;
+ }
+
+ ~this()
+ { int err;
+
+ if (inited)
+ {
+ inited = 0;
+ inflateEnd(&zs);
+ }
+ done = 1;
+ }
+
+ /**
+ * Decompress the data in buf and return the decompressed data.
+ * The buffers returned from successive calls to this should be concatenated
+ * together.
+ */
+ const(void)[] uncompress(const(void)[] buf)
+ in
+ {
+ assert(!done);
+ }
+ body
+ {
+ import core.memory : GC;
+ int err;
+ ubyte[] destbuf;
+
+ if (buf.length == 0)
+ return null;
+
+ if (!inited)
+ {
+ int windowBits = 15;
+ if (format == HeaderFormat.gzip)
+ windowBits += 16;
+ else if (format == HeaderFormat.determineFromData)
+ windowBits += 32;
+
+ err = inflateInit2(&zs, windowBits);
+ if (err)
+ error(err);
+ inited = 1;
+ }
+
+ if (!destbufsize)
+ destbufsize = to!uint(buf.length) * 2;
+ destbuf = new ubyte[zs.avail_in * 2 + destbufsize];
+ zs.next_out = destbuf.ptr;
+ zs.avail_out = to!uint(destbuf.length);
+
+ if (zs.avail_in)
+ buf = zs.next_in[0 .. zs.avail_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)
+ {
+ GC.free(destbuf.ptr);
+ error(err);
+ }
+ destbuf.length = destbuf.length - zs.avail_out;
+ return destbuf;
+ }
+
+ /**
+ * Decompress and return any remaining data.
+ * The returned data should be appended to that returned by uncompress().
+ * The UnCompress object cannot be used further.
+ */
+ void[] flush()
+ in
+ {
+ assert(!done);
+ }
+ out
+ {
+ assert(done);
+ }
+ body
+ {
+ import core.memory : GC;
+ ubyte[] extra;
+ ubyte[] destbuf;
+ int err;
+
+ done = 1;
+ if (!inited)
+ return null;
+
+ L1:
+ destbuf = new ubyte[zs.avail_in * 2 + 100];
+ zs.next_out = destbuf.ptr;
+ zs.avail_out = to!uint(destbuf.length);
+
+ err = etc.c.zlib.inflate(&zs, Z_NO_FLUSH);
+ if (err == Z_OK && zs.avail_out == 0)
+ {
+ extra ~= destbuf;
+ goto L1;
+ }
+ if (err != Z_STREAM_END)
+ {
+ GC.free(destbuf.ptr);
+ if (err == Z_OK)
+ err = Z_BUF_ERROR;
+ error(err);
+ }
+ destbuf = destbuf.ptr[0 .. zs.next_out - destbuf.ptr];
+ err = etc.c.zlib.inflateEnd(&zs);
+ inited = 0;
+ if (err)
+ error(err);
+ if (extra.length)
+ destbuf = extra ~ destbuf;
+ return destbuf;
+ }
+}
+
+/* ========================== unittest ========================= */
+
+import std.random;
+import std.stdio;
+
+@system unittest // by Dave
+{
+ debug(zlib) writeln("std.zlib.unittest");
+
+ bool CompressThenUncompress (void[] src)
+ {
+ 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(dst);
+ assert(src.length == uncompressedBuf.length);
+ assert(src == uncompressedBuf);
+
+ return true;
+ }
+
+
+ // smallish buffers
+ for (int idx = 0; idx < 25; idx++)
+ {
+ char[] buf = new char[uniform(0, 100)];
+
+ // Alternate between more & less compressible
+ foreach (ref char c; buf)
+ c = cast(char) (' ' + (uniform(0, idx % 2 ? 91 : 2)));
+
+ if (CompressThenUncompress(buf))
+ {
+ debug(zlib) writeln("; Success.");
+ }
+ else
+ {
+ return;
+ }
+ }
+
+ // larger buffers
+ 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)
+ c = cast(char) (' ' + (uniform(0, idx % 2 ? 91 : 10)));
+
+ if (CompressThenUncompress(buf))
+ {
+ debug(zlib) writefln("; Success.");
+ }
+ else
+ {
+ return;
+ }
+ }
+
+ debug(zlib) writefln("PASSED std.zlib.unittest");
+}
+
+
+@system unittest // by Artem Rebrov
+{
+ Compress cmp = new Compress;
+ UnCompress decmp = new UnCompress;
+
+ const(void)[] input;
+ input = "tesatdffadf";
+
+ const(void)[] buf = cmp.compress(input);
+ buf ~= cmp.flush();
+ const(void)[] output = decmp.uncompress(buf);
+
+ //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
+}