diff options
author | Iain Buclaw <ibuclaw@gdcproject.org> | 2019-06-18 20:42:10 +0200 |
---|---|---|
committer | Iain Buclaw <ibuclaw@gdcproject.org> | 2021-11-30 16:53:28 +0100 |
commit | 5fee5ec362f7a243f459e6378fd49dfc89dc9fb5 (patch) | |
tree | 61d1bdbca854a903c0860406f457f06b2040be7a /libphobos/src/std/algorithm/comparison.d | |
parent | b3f60112edcb85b459e60f66c44a55138b1cef49 (diff) | |
download | gcc-5fee5ec362f7a243f459e6378fd49dfc89dc9fb5.zip gcc-5fee5ec362f7a243f459e6378fd49dfc89dc9fb5.tar.gz gcc-5fee5ec362f7a243f459e6378fd49dfc89dc9fb5.tar.bz2 |
d: Import dmd b8384668f, druntime e6caaab9, phobos 5ab9ad256 (v2.098.0-beta.1)
The D front-end is now itself written in D, in order to build GDC, you
will need a working GDC compiler (GCC version 9.1 or later).
GCC changes:
- Add support for bootstrapping the D front-end.
These add the required components in order to have a D front-end written
in D itself. Because the compiler front-end only depends on the core
runtime modules, only libdruntime is built for the bootstrap stages.
D front-end changes:
- Import dmd v2.098.0-beta.1.
Druntime changes:
- Import druntime v2.098.0-beta.1.
Phobos changes:
- Import phobos v2.098.0-beta.1.
The jump from v2.076.1 to v2.098.0 covers nearly 4 years worth of
development on the D programming language and run-time libraries.
ChangeLog:
* Makefile.def: Add bootstrap to libbacktrace, libphobos, zlib, and
libatomic.
* Makefile.in: Regenerate.
* Makefile.tpl (POSTSTAGE1_HOST_EXPORTS): Fix command for GDC.
(STAGE1_CONFIGURE_FLAGS): Add --with-libphobos-druntime-only if
target-libphobos-bootstrap.
(STAGE2_CONFIGURE_FLAGS): Likewise.
* configure: Regenerate.
* configure.ac: Add support for bootstrapping D front-end.
config/ChangeLog:
* acx.m4 (ACX_PROG_GDC): New m4 function.
gcc/ChangeLog:
* Makefile.in (GDC): New variable.
(GDCFLAGS): New variable.
* configure: Regenerate.
* configure.ac: Add call to ACX_PROG_GDC. Substitute GDCFLAGS.
gcc/d/ChangeLog:
* dmd/MERGE: Merge upstream dmd b8384668f.
* Make-lang.in (d-warn): Use strict warnings.
(DMD_WARN_CXXFLAGS): Remove.
(DMD_COMPILE): Remove.
(CHECKING_DFLAGS): Define.
(WARN_DFLAGS): Define.
(ALL_DFLAGS): Define.
(DCOMPILE.base): Define.
(DCOMPILE): Define.
(DPOSTCOMPILE): Define.
(DLINKER): Define.
(DLLINKER): Define.
(D_FRONTEND_OBJS): Add new dmd front-end objects.
(D_GENERATED_SRCS): Remove.
(D_GENERATED_OBJS): Remove.
(D_ALL_OBJS): Remove D_GENERATED_OBJS.
(d21$(exeext)): Build using DLLINKER and -static-libphobos.
(d.tags): Remove dmd/*.c and dmd/root/*.c.
(d.mostlyclean): Remove D_GENERATED_SRCS, d/idgen$(build_exeext),
d/impcnvgen$(build_exeext).
(D_INCLUDES): Include $(srcdir)/d/dmd/res.
(CFLAGS-d/id.o): Remove.
(CFLAGS-d/impcnvtab.o): Remove.
(d/%.o): Build using DCOMPILE and DPOSTCOMPILE. Update dependencies
from d/dmd/%.c to d/dmd/%.d.
(d/idgen$(build_exeext)): Remove.
(d/impcnvgen$(build_exeext)): Remove.
(d/id.c): Remove.
(d/id.h): Remove.
(d/impcnvtab.c): Remove.
(d/%.dmdgen.o): Remove.
(D_SYSTEM_H): Remove.
(d/idgen.dmdgen.o): Remove.
(d/impcnvgen.dmdgen.o): Remove.
* config-lang.in (boot_language): New variable.
* d-attribs.cc: Include dmd/expression.h.
* d-builtins.cc: Include d-frontend.h.
(build_frontend_type): Update for new front-end interface.
(d_eval_constant_expression): Likewise.
(d_build_builtins_module): Likewise.
(maybe_set_builtin_1): Likewise.
(d_build_d_type_nodes): Likewise.
* d-codegen.cc (d_decl_context): Likewise.
(declaration_reference_p): Likewise.
(declaration_type): Likewise.
(parameter_reference_p): Likewise.
(parameter_type): Likewise.
(get_array_length): Likewise.
(build_delegate_cst): Likewise.
(build_typeof_null_value): Likewise.
(identity_compare_p): Likewise.
(lower_struct_comparison): Likewise.
(build_filename_from_loc): Likewise.
(build_assert_call): Remove LIBCALL_SWITCH_ERROR.
(build_bounds_index_condition): Call LIBCALL_ARRAYBOUNDS_INDEXP on
bounds error.
(build_bounds_slice_condition): Call LIBCALL_ARRAYBOUNDS_SLICEP on
bounds error.
(array_bounds_check): Update for new front-end interface.
(checkaction_trap_p): Handle CHECKACTION_context.
(get_function_type): Update for new front-end interface.
(d_build_call): Likewise.
* d-compiler.cc: Remove include of dmd/scope.h.
(Compiler::genCmain): Remove.
(Compiler::paintAsType): Update for new front-end interface.
(Compiler::onParseModule): Likewise.
* d-convert.cc (convert_expr): Remove call to LIBCALL_ARRAYCAST.
(convert_for_rvalue): Update for new front-end interface.
(convert_for_assignment): Likewise.
(convert_for_condition): Likewise.
(d_array_convert): Likewise.
* d-diagnostic.cc (error): Remove.
(errorSupplemental): Remove.
(warning): Remove.
(warningSupplemental): Remove.
(deprecation): Remove.
(deprecationSupplemental): Remove.
(message): Remove.
(vtip): New.
* d-frontend.cc (global): Remove.
(Global::_init): Remove.
(Global::startGagging): Remove.
(Global::endGagging): Remove.
(Global::increaseErrorCount): Remove.
(Loc::Loc): Remove.
(Loc::toChars): Remove.
(Loc::equals): Remove.
(isBuiltin): Update for new front-end interface.
(eval_builtin): Likewise.
(getTypeInfoType): Likewise.
(inlineCopy): Remove.
* d-incpath.cc: Include d-frontend.h.
(add_globalpaths): Call d_gc_malloc to allocate Strings.
(add_filepaths): Likewise.
* d-lang.cc: Include dmd/id.h, dmd/root/file.h, d-frontend.h. Remove
include of dmd/mars.h, id.h.
(entrypoint_module): Remove.
(entrypoint_root_module): Remove.
(deps_write_string): Update for new front-end interface.
(deps_write): Likewise.
(d_init_options): Call rt_init. Remove setting global params that are
default initialized by the front-end.
(d_handle_option): Handle OPT_fcheckaction_, OPT_fdump_c___spec_,
OPT_fdump_c___spec_verbose, OPT_fextern_std_, OPT_fpreview,
OPT_revert, OPT_fsave_mixins_, and OPT_ftransition.
(d_post_options): Propagate dip1021 and dip1000 preview flags to
dip25, and flag_diagnostics_show_caret to printErrorContext.
(d_add_entrypoint_module): Remove.
(d_parse_file): Update for new front-end interface.
(d_type_promotes_to): Likewise.
(d_types_compatible_p): Likewise.
* d-longdouble.cc (CTFloat::zero): Remove.
(CTFloat::one): Remove.
(CTFloat::minusone): Remove.
(CTFloat::half): Remove.
* d-system.h (POSIX): Remove.
(realpath): Remove.
(isalpha): Remove.
(isalnum): Remove.
(isdigit): Remove.
(islower): Remove.
(isprint): Remove.
(isspace): Remove.
(isupper): Remove.
(isxdigit): Remove.
(tolower): Remove.
(_mkdir): Remove.
(INT32_MAX): Remove.
(INT32_MIN): Remove.
(INT64_MIN): Remove.
(UINT32_MAX): Remove.
(UINT64_MAX): Remove.
* d-target.cc: Include calls.h.
(target): Remove.
(define_float_constants): Remove initialization of snan.
(Target::_init): Update for new front-end interface.
(Target::isVectorTypeSupported): Likewise.
(Target::isVectorOpSupported): Remove cases for unordered operators.
(TargetCPP::typeMangle): Update for new front-end interface.
(TargetCPP::parameterType): Likewise.
(Target::systemLinkage): Likewise.
(Target::isReturnOnStack): Likewise.
(Target::isCalleeDestroyingArgs): Define.
(Target::preferPassByRef): Define.
* d-tree.h (d_add_entrypoint_module): Remove.
* decl.cc (gcc_attribute_p): Update for new front-end interface.
(apply_pragma_crt): Define.
(DeclVisitor::visit(PragmaDeclaration *)): Handle pragmas
crt_constructor and crt_destructor.
(DeclVisitor::visit(TemplateDeclaration *)): Update for new front-end
interface.
(DeclVisitor::visit): Likewise.
(DeclVisitor::finish_vtable): Likewise.
(get_symbol_decl): Error if template has more than one nesting
context. Update for new front-end interface.
(make_thunk): Update for new front-end interface.
(get_vtable_decl): Likewise.
* expr.cc (ExprVisitor::visit): Likewise.
(build_return_dtor): Likewise.
* imports.cc (ImportVisitor::visit): Likewise.
* intrinsics.cc: Include dmd/expression.h. Remove include of
dmd/mangle.h.
(maybe_set_intrinsic): Update for new front-end interface.
* intrinsics.def (INTRINSIC_ROL): Update intrinsic signature.
(INTRINSIC_ROR): Likewise.
(INTRINSIC_ROR_TIARG): Likewise.
(INTRINSIC_TOPREC): Likewise.
(INTRINSIC_TOPRECL): Likewise.
(INTRINSIC_TAN): Update intrinsic module and signature.
(INTRINSIC_ISNAN): Likewise.
(INTRINSIC_ISFINITE): Likewise.
(INTRINSIC_COPYSIGN): Define intrinsic.
(INTRINSIC_COPYSIGNI): Define intrinsic.
(INTRINSIC_EXP): Update intrinsic module.
(INTRINSIC_EXPM1): Likewise.
(INTRINSIC_EXP2): Likewise.
(INTRINSIC_LOG): Likewise.
(INTRINSIC_LOG2): Likewise.
(INTRINSIC_LOG10): Likewise.
(INTRINSIC_POW): Likewise.
(INTRINSIC_ROUND): Likewise.
(INTRINSIC_FLOORF): Likewise.
(INTRINSIC_FLOOR): Likewise.
(INTRINSIC_FLOORL): Likewise.
(INTRINSIC_CEILF): Likewise.
(INTRINSIC_CEIL): Likewise.
(INTRINSIC_CEILL): Likewise.
(INTRINSIC_TRUNC): Likewise.
(INTRINSIC_FMIN): Likewise.
(INTRINSIC_FMAX): Likewise.
(INTRINSIC_FMA): Likewise.
(INTRINSIC_VA_ARG): Update intrinsic signature.
(INTRINSIC_VASTART): Likewise.
* lang.opt (fcheck=): Add alternate aliases for contract switches.
(fcheckaction=): New option.
(check_action): New Enum and EnumValue entries.
(fdump-c++-spec-verbose): New option.
(fdump-c++-spec=): New option.
(fextern-std=): New option.
(extern_stdcpp): New Enum and EnumValue entries
(fpreview=): New options.
(frevert=): New options.
(fsave-mixins): New option.
(ftransition=): Update options.
* modules.cc (get_internal_fn): Replace Prot with Visibility.
(build_internal_fn): Likewise.
(build_dso_cdtor_fn): Likewise.
(build_module_tree): Remove check for __entrypoint module.
* runtime.def (P5): Define.
(ARRAYBOUNDS_SLICEP): Define.
(ARRAYBOUNDS_INDEXP): Define.
(NEWTHROW): Define.
(ADCMP2): Remove.
(ARRAYCAST): Remove.
(SWITCH_STRING): Remove.
(SWITCH_USTRING): Remove.
(SWITCH_DSTRING): Remove.
(SWITCH_ERROR): Remove.
* toir.cc (IRVisitor::visit): Update for new front-end interface.
(IRVisitor::check_previous_goto): Remove checks for case and default
statements.
(IRVisitor::visit(SwitchStatement *)): Remove handling of string
switch conditions.
* typeinfo.cc: Include d-frontend.h.
(get_typeinfo_kind): Update for new front-end interface.
(make_frontend_typeinfo): Likewise.
(TypeInfoVisitor::visit): Likewise.
(builtin_typeinfo_p): Likewise.
(get_typeinfo_decl): Likewise.
(build_typeinfo): Likewise.
* types.cc (valist_array_p): Likewise.
(make_array_type): Likewise.
(merge_aggregate_types): Likewise.
(TypeVisitor::visit(TypeBasic *)): Likewise.
(TypeVisitor::visit(TypeFunction *)): Likewise.
(TypeVisitor::visit(TypeStruct *)): Update comment.
* verstr.h: Removed.
* d-frontend.h: New file.
gcc/po/ChangeLog:
* EXCLUDES: Remove d/dmd sources from list.
gcc/testsuite/ChangeLog:
* gdc.dg/Wcastresult2.d: Update test.
* gdc.dg/asm1.d: Likewise.
* gdc.dg/asm2.d: Likewise.
* gdc.dg/asm3.d: Likewise.
* gdc.dg/gdc282.d: Likewise.
* gdc.dg/imports/gdc170.d: Likewise.
* gdc.dg/intrinsics.d: Likewise.
* gdc.dg/pr101672.d: Likewise.
* gdc.dg/pr90650a.d: Likewise.
* gdc.dg/pr90650b.d: Likewise.
* gdc.dg/pr94777a.d: Likewise.
* gdc.dg/pr95250.d: Likewise.
* gdc.dg/pr96869.d: Likewise.
* gdc.dg/pr98277.d: Likewise.
* gdc.dg/pr98457.d: Likewise.
* gdc.dg/simd1.d: Likewise.
* gdc.dg/simd2a.d: Likewise.
* gdc.dg/simd2b.d: Likewise.
* gdc.dg/simd2c.d: Likewise.
* gdc.dg/simd2d.d: Likewise.
* gdc.dg/simd2e.d: Likewise.
* gdc.dg/simd2f.d: Likewise.
* gdc.dg/simd2g.d: Likewise.
* gdc.dg/simd2h.d: Likewise.
* gdc.dg/simd2i.d: Likewise.
* gdc.dg/simd2j.d: Likewise.
* gdc.dg/simd7951.d: Likewise.
* gdc.dg/torture/gdc309.d: Likewise.
* gdc.dg/torture/pr94424.d: Likewise.
* gdc.dg/torture/pr94777b.d: Likewise.
* lib/gdc-utils.exp (gdc-convert-args): Handle new compiler options.
(gdc-convert-test): Handle CXXFLAGS, EXTRA_OBJC_SOURCES, and ARG_SETS
test directives.
(gdc-do-test): Only import modules in the test run directory.
* gdc.dg/pr94777c.d: New test.
* gdc.dg/pr96156b.d: New test.
* gdc.dg/pr96157c.d: New test.
* gdc.dg/simd_ctfe.d: New test.
* gdc.dg/torture/simd17344.d: New test.
* gdc.dg/torture/simd20052.d: New test.
* gdc.dg/torture/simd6.d: New test.
* gdc.dg/torture/simd7.d: New test.
libphobos/ChangeLog:
* libdruntime/MERGE: Merge upstream druntime e6caaab9.
* libdruntime/Makefile.am (D_EXTRA_FLAGS): Build libdruntime with
-fpreview=dip1000, -fpreview=fieldwise, and -fpreview=dtorfields.
(ALL_DRUNTIME_SOURCES): Add DRUNTIME_DSOURCES_STDCXX.
(DRUNTIME_DSOURCES): Update list of C binding modules.
(DRUNTIME_DSOURCES_STDCXX): Likewise.
(DRUNTIME_DSOURCES_LINUX): Likewise.
(DRUNTIME_DSOURCES_OPENBSD): Likewise.
(DRUNTIME_DISOURCES): Remove __entrypoint.di.
* libdruntime/Makefile.in: Regenerated.
* libdruntime/__entrypoint.di: Removed.
* libdruntime/gcc/deh.d (_d_isbaseof): Update signature.
(_d_createTrace): Likewise.
(__gdc_begin_catch): Remove reference to the exception.
(_d_throw): Increment reference count of thrown object before unwind.
(__gdc_personality): Chain exceptions with Throwable.chainTogether.
* libdruntime/gcc/emutls.d: Update imports.
* libdruntime/gcc/sections/elf.d: Update imports.
(DSO.moduleGroup): Update signature.
* libdruntime/gcc/sections/macho.d: Update imports.
(DSO.moduleGroup): Update signature.
* libdruntime/gcc/sections/pecoff.d: Update imports.
(DSO.moduleGroup): Update signature.
* src/MERGE: Merge upstream phobos 5ab9ad256.
* src/Makefile.am (D_EXTRA_DFLAGS): Add -fpreview=dip1000 and
-fpreview=dtorfields flags.
(PHOBOS_DSOURCES): Update list of std modules.
* src/Makefile.in: Regenerate.
* testsuite/lib/libphobos.exp (libphobos-dg-test): Handle assembly
compile types.
(dg-test): Override.
(additional_prunes): Define.
(libphobos-dg-prune): Filter any additional_prunes set by tests.
* testsuite/libphobos.aa/test_aa.d: Update test.
* testsuite/libphobos.druntime/druntime.exp (version_flags): Add
-fversion=CoreUnittest.
* testsuite/libphobos.druntime_shared/druntime_shared.exp
(version_flags): Add -fversion=CoreUnittest -fversion=Shared.
* testsuite/libphobos.exceptions/unknown_gc.d: Update test.
* testsuite/libphobos.hash/test_hash.d: Update test.
* testsuite/libphobos.phobos/phobos.exp (version_flags): Add
-fversion=StdUnittest
* testsuite/libphobos.phobos_shared/phobos_shared.exp (version_flags):
Likewise.
* testsuite/libphobos.shared/host.c: Update test.
* testsuite/libphobos.shared/load.d: Update test.
* testsuite/libphobos.shared/load_13414.d: Update test.
* testsuite/libphobos.thread/fiber_guard_page.d: Update test.
* testsuite/libphobos.thread/tlsgc_sections.d: Update test.
* testsuite/testsuite_flags.in: Add -fpreview=dip1000 to --gdcflags.
* testsuite/libphobos.shared/link_mod_collision.d: Removed.
* testsuite/libphobos.shared/load_mod_collision.d: Removed.
* testsuite/libphobos.betterc/betterc.exp: New test.
* testsuite/libphobos.config/config.exp: New test.
* testsuite/libphobos.gc/gc.exp: New test.
* testsuite/libphobos.imports/imports.exp: New test.
* testsuite/libphobos.lifetime/lifetime.exp: New test.
* testsuite/libphobos.unittest/unittest.exp: New test.
Diffstat (limited to 'libphobos/src/std/algorithm/comparison.d')
-rw-r--r-- | libphobos/src/std/algorithm/comparison.d | 950 |
1 files changed, 566 insertions, 384 deletions
diff --git a/libphobos/src/std/algorithm/comparison.d b/libphobos/src/std/algorithm/comparison.d index faa4d44..c952544 100644 --- a/libphobos/src/std/algorithm/comparison.d +++ b/libphobos/src/std/algorithm/comparison.d @@ -1,48 +1,48 @@ // Written in the D programming language. /** This is a submodule of $(MREF std, algorithm). -It contains generic _comparison algorithms. +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)) + `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).) + `(new A()).castSwitch((A a)=>1,(B b)=>2)` returns `1`.) $(T2 clamp, - $(D clamp(1, 3, 6)) returns $(D 3). $(D clamp(4, 3, 6)) returns $(D 4).) + `clamp(1, 3, 6)` returns `3`. `clamp(4, 3, 6)` returns `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).) + `cmp("abc", "abcd")` is `-1`, `cmp("abc", "aba")` is `1`, + and `cmp("abc", "abc")` is `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).) + Return first parameter `p` that passes an `if (p)` test, e.g. + `either(0, 42, 43)` returns `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).) + `equal([1, 2, 3], [1.0, 2.0, 3.0])` returns `true`.) $(T2 isPermutation, - $(D isPermutation([1, 2], [2, 1])) returns $(D true).) + `isPermutation([1, 2], [2, 1])` returns `true`.) $(T2 isSameLength, - $(D isSameLength([1, 2, 3], [4, 5, 6])) returns $(D true).) + `isSameLength([1, 2, 3], [4, 5, 6])` returns `true`.) $(T2 levenshteinDistance, - $(D levenshteinDistance("kitten", "sitting")) returns $(D 3) by using + `levenshteinDistance("kitten", "sitting")` returns `3` by using the $(LINK2 https://en.wikipedia.org/wiki/Levenshtein_distance, - Levenshtein distance _algorithm).) + Levenshtein distance algorithm).) $(T2 levenshteinDistanceAndPath, - $(D levenshteinDistanceAndPath("kitten", "sitting")) returns - $(D tuple(3, "snnnsni")) by using the + `levenshteinDistanceAndPath("kitten", "sitting")` returns + `tuple(3, "snnnsni")` by using the $(LINK2 https://en.wikipedia.org/wiki/Levenshtein_distance, - Levenshtein distance _algorithm).) + Levenshtein distance algorithm).) $(T2 max, - $(D max(3, 4, 2)) returns $(D 4).) + `max(3, 4, 2)` returns `4`.) $(T2 min, - $(D min(3, 4, 2)) returns $(D 2).) + `min(3, 4, 2)` returns `2`.) $(T2 mismatch, - $(D mismatch("oh hi", "ohayo")) returns $(D tuple(" hi", "ayo")).) + `mismatch("oh hi", "ohayo")` returns `tuple(" hi", "ayo")`.) $(T2 predSwitch, - $(D 2.predSwitch(1, "one", 2, "two", 3, "three")) returns $(D "two").) + `2.predSwitch(1, "one", 2, "two", 3, "three")` returns `"two"`.) ) Copyright: Andrei Alexandrescu 2008-. @@ -51,25 +51,25 @@ License: $(HTTP boost.org/LICENSE_1_0.txt, Boost License 1.0). Authors: $(HTTP erdani.com, Andrei Alexandrescu) -Source: $(PHOBOSSRC std/algorithm/_comparison.d) +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.functional : unaryFun, binaryFun; import std.range.primitives; import std.traits; -// FIXME import std.meta : allSatisfy; -import std.typecons; // : tuple, Tuple, Flag, Yes; +import std.typecons : tuple, Tuple, Flag, Yes; + +import std.internal.attributes : betterC; /** -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 +Find `value` _among `values`, returning the 1-based index +of the first matching value in `values`, or `0` if `value` +is not _among `values`. The predicate `pred` is used to compare values, and uses equality by default. Params: @@ -116,7 +116,7 @@ if (isExpressionTuple!values) } /// -@safe unittest +@safe @nogc @betterC unittest { assert(3.among(1, 42, 24, 3, 2)); @@ -130,10 +130,10 @@ if (isExpressionTuple!values) } /** -Alternatively, $(D values) can be passed at compile-time, allowing for a more +Alternatively, `values` can be passed at compile-time, allowing for a more efficient search, but one that only supports matching on equality: */ -@safe unittest +@safe @nogc @betterC unittest { assert(3.among!(2, 3, 4)); assert("bar".among!("foo", "bar", "baz") == 2); @@ -214,19 +214,19 @@ private template indexOfFirstOvershadowingChoiceOnLast(choices...) 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). +The first choice that `switchObject` can be casted to the type +of argument it accepts will be called with `switchObject` casted to that +type, and the value it'll return will be returned by `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 +Throws: If none of the choice matches, a `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 + choices = The `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. @@ -235,7 +235,7 @@ Params: Returns: The value of the selected choice. -Note: $(D castSwitch) can only be used with object types. +Note: `castSwitch` can only be used with object types. */ auto castSwitch(choices...)(Object switchObject) { @@ -254,7 +254,6 @@ auto castSwitch(choices...)(Object switchObject) if (switchObject !is null) { - // Checking for exact matches: const classInfo = typeid(switchObject); foreach (index, choice; choices) @@ -517,7 +516,7 @@ auto castSwitch(choices...)(Object switchObject) /** Clamps a value into the given bounds. -This functions is equivalent to $(D max(lower, min(upper,val))). +This function is equivalent to `max(lower, min(upper, val))`. Params: val = The value to _clamp. @@ -525,23 +524,24 @@ Params: upper = The _upper bound of the _clamp. Returns: - Returns $(D val), if it is between $(D lower) and $(D upper). + Returns `val`, if it is between `lower` and `upper`. Otherwise returns the nearest of the two. */ auto clamp(T1, T2, T3)(T1 val, T2 lower, T3 upper) +if (is(typeof(max(min(val, upper), lower)))) in { import std.functional : greaterThan; - assert(!lower.greaterThan(upper)); + assert(!lower.greaterThan(upper), "Lower can't be greater than upper."); } -body +do { - return max(lower, min(upper, val)); + return max(min(val, upper), lower); } /// -@safe unittest +@safe @nogc @betterC unittest { assert(clamp(2, 1, 3) == 2); assert(clamp(0, 1, 3) == 1); @@ -576,121 +576,160 @@ body // UFCS style assert(Date(1982, 1, 4).clamp(Date.min, Date.max) == Date(1982, 1, 4)); + // Stability + struct A { + int x, y; + int opCmp(ref const A rhs) const { return (x > rhs.x) - (x < rhs.x); } + } + A x, lo, hi; + x.y = 42; + assert(x.clamp(lo, hi).y == 42); } // 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 +Performs a lexicographical comparison on two +$(REF_ALTTEXT input ranges, isInputRange, std,range,primitives). +Iterating `r1` and `r2` in lockstep, `cmp` compares each element +`e1` of `r1` with the corresponding element `e2` in `r2`. If one +of the ranges has been finished, `cmp` returns a negative value +if `r1` has fewer elements than `r2`, a positive value if `r1` +has more elements than `r2`, and `0` if the ranges have the same +number of elements. + +If the ranges are strings, `cmp` performs UTF decoding appropriately and compares the ranges one code point at a time. +A custom predicate may be specified, in which case `cmp` performs +a three-way lexicographical comparison using `pred`. Otherwise +the elements are compared using `opCmp`. + Params: - pred = The predicate used for comparison. + pred = Predicate used for comparison. Without a predicate + specified the ordering implied by `opCmp` is used. 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). - + `0` if the ranges compare equal. A negative value if `r1` is a prefix of `r2` or + the first differing element of `r1` is less than the corresponding element of `r2` + according to `pred`. A positive value if `r2` is a prefix of `r1` or the first + differing element of `r2` is less than the corresponding element of `r1` + according to `pred`. + +Note: + An earlier version of the documentation incorrectly stated that `-1` is the + only negative value returned and `1` is the only positive value returned. + Whether that is true depends on the types being compared. */ -int cmp(alias pred = "a < b", R1, R2)(R1 r1, R2 r2) -if (isInputRange!R1 && isInputRange!R2 && !(isSomeString!R1 && isSomeString!R2)) +auto cmp(R1, R2)(R1 r1, R2 r2) +if (isInputRange!R1 && isInputRange!R2) { - for (;; r1.popFront(), r2.popFront()) + alias E1 = ElementEncodingType!R1; + alias E2 = ElementEncodingType!R2; + + static if (isDynamicArray!R1 && isDynamicArray!R2 + && __traits(isUnsigned, E1) && __traits(isUnsigned, E2) + && E1.sizeof == 1 && E2.sizeof == 1 + // Both or neither must auto-decode. + && (is(immutable E1 == immutable char) == is(immutable E2 == immutable char))) { - 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; + // dstrcmp algorithm is correct for both ubyte[] and for char[]. + import core.internal.string : dstrcmp; + return dstrcmp(cast(const char[]) r1, cast(const char[]) r2); } -} - -/// 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) + else static if (!(isSomeString!R1 && isSomeString!R2)) { - 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; + for (;; r1.popFront(), r2.popFront()) + { + static if (is(typeof(r1.front.opCmp(r2.front)) R)) + alias Result = R; + else + alias Result = int; + if (r2.empty) return Result(!r1.empty); + if (r1.empty) return Result(-1); + static if (is(typeof(r1.front.opCmp(r2.front)))) + { + auto c = r1.front.opCmp(r2.front); + if (c != 0) return c; + } + else + { + auto a = r1.front, b = r2.front; + if (auto result = (b < a) - (a < b)) return result; + } + } } - - static if (typeof(r1[0]).sizeof == typeof(r2[0]).sizeof && isLessThan) + else { - static if (typeof(r1[0]).sizeof == 1) + static if (typeof(r1[0]).sizeof == typeof(r2[0]).sizeof) { - immutable len = min(r1.length, r2.length); - immutable result = __ctfe ? + return () @trusted + { + auto p1 = r1.ptr, p2 = r2.ptr, + pEnd = p1 + min(r1.length, r2.length); + for (; p1 != pEnd; ++p1, ++p2) { - 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; + if (*p1 != *p2) return cast(int) *p1 - cast(int) *p2; + } + static if (typeof(r1[0]).sizeof >= 2 && size_t.sizeof <= uint.sizeof) + return cast(int) r1.length - cast(int) r2.length; + else + return int(r1.length > r2.length) - int(r1.length < r2.length); + }(); } else { - auto p1 = r1.ptr, p2 = r2.ptr, - pEnd = p1 + min(r1.length, r2.length); - for (; p1 != pEnd; ++p1, ++p2) + import std.utf : decode; + + for (size_t i1, i2;;) { - if (*p1 != *p2) return threeWayInt(cast(int) *p1, cast(int) *p2); + if (i1 == r1.length) return -int(i2 < r2.length); + if (i2 == r2.length) return int(1); + immutable c1 = decode(r1, i1), + c2 = decode(r2, i2); + if (c1 != c2) return cast(int) c1 - cast(int) c2; } } - return threeWay(r1.length, r2.length); + } +} + +/// ditto +int cmp(alias pred, R1, R2)(R1 r1, R2 r2) +if (isInputRange!R1 && isInputRange!R2) +{ + static if (!(isSomeString!R1 && isSomeString!R2)) + { + for (;; r1.popFront(), r2.popFront()) + { + if (r2.empty) return !r1.empty; + if (r1.empty) return -1; + auto a = r1.front, b = r2.front; + if (binaryFun!pred(a, b)) return -1; + if (binaryFun!pred(b, a)) return 1; + } } else { + import std.utf : decode; + for (size_t i1, i2;;) { - if (i1 == r1.length) return threeWay(i2, r2.length); - if (i2 == r2.length) return threeWay(r1.length, i1); + if (i1 == r1.length) return -int(i2 < r2.length); + if (i2 == r2.length) return 1; immutable c1 = decode(r1, i1), c2 = decode(r2, i2); - if (c1 != c2) return threeWayInt(cast(int) c1, cast(int) c2); + if (c1 != c2) + { + if (binaryFun!pred(c2, c1)) return 1; + if (binaryFun!pred(c1, c2)) return -1; + } } } } /// -@safe unittest +pure @safe unittest { int result; @@ -712,6 +751,8 @@ if (isSomeString!R1 && isSomeString!R2) assert(result > 0); result = cmp("aaa", "aaa"d); assert(result == 0); + result = cmp("aaa"d, "aaa"d); + assert(result == 0); result = cmp(cast(int[])[], cast(int[])[]); assert(result == 0); result = cmp([1, 2, 3], [1, 2, 3]); @@ -724,129 +765,237 @@ if (isSomeString!R1 && isSomeString!R2) assert(result > 0); } +/// Example predicate that compares individual elements in reverse lexical order +pure @safe unittest +{ + int result; + + result = cmp!"a > b"("abc", "abc"); + assert(result == 0); + result = cmp!"a > b"("", ""); + assert(result == 0); + result = cmp!"a > b"("abc", "abcd"); + assert(result < 0); + result = cmp!"a > b"("abcd", "abc"); + assert(result > 0); + result = cmp!"a > b"("abc"d, "abd"); + assert(result > 0); + result = cmp!"a > b"("bbc", "abc"w); + assert(result < 0); + result = cmp!"a > b"("aaa", "aaaa"d); + assert(result < 0); + result = cmp!"a > b"("aaaa", "aaa"d); + assert(result > 0); + result = cmp!"a > b"("aaa", "aaa"d); + assert(result == 0); + result = cmp("aaa"d, "aaa"d); + assert(result == 0); + result = cmp!"a > b"(cast(int[])[], cast(int[])[]); + assert(result == 0); + result = cmp!"a > b"([1, 2, 3], [1, 2, 3]); + assert(result == 0); + result = cmp!"a > b"([1, 3, 2], [1, 2, 3]); + assert(result < 0); + result = cmp!"a > b"([1, 2, 3], [1L, 2, 3, 4]); + assert(result < 0); + result = cmp!"a > b"([1L, 2, 3], [1, 2]); + assert(result > 0); +} + +// cmp for string with custom predicate fails if distinct chars can compare equal +// https://issues.dlang.org/show_bug.cgi?id=18286 +@nogc nothrow pure @safe unittest +{ + static bool ltCi(dchar a, dchar b)// less than, case insensitive + { + import std.ascii : toUpper; + return toUpper(a) < toUpper(b); + } + static assert(cmp!ltCi("apple2", "APPLE1") > 0); + static assert(cmp!ltCi("apple1", "APPLE2") < 0); + static assert(cmp!ltCi("apple", "APPLE1") < 0); + static assert(cmp!ltCi("APPLE", "apple1") < 0); + static assert(cmp!ltCi("apple", "APPLE") == 0); +} + +// for non-string ranges check that opCmp is evaluated only once per pair. +// https://issues.dlang.org/show_bug.cgi?id=18280 +@nogc nothrow @safe unittest +{ + static int ctr = 0; + struct S + { + int opCmp(ref const S rhs) const + { + ++ctr; + return 0; + } + bool opEquals(T)(T o) const { return false; } + size_t toHash() const { return 0; } + } + immutable S[4] a; + immutable S[4] b; + immutable result = cmp(a[], b[]); + assert(result == 0, "neither should compare greater than the other!"); + assert(ctr == a.length, "opCmp should be called exactly once per pair of items!"); +} + +nothrow pure @safe @nogc unittest +{ + import std.array : staticArray; + // Test cmp when opCmp returns float. + struct F + { + float value; + float opCmp(const ref F rhs) const + { + return value - rhs.value; + } + bool opEquals(T)(T o) const { return false; } + size_t toHash() const { return 0; } + } + auto result = cmp([F(1), F(2), F(3)].staticArray[], [F(1), F(2), F(3)].staticArray[]); + assert(result == 0); + assert(is(typeof(result) == float)); + result = cmp([F(1), F(3), F(2)].staticArray[], [F(1), F(2), F(3)].staticArray[]); + assert(result > 0); + result = cmp([F(1), F(2), F(3)].staticArray[], [F(1), F(2), F(3), F(4)].staticArray[]); + assert(result < 0); + result = cmp([F(1), F(2), F(3)].staticArray[], [F(1), F(2)].staticArray[]); + assert(result > 0); +} + +nothrow pure @safe unittest +{ + // Parallelism (was broken by inferred return type "immutable int") + import std.parallelism : task; + auto t = task!cmp("foo", "bar"); +} + // equal /** -Compares two ranges for equality, as defined by predicate $(D pred) -(which is $(D ==) by default). +Compares two ranges for equality, as defined by predicate `pred` +(which is `==` 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). + different element types, as long as `pred(r1.front, r2.front)` + evaluates to `bool`. + Performs $(BIGOH min(r1.length, r2.length)) evaluations of `pred`. + + At least one of the ranges must be finite. If one range involved is infinite, the result is + (statically known to be) `false`. + + If the two ranges are different kinds of UTF code unit (`char`, `wchar`, or + `dchar`), then the arrays are compared using UTF decoding to avoid + accidentally integer-promoting units. 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) + `true` if and only if the two ranges compare _equal element + for element, according to binary predicate `pred`. +/ bool equal(Range1, Range2)(Range1 r1, Range2 r2) if (isInputRange!Range1 && isInputRange!Range2 && + !(isInfinite!Range1 && isInfinite!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) + // Use code points when comparing two ranges of UTF code units that aren't + // the same type. This is for backwards compatibility with autodecode + // strings. + enum useCodePoint = + isSomeChar!(ElementEncodingType!Range1) && isSomeChar!(ElementEncodingType!Range2) && + ElementEncodingType!Range1.sizeof != ElementEncodingType!Range2.sizeof; + + static if (useCodePoint) { - return r1.empty && r2.empty; + import std.utf : byDchar; + return equal(r1.byDchar, r2.byDchar); } - 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)) + else { - import std.utf : byCodeUnit; - - static if (isAutodecodableString!Range1) + static if (isInfinite!Range1 || isInfinite!Range2) { - return equal(r1.byCodeUnit, r2); + // No finite range can be ever equal to an infinite range. + return false; } - else + // Detect default pred and compatible dynamic arrays. + else static if (is(typeof(pred) == string) && pred == "a == b" && + isArray!Range1 && isArray!Range2 && is(typeof(r1 == r2))) { - return equal(r2.byCodeUnit, r1); + return r1 == r2; } - } - //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 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(immutable ElementEncodingType!Range1 == immutable ElementEncodingType!Range2)) { - if (!binaryFun!(pred)(r1.front, r2.front)) return false; + import std.utf : byCodeUnit; + + static if (isAutodecodableString!Range1) + return equal(r1.byCodeUnit, r2); + else + return equal(r2.byCodeUnit, r1); } - return true; - } - else - { - //Generic case, we have to walk both ranges making sure neither is empty - for (; !r1.empty; r1.popFront(), r2.popFront()) + // Try a fast implementation when the ranges have comparable lengths. + else static if (hasLength!Range1 && hasLength!Range2 && is(typeof(r1.length == r2.length))) { - if (r2.empty) return false; - if (!binaryFun!(pred)(r1.front, r2.front)) return false; + 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; } - static if (!isInfinite!Range1) + else + { + //Generic case, we have to walk both ranges making sure neither is empty + for (; !r1.empty; r1.popFront(), r2.popFront()) + { + if (r2.empty || !binaryFun!(pred)(r1.front, r2.front)) return false; + } return r2.empty; + } } } } /// -@safe unittest +@safe @nogc unittest { import std.algorithm.comparison : equal; - import std.math : approxEqual; + import std.math.operations : isClose; - int[] a = [ 1, 2, 4, 3 ]; - assert(!equal(a, a[1..$])); - assert(equal(a, a)); - assert(equal!((a, b) => a == b)(a, a)); + int[4] 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)); + double[4] 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)); + double[4] c = [ 1.0000000005, 2, 4, 3]; + assert(equal!isClose(b[], c[])); } /++ -Tip: $(D equal) can itself be used as a predicate to other functions. +Tip: `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. In particular, `equal` can be its own predicate, allowing range of range (of range...) comparisons. +/ @safe unittest @@ -864,7 +1013,7 @@ range of range (of range...) comparisons. import std.algorithm.iteration : map; import std.internal.test.dummyrange : ReferenceForwardRange, ReferenceInputRange, ReferenceInfiniteForwardRange; - import std.math : approxEqual; + import std.math.operations : isClose; // various strings assert(equal("æøå", "æøå")); //UTF8 vs UTF8 @@ -898,11 +1047,11 @@ range of range (of range...) comparisons. 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))); + double[] c = [ 1.0000000005, 2, 4, 3]; + assert(equal!isClose(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))); + assert(!equal!isClose(map!"a*3"(b), map!"a*2"(c))); //Tests with some fancy reference ranges. ReferenceInputRange!int cir = new ReferenceInputRange!int([1, 2, 4, 3]); @@ -923,19 +1072,33 @@ range of range (of range...) comparisons. assert(!equal(cir, ifr)); } -@safe pure unittest +@safe @nogc pure unittest { - import std.utf : byChar, byWchar, byDchar; + import std.utf : byChar, byDchar, byWchar; assert(equal("æøå".byChar, "æøå")); + assert(equal("æøå".byChar, "æøå"w)); + assert(equal("æøå".byChar, "æøå"d)); assert(equal("æøå", "æøå".byChar)); + assert(equal("æøå"w, "æøå".byChar)); + assert(equal("æøå"d, "æøå".byChar)); + + assert(equal("æøå".byWchar, "æøå")); assert(equal("æøå".byWchar, "æøå"w)); + assert(equal("æøå".byWchar, "æøå"d)); + assert(equal("æøå", "æøå".byWchar)); assert(equal("æøå"w, "æøå".byWchar)); + assert(equal("æøå"d, "æøå".byWchar)); + + assert(equal("æøå".byDchar, "æøå")); + assert(equal("æøå".byDchar, "æøå"w)); assert(equal("æøå".byDchar, "æøå"d)); + assert(equal("æøå", "æøå".byDchar)); + assert(equal("æøå"w, "æøå".byDchar)); assert(equal("æøå"d, "æøå".byDchar)); } -@safe pure unittest +@safe @nogc pure unittest { struct R(bool _empty) { enum empty = _empty; @@ -956,37 +1119,14 @@ range of range (of range...) comparisons. 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: +another. Given sequences `s` (source) and `t` (target), a +sequence of `EditOp` encodes the steps that need to be taken to +convert `s` into `t`. For example, if `s = "cat"` and $(D +"cars"), the minimal sequence that transforms `s` into `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 @@ -1074,7 +1214,8 @@ private: import core.checkedint : mulu; bool overflow; const rc = mulu(r, c, overflow); - if (overflow) assert(0); + assert(!overflow, "Overflow during multiplication to determine number " + ~ " of matrix elements"); rows = r; cols = c; if (_matrix.length < rc) @@ -1082,7 +1223,8 @@ private: import core.exception : onOutOfMemoryError; import core.stdc.stdlib : realloc; const nbytes = mulu(rc, _matrix[0].sizeof, overflow); - if (overflow) assert(0); + assert(!overflow, "Overflow during multiplication to determine " + ~ " number of bytes of matrix"); auto m = cast(CostType *) realloc(_matrix.ptr, nbytes); if (!m) onOutOfMemoryError(); @@ -1193,10 +1335,10 @@ private: /** 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. +distance) between `s` and `t`. The Levenshtein distance computes +the minimal amount of edit operations necessary to transform `s` +into `t`. Performs $(BIGOH s.length * t.length) evaluations of $(D +equals) and occupies $(BIGOH min(s.length, t.length)) storage. Params: equals = The binary predicate to compare the elements of the two ranges. @@ -1244,7 +1386,7 @@ if (isForwardRange!(Range1) && isForwardRange!(Range2)) return eq(s.front, t.front) ? 0 : 1; } - if (slen > tlen) + if (slen < tlen) { Levenshtein!(Range1, eq, size_t) lev; return lev.distanceLowMem(s, t, slen, tlen); @@ -1305,8 +1447,8 @@ if (isConvertibleToString!Range1 || isConvertibleToString!Range2) } /** -Returns the Levenshtein distance and the edit path between $(D s) and -$(D t). +Returns the Levenshtein distance and the edit path between `s` and +`t`. Params: equals = The binary predicate to compare the elements of the two ranges. @@ -1367,50 +1509,73 @@ if (isConvertibleToString!Range1 || isConvertibleToString!Range2) assert(levenshteinDistanceAndPath(S("cat"), "rat")[0] == 1); } + // max /** -Iterates the passed arguments and return the maximum value. +Iterates the passed arguments and returns the maximum value. Params: args = The values to select the maximum from. At least two arguments must - be passed. + be passed, and they must be comparable with `<`. Returns: - The maximum of the passed-in args. The type of the returned value is + The maximum of the passed-in values. The type of the returned value is the type among the passed arguments that is able to store the largest value. + If at least one of the arguments is NaN, the result is an unspecified value. + See $(REF maxElement, std,algorithm,searching) for examples on how to cope + with NaNs. See_Also: $(REF maxElement, std,algorithm,searching) */ -MaxType!T max(T...)(T args) -if (T.length >= 2) +auto max(T...)(T args) +if (T.length >= 2 && !is(CommonType!T == void)) { - //Get "a" - static if (T.length <= 2) + // Get left-hand side of the comparison. + static if (T.length == 2) alias a = args[0]; else - auto a = max(args[0 .. ($+1)/2]); + auto a = max(args[0 .. ($ + 1) / 2]); alias T0 = typeof(a); - //Get "b" + // Get right-hand side. static if (T.length <= 3) - alias b = args[$-1]; + alias b = args[$ - 1]; else - auto b = max(args[($+1)/2 .. $]); + 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)); + "Invalid arguments: Cannot compare types " ~ T0.stringof ~ + " and " ~ T1.stringof ~ " for ordering."); + + // Compute the returned type. + static if (is(typeof(mostNegative!T0 < mostNegative!T1))) + // Both are numeric (or character or Boolean), so we choose the one with the highest maximum. + // (We use mostNegative for num/bool/char testing purposes even if it's not used otherwise.) + alias Result = Select!(T1.max > T0.max, T1, T0); + else + // At least one is non-numeric, so just go with the common type. + alias Result = CommonType!(T0, T1); - //Do the "max" proper with a and b + // Perform the computation. import std.functional : lessThan; immutable chooseB = lessThan!(T0, T1)(a, b); - return cast(typeof(return)) (chooseB ? b : a); + return cast(Result) (chooseB ? b : a); +} + +/// ditto +T max(T, U)(T a, U b) +if (is(T == U) && is(typeof(a < b))) +{ + /* Handle the common case without all the template expansions + * of the general case + */ + return a < b ? b : a; } /// -@safe unittest +@safe @betterC @nogc unittest { int a = 5; short b = 6; @@ -1423,7 +1588,7 @@ if (T.length >= 2) assert(e == 2); } -@safe unittest +@safe unittest // not @nogc due to `Date` { int a = 5; short b = 6; @@ -1452,77 +1617,76 @@ if (T.length >= 2) 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. +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. The type of the returned value is + the type among the passed arguments that is able to store the smallest value. + If at least one of the arguments is NaN, the result is an unspecified value. + See $(REF minElement, std,algorithm,searching) for examples on how to cope + with NaNs. + See_Also: $(REF minElement, std,algorithm,searching) */ -MinType!T min(T...)(T args) -if (T.length >= 2) +auto min(T...)(T args) +if (T.length >= 2 && !is(CommonType!T == void)) { - //Get "a" + // Get the left-hand side of the comparison. static if (T.length <= 2) alias a = args[0]; else - auto a = min(args[0 .. ($+1)/2]); + auto a = min(args[0 .. ($ + 1) / 2]); alias T0 = typeof(a); - //Get "b" + // Get the right-hand side. static if (T.length <= 3) - alias b = args[$-1]; + alias b = args[$ - 1]; else - auto b = min(args[($+1)/2 .. $]); + 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)); + "Invalid arguments: Cannot compare types " ~ T0.stringof ~ + " and " ~ T1.stringof ~ " for ordering."); + + // Compute the returned type. + static if (is(typeof(mostNegative!T0 < mostNegative!T1))) + // Both are numeric (or character or Boolean), so we choose the one with the lowest minimum. + // If they have the same minimum, choose the one with the smallest size. + // If both mostNegative and sizeof are equal, go for stability: pick the type of the first one. + alias Result = Select!(mostNegative!T1 < mostNegative!T0 || + mostNegative!T1 == mostNegative!T0 && T1.sizeof < T0.sizeof, + T1, T0); + else + // At least one is non-numeric, so just go with the common type. + alias Result = CommonType!(T0, T1); - //Do the "min" proper with a and b + // Engage! import std.functional : lessThan; - immutable chooseA = lessThan!(T0, T1)(a, b); - return cast(typeof(return)) (chooseA ? a : b); + immutable chooseB = lessThan!(T1, T0)(b, a); + return cast(Result) (chooseB ? b : a); +} + +/// ditto +T min(T, U)(T a, U b) +if (is(T == U) && is(typeof(a < b))) +{ + /* Handle the common case without all the template expansions + * of the general case + */ + return b < a ? b : a; } + /// -@safe unittest +@safe @nogc @betterC unittest { int a = 5; short b = 6; @@ -1533,15 +1697,31 @@ if (T.length >= 2) auto e = min(a, b, c); static assert(is(typeof(e) == double)); assert(e == 2); + ulong f = 0xffff_ffff_ffff; + const uint g = min(f, 0xffff_0000); + assert(g == 0xffff_0000); + dchar h = 100; + uint i = 101; + static assert(is(typeof(min(h, i)) == dchar)); + static assert(is(typeof(min(i, h)) == uint)); + assert(min(h, i) == 100); +} - // With arguments of mixed signedness, the return type is the one that can - // store the lowest values. - a = -10; +/** +With arguments of mixed signedness, the return type is the one that can +store the lowest values. +*/ +@safe @nogc @betterC unittest +{ + int 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. +/// User-defined types that support comparison with < are supported. +@safe unittest // not @nogc due to `Date` +{ 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)); @@ -1553,16 +1733,26 @@ if (T.length >= 2) assert(min(Date.max, Date.min) == Date.min); } +// min must be stable: when in doubt, return the first argument. +@safe unittest +{ + assert(min(1.0, double.nan) == 1.0); + assert(min(double.nan, 1.0) is double.nan); + static struct A { + int x; + string y; + int opCmp(const A a) const { return int(x > a.x) - int(x < a.x); } + } + assert(min(A(1, "first"), A(1, "second")) == A(1, "first")); +} + // mismatch /** -Sequentially compares elements in $(D r1) and $(D r2) in lockstep, and -stops at the first mismatch (according to $(D pred), by default +Sequentially compares elements in `r1` and `r2` in lockstep, and +stops at the first mismatch (according to `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) +evaluations of `pred`. */ Tuple!(Range1, Range2) mismatch(alias pred = "a == b", Range1, Range2)(Range1 r1, Range2 r2) @@ -1576,32 +1766,34 @@ if (isInputRange!(Range1) && isInputRange!(Range2)) } /// -@safe unittest +@safe @nogc unittest { - int[] x = [ 1, 5, 2, 7, 4, 3 ]; - double[] y = [ 1.0, 5, 2, 7.3, 4, 8 ]; - auto m = mismatch(x, y); + int[6] x = [ 1, 5, 2, 7, 4, 3 ]; + double[6] 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 +@safe @nogc 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]); + import std.range : only; + + int[3] a = [ 1, 2, 3 ]; + int[4] b = [ 1, 2, 4, 5 ]; + auto mm = mismatch(a[], b[]); + assert(equal(mm[0], only(3))); + assert(equal(mm[1], only(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. +`choices` needs to be composed of pairs of test expressions and return +expressions. Each test-expression is compared with `switchExpression` using +`pred`(`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. @@ -1622,7 +1814,7 @@ 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 +yield true with any test expression - `SwitchError` is thrown. $(D SwitchError) is also thrown if a void return expression was executed without throwing anything. */ @@ -1734,74 +1926,48 @@ auto predSwitch(alias pred = "a == b", T, R ...)(T switchExpression, lazy R choi /** 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 +optimized to always take advantage of the `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)). +Infinite ranges are considered of the same length. An infinite range has never the same length as a +finite range. + 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. + `true` if both ranges have the same length, `false` otherwise. */ bool isSameLength(Range1, Range2)(Range1 r1, Range2 r2) -if (isInputRange!Range1 && - isInputRange!Range2 && - !isInfinite!Range1 && - !isInfinite!Range2) +if (isInputRange!Range1 && isInputRange!Range2) { - static if (hasLength!(Range1) && hasLength!(Range2)) + static if (isInfinite!Range1 || isInfinite!Range2) + { + return isInfinite!Range1 && isInfinite!Range2; + } + else 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); + return r2.walkLength(r1.length + 1) == 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); + return r1.walkLength(r2.length + 1) == r2.length; } else { - while (!r1.empty) + for (; !r1.empty; r1.popFront, r2.popFront) { if (r2.empty) - { return false; - } - - r1.popFront; - r2.popFront; } - return r2.empty; } } @@ -1823,7 +1989,7 @@ if (isInputRange!Range1 && } // Test CTFE -@safe pure unittest +@safe @nogc pure @betterC unittest { enum result1 = isSameLength([1, 2, 3], [4, 5, 6]); static assert(result1); @@ -1832,6 +1998,14 @@ if (isInputRange!Range1 && static assert(!result2); } +@safe @nogc pure unittest +{ + import std.range : only; + assert(isSameLength(only(1, 2, 3), only(4, 5, 6))); + assert(isSameLength(only(0.3, 90.4, 23.7, 119.2), only(42.6, 23.6, 95.5, 6.3))); + assert(!isSameLength(only(1, 3, 3), only(4, 5))); +} + @safe nothrow pure unittest { import std.internal.test.dummyrange; @@ -1861,38 +2035,38 @@ if (isInputRange!Range1 && assert(!isSameLength(r11, r12)); } -/// For convenience +// Still functional but not documented anymore. 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 function can allocate if the `Yes.allocateGC` flag is passed. This has +the benefit of have better complexity than the `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) +element's `toHash` method. If customized equality is needed, then the `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). +how to define `pred`. Non-allocating forward range option: $(BIGOH n^2) -Non-allocating forward range option with custom $(D pred): $(BIGOH n^2) +Non-allocating forward range option with custom `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) + allocateGC = `Yes.allocateGC`/`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). + `true` if all of the elements in `r1` appear the same number of times in `r2`. + Otherwise, returns `false`. */ -bool isPermutation(AllocateGC allocate_gc, Range1, Range2) +bool isPermutation(Flag!"allocateGC" allocateGC, Range1, Range2) (Range1 r1, Range2 r2) -if (allocate_gc == Yes.allocateGC && +if (allocateGC == Yes.allocateGC && isForwardRange!Range1 && isForwardRange!Range2 && !isInfinite!Range1 && @@ -2111,7 +2285,7 @@ if (alternatives.length >= 1 && } /// -@safe pure unittest +@safe pure @betterC unittest { const a = 1; const b = 2; @@ -2130,7 +2304,11 @@ if (alternatives.length >= 1 && auto ef = either(e, f); static assert(is(typeof(ef) == int)); assert(ef == f); +} +/// +@safe pure unittest +{ immutable p = 1; immutable q = 2; auto pq = either(p, q); @@ -2141,7 +2319,11 @@ if (alternatives.length >= 1 && assert(either(0, 4) == 4); assert(either(0, 0) == 0); assert(either("", "a") == ""); +} +/// +@safe pure unittest +{ string r = null; assert(either(r, "a") == "a"); assert(either("a", "") == "a"); |