.gitmodules Dockerfile.aarch64 LICENSE NOTICE cpp_example/eye.d cpp_example/init_rcarray.d cpp_example/main.cpp cpp_example/meson.build dub.sdl include/mir/interpolate.h include/mir/ndslice.h include/mir/numeric.h include/mir/rcarray.h include/mir/rcptr.h include/mir/series.h include/mir/slim_rcptr.h include/mir/small_string.h index.d meson.build ndslice.graffle ndslice.svg source/mir/algebraic_alias/json.d source/mir/algorithm/iteration.d source/mir/algorithm/setops.d source/mir/appender.d source/mir/array/allocation.d source/mir/bignum/decimal.d source/mir/bignum/fixed.d source/mir/bignum/fp.d source/mir/bignum/integer.d source/mir/bignum/internal/dec2flt_table.d source/mir/bignum/internal/ryu/generic_128.d source/mir/bignum/low_level_view.d source/mir/combinatorics/package.d source/mir/container/binaryheap.d source/mir/cpp_export/numeric.d source/mir/date.d source/mir/format.d source/mir/format_impl.d source/mir/graph/package.d source/mir/graph/tarjan.d source/mir/interpolate/constant.d source/mir/interpolate/generic.d source/mir/interpolate/linear.d source/mir/interpolate/package.d source/mir/interpolate/polynomial.d source/mir/interpolate/spline.d source/mir/interpolate/utility.d source/mir/lob.d source/mir/math/func/expdigamma.d source/mir/math/numeric.d source/mir/math/stat.d source/mir/math/sum.d source/mir/ndslice/allocation.d source/mir/ndslice/chunks.d source/mir/ndslice/concatenation.d source/mir/ndslice/connect/cpython.d source/mir/ndslice/dynamic.d source/mir/ndslice/field.d source/mir/ndslice/filling.d source/mir/ndslice/fuse.d source/mir/ndslice/internal.d source/mir/ndslice/iterator.d source/mir/ndslice/mutation.d source/mir/ndslice/ndfield.d source/mir/ndslice/package.d source/mir/ndslice/slice.d source/mir/ndslice/sorting.d source/mir/ndslice/topology.d source/mir/ndslice/traits.d source/mir/numeric.d source/mir/parse.d source/mir/polynomial.d source/mir/range.d source/mir/rc/array.d source/mir/rc/context.d source/mir/rc/package.d source/mir/rc/ptr.d source/mir/rc/slim_ptr.d source/mir/serde.d source/mir/series.d source/mir/small_array.d source/mir/small_string.d source/mir/string_map.d source/mir/timestamp.d source/mir/type_info.d subprojects/mir-core.wrap test_travis.sh <<<<<< network # path=./source-mir-bignum-internal-dec2flt_table.lst |/++ |Tables of approximations of powers of ten. |DO NOT MODIFY: Generated by `etc/dec2flt_table.py` |+/ |module mir.bignum.internal.dec2flt_table; | |enum min_p10_e = -512; |enum max_p10_e = 512; | |// |static immutable align(16) ulong[2][1025] p10_coefficients = [ | [0x9049EE32DB23D21C, 0x7132D332E3F204D5], | [0xB45C69BF91ECC6A4, 0x8D7F87FF9CEE860A], | [0xE173842F7667F84C, 0x70DF69FF842A278D], | [0x8CE8329DAA00FB30, 0xC68BA23FB29A58B8], | [0xB0223F45148139FC, 0xB82E8ACF9F40EEE6], | [0xDC2ACF1659A1887B, 0xA63A2D8387112A9F], | [0x899AC16DF804F54D, 0xA7E45C72346ABAA4], | [0xAC0171C9760632A0, 0xD1DD738EC185694D], | [0xD701CE3BD387BF48, 0xC654D07271E6C3A0], | [0x866120E56434D78D, 0xDBF5024787303A44], | [0xA7F9691EBD420D70, 0x12F242D968FC48D5], | [0xD1F7C3666C9290CC, 0x17AED38FC33B5B0A], | [0x833ADA2003DB9A80, 0x8ECD4439DA0518E6], | [0xA40990A804D2811F, 0x7280954850865F20], | [0xCD0BF4D206072167, 0x4F20BA9A64A7F6E8], | [0x8027790343C474E1, 0x917474A07EE8FA51], | [0xA031574414B59219, 0xB5D191C89EA338E5], | [0xC83DAD1519E2F69F, 0xE345F63AC64C071E], | [0xFA4D185A605BB447, 0x9C1773C977DF08E6], | [0x9C702F387C3950AC, 0x218EA85DEAEB6590], | [0xC38C3B069B47A4D7, 0x29F2527565A63EF4], | [0xF46F49C842198E0D, 0xF46EE712BF0FCEB0], | [0x98C58E1D294FF8C8, 0x18C5506BB769E12E], | [0xBEF6F1A473A3F6FA, 0x1EF6A486A544597A], | [0xEEB4AE0D908CF4B9, 0xA6B44DA84E956FD8], | [0x9530ECC87A5818F3, 0x6830B089311D65E7], | [0xBA7D27FA98EE1F30, 0x423CDCAB7D64BF61], | [0xE91C71F93F29A6FC, 0x52CC13D65CBDEF39], | [0x91B1C73BC77A085E, 0xB3BF8C65F9F6B584], | [0xB61E390AB9588A75, 0x20AF6F7F787462E5], | [0xE3A5C74D67AEAD12, 0x68DB4B5F56917B9E], | [0x8E479C9060CD2C2C, 0x81890F1B961AED43], | [0xB1D983B479007736, 0x61EB52E27BA1A893], | [0xDE4FE4A197409504, 0xFA66279B1A8A12B8], | [0x8AF1EEE4FE885D22, 0x7C7FD8C0F0964BB3], | [0xADAE6A9E3E2A746B, 0x1B9FCEF12CBBDEA0], | [0xD91A0545CDB51186, 0xE287C2AD77EAD648], | [0x87B0434BA0912AF4, 0xAD94D9AC6AF2C5ED], | [0xA99C541E88B575B1, 0x98FA101785AF7768], | [0xD40369262AE2D31D, 0xBF38941D671B5542], | [0x848221B7DACDC3F2, 0xF7835C9260711549], | [0xA5A2AA25D18134EE, 0x756433B6F88D5A9C], | [0xCF0B54AF45E1822A, 0x12BD40A4B6B0B143], | [0x816714ED8BACF15A, 0x4BB64866F22E6ECA], | [0xA1C0DA28EE982DB1, 0xDEA3DA80AEBA0A7C], | [0xCA3110B32A3E391D, 0x164CD120DA688D1B], | [0xFCBD54DFF4CDC764, 0x5BE005691102B062], | [0x9DF6550BF9009C9F, 0xB96C0361AAA1AE3D], | [0xC573EA4EF740C3C6, 0x67C7043A154A19CC], | [0xF6D0E4E2B510F4B8, 0x01B8C5489A9CA040], | [0x9A428F0DB12A98F3, 0x01137B4D60A1E428], | [0xC0D332D11D753F30, 0xC1585A20B8CA5D32], | [0xF107FF8564D28EFC, 0xB1AE70A8E6FCF47E], | [0x96A4FFB35F03995D, 0x4F0D0669905E18CF], | [0xBC4E3FA036C47FB5, 0xA2D04803F4759F02], | [0xEB61CF8844759FA2, 0xCB845A04F19306C3], | [0x931D21B52AC983C5, 0x1F32B84316FBE43A], | [0xB7E46A22757BE4B6, 0x66FF6653DCBADD48], | [0xE5DD84AB12DADDE4, 0x00BF3FE8D3E9949A], | [0x8FAA72EAEBC8CAAF, 0x807787F18471FCE1], | [0xB3950FA5A6BAFD5A, 0x209569EDE58E7C19], | [0xE07A538F1069BCB1, 0xA8BAC4695EF21B1F], | [0x8C4C74396A4215EE, 0x6974BAC1DB5750F3], | [0xAF5F9147C4D29B6A, 0x03D1E972522D2530], | [0xDB377599B6074245, 0x84C663CEE6B86E7C], | [0x8902A98011C4896B, 0xD2FBFE615033450E], | [0xAB4353E01635ABC6, 0x87BAFDF9A4401651], | [0xD61428D81BC316B7, 0xE9A9BD780D501BE5], | [0x85CC99871159EE32, 0x520A166B0852116F], | [0xA73FBFE8D5B069BF, 0xE68C9C05CA6695CB], | [0xD10FAFE30B1C842F, 0xA02FC3073D003B3E], | [0x82A9CDEDE6F1D29D, 0x241DD9E486202507], | [0xA354416960AE4744, 0x6D25505DA7A82E48], | [0xCC2951C3B8D9D916, 0x886EA475119239DA], | [0xFF33A634A7104F5B, 0xEA8A4D9255F6C851], | [0x9F8047E0E86A3199, 0xD296707B75BA3D33], | [0xC76059D92284BDFF, 0x073C0C9A5328CC7F], | [0xF938704F6B25ED7F, 0xC90B0FC0E7F2FF9F], | [0x9BC34631A2F7B46F, 0x3DA6E9D890F7DFC3], | [0xC2B417BE0BB5A18B, 0x0D10A44EB535D7B4], | [0xF3611DAD8EA309EE, 0xD054CD6262834DA1], | [0x981CB28C7925E635, 0xA235005D7D921085], | [0xBE23DF2F976F5FC2, 0xCAC24074DCF694A6], | [0xEDACD6FB7D4B37B2, 0x3D72D092143439D0], | [0x948C065D2E4F02CF, 0x6667C25B4CA0A422], | [0xB9AF07F479E2C383, 0x4001B2F21FC8CD2A], | [0xE81AC9F1985B7464, 0x10021FAEA7BB0075], | [0x9110BE36FF3928BF, 0x8A0153CD28D4E049], | [0xB554EDC4BF0772EE, 0x2C81A8C0730A185B], | [0xE2AA2935EEC94FAA, 0xB7A212F08FCC9E72], | [0x8DAA59C1B53DD1CA, 0x12C54BD659DFE307], | [0xB114F032228D463D, 0x97769ECBF057DBC9], | [0xDD5A2C3EAB3097CC, 0xBD54467EEC6DD2BB], | [0x8A585BA72AFE5EDF, 0x5654AC0F53C4A3B5], | [0xACEE7290F5BDF697, 0x2BE9D71328B5CCA2], | [0xD82A0F35332D743D, 0xF6E44CD7F2E33FCB], | [0x871A49813FFC68A6, 0x1A4EB006F7CE07DF], | [0xA8E0DBE18FFB82D0, 0xA0E25C08B5C189D7], | [0xD31912D9F3FA6384, 0x891AF30AE331EC4C], | [0x83EFABC8387C7E32, 0x35B0D7E6CDFF33B0], | [0xA4EB96BA469B9DBF, 0xC31D0DE0817F009C], | [0xCE267C68D842852E, 0x73E45158A1DEC0C2], | [0x80D80DC18729933D, 0x086EB2D7652B387A], | [0xA10E1131E8F3F80C, 0x4A8A5F8D3E760698], | [0xC951957E6330F60F, 0x5D2CF7708E13883E], | [0xFBA5FADDFBFD3393, 0x3478354CB1986A4D], | [0x9D47BCCABD7E403C, 0x00CB214FEEFF4270], | [0xC499ABFD6CDDD04B, 0x00FDE9A3EABF130C], | [0xF5C016FCC815445E, 0xC13D640CE56ED7D0], | [0x99980E5DFD0D4ABB, 0x98C65E880F6546E2], | [0xBFFE11F57C509D69, 0x3EF7F62A133E989A], | [0xEFFD9672DB64C4C4, 0x8EB5F3B4980E3EC1], | [0x95FE7E07C91EFAFA, 0x3931B850DF08E738], | [0xBB7E1D89BB66B9B9, 0xC77E266516CB2106], | [0xEA5DA4EC2A406827, 0xF95DAFFE5C7DE948], | [0x927A87139A684118, 0x5BDA8DFEF9CEB1CD], | [0xB71928D88102515E, 0x72D1317EB8425E40], | [0xE4DF730EA142E5B6, 0x0F857DDE6652F5D0], | [0x8F0BA7E924C9CF92, 0xC9B36EAAFFF3D9A2], | [0xB2CE91E36DFC4376, 0x3C204A55BFF0D00B], | [0xDF82365C497B5454, 0xCB285CEB2FED040E], | [0x8BB161F9ADED14B4, 0x5EF93A12FDF42288], | [0xAE9DBA78196859E1, 0x76B78897BD712B2B], | [0xDA4529161FC2705A, 0xD4656ABDACCD75F5], | [0x886B39ADD3D98638, 0x24BF62B68C0069B9], | [0xAA86081948CFE7C6, 0x2DEF3B642F008428], | [0xD5278A1F9B03E1B8, 0xB96B0A3D3AC0A531], | [0x8538B653C0E26D13, 0xD3E2E66644B8673F], | [0xA686E3E8B11B0858, 0x88DB9FFFD5E6810F], | [0xD0289CE2DD61CA6D, 0x6B1287FFCB602152], | [0x8219620DCA5D1E84, 0x62EB94FFDF1C14D3], | [0xA29FBA913CF46625, 0x7BA67A3FD6E31A08], | [0xCB47A9358C317FAF, 0xDA9018CFCC9BE08A], | [0xFE199382EF3DDF9B, 0x91341F03BFC2D8AD], | [0x9ECFFC31D586ABC1, 0x9AC0936257D9C76C], | [0xC683FB3E4AE856B1, 0xC170B83AEDD03947], | [0xF824FA0DDDA26C5D, 0xF1CCE649A9444799], | [0x9B171C48AA8583BA, 0x17200FEE09CAACC0], | [0xC1DCE35AD526E4A9, 0x9CE813E98C3D57EF], | [0xF2541C318A709DD3, 0xC42218E3EF4CADEB], | [0x9774919EF68662A4, 0xBA954F8E758FECB3], | [0xBD51B606B427FB4D, 0xA93AA37212F3E7E0], | [0xECA623886131FA20, 0xD3894C4E97B0E1D8], | [0x93E7D6353CBF3C54, 0xE435CFB11ECE8D27], | [0xB8E1CBC28BEF0B69, 0xDD43439D66823071], | [0xE71A3EB32EEACE43, 0x14941484C022BC8D], | [0x9070672FFD52C0EA, 0xECDC8CD2F815B5D8], | [0xB48C80FBFCA77124, 0x6813B007B61B234E], | [0xE1AFA13AFBD14D6E, 0x82189C09A3A1EC21], | [0x8D0DC4C4DD62D064, 0x714F618606453395], | [0xB05135F614BB847E, 0x8DA339E787D6807A], | [0xDC65837399EA659D, 0xF10C086169CC2099], | [0x89BF722840327F82, 0x16A7853CE21F945F], | [0xAC2F4EB2503F1F63, 0x9C51668C1AA77977], | [0xD73B225EE44EE73B, 0x4365C02F215157D5], | [0x8684F57B4EB15085, 0x0A1F981D74D2D6E5], | [0xA82632DA225DA4A6, 0x4CA77E24D2078C9E], | [0xD22FBF90AAF50DD0, 0xDFD15DAE06896FC6], | [0x835DD7BA6AD928A2, 0xEBE2DA8CC415E5DC], | [0xA4354DA9058F72CA, 0x66DB912FF51B5F53], | [0xCD42A11346F34F7D, 0x0092757BF2623727], | [0x8049A4AC0C5811AE, 0x205B896D777D6279], | [0xA05C0DD70F6E161A, 0xA8726BC8D55CBB17], | [0xC873114CD3499BA0, 0x128F06BB0AB3E9DD], | [0xFA8FD5A0081C0288, 0x1732C869CD60E454], | [0x9C99E58405118195, 0x0E7FBD42205C8EB4], | [0xC3C05EE50655E1FA, 0x521FAC92A873B261], | [0xF4B0769E47EB5A79, 0xE6A797B752909EFA], | [0x98EE4A22ECF3188C, 0x9028BED2939A635C], | [0xBF29DCABA82FDEAE, 0x7432EE873880FC33], | [0xEEF453D6923BD65A, 0x113FAA2906A13B40], | [0x9558B4661B6565F8, 0x4AC7CA59A424C508], | [0xBAAEE17FA23EBF76, 0x5D79BCF00D2DF64A], | [0xE95A99DF8ACE6F54, 0xF4D82C2C107973DC], | [0x91D8A02BB6C10594, 0x79071B9B8A4BE86A], | [0xB64EC836A47146FA, 0x9748E2826CDEE284], | [0xE3E27A444D8D98B8, 0xFD1B1B2308169B25], | [0x8E6D8C6AB0787F73, 0xFE30F0F5E50E20F7], | [0xB208EF855C969F50, 0xBDBD2D335E51A935], | [0xDE8B2B66B3BC4724, 0xAD2C788035E61382], | [0x8B16FB203055AC76, 0x4C3BCB5021AFCC31], | [0xADDCB9E83C6B1794, 0xDF4ABE242A1BBF3E], | [0xD953E8624B85DD79, 0xD71D6DAD34A2AF0D], | [0x87D4713D6F33AA6C, 0x8672648C40E5AD68], | [0xA9C98D8CCB009506, 0x680EFDAF511F18C2], | [0xD43BF0EFFDC0BA48, 0x0212BD1B2566DEF3], | [0x84A57695FE98746D, 0x014BB630F7604B58], | [0xA5CED43B7E3E9188, 0x419EA3BD35385E2E], | [0xCF42894A5DCE35EA, 0x52064CAC828675B9], | [0x818995CE7AA0E1B2, 0x7343EFEBD1940994], | [0xA1EBFB4219491A1F, 0x1014EBE6C5F90BF9], | [0xCA66FA129F9B60A7, 0xD41A26E077774EF7], | [0xFD00B897478238D1, 0x8920B098955522B5], | [0x9E20735E8CB16382, 0x55B46E5F5D5535B1], | [0xC5A890362FDDBC63, 0xEB2189F734AA831D], | [0xF712B443BBD52B7C, 0xA5E9EC7501D523E4], | [0x9A6BB0AA55653B2D, 0x47B233C92125366F], | [0xC1069CD4EABE89F9, 0x999EC0BB696E840A], | [0xF148440A256E2C77, 0xC00670EA43CA250D], | [0x96CD2A865764DBCA, 0x380406926A5E5728], | [0xBC807527ED3E12BD, 0xC605083704F5ECF2], | [0xEBA09271E88D976C, 0xF7864A44C633682F], | [0x93445B8731587EA3, 0x7AB3EE6AFBE0211D], | [0xB8157268FDAE9E4C, 0x5960EA05BAD82965], | [0xE61ACF033D1A45DF, 0x6FB92487298E33BE], | [0x8FD0C16206306BAC, 0xA5D3B6D479F8E057], | [0xB3C4F1BA87BC8697, 0x8F48A4899877186C], | [0xE0B62E2929ABA83C, 0x331ACDABFE94DE87], | [0x8C71DCD9BA0B4926, 0x9FF0C08B7F1D0B15], | [0xAF8E5410288E1B6F, 0x07ECF0AE5EE44DDA], | [0xDB71E91432B1A24B, 0xC9E82CD9F69D6150], | [0x892731AC9FAF056F, 0xBE311C083A225CD2], | [0xAB70FE17C79AC6CA, 0x6DBD630A48AAF407], | [0xD64D3D9DB981787D, 0x092CBBCCDAD5B108], | [0x85F0468293F0EB4E, 0x25BBF56008C58EA5], | [0xA76C582338ED2622, 0xAF2AF2B80AF6F24E], | [0xD1476E2C07286FAA, 0x1AF5AF660DB4AEE2], | [0x82CCA4DB847945CA, 0x50D98D9FC890ED4D], | [0xA37FCE126597973D, 0xE50FF107BAB528A1], | [0xCC5FC196FEFD7D0C, 0x1E53ED49A96272C9], | [0xFF77B1FCBEBCDC4F, 0x25E8E89C13BB0F7B], | [0x9FAACF3DF73609B1, 0x77B191618C54E9AD], | [0xC795830D75038C1E, 0xD59DF5B9EF6A2418], | [0xF97AE3D0D2446F25, 0x4B0573286B44AD1E], | [0x9BECCE62836AC577, 0x4EE367F9430AEC33], | [0xC2E801FB244576D5, 0x229C41F793CDA73F], | [0xF3A20279ED56D48A, 0x6B43527578C1110F], | [0x9845418C345644D7, 0x830A13896B78AAAA], | [0xBE5691EF416BD60C, 0x23CC986BC656D554], | [0xEDEC366B11C6CB8F, 0x2CBFBE86B7EC8AA9], | [0x94B3A202EB1C3F39, 0x7BF7D71432F3D6AA], | [0xB9E08A83A5E34F08, 0xDAF5CCD93FB0CC54], | [0xE858AD248F5C22CA, 0xD1B3400F8F9CFF69], | [0x91376C36D99995BE, 0x23100809B9C21FA2], | [0xB58547448FFFFB2E, 0xABD40A0C2832A78A], | [0xE2E69915B3FFF9F9, 0x16C90C8F323F516D], | [0x8DD01FAD907FFC3C, 0xAE3DA7D97F6792E4], | [0xB1442798F49FFB4B, 0x99CD11CFDF41779D], | [0xDD95317F31C7FA1D, 0x40405643D711D584], | [0x8A7D3EEF7F1CFC52, 0x482835EA666B2572], | [0xAD1C8EAB5EE43B67, 0xDA3243650005EECF], | [0xD863B256369D4A41, 0x90BED43E40076A83], | [0x873E4F75E2224E68, 0x5A7744A6E804A292], | [0xA90DE3535AAAE202, 0x711515D0A205CB36], | [0xD3515C2831559A83, 0x0D5A5B44CA873E04], | [0x8412D9991ED58092, 0xE858790AFE9486C2], | [0xA5178FFF668AE0B6, 0x626E974DBE39A873], | [0xCE5D73FF402D98E4, 0xFB0A3D212DC81290], | [0x80FA687F881C7F8E, 0x7CE66634BC9D0B9A], | [0xA139029F6A239F72, 0x1C1FFFC1EBC44E80], | [0xC987434744AC874F, 0xA327FFB266B56220], | [0xFBE9141915D7A922, 0x4BF1FF9F0062BAA8], | [0x9D71AC8FADA6C9B5, 0x6F773FC3603DB4A9], | [0xC4CE17B399107C23, 0xCB550FB4384D21D4], | [0xF6019DA07F549B2B, 0x7E2A53A146606A48], | [0x99C102844F94E0FB, 0x2EDA7444CBFC426D], | [0xC0314325637A193A, 0xFA911155FEFB5309], | [0xF03D93EEBC589F88, 0x793555AB7EBA27CB], | [0x96267C7535B763B5, 0x4BC1558B2F3458DF], | [0xBBB01B9283253CA3, 0x9EB1AAEDFB016F16], | [0xEA9C227723EE8BCB, 0x465E15A979C1CADC], | [0x92A1958A7675175F, 0x0BFACD89EC191ECA], | [0xB749FAED14125D37, 0xCEF980EC671F667C], | [0xE51C79A85916F485, 0x82B7E12780E7401B], | [0x8F31CC0937AE58D3, 0xD1B2ECB8B0908811], | [0xB2FE3F0B8599EF08, 0x861FA7E6DCB4AA15], | [0xDFBDCECE67006AC9, 0x67A791E093E1D49A], | [0x8BD6A141006042BE, 0xE0C8BB2C5C6D24E0], | [0xAECC49914078536D, 0x58FAE9F773886E19], | [0xDA7F5BF590966849, 0xAF39A475506A899F], | [0x888F99797A5E012D, 0x6D8406C952429603], | [0xAAB37FD7D8F58179, 0xC8E5087BA6D33B84], | [0xD5605FCDCF32E1D7, 0xFB1E4A9A90880A65], | [0x855C3BE0A17FCD26, 0x5CF2EEA09A55067F], | [0xA6B34AD8C9DFC070, 0xF42FAA48C0EA481F], | [0xD0601D8EFC57B08C, 0xF13B94DAF124DA27], | [0x823C12795DB6CE57, 0x76C53D08D6B70858], | [0xA2CB1717B52481ED, 0x54768C4B0C64CA6E], | [0xCB7DDCDDA26DA269, 0xA9942F5DCF7DFD0A], | [0xFE5D54150B090B03, 0xD3F93B35435D7C4C], | [0x9EFA548D26E5A6E2, 0xC47BC5014A1A6DB0], | [0xC6B8E9B0709F109A, 0x359AB6419CA1091B], | [0xF867241C8CC6D4C1, 0xC30163D203C94B62], | [0x9B407691D7FC44F8, 0x79E0DE63425DCF1D], | [0xC21094364DFB5637, 0x985915FC12F542E5], | [0xF294B943E17A2BC4, 0x3E6F5B7B17B2939E], | [0x979CF3CA6CEC5B5B, 0xA705992CEECF9C43], | [0xBD8430BD08277231, 0x50C6FF782A838353], | [0xECE53CEC4A314EBE, 0xA4F8BF5635246428], | [0x940F4613AE5ED137, 0x871B7795E136BE99], | [0xB913179899F68584, 0x28E2557B59846E3F], | [0xE757DD7EC07426E5, 0x331AEADA2FE589CF], | [0x9096EA6F3848984F, 0x3FF0D2C85DEF7622], | [0xB4BCA50B065ABE63, 0x0FED077A756B53AA], | [0xE1EBCE4DC7F16DFC, 0xD3E8495912C62894], | [0x8D3360F09CF6E4BD, 0x64712DD7ABBBD95D], | [0xB080392CC4349DED, 0xBD8D794D96AACFB4], | [0xDCA04777F541C568, 0xECF0D7A0FC5583A1], | [0x89E42CAAF9491B61, 0xF41686C49DB57245], | [0xAC5D37D5B79B6239, 0x311C2875C522CED6], | [0xD77485CB25823AC7, 0x7D633293366B828B], | [0x86A8D39EF77164BD, 0xAE5DFF9C02033197], | [0xA8530886B54DBDEC, 0xD9F57F830283FDFD], | [0xD267CAA862A12D67, 0xD072DF63C324FD7C], | [0x8380DEA93DA4BC60, 0x4247CB9E59F71E6D], | [0xA46116538D0DEB78, 0x52D9BE85F074E609], | [0xCD795BE870516656, 0x67902E276C921F8B], | [0x806BD9714632DFF6, 0x00BA1CD8A3DB53B7], | [0xA086CFCD97BF97F4, 0x80E8A40ECCD228A5], | [0xC8A883C0FDAF7DF0, 0x6122CD128006B2CE], | [0xFAD2A4B13D1B5D6C, 0x796B805720085F81], | [0x9CC3A6EEC6311A64, 0xCBE3303674053BB1], | [0xC3F490AA77BD60FD, 0xBEDBFC4411068A9D], | [0xF4F1B4D515ACB93C, 0xEE92FB5515482D44], | [0x991711052D8BF3C5, 0x751BDD152D4D1C4B], | [0xBF5CD54678EEF0B7, 0xD262D45A78A0635D], | [0xEF340A98172AACE5, 0x86FB897116C87C35], | [0x9580869F0E7AAC0F, 0xD45D35E6AE3D4DA1], | [0xBAE0A846D2195713, 0x8974836059CCA109], | [0xE998D258869FACD7, 0x2BD1A438703FC94B], | [0x91FF83775423CC06, 0x7B6306A34627DDCF], | [0xB67F6455292CBF08, 0x1A3BC84C17B1D543], | [0xE41F3D6A7377EECA, 0x20CABA5F1D9E4A94], | [0x8E938662882AF53E, 0x547EB47B7282EE9C], | [0xB23867FB2A35B28E, 0xE99E619A4F23AA43], | [0xDEC681F9F4C31F31, 0x6405FA00E2EC94D4], | [0x8B3C113C38F9F37F, 0xDE83BC408DD3DD05], | [0xAE0B158B4738705F, 0x9624AB50B148D446], | [0xD98DDAEE19068C76, 0x3BADD624DD9B0957], | [0x87F8A8D4CFA417CA, 0xE54CA5D70A80E5D6], | [0xA9F6D30A038D1DBC, 0x5E9FCF4CCD211F4C], | [0xD47487CC8470652B, 0x7647C3200069671F], | [0x84C8D4DFD2C63F3B, 0x29ECD9F40041E073], | [0xA5FB0A17C777CF0A, 0xF468107100525890], | [0xCF79CC9DB955C2CC, 0x7182148D4066EEB4], | [0x81AC1FE293D599C0, 0xC6F14CD848405531], | [0xA21727DB38CB0030, 0xB8ADA00E5A506A7D], | [0xCA9CF1D206FDC03C, 0xA6D90811F0E4851C], | [0xFD442E4688BD304B, 0x908F4A166D1DA663], | [0x9E4A9CEC15763E2F, 0x9A598E4E043287FE], | [0xC5DD44271AD3CDBA, 0x40EFF1E1853F29FE], | [0xF7549530E188C129, 0xD12BEE59E68EF47D], | [0x9A94DD3E8CF578BA, 0x82BB74F8301958CE], | [0xC13A148E3032D6E8, 0xE36A52363C1FAF02], | [0xF18899B1BC3F8CA2, 0xDC44E6C3CB279AC2], | [0x96F5600F15A7B7E5, 0x29AB103A5EF8C0B9], | [0xBCB2B812DB11A5DE, 0x7415D448F6B6F0E8], | [0xEBDF661791D60F56, 0x111B495B3464AD21], | [0x936B9FCEBB25C996, 0xCAB10DD900BEEC35], | [0xB84687C269EF3BFB, 0x3D5D514F40EEA742], | [0xE65829B3046B0AFA, 0x0CB4A5A3112A5113], | [0x8FF71A0FE2C2E6DC, 0x47F0E785EABA72AC], | [0xB3F4E093DB73A093, 0x59ED216765690F57], | [0xE0F218B8D25088B8, 0x306869C13EC3532C], | [0x8C974F7383725573, 0x1E414218C73A13FC], | [0xAFBD2350644EEAD0, 0xE5D1929EF90898FB], | [0xDBAC6C247D62A584, 0xDF45F746B74ABF39], | [0x894BC396CE5DA772, 0x6B8BBA8C328EB784], | [0xAB9EB47C81F5114F, 0x066EA92F3F326565], | [0xD686619BA27255A3, 0xC80A537B0EFEFEBE], | [0x8613FD0145877586, 0xBD06742CE95F5F37], | [0xA798FC4196E952E7, 0x2C48113823B73704], | [0xD17F3B51FCA3A7A1, 0xF75A15862CA504C5], | [0x82EF85133DE648C5, 0x9A984D73DBE722FB], | [0xA3AB66580D5FDAF6, 0xC13E60D0D2E0EBBA], | [0xCC963FEE10B7D1B3, 0x318DF905079926A9], | [0xFFBBCFE994E5C620, 0xFDF17746497F7053], | [0x9FD561F1FD0F9BD4, 0xFEB6EA8BEDEFA634], | [0xC7CABA6E7C5382C9, 0xFE64A52EE96B8FC1], | [0xF9BD690A1B68637B, 0x3DFDCE7AA3C673B1], | [0x9C1661A651213E2D, 0x06BEA10CA65C084F], | [0xC31BFA0FE5698DB8, 0x486E494FCFF30A62], | [0xF3E2F893DEC3F126, 0x5A89DBA3C3EFCCFB], | [0x986DDB5C6B3A76B8, 0xF89629465A75E01D], | [0xBE89523386091466, 0xF6BBB397F1135824], | [0xEE2BA6C0678B597F, 0x746AA07DED582E2D], | [0x94DB483840B717F0, 0xA8C2A44EB4571CDC], | [0xBA121A4650E4DDEC, 0x92F34D62616CE413], | [0xE896A0D7E51E1566, 0x77B020BAF9C81D18], | [0x915E2486EF32CD60, 0x0ACE1474DC1D122F], | [0xB5B5ADA8AAFF80B8, 0x0D819992132456BB], | [0xE3231912D5BF60E6, 0x10E1FFF697ED6C69], | [0x8DF5EFABC5979C90, 0xCA8D3FFA1EF463C2], | [0xB1736B96B6FD83B4, 0xBD308FF8A6B17CB2], | [0xDDD0467C64BCE4A1, 0xAC7CB3F6D05DDBDF], | [0x8AA22C0DBEF60EE4, 0x6BCDF07A423AA96B], | [0xAD4AB7112EB3929E, 0x86C16C98D2C953C6], | [0xD89D64D57A607745, 0xE871C7BF077BA8B8], | [0x87625F056C7C4A8B, 0x11471CD764AD4973], | [0xA93AF6C6C79B5D2E, 0xD598E40D3DD89BCF], | [0xD389B47879823479, 0x4AFF1D108D4EC2C3], | [0x843610CB4BF160CC, 0xCEDF722A585139BA], | [0xA54394FE1EEDB8FF, 0xC2974EB4EE658829], | [0xCE947A3DA6A9273E, 0x733D226229FEEA33], | [0x811CCC668829B887, 0x0806357D5A3F5260], | [0xA163FF802A3426A9, 0xCA07C2DCB0CF26F8], | [0xC9BCFF6034C13053, 0xFC89B393DD02F0B6], | [0xFC2C3F3841F17C68, 0xBBAC2078D443ACE3], | [0x9D9BA7832936EDC1, 0xD54B944B84AA4C0E], | [0xC5029163F384A931, 0x0A9E795E65D4DF11], | [0xF64335BCF065D37D, 0x4D4617B5FF4A16D6], | [0x99EA0196163FA42E, 0x504BCED1BF8E4E46], | [0xC06481FB9BCF8D3A, 0xE45EC2862F71E1D7], | [0xF07DA27A82C37088, 0x5D767327BB4E5A4D], | [0x964E858C91BA2655, 0x3A6A07F8D510F870], | [0xBBE226EFB628AFEB, 0x890489F70A55368C], | [0xEADAB0ABA3B2DBE5, 0x2B45AC74CCEA842F], | [0x92C8AE6B464FC96F, 0x3B0B8BC90012929D], | [0xB77ADA0617E3BBCB, 0x09CE6EBB40173745], | [0xE55990879DDCAABE, 0xCC420A6A101D0516], | [0x8F57FA54C2A9EAB7, 0x9FA946824A12232E], | [0xB32DF8E9F3546564, 0x47939822DC96ABF9], | [0xDFF9772470297EBD, 0x59787E2B93BC56F7], | [0x8BFBEA76C619EF36, 0x57EB4EDB3C55B65B], | [0xAEFAE51477A06B04, 0xEDE622920B6B23F1], | [0xDAB99E59958885C5, 0xE95FAB368E45ECED], | [0x88B402F7FD75539B, 0x11DBCB0218EBB414], | [0xAAE103B5FCD2A882, 0xD652BDC29F26A11A], | [0xD59944A37C0752A2, 0x4BE76D3346F04960], | [0x857FCAE62D8493A5, 0x6F70A4400C562DDC], | [0xA6DFBD9FB8E5B88F, 0xCB4CCD500F6BB953], | [0xD097AD07A71F26B2, 0x7E2000A41346A7A8], | [0x825ECC24C8737830, 0x8ED400668C0C28C9], | [0xA2F67F2DFA90563B, 0x728900802F0F32FB], | [0xCBB41EF979346BCA, 0x4F2B40A03AD2FFBA], | [0xFEA126B7D78186BD, 0xE2F610C84987BFA8], | [0x9F24B832E6B0F436, 0x0DD9CA7D2DF4D7C9], | [0xC6EDE63FA05D3144, 0x91503D1C79720DBB], | [0xF8A95FCF88747D94, 0x75A44C6397CE912A], | [0x9B69DBE1B548CE7D, 0xC986AFBE3EE11ABA], | [0xC24452DA229B021C, 0xFBE85BADCE996169], | [0xF2D56790AB41C2A3, 0xFAE27299423FB9C3], | [0x97C560BA6B0919A6, 0xDCCD879FC967D41A], | [0xBDB6B8E905CB600F, 0x5400E987BBC1C921], | [0xED246723473E3813, 0x290123E9AAB23B69], | [0x9436C0760C86E30C, 0xF9A0B6720AAF6521], | [0xB94470938FA89BCF, 0xF808E40E8D5B3E6A], | [0xE7958CB87392C2C3, 0xB60B1D1230B20E04], | [0x90BD77F3483BB9BA, 0xB1C6F22B5E6F48C3], | [0xB4ECD5F01A4AA828, 0x1E38AEB6360B1AF3], | [0xE2280B6C20DD5232, 0x25C6DA63C38DE1B0], | [0x8D590723948A535F, 0x579C487E5A38AD0E], | [0xB0AF48EC79ACE837, 0x2D835A9DF0C6D852], | [0xDCDB1B2798182245, 0xF8E431456CF88E66], | [0x8A08F0F8BF0F156B, 0x1B8E9ECB641B5900], | [0xAC8B2D36EED2DAC6, 0xE272467E3D222F40], | [0xD7ADF884AA879177, 0x5B0ED81DCC6ABB10], | [0x86CCBB52EA94BAEB, 0x98E947129FC2B4EA], | [0xA87FEA27A539E9A5, 0x3F2398D747B36224], | [0xD29FE4B18E88640F, 0x8EEC7F0D19A03AAD], | [0x83A3EEEEF9153E89, 0x1953CF68300424AC], | [0xA48CEAAAB75A8E2B, 0x5FA8C3423C052DD7], | [0xCDB02555653131B6, 0x3792F412CB06794D], | [0x808E17555F3EBF12, 0xE2BBD88BBEE40BD0], | [0xA0B19D2AB70E6ED6, 0x5B6ACEAEAE9D0EC4], | [0xC8DE047564D20A8C, 0xF245825A5A445275], | [0xFB158592BE068D2F, 0xEED6E2F0F0D56713], | [0x9CED737BB6C4183D, 0x55464DD69685606C], | [0xC428D05AA4751E4D, 0xAA97E14C3C26B887], | [0xF53304714D9265E0, 0xD53DD99F4B3066A8], | [0x993FE2C6D07B7FAC, 0xE546A8038EFE4029], | [0xBF8FDB78849A5F97, 0xDE98520472BDD033], | [0xEF73D256A5C0F77D, 0x963E66858F6D4440], | [0x95A8637627989AAE, 0xDDE7001379A44AA8], | [0xBB127C53B17EC159, 0x5560C018580D5D52], | [0xE9D71B689DDE71B0, 0xAAB8F01E6E10B4A7], | [0x9226712162AB070E, 0xCAB3961304CA70E8], | [0xB6B00D69BB55C8D1, 0x3D607B97C5FD0D22], | [0xE45C10C42A2B3B06, 0x8CB89A7DB77C506B], | [0x8EB98A7A9A5B04E3, 0x77F3608E92ADB243], | [0xB267ED1940F1C61C, 0x55F038B237591ED3], | [0xDF01E85F912E37A3, 0x6B6C46DEC52F6688], | [0x8B61313BBABCE2C6, 0x2323AC4B3B3DA015], | [0xAE397D8AA96C1B78, 0xABEC975E0A0D081B], | [0xD9C7DCED53C72256, 0x96E7BD358C904A21], | [0x881CEA14545C7575, 0x7E50D64177DA2E55], | [0xAA242499697392D3, 0xDDE50BD1D5D0B9EA], | [0xD4AD2DBFC3D07788, 0x955E4EC64B44E864], | [0x84EC3C97DA624AB5, 0xBD5AF13BEF0B113F], | [0xA6274BBDD0FADD62, 0xECB1AD8AEACDD58E], | [0xCFB11EAD453994BA, 0x67DE18EDA5814AF2], | [0x81CEB32C4B43FCF5, 0x80EACF948770CED7], | [0xA2425FF75E14FC32, 0xA1258379A94D028D], | [0xCAD2F7F5359A3B3E, 0x096EE45813A04330], | [0xFD87B5F28300CA0E, 0x8BCA9D6E188853FC], | [0x9E74D1B791E07E48, 0x775EA264CF55347E], | [0xC612062576589DDB, 0x95364AFE032A819D], | [0xF79687AED3EEC551, 0x3A83DDBD83F52205], | [0x9ABE14CD44753B53, 0xC4926A9672793543], | [0xC16D9A0095928A27, 0x75B7053C0F178294], | [0xF1C90080BAF72CB1, 0x5324C68B12DD6338], | [0x971DA05074DA7BEF, 0xD3F6FC16EBCA5E03], | [0xBCE5086492111AEB, 0x88F4BB1CA6BCF584], | [0xEC1E4A7DB69561A5, 0x2B31E9E3D06C32E5], | [0x9392EE8E921D5D07, 0x3AFF322E62439FCF], | [0xB877AA3236A4B449, 0x09BEFEB9FAD487C3], | [0xE69594BEC44DE15B, 0x4C2EBE687989A9B4], | [0x901D7CF73AB0ACD9, 0x0F9D37014BF60A10], | [0xB424DC35095CD80F, 0x538484C19EF38C94], | [0xE12E13424BB40E13, 0x2865A5F206B06FBA], | [0x8CBCCC096F5088CC, 0xF93F87B7442E45D4], | [0xAFEBFF0BCB24AAFF, 0xF78F69A51539D749], | [0xDBE6FECEBDEDD5BF, 0xB573440E5A884D1B], | [0x89705F4136B4A597, 0x31680A88F8953031], | [0xABCC77118461CEFD, 0xFDC20D2B36BA7C3D], | [0xD6BF94D5E57A42BC, 0x3D32907604691B4D], | [0x8637BD05AF6C69B6, 0xA63F9A49C2C1B110], | [0xA7C5AC471B478423, 0x0FCF80DC33721D54], | [0xD1B71758E219652C, 0xD3C36113404EA4A9], | [0x83126E978D4FDF3B, 0x645A1CAC083126E9], | [0xA3D70A3D70A3D70A, 0x3D70A3D70A3D70A4], | [0xCCCCCCCCCCCCCCCD, 0xCCCCCCCCCCCCCCCD], | [0x8000000000000000, 0x0000000000000000], | [0xA000000000000000, 0x0000000000000000], | [0xC800000000000000, 0x0000000000000000], | [0xFA00000000000000, 0x0000000000000000], | [0x9C40000000000000, 0x0000000000000000], | [0xC350000000000000, 0x0000000000000000], | [0xF424000000000000, 0x0000000000000000], | [0x9896800000000000, 0x0000000000000000], | [0xBEBC200000000000, 0x0000000000000000], | [0xEE6B280000000000, 0x0000000000000000], | [0x9502F90000000000, 0x0000000000000000], | [0xBA43B74000000000, 0x0000000000000000], | [0xE8D4A51000000000, 0x0000000000000000], | [0x9184E72A00000000, 0x0000000000000000], | [0xB5E620F480000000, 0x0000000000000000], | [0xE35FA931A0000000, 0x0000000000000000], | [0x8E1BC9BF04000000, 0x0000000000000000], | [0xB1A2BC2EC5000000, 0x0000000000000000], | [0xDE0B6B3A76400000, 0x0000000000000000], | [0x8AC7230489E80000, 0x0000000000000000], | [0xAD78EBC5AC620000, 0x0000000000000000], | [0xD8D726B7177A8000, 0x0000000000000000], | [0x878678326EAC9000, 0x0000000000000000], | [0xA968163F0A57B400, 0x0000000000000000], | [0xD3C21BCECCEDA100, 0x0000000000000000], | [0x84595161401484A0, 0x0000000000000000], | [0xA56FA5B99019A5C8, 0x0000000000000000], | [0xCECB8F27F4200F3A, 0x0000000000000000], | [0x813F3978F8940984, 0x4000000000000000], | [0xA18F07D736B90BE5, 0x5000000000000000], | [0xC9F2C9CD04674EDF, 0xA400000000000000], | [0xFC6F7C4045812296, 0x4D00000000000000], | [0x9DC5ADA82B70B59E, 0xF020000000000000], | [0xC5371912364CE305, 0x6C28000000000000], | [0xF684DF56C3E01BC7, 0xC732000000000000], | [0x9A130B963A6C115C, 0x3C7F400000000000], | [0xC097CE7BC90715B3, 0x4B9F100000000000], | [0xF0BDC21ABB48DB20, 0x1E86D40000000000], | [0x96769950B50D88F4, 0x1314448000000000], | [0xBC143FA4E250EB31, 0x17D955A000000000], | [0xEB194F8E1AE525FD, 0x5DCFAB0800000000], | [0x92EFD1B8D0CF37BE, 0x5AA1CAE500000000], | [0xB7ABC627050305AE, 0xF14A3D9E40000000], | [0xE596B7B0C643C719, 0x6D9CCD05D0000000], | [0x8F7E32CE7BEA5C70, 0xE4820023A2000000], | [0xB35DBF821AE4F38C, 0xDDA2802C8A800000], | [0xE0352F62A19E306F, 0xD50B2037AD200000], | [0x8C213D9DA502DE45, 0x4526F422CC340000], | [0xAF298D050E4395D7, 0x9670B12B7F410000], | [0xDAF3F04651D47B4C, 0x3C0CDD765F114000], | [0x88D8762BF324CD10, 0xA5880A69FB6AC800], | [0xAB0E93B6EFEE0054, 0x8EEA0D047A457A00], | [0xD5D238A4ABE98068, 0x72A4904598D6D880], | [0x85A36366EB71F041, 0x47A6DA2B7F864750], | [0xA70C3C40A64E6C52, 0x999090B65F67D924], | [0xD0CF4B50CFE20766, 0xFFF4B4E3F741CF6D], | [0x82818F1281ED44A0, 0xBFF8F10E7A8921A4], | [0xA321F2D7226895C8, 0xAFF72D52192B6A0D], | [0xCBEA6F8CEB02BB3A, 0x9BF4F8A69F764490], | [0xFEE50B7025C36A08, 0x02F236D04753D5B5], | [0x9F4F2726179A2245, 0x01D762422C946591], | [0xC722F0EF9D80AAD6, 0x424D3AD2B7B97EF5], | [0xF8EBAD2B84E0D58C, 0xD2E0898765A7DEB2], | [0x9B934C3B330C8577, 0x63CC55F49F88EB2F], | [0xC2781F49FFCFA6D5, 0x3CBF6B71C76B25FB], | [0xF316271C7FC3908B, 0x8BEF464E3945EF7A], | [0x97EDD871CFDA3A57, 0x97758BF0E3CBB5AC], | [0xBDE94E8E43D0C8EC, 0x3D52EEED1CBEA317], | [0xED63A231D4C4FB27, 0x4CA7AAA863EE4BDD], | [0x945E455F24FB1CF9, 0x8FE8CAA93E74EF6A], | [0xB975D6B6EE39E437, 0xB3E2FD538E122B45], | [0xE7D34C64A9C85D44, 0x60DBBCA87196B616], | [0x90E40FBEEA1D3A4B, 0xBC8955E946FE31CE], | [0xB51D13AEA4A488DD, 0x6BABAB6398BDBE41], | [0xE264589A4DCDAB15, 0xC696963C7EED2DD2], | [0x8D7EB76070A08AED, 0xFC1E1DE5CF543CA3], | [0xB0DE65388CC8ADA8, 0x3B25A55F43294BCC], | [0xDD15FE86AFFAD912, 0x49EF0EB713F39EBF], | [0x8A2DBF142DFCC7AB, 0x6E3569326C784337], | [0xACB92ED9397BF996, 0x49C2C37F07965405], | [0xD7E77A8F87DAF7FC, 0xDC33745EC97BE906], | [0x86F0AC99B4E8DAFD, 0x69A028BB3DED71A4], | [0xA8ACD7C0222311BD, 0xC40832EA0D68CE0D], | [0xD2D80DB02AABD62C, 0xF50A3FA490C30190], | [0x83C7088E1AAB65DB, 0x792667C6DA79E0FA], | [0xA4B8CAB1A1563F52, 0x577001B891185939], | [0xCDE6FD5E09ABCF27, 0xED4C0226B55E6F87], | [0x80B05E5AC60B6178, 0x544F8158315B05B4], | [0xA0DC75F1778E39D6, 0x696361AE3DB1C721], | [0xC913936DD571C84C, 0x03BC3A19CD1E38EA], | [0xFB5878494ACE3A5F, 0x04AB48A04065C724], | [0x9D174B2DCEC0E47B, 0x62EB0D64283F9C76], | [0xC45D1DF942711D9A, 0x3BA5D0BD324F8394], | [0xF5746577930D6501, 0xCA8F44EC7EE36479], | [0x9968BF6ABBE85F20, 0x7E998B13CF4E1ECC], | [0xBFC2EF456AE276E9, 0x9E3FEDD8C321A67F], | [0xEFB3AB16C59B14A3, 0xC5CFE94EF3EA101E], | [0x95D04AEE3B80ECE6, 0xBBA1F1D158724A13], | [0xBB445DA9CA61281F, 0x2A8A6E45AE8EDC98], | [0xEA1575143CF97227, 0xF52D09D71A3293BE], | [0x924D692CA61BE758, 0x593C2626705F9C56], | [0xB6E0C377CFA2E12E, 0x6F8B2FB00C77836C], | [0xE498F455C38B997A, 0x0B6DFB9C0F956447], | [0x8EDF98B59A373FEC, 0x4724BD4189BD5EAC], | [0xB2977EE300C50FE7, 0x58EDEC91EC2CB658], | [0xDF3D5E9BC0F653E1, 0x2F2967B66737E3ED], | [0x8B865B215899F46D, 0xBD79E0D20082EE74], | [0xAE67F1E9AEC07188, 0xECD8590680A3AA11], | [0xDA01EE641A708DEA, 0xE80E6F4820CC9496], | [0x884134FE908658B2, 0x3109058D147FDCDE], | [0xAA51823E34A7EEDF, 0xBD4B46F0599FD415], | [0xD4E5E2CDC1D1EA96, 0x6C9E18AC7007C91A], | [0x850FADC09923329E, 0x03E2CF6BC604DDB0], | [0xA6539930BF6BFF46, 0x84DB8346B786151D], | [0xCFE87F7CEF46FF17, 0xE612641865679A64], | [0x81F14FAE158C5F6E, 0x4FCB7E8F3F60C07E], | [0xA26DA3999AEF774A, 0xE3BE5E330F38F09E], | [0xCB090C8001AB551C, 0x5CADF5BFD3072CC5], | [0xFDCB4FA002162A63, 0x73D9732FC7C8F7F7], | [0x9E9F11C4014DDA7E, 0x2867E7FDDCDD9AFA], | [0xC646D63501A1511E, 0xB281E1FD541501B9], | [0xF7D88BC24209A565, 0x1F225A7CA91A4227], | [0x9AE757596946075F, 0x3375788DE9B06958], | [0xC1A12D2FC3978937, 0x0052D6B1641C83AE], | [0xF209787BB47D6B85, 0xC0678C5DBD23A49A], | [0x9745EB4D50CE6333, 0xF840B7BA963646E0], | [0xBD176620A501FC00, 0xB650E5A93BC3D898], | [0xEC5D3FA8CE427B00, 0xA3E51F138AB4CEBE], | [0x93BA47C980E98CE0, 0xC66F336C36B10137], | [0xB8A8D9BBE123F018, 0xB80B0047445D4185], | [0xE6D3102AD96CEC1E, 0xA60DC059157491E6], | [0x9043EA1AC7E41393, 0x87C89837AD68DB30], | [0xB454E4A179DD1877, 0x29BABE4598C311FC], | [0xE16A1DC9D8545E95, 0xF4296DD6FEF3D67B], | [0x8CE2529E2734BB1D, 0x1899E4A65F58660D], | [0xB01AE745B101E9E4, 0x5EC05DCFF72E7F90], | [0xDC21A1171D42645D, 0x76707543F4FA1F74], | [0x899504AE72497EBA, 0x6A06494A791C53A8], | [0xABFA45DA0EDBDE69, 0x0487DB9D17636892], | [0xD6F8D7509292D603, 0x45A9D2845D3C42B7], | [0x865B86925B9BC5C2, 0x0B8A2392BA45A9B2], | [0xA7F26836F282B733, 0x8E6CAC7768D7141F], | [0xD1EF0244AF2364FF, 0x3207D795430CD927], | [0x8335616AED761F1F, 0x7F44E6BD49E807B8], | [0xA402B9C5A8D3A6E7, 0x5F16206C9C6209A6], | [0xCD036837130890A1, 0x36DBA887C37A8C10], | [0x802221226BE55A65, 0xC2494954DA2C978A], | [0xA02AA96B06DEB0FE, 0xF2DB9BAA10B7BD6C], | [0xC83553C5C8965D3D, 0x6F92829494E5ACC7], | [0xFA42A8B73ABBF48D, 0xCB772339BA1F17F9], | [0x9C69A97284B578D8, 0xFF2A760414536EFC], | [0xC38413CF25E2D70E, 0xFEF5138519684ABB], | [0xF46518C2EF5B8CD1, 0x7EB258665FC25D69], | [0x98BF2F79D5993803, 0xEF2F773FFBD97A62], | [0xBEEEFB584AFF8604, 0xAAFB550FFACFD8FA], | [0xEEAABA2E5DBF6785, 0x95BA2A53F983CF39], | [0x952AB45CFA97A0B3, 0xDD945A747BF26184], | [0xBA756174393D88E0, 0x94F971119AEEF9E4], | [0xE912B9D1478CEB17, 0x7A37CD5601AAB85E], | [0x91ABB422CCB812EF, 0xAC62E055C10AB33B], | [0xB616A12B7FE617AA, 0x577B986B314D6009], | [0xE39C49765FDF9D95, 0xED5A7E85FDA0B80B], | [0x8E41ADE9FBEBC27D, 0x14588F13BE847307], | [0xB1D219647AE6B31C, 0x596EB2D8AE258FC9], | [0xDE469FBD99A05FE3, 0x6FCA5F8ED9AEF3BB], | [0x8AEC23D680043BEE, 0x25DE7BB9480D5855], | [0xADA72CCC20054AEA, 0xAF561AA79A10AE6A], | [0xD910F7FF28069DA4, 0x1B2BA1518094DA05], | [0x87AA9AFF79042287, 0x90FB44D2F05D0843], | [0xA99541BF57452B28, 0x353A1607AC744A54], | [0xD3FA922F2D1675F2, 0x42889B8997915CE9], | [0x847C9B5D7C2E09B7, 0x69956135FEBADA11], | [0xA59BC234DB398C25, 0x43FAB9837E699096], | [0xCF02B2C21207EF2F, 0x94F967E45E03F4BB], | [0x8161AFB94B44F57D, 0x1D1BE0EEBAC278F5], | [0xA1BA1BA79E1632DC, 0x6462D92A69731732], | [0xCA28A291859BBF93, 0x7D7B8F7503CFDCFF], | [0xFCB2CB35E702AF78, 0x5CDA735244C3D43F], | [0x9DEFBF01B061ADAB, 0x3A0888136AFA64A7], | [0xC56BAEC21C7A1916, 0x088AAA1845B8FDD1], | [0xF6C69A72A3989F5C, 0x8AAD549E57273D45], | [0x9A3C2087A63F6399, 0x36AC54E2F678864B], | [0xC0CB28A98FCF3C80, 0x84576A1BB416A7DE], | [0xF0FDF2D3F3C30B9F, 0x656D44A2A11C51D5], | [0x969EB7C47859E744, 0x9F644AE5A4B1B325], | [0xBC4665B596706115, 0x873D5D9F0DDE1FEF], | [0xEB57FF22FC0C795A, 0xA90CB506D155A7EA], | [0x9316FF75DD87CBD8, 0x09A7F12442D588F3], | [0xB7DCBF5354E9BECE, 0x0C11ED6D538AEB2F], | [0xE5D3EF282A242E82, 0x8F1668C8A86DA5FB], | [0x8FA475791A569D11, 0xF96E017D694487BD], | [0xB38D92D760EC4455, 0x37C981DCC395A9AC], | [0xE070F78D3927556B, 0x85BBE253F47B1417], | [0x8C469AB843B89563, 0x93956D7478CCEC8E], | [0xAF58416654A6BABB, 0x387AC8D1970027B2], | [0xDB2E51BFE9D0696A, 0x06997B05FCC0319F], | [0x88FCF317F22241E2, 0x441FECE3BDF81F03], | [0xAB3C2FDDEEAAD25B, 0xD527E81CAD7626C4], | [0xD60B3BD56A5586F2, 0x8A71E223D8D3B075], | [0x85C7056562757457, 0xF6872D5667844E49], | [0xA738C6BEBB12D16D, 0xB428F8AC016561DB], | [0xD106F86E69D785C8, 0xE13336D701BEBA52], | [0x82A45B450226B39D, 0xECC0024661173473], | [0xA34D721642B06084, 0x27F002D7F95D0190], | [0xCC20CE9BD35C78A5, 0x31EC038DF7B441F4], | [0xFF290242C83396CE, 0x7E67047175A15271], | [0x9F79A169BD203E41, 0x0F0062C6E984D387], | [0xC75809C42C684DD1, 0x52C07B78A3E60868], | [0xF92E0C3537826146, 0xA7709A56CCDF8A83], | [0x9BBCC7A142B17CCC, 0x88A66076400BB692], | [0xC2ABF989935DDBFE, 0x6ACFF893D00EA436], | [0xF356F7EBF83552FE, 0x0583F6B8C4124D43], | [0x98165AF37B2153DF, 0xC3727A337A8B704A], | [0xBE1BF1B059E9A8D6, 0x744F18C0592E4C5D], | [0xEDA2EE1C7064130C, 0x1162DEF06F79DF74], | [0x9485D4D1C63E8BE8, 0x8ADDCB5645AC2BA8], | [0xB9A74A0637CE2EE1, 0x6D953E2BD7173693], | [0xE8111C87C5C1BA9A, 0xC8FA8DB6CCDD0437], | [0x910AB1D4DB9914A0, 0x1D9C9892400A22A2], | [0xB54D5E4A127F59C8, 0x2503BEB6D00CAB4B], | [0xE2A0B5DC971F303A, 0x2E44AE64840FD61E], | [0x8DA471A9DE737E24, 0x5CEAECFED289E5D3], | [0xB10D8E1456105DAD, 0x7425A83E872C5F47], | [0xDD50F1996B947519, 0xD12F124E28F77719], | [0x8A5296FFE33CC930, 0x82BD6B70D99AAA70], | [0xACE73CBFDC0BFB7B, 0x636CC64D1001550C], | [0xD8210BEFD30EFA5A, 0x3C47F7E05401AA4F], | [0x8714A775E3E95C78, 0x65ACFAEC34810A71], | [0xA8D9D1535CE3B396, 0x7F1839A741A14D0D], | [0xD31045A8341CA07C, 0x1EDE48111209A051], | [0x83EA2B892091E44E, 0x934AED0AAB460432], | [0xA4E4B66B68B65D61, 0xF81DA84D5617853F], | [0xCE1DE40642E3F4B9, 0x36251260AB9D668F], | [0x80D2AE83E9CE78F4, 0xC1D72B7C6B426019], | [0xA1075A24E4421731, 0xB24CF65B8612F820], | [0xC94930AE1D529CFD, 0xDEE033F26797B628], | [0xFB9B7CD9A4A7443C, 0x169840EF017DA3B1], | [0x9D412E0806E88AA6, 0x8E1F289560EE864F], | [0xC491798A08A2AD4F, 0xF1A6F2BAB92A27E3], | [0xF5B5D7EC8ACB58A3, 0xAE10AF696774B1DB], | [0x9991A6F3D6BF1766, 0xACCA6DA1E0A8EF29], | [0xBFF610B0CC6EDD3F, 0x17FD090A58D32AF3], | [0xEFF394DCFF8A948F, 0xDDFC4B4CEF07F5B0], | [0x95F83D0A1FB69CD9, 0x4ABDAF101564F98E], | [0xBB764C4CA7A44410, 0x9D6D1AD41ABE37F2], | [0xEA53DF5FD18D5514, 0x84C86189216DC5EE], | [0x92746B9BE2F8552C, 0x32FD3CF5B4E49BB5], | [0xB7118682DBB66A77, 0x3FBC8C33221DC2A2], | [0xE4D5E82392A40515, 0x0FABAF3FEAA5334A], | [0x8F05B1163BA6832D, 0x29CB4D87F2A7400E], | [0xB2C71D5BCA9023F8, 0x743E20E9EF511012], | [0xDF78E4B2BD342CF7, 0x914DA9246B255417], | [0x8BAB8EEFB6409C1A, 0x1AD089B6C2F7548E], | [0xAE9672ABA3D0C321, 0xA184AC2473B529B2], | [0xDA3C0F568CC4F3E9, 0xC9E5D72D90A2741E], | [0x8865899617FB1871, 0x7E2FA67C7A658893], | [0xAA7EEBFB9DF9DE8E, 0xDDBB901B98FEEAB8], | [0xD51EA6FA85785631, 0x552A74227F3EA565], | [0x8533285C936B35DF, 0xD53A88958F87275F], | [0xA67FF273B8460357, 0x8A892ABAF368F137], | [0xD01FEF10A657842C, 0x2D2B7569B0432D85], | [0x8213F56A67F6B29C, 0x9C3B29620E29FC73], | [0xA298F2C501F45F43, 0x8349F3BA91B47B90], | [0xCB3F2F7642717713, 0x241C70A936219A74], | [0xFE0EFB53D30DD4D8, 0xED238CD383AA0111], | [0x9EC95D1463E8A507, 0xF4363804324A40AB], | [0xC67BB4597CE2CE49, 0xB143C6053EDCD0D5], | [0xF81AA16FDC1B81DB, 0xDD94B7868E94050A], | [0x9B10A4E5E9913129, 0xCA7CF2B4191C8327], | [0xC1D4CE1F63F57D73, 0xFD1C2F611F63A3F0], | [0xF24A01A73CF2DCD0, 0xBC633B39673C8CEC], | [0x976E41088617CA02, 0xD5BE0503E085D814], | [0xBD49D14AA79DBC82, 0x4B2D8644D8A74E19], | [0xEC9C459D51852BA3, 0xDDF8E7D60ED1219F], | [0x93E1AB8252F33B46, 0xCABB90E5C942B503], | [0xB8DA1662E7B00A17, 0x3D6A751F3B936244], | [0xE7109BFBA19C0C9D, 0x0CC512670A783AD5], | [0x906A617D450187E2, 0x27FB2B80668B24C5], | [0xB484F9DC9641E9DB, 0xB1F9F660802DEDF6], | [0xE1A63853BBD26451, 0x5E7873F8A0396974], | [0x8D07E33455637EB3, 0xDB0B487B6423E1E8], | [0xB049DC016ABC5E60, 0x91CE1A9A3D2CDA63], | [0xDC5C5301C56B75F7, 0x7641A140CC7810FB], | [0x89B9B3E11B6329BB, 0xA9E904C87FCB0A9D], | [0xAC2820D9623BF429, 0x546345FA9FBDCD44], | [0xD732290FBACAF134, 0xA97C177947AD4095], | [0x867F59A9D4BED6C0, 0x49ED8EABCCCC485D], | [0xA81F301449EE8C70, 0x5C68F256BFFF5A75], | [0xD226FC195C6A2F8C, 0x73832EEC6FFF3112], | [0x83585D8FD9C25DB8, 0xC831FD53C5FF7EAB], | [0xA42E74F3D032F526, 0xBA3E7CA8B77F5E56], | [0xCD3A1230C43FB26F, 0x28CE1BD2E55F35EB], | [0x80444B5E7AA7CF85, 0x7980D163CF5B81B3], | [0xA0555E361951C367, 0xD7E105BCC3326220], | [0xC86AB5C39FA63441, 0x8DD9472BF3FEFAA8], | [0xFA856334878FC151, 0xB14F98F6F0FEB952], | [0x9C935E00D4B9D8D2, 0x6ED1BF9A569F33D3], | [0xC3B8358109E84F07, 0x0A862F80EC4700C8], | [0xF4A642E14C6262C9, 0xCD27BB612758C0FA], | [0x98E7E9CCCFBD7DBE, 0x8038D51CB897789C], | [0xBF21E44003ACDD2D, 0xE0470A63E6BD56C3], | [0xEEEA5D5004981478, 0x1858CCFCE06CAC74], | [0x95527A5202DF0CCB, 0x0F37801E0C43EBC9], | [0xBAA718E68396CFFE, 0xD30560258F54E6BB], | [0xE950DF20247C83FD, 0x47C6B82EF32A2069], | [0x91D28B7416CDD27E, 0x4CDC331D57FA5442], | [0xB6472E511C81471E, 0xE0133FE4ADF8E952], | [0xE3D8F9E563A198E5, 0x58180FDDD97723A7], | [0x8E679C2F5E44FF8F, 0x570F09EAA7EA7648], | [0xB201833B35D63F73, 0x2CD2CC6551E513DA], | [0xDE81E40A034BCF50, 0xF8077F7EA65E58D1], | [0x8B112E86420F6192, 0xFB04AFAF27FAF783], | [0xADD57A27D29339F6, 0x79C5DB9AF1F9B563], | [0xD94AD8B1C7380874, 0x18375281AE7822BC], | [0x87CEC76F1C830549, 0x8F2293910D0B15B6], | [0xA9C2794AE3A3C69B, 0xB2EB3875504DDB23], | [0xD433179D9C8CB841, 0x5FA60692A46151EC], | [0x849FEEC281D7F329, 0xDBC7C41BA6BCD333], | [0xA5C7EA73224DEFF3, 0x12B9B522906C0800], | [0xCF39E50FEAE16BF0, 0xD768226B34870A00], | [0x81842F29F2CCE376, 0xE6A1158300D46640], | [0xA1E53AF46F801C53, 0x60495AE3C1097FD0], | [0xCA5E89B18B602368, 0x385BB19CB14BDFC4], | [0xFCF62C1DEE382C42, 0x46729E03DD9ED7B5], | [0x9E19DB92B4E31BA9, 0x6C07A2C26A8346D1], | [0xC5A05277621BE294, 0xC7098B7305241886], | [0xF70867153AA2DB39, 0xB8CBEE4FC66D1EA7], | [0x9A65406D44A5C903, 0x737F74F1DC043328], | [0xC0FE908895CF3B44, 0x505F522E53053FF2], | [0xF13E34AABB430A15, 0x647726B9E7C68FEF], | [0x96C6E0EAB509E64D, 0x5ECA783430DC19F5], | [0xBC789925624C5FE1, 0xB67D16413D132073], | [0xEB96BF6EBADF77D9, 0xE41C5BD18C57E88F], | [0x933E37A534CBAAE8, 0x8E91B962F7B6F15A], | [0xB80DC58E81FE95A1, 0x723627BBB5A4ADB0], | [0xE61136F2227E3B0A, 0xCEC3B1AAA30DD91C], | [0x8FCAC257558EE4E6, 0x213A4F0AA5E8A7B2], | [0xB3BD72ED2AF29E20, 0xA988E2CD4F62D19E], | [0xE0ACCFA875AF45A8, 0x93EB1B80A33B8605], | [0x8C6C01C9498D8B89, 0xBC72F130660533C3], | [0xAF87023B9BF0EE6B, 0xEB8FAD7C7F8680B4], | [0xDB68C2CA82ED2A06, 0xA67398DB9F6820E1], | [0x892179BE91D43A44, 0x88083F8943A1148D], | [0xAB69D82E364948D4, 0x6A0A4F6B948959B0], | [0xD6444E39C3DB9B0A, 0x848CE34679ABB01C], | [0x85EAB0E41A6940E6, 0xF2D80E0C0C0B4E12], | [0xA7655D1D2103911F, 0x6F8E118F0F0E2196], | [0xD13EB46469447567, 0x4B7195F2D2D1A9FB], | [0x82C730BEC1CAC961, 0x8F26FDB7C3C30A3D], | [0xA378FCEE723D7BB9, 0xB2F0BD25B4B3CCCC], | [0xCC573C2A0ECCDAA7, 0xDFACEC6F21E0C000], | [0xFF6D0B3492801151, 0x9798278AEA58EFFF], | [0x9FA42700DB900AD2, 0x5EBF18B6D2779600], | [0xC78D30C112740D87, 0xF66EDEE487157B80], | [0xF9707CF1571110E9, 0xB40A969DA8DADA5F], | [0x9BE64E16D66AAA91, 0x70869E228988C87C], | [0xC2DFE19C8C055536, 0xCCA845AB2BEAFA9B], | [0xF397DA03AF06AA83, 0x3FD25715F6E5B941], | [0x983EE8424D642A92, 0x07E3766DBA4F93C9], | [0xBE4EA252E0BD3537, 0x89DC540928E378BB], | [0xEDE24AE798EC8284, 0x2C53690B731C56EA], | [0x94AD6ED0BF93D193, 0x9BB421A727F1B652], | [0xB9D8CA84EF78C5F7, 0x42A12A10F1EE23E7], | [0xE84EFD262B56F775, 0x134974952E69ACE0], | [0x91315E37DB165AA9, 0x2C0DE8DD3D020C0C], | [0xB57DB5C5D1DBF153, 0x771163148C428F0F], | [0xE2DD23374652EDA8, 0x54D5BBD9AF5332D3], | [0x8DCA36028BF3D489, 0x350595680D93FFC4], | [0xB13CC3832EF0C9AC, 0x8246FAC210F8FFB5], | [0xDD8BF463FAACFC16, 0x62D8B97295373FA2], | [0x8A7778BE7CAC1D8E, 0xFDC773E79D4287C5], | [0xAD1556EE1BD724F1, 0x7D3950E1849329B7], | [0xD85AACA9A2CCEE2E, 0xDC87A519E5B7F424], | [0x8738ABEA05C014DD, 0xA9D4C7302F92F897], | [0xA906D6E487301A14, 0xD449F8FC3B77B6BC], | [0xD3488C9DA8FC2099, 0xC95C773B4A55A46B], | [0x840D57E2899D945F, 0x7DD9CA850E7586C3], | [0xA510ADDB2C04F977, 0x5D503D265212E874], | [0xCE54D951F70637D5, 0x34A44C6FE697A291], | [0x80F507D33A63E2E5, 0x40E6AFC5F01EC59B], | [0xA13249C808FCDB9F, 0x91205BB76C267701], | [0xC97EDC3A0B3C1286, 0x356872A5473014C1], | [0xFBDE93488E0B1728, 0xC2C28F4E98FC19F2], | [0x9D6B1C0D58C6EE79, 0xD9B999911F9D9037], | [0xC4C5E310AEF8AA17, 0x1027FFF56784F445], | [0xF5F75BD4DAB6D49D, 0xD431FFF2C1663156], | [0x99BA996508B244E2, 0x049F3FF7B8DFDED6], | [0xC0293FBE4ADED61B, 0x85C70FF5A717D68B], | [0xF0338FADDD968BA1, 0x2738D3F310DDCC2E], | [0x962039CCAA7E1745, 0xB8838477EA8A9F9D], | [0xBBA8483FD51D9D16, 0xE6A46595E52D4784], | [0xEA925A4FCA65045B, 0x604D7EFB5E789965], | [0x929B7871DE7F22B9, 0x1C306F5D1B0B5FDF], | [0xB742568E561EEB67, 0x633C8B3461CE37D7], | [0xE512EC31EBA6A641, 0x3C0BAE017A41C5CD], | [0x8F2BD39F334827E9, 0xC5874CC0EC691BA0], | [0xB2F6C887001A31E3, 0xF6E91FF127836288], | [0xDFB47AA8C020BE5C, 0xB4A367ED71643B2A], | [0x8BD0CCA9781476F9, 0x50E620F466DEA4FA], | [0xAEC4FFD3D61994B8, 0xA51FA93180964E39], | [0xDA763FC8CB9FF9E6, 0x8E67937DE0BBE1C7], | [0x8889E7DD7F43FC2F, 0x7900BC2EAC756D1C], | [0xAAAC61D4DF14FB3B, 0x5740EB3A5792C863], | [0xD5577A4A16DA3A0A, 0x2D112608ED777A7C], | [0x8556AC6E4E486446, 0x5C2AB7C5946AAC8E], | [0xA6AC5789E1DA7D58, 0xF33565B6F98557B1], | [0xD0576D6C5A511CAE, 0xF002BF24B7E6AD9D], | [0x8236A463B872B1ED, 0xB601B776F2F02C82], | [0xA2C44D7CA68F5E68, 0xE3822554AFAC37A3], | [0xCB7560DBD0333602, 0xDC62AEA9DB97458C], | [0xFE52B912C4400382, 0x537B5A54527D16EE], | [0x9EF3B3ABBAA80231, 0x742D1874B38E2E55], | [0xC6B0A096A95202BE, 0xD1385E91E071B9EA], | [0xF85CC8BC53A6836D, 0x45867636588E2865], | [0x9B39FD75B4481224, 0x4B7409E1F758D93F], | [0xC2087CD3215A16AD, 0x5E510C5A752F0F8F], | [0xF28A9C07E9B09C59, 0xB5E54F71127AD373], | [0x9796A184F20E61B7, 0x71AF51A6AB8CC428], | [0xBD7C49E62E91FA25, 0x4E1B2610566FF531], | [0xECDB5C5FBA3678AF, 0xA1A1EF946C0BF27E], | [0x940919BBD4620B6D, 0x250535BCC387778F], | [0xB90B602AC97A8E48, 0x6E46832BF4695572], | [0xE74E38357BD931DB, 0x89D823F6F183AACF], | [0x9090E3216D67BF29, 0x9627167A56F24AC1], | [0xB4B51BE9C8C1AEF3, 0xBBB0DC18ECAEDD72], | [0xE1E262E43AF21AAF, 0x6A9D131F27DA94CE], | [0x8D2D7DCEA4D750AE, 0xA2A22BF378E89D01], | [0xB078DD424E0D24D9, 0x0B4AB6F05722C441], | [0xDC971492E1906E0F, 0x4E1D64AC6CEB7551], | [0x89DE6CDBCCFA44CA, 0x90D25EEBC4132953], | [0xAC560812C038D5FC, 0xF506F6A6B517F3A7], | [0xD76B8A1770470B7B, 0xF248B450625DF091], | [0x86A3364EA62C672D, 0xD76D70B23D7AB65B], | [0xA84C03E24FB780F8, 0x0D48CCDECCD963F2], | [0xD25F04DAE3A56136, 0x109B0016800FBCEE], | [0x837B6308CE475CC2, 0xCA60E00E1009D615], | [0xA45A3BCB01D933F2, 0x3CF91811940C4B9A], | [0xCD70CABDC24F80EF, 0xCC375E15F90F5E80], | [0x80667EB69971B095, 0x3FA29ACDBBA99B10], | [0xA0801E643FCE1CBB, 0x8F8B41812A9401D4], | [0xC8A025FD4FC1A3E9, 0x336E11E175390249], | [0xFAC82F7CA3B20CE4, 0x80499659D28742DC], | [0x9CBD1DADE64F480E, 0x302DFDF8239489C9], | [0xC3EC65195FE31A12, 0xBC397D762C79AC3C], | [0xF4E77E5FB7DBE096, 0x2B47DCD3B798174B], | [0x9910AEFBD2E96C5E, 0xDB0CEA0452BF0E8F], | [0xBF54DABAC7A3C775, 0x51D02485676ED232], | [0xEF2A1169798CB953, 0xA6442DA6C14A86BF], | [0x957A4AE1EBF7F3D4, 0xA7EA9C8838CE9437], | [0xBAD8DD9A66F5F0C9, 0x91E543AA47023945], | [0xE98F150100B36CFB, 0xB65E9494D8C2C796], | [0x91F96D20A070241D, 0xB1FB1CDD0779BCBE], | [0xB677C868C88C2D24, 0xDE79E41449582BED], | [0xE415BA82FAAF386D, 0xD6185D195BAE36E9], | [0x8E8D9491DCAD8344, 0x05CF3A2FD94CE251], | [0xB230F9B653D8E415, 0x074308BBCFA01AE6], | [0xDEBD3823E8CF1D1A, 0x4913CAEAC388219F], | [0x8B36431671817230, 0x6DAC5ED2BA351504], | [0xAE03D3DC0DE1CEBD, 0x8917768768C25A44], | [0xD984C8D3115A426C, 0xAB5D542942F2F0D6], | [0x87F2FD83EAD86983, 0x4B1A5499C9D7D685], | [0xA9EFBCE4E58E83E4, 0x1DE0E9C03C4DCC27], | [0xD46BAC1E1EF224DD, 0x255924304B613F31], | [0x84C34B92D357570A, 0x3757B69E2F1CC77E], | [0xA5F41E77882D2CCD, 0xC52DA445BAE3F95E], | [0xCF7126156A387800, 0xF6790D57299CF7B5], | [0x81A6B7CD62634B00, 0xFA0BA8567A021AD1], | [0xA21065C0BAFC1DC0, 0xF88E926C1882A186], | [0xCA947F30E9BB2530, 0xF6B237071EA349E7], | [0xFD399EFD2429EE7C, 0xF45EC4C8E64C1C61], | [0x9E44035E369A350D, 0x78BB3AFD8FEF91BD], | [0xC5D50435C440C251, 0xD6EA09BCF3EB762C], | [0xF74A45433550F2E5, 0x0CA48C2C30E653B7], | [0x9A8E6B4A015297CF, 0x27E6D79B9E8FF452], | [0xC132061C81A73DC3, 0xF1E08D828633F167], | [0xF17E87A3A2110D34, 0xAE58B0E327C0EDC0], | [0x96EF14C6454AA840, 0x4CF76E8DF8D89498], | [0xBCAAD9F7D69D5250, 0x60354A31770EB9BE], | [0xEBD59075CC44A6E4, 0x78429CBDD4D2682E], | [0x93657A499FAAE84F, 0xCB29A1F6A503811D], | [0xB83ED8DC0795A262, 0x7DF40A744E446164], | [0xE64E8F13097B0AFB, 0x1D710D1161D579BD], | [0x8FF1196BE5ECE6DD, 0xF266A82ADD256C16], | [0xB3ED5FC6DF682094, 0x2F005235946EC71C], | [0xE0E8B7B8974228B9, 0x3AC066C2F98A78E2], | [0x8C9172D35E895974, 0xC4B84039DBF68B8E], | [0xAFB5CF88362BAFD1, 0xB5E6504852F42E71], | [0xDBA3436A43B69BC5, 0xE35FE45A67B13A0D], | [0x89460A226A52215B, 0x0E1BEEB880CEC448], | [0xAB978CAB04E6A9B2, 0xD1A2EA66A102755A], | [0xD67D6FD5C620541E, 0x460BA500494312B1], | [0x860E65E59BD43493, 0xEBC747202DC9EBAF], | [0xA791FF5F02C941B8, 0xA6B918E8393C669A], | [0xD1767F36C37B9226, 0x90675F22478B8041], | [0x82EA0F823A2D3B57, 0x7A409B756CB73028], | [0xA3A49362C8B88A2D, 0x58D0C252C7E4FC33], | [0xCC8DB83B7AE6ACB9, 0xAF04F2E779DE3B3F], | [0xFFB1264A59A057E7, 0xDAC62FA15855CA0F], | [0x9FCEB7EE780436F0, 0x48BBDDC4D7359E49], | [0xC7C265EA160544AC, 0x5AEAD5360D0305DC], | [0xF9B2FF649B8695D7, 0x71A58A839043C753], | [0x9C0FDF9EE1341DA7, 0xA70776923A2A5C94], | [0xC313D78699812510, 0x50C95436C8B4F3B9], | [0xF3D8CD683FE16E54, 0x64FBA9447AE230A7], | [0x9867806127ECE4F5, 0xBF1D49CACCCD5E68], | [0xBE81607971E81E32, 0xEEE49C3D8000B602], | [0xEE21B897CE6225BE, 0x6A9DC34CE000E383], | [0x94D5135EE0FD5797, 0x02A29A100C008E32], | [0xBA0A5836993CAD7D, 0xC34B40940F00B1BE], | [0xE88CEE443F8BD8DC, 0xF41E10B912C0DE2E], | [0x915814EAA7B76789, 0x7892CA73ABB88ADD], | [0xB5AE1A2551A5416C, 0xD6B77D1096A6AD94], | [0xE319A0AEA60E91C7, 0xCC655C54BC5058F9], |]; | |// |static immutable short[1025] p10_exponents = [ | -1764, | -1761, | -1758, | -1754, | -1751, | -1748, | -1744, | -1741, | -1738, | -1734, | -1731, | -1728, | -1724, | -1721, | -1718, | -1714, | -1711, | -1708, | -1705, | -1701, | -1698, | -1695, | -1691, | -1688, | -1685, | -1681, | -1678, | -1675, | -1671, | -1668, | -1665, | -1661, | -1658, | -1655, | -1651, | -1648, | -1645, | -1641, | -1638, | -1635, | -1631, | -1628, | -1625, | -1621, | -1618, | -1615, | -1612, | -1608, | -1605, | -1602, | -1598, | -1595, | -1592, | -1588, | -1585, | -1582, | -1578, | -1575, | -1572, | -1568, | -1565, | -1562, | -1558, | -1555, | -1552, | -1548, | -1545, | -1542, | -1538, | -1535, | -1532, | -1528, | -1525, | -1522, | -1519, | -1515, | -1512, | -1509, | -1505, | -1502, | -1499, | -1495, | -1492, | -1489, | -1485, | -1482, | -1479, | -1475, | -1472, | -1469, | -1465, | -1462, | -1459, | -1455, | -1452, | -1449, | -1445, | -1442, | -1439, | -1435, | -1432, | -1429, | -1425, | -1422, | -1419, | -1416, | -1412, | -1409, | -1406, | -1402, | -1399, | -1396, | -1392, | -1389, | -1386, | -1382, | -1379, | -1376, | -1372, | -1369, | -1366, | -1362, | -1359, | -1356, | -1352, | -1349, | -1346, | -1342, | -1339, | -1336, | -1332, | -1329, | -1326, | -1323, | -1319, | -1316, | -1313, | -1309, | -1306, | -1303, | -1299, | -1296, | -1293, | -1289, | -1286, | -1283, | -1279, | -1276, | -1273, | -1269, | -1266, | -1263, | -1259, | -1256, | -1253, | -1249, | -1246, | -1243, | -1239, | -1236, | -1233, | -1229, | -1226, | -1223, | -1220, | -1216, | -1213, | -1210, | -1206, | -1203, | -1200, | -1196, | -1193, | -1190, | -1186, | -1183, | -1180, | -1176, | -1173, | -1170, | -1166, | -1163, | -1160, | -1156, | -1153, | -1150, | -1146, | -1143, | -1140, | -1136, | -1133, | -1130, | -1127, | -1123, | -1120, | -1117, | -1113, | -1110, | -1107, | -1103, | -1100, | -1097, | -1093, | -1090, | -1087, | -1083, | -1080, | -1077, | -1073, | -1070, | -1067, | -1063, | -1060, | -1057, | -1053, | -1050, | -1047, | -1043, | -1040, | -1037, | -1034, | -1030, | -1027, | -1024, | -1020, | -1017, | -1014, | -1010, | -1007, | -1004, | -1000, | -997, | -994, | -990, | -987, | -984, | -980, | -977, | -974, | -970, | -967, | -964, | -960, | -957, | -954, | -950, | -947, | -944, | -940, | -937, | -934, | -931, | -927, | -924, | -921, | -917, | -914, | -911, | -907, | -904, | -901, | -897, | -894, | -891, | -887, | -884, | -881, | -877, | -874, | -871, | -867, | -864, | -861, | -857, | -854, | -851, | -847, | -844, | -841, | -838, | -834, | -831, | -828, | -824, | -821, | -818, | -814, | -811, | -808, | -804, | -801, | -798, | -794, | -791, | -788, | -784, | -781, | -778, | -774, | -771, | -768, | -764, | -761, | -758, | -754, | -751, | -748, | -744, | -741, | -738, | -735, | -731, | -728, | -725, | -721, | -718, | -715, | -711, | -708, | -705, | -701, | -698, | -695, | -691, | -688, | -685, | -681, | -678, | -675, | -671, | -668, | -665, | -661, | -658, | -655, | -651, | -648, | -645, | -642, | -638, | -635, | -632, | -628, | -625, | -622, | -618, | -615, | -612, | -608, | -605, | -602, | -598, | -595, | -592, | -588, | -585, | -582, | -578, | -575, | -572, | -568, | -565, | -562, | -558, | -555, | -552, | -549, | -545, | -542, | -539, | -535, | -532, | -529, | -525, | -522, | -519, | -515, | -512, | -509, | -505, | -502, | -499, | -495, | -492, | -489, | -485, | -482, | -479, | -475, | -472, | -469, | -465, | -462, | -459, | -455, | -452, | -449, | -446, | -442, | -439, | -436, | -432, | -429, | -426, | -422, | -419, | -416, | -412, | -409, | -406, | -402, | -399, | -396, | -392, | -389, | -386, | -382, | -379, | -376, | -372, | -369, | -366, | -362, | -359, | -356, | -353, | -349, | -346, | -343, | -339, | -336, | -333, | -329, | -326, | -323, | -319, | -316, | -313, | -309, | -306, | -303, | -299, | -296, | -293, | -289, | -286, | -283, | -279, | -276, | -273, | -269, | -266, | -263, | -259, | -256, | -253, | -250, | -246, | -243, | -240, | -236, | -233, | -230, | -226, | -223, | -220, | -216, | -213, | -210, | -206, | -203, | -200, | -196, | -193, | -190, | -186, | -183, | -180, | -176, | -173, | -170, | -166, | -163, | -160, | -157, | -153, | -150, | -147, | -143, | -140, | -137, | -133, | -130, | -127, | -123, | -120, | -117, | -113, | -110, | -107, | -103, | -100, | -97, | -93, | -90, | -87, | -83, | -80, | -77, | -73, | -70, | -67, | -63, | -60, | -57, | -54, | -50, | -47, | -44, | -40, | -37, | -34, | -30, | -27, | -24, | -20, | -17, | -14, | -10, | -7, | -4, | 0, | 3, | 6, | 10, | 13, | 16, | 20, | 23, | 26, | 30, | 33, | 36, | 39, | 43, | 46, | 49, | 53, | 56, | 59, | 63, | 66, | 69, | 73, | 76, | 79, | 83, | 86, | 89, | 93, | 96, | 99, | 103, | 106, | 109, | 113, | 116, | 119, | 123, | 126, | 129, | 132, | 136, | 139, | 142, | 146, | 149, | 152, | 156, | 159, | 162, | 166, | 169, | 172, | 176, | 179, | 182, | 186, | 189, | 192, | 196, | 199, | 202, | 206, | 209, | 212, | 216, | 219, | 222, | 226, | 229, | 232, | 235, | 239, | 242, | 245, | 249, | 252, | 255, | 259, | 262, | 265, | 269, | 272, | 275, | 279, | 282, | 285, | 289, | 292, | 295, | 299, | 302, | 305, | 309, | 312, | 315, | 319, | 322, | 325, | 328, | 332, | 335, | 338, | 342, | 345, | 348, | 352, | 355, | 358, | 362, | 365, | 368, | 372, | 375, | 378, | 382, | 385, | 388, | 392, | 395, | 398, | 402, | 405, | 408, | 412, | 415, | 418, | 422, | 425, | 428, | 431, | 435, | 438, | 441, | 445, | 448, | 451, | 455, | 458, | 461, | 465, | 468, | 471, | 475, | 478, | 481, | 485, | 488, | 491, | 495, | 498, | 501, | 505, | 508, | 511, | 515, | 518, | 521, | 524, | 528, | 531, | 534, | 538, | 541, | 544, | 548, | 551, | 554, | 558, | 561, | 564, | 568, | 571, | 574, | 578, | 581, | 584, | 588, | 591, | 594, | 598, | 601, | 604, | 608, | 611, | 614, | 617, | 621, | 624, | 627, | 631, | 634, | 637, | 641, | 644, | 647, | 651, | 654, | 657, | 661, | 664, | 667, | 671, | 674, | 677, | 681, | 684, | 687, | 691, | 694, | 697, | 701, | 704, | 707, | 711, | 714, | 717, | 720, | 724, | 727, | 730, | 734, | 737, | 740, | 744, | 747, | 750, | 754, | 757, | 760, | 764, | 767, | 770, | 774, | 777, | 780, | 784, | 787, | 790, | 794, | 797, | 800, | 804, | 807, | 810, | 813, | 817, | 820, | 823, | 827, | 830, | 833, | 837, | 840, | 843, | 847, | 850, | 853, | 857, | 860, | 863, | 867, | 870, | 873, | 877, | 880, | 883, | 887, | 890, | 893, | 897, | 900, | 903, | 907, | 910, | 913, | 916, | 920, | 923, | 926, | 930, | 933, | 936, | 940, | 943, | 946, | 950, | 953, | 956, | 960, | 963, | 966, | 970, | 973, | 976, | 980, | 983, | 986, | 990, | 993, | 996, | 1000, | 1003, | 1006, | 1009, | 1013, | 1016, | 1019, | 1023, | 1026, | 1029, | 1033, | 1036, | 1039, | 1043, | 1046, | 1049, | 1053, | 1056, | 1059, | 1063, | 1066, | 1069, | 1073, | 1076, | 1079, | 1083, | 1086, | 1089, | 1093, | 1096, | 1099, | 1102, | 1106, | 1109, | 1112, | 1116, | 1119, | 1122, | 1126, | 1129, | 1132, | 1136, | 1139, | 1142, | 1146, | 1149, | 1152, | 1156, | 1159, | 1162, | 1166, | 1169, | 1172, | 1176, | 1179, | 1182, | 1186, | 1189, | 1192, | 1196, | 1199, | 1202, | 1205, | 1209, | 1212, | 1215, | 1219, | 1222, | 1225, | 1229, | 1232, | 1235, | 1239, | 1242, | 1245, | 1249, | 1252, | 1255, | 1259, | 1262, | 1265, | 1269, | 1272, | 1275, | 1279, | 1282, | 1285, | 1289, | 1292, | 1295, | 1298, | 1302, | 1305, | 1308, | 1312, | 1315, | 1318, | 1322, | 1325, | 1328, | 1332, | 1335, | 1338, | 1342, | 1345, | 1348, | 1352, | 1355, | 1358, | 1362, | 1365, | 1368, | 1372, | 1375, | 1378, | 1382, | 1385, | 1388, | 1392, | 1395, | 1398, | 1401, | 1405, | 1408, | 1411, | 1415, | 1418, | 1421, | 1425, | 1428, | 1431, | 1435, | 1438, | 1441, | 1445, | 1448, | 1451, | 1455, | 1458, | 1461, | 1465, | 1468, | 1471, | 1475, | 1478, | 1481, | 1485, | 1488, | 1491, | 1494, | 1498, | 1501, | 1504, | 1508, | 1511, | 1514, | 1518, | 1521, | 1524, | 1528, | 1531, | 1534, | 1538, | 1541, | 1544, | 1548, | 1551, | 1554, | 1558, | 1561, | 1564, | 1568, | 1571, | 1574, | 1578, | 1581, | 1584, | 1587, | 1591, | 1594, | 1597, | 1601, | 1604, | 1607, | 1611, | 1614, | 1617, | 1621, | 1624, | 1627, | 1631, | 1634, | 1637, |]; source/mir/bignum/internal/dec2flt_table.d has no code <<<<<< EOF # path=./source-mir-ndslice-sorting.lst |/++ |This is a submodule of $(MREF mir,ndslice). | |Note: | The combination of | $(SUBREF topology, pairwise) with lambda `"a <= b"` (`"a < b"`) and $(SUBREF algorithm, all) can be used | to check if an ndslice is sorted (strictly monotonic). | $(SUBREF topology, iota) can be used to make an index. | $(SUBREF topology, map) and $(SUBREF topology, zip) can be used to create Schwartzian transform. | See also the examples in the module. | | |See_also: $(SUBREF topology, flattened) | |`isSorted` and `isStrictlyMonotonic` | |License: $(HTTP www.apache.org/licenses/LICENSE-2.0, Apache-2.0) |Copyright: 2020 Ilya Yaroshenko, Kaleidic Associates Advisory Limited, Symmetry Investments |Authors: Andrei Alexandrescu (Phobos), Ilya Yaroshenko (API, rework, Mir adoptation) | |Macros: | SUBREF = $(REF_ALTTEXT $(TT $2), $2, mir, ndslice, $1)$(NBSP) |+/ |module mir.ndslice.sorting; | |/// Check if ndslice is sorted, or strictly monotonic. |@safe pure version(mir_test) unittest |{ | import mir.algorithm.iteration: all; | import mir.ndslice.slice: sliced; | import mir.ndslice.sorting: sort; | import mir.ndslice.topology: pairwise; | 1| auto arr = [1, 1, 2].sliced; | 1| assert(arr.pairwise!"a <= b".all); 1| assert(!arr.pairwise!"a < b".all); | 1| arr = [4, 3, 2, 1].sliced; | 1| assert(!arr.pairwise!"a <= b".all); 1| assert(!arr.pairwise!"a < b".all); | 1| sort(arr); | 1| assert(arr.pairwise!"a <= b".all); 1| assert(arr.pairwise!"a < b".all); |} | |/// Create index |version(mir_test) unittest |{ | import mir.algorithm.iteration: all; | import mir.ndslice.allocation: slice; | import mir.ndslice.slice: sliced; | import mir.ndslice.sorting: sort; | import mir.ndslice.topology: iota, pairwise; | 1| auto arr = [4, 2, 3, 1].sliced; | 1| auto index = arr.length.iota.slice; 7| index.sort!((a, b) => arr[a] < arr[b]); | 1| assert(arr[index].pairwise!"a <= b".all); |} | |/// Schwartzian transform |version(mir_test) unittest |{ | import mir.algorithm.iteration: all; | import mir.ndslice.allocation: slice; | import mir.ndslice.slice: sliced; | import mir.ndslice.sorting: sort; | import mir.ndslice.topology: zip, map, pairwise; | 10| alias transform = (a) => (a - 3) ^^ 2; | 1| auto arr = [4, 2, 3, 1].sliced; | 6| arr.map!transform.slice.zip(arr).sort!((l, r) => l.a < r.a); | 1| assert(arr.map!transform.pairwise!"a <= b".all); |} | |import mir.ndslice.slice; |import mir.math.common: optmath; | |@optmath: | |@safe pure version(mir_test) unittest |{ | import mir.algorithm.iteration: all; | import mir.ndslice.topology: pairwise; | 1| auto a = [1, 2, 3].sliced; 1| assert(a[0 .. 0].pairwise!"a <= b".all); 1| assert(a[0 .. 1].pairwise!"a <= b".all); 1| assert(a.pairwise!"a <= b".all); 1| auto b = [1, 3, 2].sliced; 1| assert(!b.pairwise!"a <= b".all); | | // ignores duplicates 1| auto c = [1, 1, 2].sliced; 1| assert(c.pairwise!"a <= b".all); |} | |@safe pure version(mir_test) unittest |{ | import mir.algorithm.iteration: all; | import mir.ndslice.topology: pairwise; | 1| assert([1, 2, 3][0 .. 0].sliced.pairwise!"a < b".all); 1| assert([1, 2, 3][0 .. 1].sliced.pairwise!"a < b".all); 1| assert([1, 2, 3].sliced.pairwise!"a < b".all); 1| assert(![1, 3, 2].sliced.pairwise!"a < b".all); 1| assert(![1, 1, 2].sliced.pairwise!"a < b".all); |} | | |/++ |Sorts ndslice, array, or series. | |See_also: $(SUBREF topology, flattened). |+/ |template sort(alias less = "a < b") |{ | import mir.functional: naryFun; | import mir.series: Series; | static if (__traits(isSame, naryFun!less, less)) | { |@optmath: | /++ | Sort n-dimensional slice. | +/ | Slice!(Iterator, N, kind) sort(Iterator, size_t N, SliceKind kind) | (Slice!(Iterator, N, kind) slice) | { 77| if (false) // break safety | { | import mir.utility : swapStars; 0000000| auto elem = typeof(*slice._iterator).init; 0000000| elem = elem; 0000000| auto l = less(elem, elem); | } | import mir.ndslice.topology: flattened; 77| if (slice.anyEmpty) 0000000| return slice; 77| .quickSortImpl!less(slice.flattened); 77| return slice; | } | | /++ | Sort for arrays | +/ | T[] sort(T)(T[] ar) | { | return .sort!less(ar.sliced).field; | } | | /++ | Sort for one-dimensional Series. | +/ | Series!(IndexIterator, Iterator, N, kind) | sort(IndexIterator, Iterator, size_t N, SliceKind kind) | (Series!(IndexIterator, Iterator, N, kind) series) | if (N == 1) | { | import mir.ndslice.sorting: sort; | import mir.ndslice.topology: zip; 36| with(series) 108| index.zip(data).sort!((a, b) => less(a.a, b.a)); 36| return series; | } | | /++ | Sort for n-dimensional Series. | +/ | Series!(IndexIterator, Iterator, N, kind) | sort( | IndexIterator, | Iterator, | size_t N, | SliceKind kind, | SortIndexIterator, | DataIterator, | ) | ( | Series!(IndexIterator, Iterator, N, kind) series, | Slice!SortIndexIterator indexBuffer, | Slice!DataIterator dataBuffer, | ) | { | import mir.algorithm.iteration: each; | import mir.ndslice.sorting: sort; | import mir.ndslice.topology: iota, zip, ipack, evertPack; | | assert(indexBuffer.length == series.length); | assert(dataBuffer.length == series.length); | indexBuffer[] = indexBuffer.length.iota!(typeof(indexBuffer.front)); 6| series.index.zip(indexBuffer).sort!((a, b) => less(a.a, b.a)); | series.data.ipack!1.evertPack.each!((sl){ | { | assert(sl.shape == dataBuffer.shape); | dataBuffer[] = sl[indexBuffer]; | sl[] = dataBuffer; | }}); | return series; | } | } | else | alias sort = .sort!(naryFun!less); |} | |/// |@safe pure version(mir_test) unittest |{ | import mir.algorithm.iteration: all; | import mir.ndslice.slice; | import mir.ndslice.sorting: sort; | import mir.ndslice.topology: pairwise; | 1| int[10] arr = [7,1,3,2,9,0,5,4,8,6]; | 1| auto data = arr[].sliced(arr.length); 1| data.sort(); 1| assert(data.pairwise!"a <= b".all); |} | |/// one-dimensional series |pure version(mir_test) unittest |{ | import mir.series; | 1| auto index = [4, 2, 1, 3, 0].sliced; 1| auto data = [5.6, 3.4, 2.1, 7.8, 0.1].sliced; 1| auto series = index.series(data); 1| series.sort; 1| assert(series.index == [0, 1, 2, 3, 4]); 1| assert(series.data == [0.1, 2.1, 3.4, 7.8, 5.6]); | /// initial index and data are the same 1| assert(index.iterator is series.index.iterator); 1| assert(data.iterator is series.data.iterator); | 17| foreach(obs; series) | { | static assert(is(typeof(obs) == Observation!(int, double))); | } |} | |/// two-dimensional series |pure version(mir_test) unittest |{ | import mir.series; | import mir.ndslice.allocation: uninitSlice; | 1| auto index = [4, 2, 3, 1].sliced; 1| auto data = | [2.1, 3.4, | 5.6, 7.8, | 3.9, 9.0, | 4.0, 2.0].sliced(4, 2); 1| auto series = index.series(data); | 1| series.sort( | uninitSlice!size_t(series.length), // index buffer | uninitSlice!double(series.length), // data buffer | ); | 1| assert(series.index == [1, 2, 3, 4]); 1| assert(series.data == | [[4.0, 2.0], | [5.6, 7.8], | [3.9, 9.0], | [2.1, 3.4]]); | /// initial index and data are the same 1| assert(index.iterator is series.index.iterator); 1| assert(data.iterator is series.data.iterator); |} | |void quickSortImpl(alias less, Iterator)(Slice!Iterator slice) @trusted |{ | import mir.utility : swap, swapStars; | | enum max_depth = 64; | enum naive_est = 1024 / slice.Element!0.sizeof; | enum size_t naive = 32 > naive_est ? 32 : naive_est; | //enum size_t naive = 1; | static assert(naive >= 1); | | for(;;) | { 77| auto l = slice._iterator; 77| auto r = l; 77| r += slice.length; | | static if (naive > 1) | { 77| if (slice.length <= naive || __ctfe) | { 77| auto p = r; 77| --p; 401| while(p != l) | { 324| --p; | //static if (is(typeof(() nothrow | // { | // auto t = slice[0]; if (less(t, slice[0])) slice[0] = slice[0]; | // }))) | //{ 324| auto d = p; | import mir.functional: unref; 324| auto temp = unref(*d); 324| auto c = d; 324| ++c; 324| if (less(*c, temp)) | { | do | { 845| d[0] = *c; 845| ++d; 845| ++c; | } 1598| while (c != r && less(*c, temp)); 250| d[0] = temp; | } | //} | //else | //{ | // auto d = p; | // auto c = d; | // ++c; | // while (less(*c, *d)) | // { | // swap(*d, *c); | // d = c; | // ++c; | // if (c == maxJ) break; | // } | //} | } 77| return; | } | } | else | { | if(slice.length <= 1) | return; | } | | // partition 0000000| auto lessI = l; 0000000| --r; 0000000| auto pivotIdx = l + slice.length / 2; 0000000| setPivot!less(slice.length, l, pivotIdx, r); | import mir.functional: unref; 0000000| auto pivot = unref(*pivotIdx); 0000000| --lessI; 0000000| auto greaterI = r; 0000000| swapStars(pivotIdx, greaterI); | | outer: for (;;) | { 0000000| do ++lessI; 0000000| while (less(*lessI, pivot)); 0000000| assert(lessI <= greaterI, "sort: invalid comparison function."); | for (;;) | { 0000000| if (greaterI == lessI) 0000000| break outer; 0000000| --greaterI; 0000000| if (!less(pivot, *greaterI)) 0000000| break; | } 0000000| assert(lessI <= greaterI, "sort: invalid comparison function."); 0000000| if (lessI == greaterI) 0000000| break; 0000000| swapStars(lessI, greaterI); | } | 0000000| swapStars(r, lessI); | 0000000| ptrdiff_t len = lessI - l; 0000000| auto tail = slice[len + 1 .. $]; 0000000| slice = slice[0 .. len]; 0000000| if (tail.length > slice.length) 0000000| swap(slice, tail); 0000000| quickSortImpl!less(tail); | } |} | |void setPivot(alias less, Iterator)(size_t length, ref Iterator l, ref Iterator mid, ref Iterator r) @trusted |{ 0000000| if (length < 512) | { 0000000| if (length >= 32) 0000000| medianOf!less(l, mid, r); 0000000| return; | } 0000000| auto quarter = length >> 2; 0000000| auto b = mid - quarter; 0000000| auto e = mid + quarter; 0000000| medianOf!less(l, e, mid, b, r); |} | |void medianOf(alias less, bool leanRight = false, Iterator) | (ref Iterator a, ref Iterator b) @trusted |{ | import mir.utility : swapStars; | 5| if (less(*b, *a)) { 2| swapStars(a, b); | } 5| assert(!less(*b, *a)); |} | |void medianOf(alias less, bool leanRight = false, Iterator) | (ref Iterator a, ref Iterator b, ref Iterator c) @trusted |{ | import mir.utility : swapStars; | 243| if (less(*c, *a)) // c < a | { 117| if (less(*a, *b)) // c < a < b | { 35| swapStars(a, b); 35| swapStars(a, c); | } | else // c < a, b <= a | { 82| swapStars(a, c); 119| if (less(*b, *a)) swapStars(a, b); | } | } | else // a <= c | { 126| if (less(*b, *a)) // b < a <= c | { 27| swapStars(a, b); | } | else // a <= c, a <= b | { 140| if (less(*c, *b)) swapStars(b, c); | } | } 243| assert(!less(*b, *a)); 243| assert(!less(*c, *b)); |} | |void medianOf(alias less, bool leanRight = false, Iterator) | (ref Iterator a, ref Iterator b, ref Iterator c, ref Iterator d) @trusted |{ | import mir.utility: swapStars; | | static if (!leanRight) | { | // Eliminate the rightmost from the competition 50| if (less(*d, *c)) swapStars(c, d); // c <= d 38| if (less(*d, *b)) swapStars(b, d); // b <= d 29| medianOf!less(a, b, c); | } | else | { | // Eliminate the leftmost from the competition 75| if (less(*b, *a)) swapStars(a, b); // a <= b 64| if (less(*c, *a)) swapStars(a, c); // a <= c 50| medianOf!less(b, c, d); | } |} | |void medianOf(alias less, bool leanRight = false, Iterator) | (ref Iterator a, ref Iterator b, ref Iterator c, ref Iterator d, ref Iterator e) @trusted |{ | import mir.utility: swapStars; // Credit: Teppo Niinimäki | 4| version(unittest) scope(success) | { 4| assert(!less(*c, *a)); 4| assert(!less(*c, *b)); 4| assert(!less(*d, *c)); 4| assert(!less(*e, *c)); | } | 7| if (less(*c, *a)) swapStars(a, c); 4| if (less(*d, *b)) swapStars(b, d); 4| if (less(*d, *c)) | { 3| swapStars(c, d); 3| swapStars(a, b); | } 4| if (less(*e, *b)) swapStars(b, e); 4| if (less(*e, *c)) | { 0000000| swapStars(c, e); 0000000| if (less(*c, *a)) swapStars(a, c); | } | else | { 4| if (less(*c, *b)) swapStars(b, c); | } |} | | |/++ |Returns: `true` if a sorted array contains the value. | |Params: | test = strict ordering symmetric predicate | |For non-symmetric predicates please use a structure with two `opCall`s or an alias of two global functions, |that correponds to `(array[i], value)` and `(value, array[i])` cases. | |See_also: $(LREF transitionIndex). |+/ |template assumeSortedContains(alias test = "a < b") |{ | import mir.functional: naryFun; | static if (__traits(isSame, naryFun!test, test)) | { |@optmath: | /++ | Params: | slice = sorted one-dimensional slice or array. | v = value to test with. It is passed to second argument. | +/ | bool assumeSortedContains(Iterator, SliceKind kind, V) | (auto ref Slice!(Iterator, 1, kind) slice, auto ref scope const V v) | { | auto ti = transitionIndex!test(slice, v); | return ti < slice.length && !test(v, slice[ti]); | } | | /// ditto | bool assumeSortedContains(T, V)(scope T[] ar, auto ref scope const V v) | { | return .assumeSortedContains!test(ar.sliced, v); | } | } | else | alias assumeSortedContains = .assumeSortedContains!(naryFun!test); |} | |/++ |Returns: the smallest index of a sorted array such | that the index corresponds to the arrays element at the index according to the predicate | and `-1` if the array doesn't contain corresponding element. | |Params: | test = strict ordering symmetric predicate. | |For non-symmetric predicates please use a structure with two `opCall`s or an alias of two global functions, |that correponds to `(array[i], value)` and `(value, array[i])` cases. | |See_also: $(LREF transitionIndex). |+/ |template assumeSortedEqualIndex(alias test = "a < b") |{ | import mir.functional: naryFun; | static if (__traits(isSame, naryFun!test, test)) | { |@optmath: | /++ | Params: | slice = sorted one-dimensional slice or array. | v = value to test with. It is passed to second argument. | +/ | sizediff_t assumeSortedEqualIndex(Iterator, SliceKind kind, V) | (auto ref Slice!(Iterator, 1, kind) slice, auto ref scope const V v) | { 3| auto ti = transitionIndex!test(slice, v); 9| return ti < slice.length && !test(v, slice[ti]) ? ti : -1; | } | | /// ditto | sizediff_t assumeSortedEqualIndex(T, V)(scope T[] ar, auto ref scope const V v) | { 3| return .assumeSortedEqualIndex!test(ar.sliced, v); | } | } | else | alias assumeSortedEqualIndex = .assumeSortedEqualIndex!(naryFun!test); |} | |/// |version(mir_test) |@safe pure unittest |{ | // sorted: a < b 1| auto a = [0, 1, 2, 3, 4, 6]; | 1| assert(a.assumeSortedEqualIndex(2) == 2); 1| assert(a.assumeSortedEqualIndex(5) == -1); | | // <= non strict predicates doesn't work 1| assert(a.assumeSortedEqualIndex!"a <= b"(2) == -1); |} | |/++ |Computes transition index using binary search. |It is low-level API for lower and upper bounds of a sorted array. | |Params: | test = ordering predicate for (`(array[i], value)`) pairs. | |See_also: $(SUBREF topology, assumeSortedEqualIndex). |+/ |template transitionIndex(alias test = "a < b") |{ | import mir.functional: naryFun; | static if (__traits(isSame, naryFun!test, test)) | { |@optmath: | /++ | Params: | slice = sorted one-dimensional slice or array. | v = value to test with. It is passed to second argument. | +/ | size_t transitionIndex(Iterator, SliceKind kind, V) | (auto ref Slice!(Iterator, 1, kind) slice, auto ref scope const V v) | { 3242| size_t first = 0, count = slice.length; 4949| while (count > 0) | { 6656| immutable step = count / 2, it = first + step; 3328| if (test(slice[it], v)) | { 1471| first = it + 1; 1471| count -= step + 1; | } | else | { 1857| count = step; | } | } 1621| return first; | } | | /// ditto | size_t transitionIndex(T, V)(scope T[] ar, auto ref scope const V v) | { 1406| return .transitionIndex!test(ar.sliced, v); | } | | } | else | alias transitionIndex = .transitionIndex!(naryFun!test); |} | |/// |version(mir_test) |@safe pure unittest |{ | // sorted: a < b 1| auto a = [0, 1, 2, 3, 4, 6]; | 1| auto i = a.transitionIndex(2); 1| assert(i == 2); 1| auto lowerBound = a[0 .. i]; | 1| auto j = a.transitionIndex!"a <= b"(2); 1| assert(j == 3); 1| auto upperBound = a[j .. $]; | 1| assert(a.transitionIndex(a[$ - 1]) == a.length - 1); 1| assert(a.transitionIndex!"a <= b"(a[$ - 1]) == a.length); |} | |/++ |Computes an index for `r` based on the comparison `less`. The |index is a sorted array of 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 indices, 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 `sort`'s. | |Can be combined with $(SUBREF topology, indexed) to create a view that is sorted |based on the index. | |Params: | less = The comparison to use. | r = The slice/array to index. | |Returns: | Index slice/array. | |See_Also: | $(HTTPS numpy.org/doc/stable/reference/generated/numpy.argsort.html, numpy.argsort) |+/ |Slice!(I*) makeIndex(I = size_t, alias less = "a < b", Iterator, SliceKind kind)(Slice!(Iterator, 1, kind) r) |{ | import mir.functional: naryFun; | import mir.ndslice.allocation: slice; | import mir.ndslice.topology: iota; 12| return r | .length | .iota!I | .slice 26| .sort!((a, b) => naryFun!less(r[a], r[b])); |} | |/// |I[] makeIndex(I = size_t, alias less = "a < b", T)(scope T[] r) |{ 12| return .makeIndex!(I, less)(r.sliced).field; |} | |/// |version(mir_test) |@safe pure nothrow |unittest |{ | import mir.algorithm.iteration: all; | import mir.ndslice.topology: indexed, pairwise; | 1| immutable arr = [ 2, 3, 1, 5, 0 ]; 1| auto index = arr.makeIndex; | 1| assert(arr.indexed(index).pairwise!"a < b".all); |} | |/// Sort based on index created from a separate array |version(mir_test) |@safe pure nothrow |unittest |{ | import mir.algorithm.iteration: equal; | import mir.ndslice.topology: indexed; | 1| immutable arr0 = [ 2, 3, 1, 5, 0 ]; 1| immutable arr1 = [ 1, 5, 4, 2, -1 ]; | 1| auto index = makeIndex(arr0); 1| assert(index.equal([4, 2, 0, 1, 3])); 1| auto view = arr1.indexed(index); 1| assert(view.equal([-1, 4, 1, 5, 2])); |} | |/++ |Partitions `slice` 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 `slice` and returns |an index `k < slice.length` such that: | |$(UL | |$(LI `slice[pivot]` is swapped to `slice[k]`) | | |$(LI All elements `e` in subrange `slice[0 .. k]` satisfy `!less(slice[k], e)` |(i.e. `slice[k]` is greater than or equal to each element to its left according |to predicate `less`)) | |$(LI All elements `e` in subrange `slice[k .. $]` satisfy |`!less(e, slice[k])` (i.e. `slice[k]` is less than or equal to each element to |its right according to predicate `less`))) | |If `slice` contains equivalent elements, multiple permutations of `slice` may |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 `slice.length / 2`. | |Params: | less = The predicate used for comparison | |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. |+/ |@trusted |template pivotPartition(alias less = "a < b") |{ | import mir.functional: naryFun; | | static if (__traits(isSame, naryFun!less, less)) | { | /++ | Params: | slice = slice being partitioned | pivot = The index of the pivot for partitioning, must be less than | `slice.length` or `0` if `slice.length` is `0` | +/ | size_t pivotPartition(Iterator, size_t N, SliceKind kind) | (Slice!(Iterator, N, kind) slice, | size_t pivot) | { 16| assert(pivot < slice.elementCount || slice.elementCount == 0 && pivot == 0, "pivotPartition: pivot must be less than the elementCount of the slice or the slice must be empty and pivot zero"); | 18| if (slice.elementCount <= 1) return 0; | | import mir.ndslice.topology: flattened; | 14| auto flattenedSlice = slice.flattened; 14| auto frontI = flattenedSlice._iterator; 14| auto lastI = frontI + flattenedSlice.length - 1; 14| auto pivotI = frontI + pivot; 14| pivotPartitionImpl!less(frontI, lastI, pivotI); 14| return pivotI - frontI; | } | } else { | alias pivotPartition = .pivotPartition!(naryFun!less); | } |} | |/// pivotPartition with 1-dimensional Slice |version(mir_test) |@safe pure nothrow |unittest |{ | import mir.ndslice.slice: sliced; | import mir.algorithm.iteration: all; | 1| auto x = [5, 3, 2, 6, 4, 1, 3, 7].sliced; 1| size_t pivot = pivotPartition(x, x.length / 2); | 5| assert(x[0 .. pivot].all!(a => a <= x[pivot])); 5| assert(x[pivot .. $].all!(a => a >= x[pivot])); |} | |/// pivotPartition with 2-dimensional Slice |version(mir_test) |@safe pure |unittest |{ | import mir.ndslice.fuse: fuse; | import mir.ndslice.topology: flattened; | import mir.algorithm.iteration: all; | 1| auto x = [ | [5, 3, 2, 6], | [4, 1, 3, 7] | ].fuse; | 1| size_t pivot = pivotPartition(x, x.elementCount / 2); | 1| auto xFlattened = x.flattened; 5| assert(xFlattened[0 .. pivot].all!(a => a <= xFlattened[pivot])); 5| assert(xFlattened[pivot .. $].all!(a => a >= xFlattened[pivot])); |} | |version(mir_test) |@safe |unittest |{ | void test(alias less)() | { | import mir.ndslice.slice: sliced; | import mir.algorithm.iteration: all, equal; | 2| Slice!(int*) x; 2| size_t pivot; | 2| x = [-9, -4, -2, -2, 9].sliced; 2| pivot = pivotPartition!less(x, x.length / 2); | 6| assert(x[0 .. pivot].all!(a => a <= x[pivot])); 8| assert(x[pivot .. $].all!(a => a >= x[pivot])); | 2| x = [9, 2, 8, -5, 5, 4, -8, -4, 9].sliced; 2| pivot = pivotPartition!less(x, x.length / 2); | 12| assert(x[0 .. pivot].all!(a => a <= x[pivot])); 10| assert(x[pivot .. $].all!(a => a >= x[pivot])); | 2| x = [ 42 ].sliced; 2| pivot = pivotPartition!less(x, x.length / 2); | 2| assert(pivot == 0); 2| assert(x.equal([ 42 ])); | 2| x = [ 43, 42 ].sliced; 2| pivot = pivotPartition!less(x, 0); 2| assert(pivot == 1); 2| assert(x.equal([ 42, 43 ])); | 2| x = [ 43, 42 ].sliced; 2| pivot = pivotPartition!less(x, 1); | 2| assert(pivot == 0); 2| assert(x.equal([ 42, 43 ])); | 2| x = [ 42, 42 ].sliced; 2| pivot = pivotPartition!less(x, 0); | 3| assert(pivot == 0 || pivot == 1); 2| assert(x.equal([ 42, 42 ])); | 2| pivot = pivotPartition!less(x, 1); | 3| assert(pivot == 0 || pivot == 1); 2| assert(x.equal([ 42, 42 ])); | } 1| test!"a < b"; | static bool myLess(int a, int b) | { | static bool bogus; 18| if (bogus) throw new Exception(""); // just to make it no-nothrow 18| return a < b; | } 1| test!myLess; |} | |@trusted |template pivotPartitionImpl(alias less) |{ | void pivotPartitionImpl(Iterator) | (ref Iterator frontI, | ref Iterator lastI, | ref Iterator pivotI) | { 706| assert(pivotI <= lastI && pivotI >= frontI, "pivotPartition: pivot must be less than the length of slice or slice must be empty and pivot zero"); | 353| if (frontI == lastI) return; | | import mir.utility: swapStars; | | // Pivot at the front 353| swapStars(pivotI, frontI); | | // 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 = frontI; x = frontI; return less(*x, *x); } | ))) | { | // Plant the pivot in the end as well as a sentinel 347| auto loI = frontI; 347| auto hiI = lastI; 347| auto save = *hiI; 347| *hiI = *frontI; // Vacancy is in r[$ - 1] now | | // Start process | for (;;) | { | // Loop invariant | version(mir_test) | { | // this used to import std.algorithm.all, but we want to | // save imports when unittests are enabled if possible. 527| size_t len = lastI - frontI + 1; 2451| foreach (x; 0 .. (loI - frontI)) 290| assert(!less(*frontI, frontI[x]), "pivotPartition: *frontI must not be less than frontI[x]"); 2760| foreach (x; (hiI - frontI + 1) .. len) 393| assert(!less(frontI[x], *frontI), "pivotPartition: frontI[x] must not be less than *frontI"); | } 2362| do ++loI; while (less(*loI, *frontI)); 527| *(hiI) = *(loI); | // Vacancy is now in slice[lo] 2276| do --hiI; while (less(*frontI, *hiI)); 874| if (loI >= hiI) break; 180| *(loI) = *(hiI); | // Vacancy is not in slice[hi] | } | // Fixup 347| assert(loI - hiI <= 2, "pivotPartition: Following compare not possible"); 347| assert(!less(*frontI, *hiI), "pivotPartition: *hiI must not be less than *frontI"); 347| if (loI - hiI == 2) | { 46| assert(!less(hiI[1], *frontI), "pivotPartition: *(hiI + 1) must not be less than *frontI"); 46| *(loI) = hiI[1]; 46| --loI; | } 347| *loI = save; 532| if (less(*frontI, save)) --loI; 347| assert(!less(*frontI, *loI), "pivotPartition: *frontI must not be less than *loI"); | } else { 6| auto loI = frontI; 6| ++loI; 6| auto hiI = lastI; | 2| loop: for (;; loI++, hiI--) | { 6| for (;; ++loI) | { 16| if (loI > hiI) break loop; 18| if (!less(*loI, *frontI)) break; | } | // found the left bound: !less(*loI, *frontI) 6| assert(loI <= hiI, "pivotPartition: loI must be less or equal than hiI"); 2| for (;; --hiI) | { 12| if (loI >= hiI) break loop; 6| if (!less(*frontI, *hiI)) break; | } | // found the right bound: !less(*frontI, *hiI), swap & make progress 2| assert(!less(*loI, *hiI), "pivotPartition: *lowI must not be less than *hiI"); 2| swapStars(loI, hiI); | } 6| --loI; | } | 353| swapStars(loI, frontI); 353| pivotI = loI; | } |} | |version(mir_test) |@safe pure nothrow |unittest { | import mir.ndslice.sorting: partitionAt; | import mir.ndslice.allocation: rcslice; 2| auto x = rcslice!double(4); 1| x[0] = 3; 1| x[1] = 2; 1| x[2] = 1; 1| x[3] = 0; 1| partitionAt!("a > b")(x, 2); |} | | |version(mir_test) |@trusted pure nothrow |unittest |{ | import mir.ndslice.slice: sliced; | import mir.algorithm.iteration: all; | 1| auto x = [5, 3, 2, 6, 4, 1, 3, 7].sliced; 1| auto frontI = x._iterator; 1| auto lastI = x._iterator + x.length - 1; 1| auto pivotI = frontI + x.length / 2; 23| alias less = (a, b) => (a < b); 1| pivotPartitionImpl!less(frontI, lastI, pivotI); 1| size_t pivot = pivotI - frontI; | 5| assert(x[0 .. pivot].all!(a => a <= x[pivot])); 5| assert(x[pivot .. $].all!(a => a >= x[pivot])); |} | |/++ |Partitions `slice`, such that all elements `e1` from `slice[0]` to `slice[nth]` |satisfy `!less(slice[nth], e1)`, and all elements `e2` from `slice[nth]` to |`slice[slice.length]` satisfy `!less(e2, slice[nth])`. This effectively reorders |`slice` such that `slice[nth]` refers to the element that would fall there if |the range were fully sorted. Performs an expected $(BIGOH slice.length) |evaluations of `less` and `swap`, with a worst case of $(BIGOH slice.length^^2). | |This function implements the [Fast, Deterministic Selection](https://erdani.com/research/sea2017.pdf) |algorithm that is implemented in the [`topN`](https://dlang.org/library/std/algorithm/sorting/top_n.html) |function in D's standard library (as of version `2.092.0`). | |Params: | less = The predicate to sort by. | |See_Also: | $(LREF pivotPartition), https://erdani.com/research/sea2017.pdf | |+/ |template partitionAt(alias less = "a < b") |{ | import mir.functional: naryFun; | | static if (__traits(isSame, naryFun!less, less)) | { | /++ | Params: | slice = n-dimensional slice | nth = The index of the element that should be in sorted position after the | function is finished. | +/ | void partitionAt(Iterator, size_t N, SliceKind kind) | (Slice!(Iterator, N, kind) slice, size_t nth) @trusted nothrow @nogc | { | import mir.qualifier: lightScope; | import core.lifetime: move; | import mir.ndslice.topology: flattened; | 241| assert(slice.elementCount > 0, "partitionAt: slice must have elementCount greater than 0"); 241| assert(nth >= 0, "partitionAt: nth must be greater than or equal to zero"); 241| assert(nth < slice.elementCount, "partitionAt: nth must be less than the elementCount of the slice"); | 241| bool useSampling = true; 242| auto flattenedSlice = slice.move.flattened; 241| auto frontI = flattenedSlice._iterator.lightScope; 241| auto lastI = frontI + (flattenedSlice.length - 1); 241| partitionAtImpl!less(frontI, lastI, nth, useSampling); | } | } | else | alias partitionAt = .partitionAt!(naryFun!less); |} | |/// Partition 1-dimensional slice at nth |version(mir_test) |@safe pure nothrow |unittest { | import mir.ndslice.slice: sliced; | 1| size_t nth = 2; 1| auto x = [3, 1, 5, 2, 0].sliced; 1| x.partitionAt(nth); 1| assert(x[nth] == 2); |} | |/// Partition 2-dimensional slice |version(mir_test) |@safe pure nothrow |unittest { | import mir.ndslice.slice: sliced; | 1| size_t nth = 4; 1| auto x = [3, 1, 5, 2, 0, 7].sliced(3, 2); 1| x.partitionAt(nth); 1| assert(x[2, 0] == 5); |} | |/// Can supply alternate ordering function |version(mir_test) |@safe pure nothrow |unittest { | import mir.ndslice.slice: sliced; | 1| size_t nth = 2; 1| auto x = [3, 1, 5, 2, 0].sliced; 1| x.partitionAt!("a > b")(nth); 1| assert(x[nth] == 2); |} | |// Check issue #328 fixed |version(mir_test) |@safe pure nothrow |unittest { | import mir.ndslice.slice: sliced; | 1| auto slice = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17].sliced; 1| partitionAt(slice, 8); 1| partitionAt(slice, 9); |} | |version(unittest) { | template checkPartitionAtAll(alias less = "a < b") | { | import mir.functional: naryFun; | import mir.ndslice.slice: SliceKind, Slice; | | static if (__traits(isSame, naryFun!less, less)) | { | @safe pure nothrow | static bool checkPartitionAtAll | (Iterator, SliceKind kind)( | Slice!(Iterator, 1, kind) x) | { 15| auto x_sorted = x.dup; 15| x_sorted.sort!less; | 15| bool result = true; | 546| foreach (nth; 0 .. x.length) | { 167| auto x_i = x.dup; 167| x_i.partitionAt!less(nth); 167| if (x_i[nth] != x_sorted[nth]) { 0000000| result = false; 0000000| break; | } | } 15| return result; | } | } else { | alias checkPartitionAtAll = .checkPartitionAtAll!(naryFun!less); | } | } |} | |version(mir_test) |@safe pure nothrow |unittest { | import mir.ndslice.slice: sliced; | 1| assert(checkPartitionAtAll([2, 2].sliced)); | 1| assert(checkPartitionAtAll([3, 1, 5, 2, 0].sliced)); 1| assert(checkPartitionAtAll([3, 1, 5, 0, 2].sliced)); 1| assert(checkPartitionAtAll([0, 0, 4, 3, 3].sliced)); 1| assert(checkPartitionAtAll([5, 1, 5, 1, 5].sliced)); 1| assert(checkPartitionAtAll([2, 2, 0, 0, 0].sliced)); | 1| assert(checkPartitionAtAll([ 2, 12, 10, 8, 1, 20, 19, 1, 2, 7].sliced)); 1| assert(checkPartitionAtAll([ 4, 18, 16, 0, 15, 6, 2, 17, 10, 16].sliced)); 1| assert(checkPartitionAtAll([ 7, 5, 9, 4, 4, 2, 12, 20, 15, 15].sliced)); | 1| assert(checkPartitionAtAll([17, 87, 58, 50, 34, 98, 25, 77, 88, 79].sliced)); | 1| assert(checkPartitionAtAll([ 6, 7, 10, 25, 5, 10, 9, 0, 2, 15, 7, 9, 11, 8, 13, 18, 17, 13, 25, 22].sliced)); 1| assert(checkPartitionAtAll([21, 3, 11, 22, 24, 12, 14, 12, 15, 15, 1, 3, 12, 15, 25, 19, 9, 16, 16, 19].sliced)); 1| assert(checkPartitionAtAll([22, 6, 18, 0, 1, 8, 13, 13, 16, 19, 23, 17, 4, 6, 12, 24, 15, 20, 11, 17].sliced)); 1| assert(checkPartitionAtAll([19, 23, 14, 5, 12, 3, 13, 7, 25, 25, 24, 9, 21, 25, 12, 22, 15, 22, 7, 11].sliced)); 1| assert(checkPartitionAtAll([ 0, 2, 7, 16, 2, 20, 1, 11, 17, 5, 22, 17, 25, 13, 14, 5, 22, 21, 24, 14].sliced)); |} | |private @trusted pure nothrow @nogc |void partitionAtImpl(alias less, Iterator)( | Iterator loI, | Iterator hiI, | size_t n, | bool useSampling) |{ 410| assert(loI <= hiI, "partitionAtImpl: frontI must be less than or equal to lastI"); | | import mir.utility: swapStars; | import mir.functional: reverseArgs; | 410| Iterator pivotI; 410| size_t len; | | for (;;) { 818| len = hiI - loI + 1; | 818| if (len <= 1) { 125| break; | } | 693| if (n == 0) { 101| pivotI = loI; 1602| foreach (i; 1 .. len) { 433| if (less(loI[i], *pivotI)) { 84| pivotI = loI + i; | } | } 101| swapStars(loI + n, pivotI); 101| break; | } | 592| if (n + 1 == len) { 116| pivotI = loI; 1791| foreach (i; 1 .. len) { 481| if (reverseArgs!less(loI[i], *pivotI)) { 135| pivotI = loI + i; | } | } 116| swapStars(loI + n, pivotI); 116| break; | } | 476| if (len <= 12) { 338| pivotI = loI + len / 2; 338| pivotPartitionImpl!less(loI, hiI, pivotI); 138| } else if (n * 16 <= (len - 1) * 7) { 61| pivotI = partitionAtPartitionOffMedian!(less, false)(loI, hiI, n, useSampling); | // Quality check 61| if (useSampling) | { 55| auto pivot = pivotI - loI; 55| if (pivot < n) | { 8| if (pivot * 4 < len) | { 5| useSampling = false; | } | } 47| else if ((len - pivot) * 8 < len * 3) | { 1| useSampling = false; | } | } 77| } else if (n * 16 >= (len - 1) * 9) { 61| pivotI = partitionAtPartitionOffMedian!(less, true)(loI, hiI, n, useSampling); | // Quality check 61| if (useSampling) | { 49| auto pivot = pivotI - loI; 49| if (pivot < n) | { 37| if (pivot * 8 < len * 3) | { 8| useSampling = false; | } | } 12| else if ((len - pivot) * 4 < len) | { 7| useSampling = false; | } | } | } else { 16| pivotI = partitionAtPartition!less(loI, hiI, n, useSampling); | // Quality check 16| if (useSampling) { 15| auto pivot = pivotI - loI; 28| if (pivot * 9 < len * 2 || pivot * 9 > len * 7) | { | // Failed - abort sampling going forward 2| useSampling = false; | } | } | } | 476| if (n < (pivotI - loI)) { 188| hiI = pivotI - 1; 288| } else if (n > (pivotI - loI)) { 220| n -= (pivotI - loI + 1); 220| loI = pivotI; 220| ++loI; | } else { 68| break; | } | } |} | |version(mir_test) |@trusted pure nothrow |unittest { | import mir.ndslice.slice: sliced; | 1| size_t nth = 2; 1| auto x = [3, 1, 5, 2, 0].sliced; 1| auto frontI = x._iterator; 1| auto lastI = frontI + x.elementCount - 1; 22| partitionAtImpl!((a, b) => (a < b))(frontI, lastI, 1, true); 1| assert(x[nth] == 2); |} | |version(mir_test) |@trusted pure nothrow |unittest { | import mir.ndslice.slice: sliced; | 1| size_t nth = 4; 1| auto x = [3, 1, 5, 2, 0, 7].sliced(3, 2); 1| auto frontI = x._iterator; 1| auto lastI = frontI + x.elementCount - 1; 20| partitionAtImpl!((a, b) => (a < b))(frontI, lastI, nth, true); 1| assert(x[2, 0] == 5); |} | |version(mir_test) |@trusted pure nothrow |unittest { | import mir.ndslice.slice: sliced; | 1| size_t nth = 1; 1| auto x = [0, 0, 4, 3, 3].sliced; 1| auto frontI = x._iterator; 1| auto lastI = frontI + x.elementCount - 1; 17| partitionAtImpl!((a, b) => (a < b))(frontI, lastI, nth, true); 1| assert(x[nth] == 0); |} | |version(mir_test) |@trusted pure nothrow |unittest { | import mir.ndslice.slice: sliced; | 1| size_t nth = 2; 1| auto x = [0, 0, 4, 3, 3].sliced; 1| auto frontI = x._iterator; 1| auto lastI = frontI + x.elementCount - 1; 20| partitionAtImpl!((a, b) => (a < b))(frontI, lastI, nth, true); 1| assert(x[nth] == 3); |} | |version(mir_test) |@trusted pure nothrow |unittest { | import mir.ndslice.slice: sliced; | 1| size_t nth = 3; 1| auto x = [0, 0, 4, 3, 3].sliced; 1| auto frontI = x._iterator; 1| auto lastI = frontI + x.elementCount - 1; 12| partitionAtImpl!((a, b) => (a < b))(frontI, lastI, nth, true); 1| assert(x[nth] == 3); |} | |version(mir_test) |@trusted pure nothrow |unittest { | import mir.ndslice.slice: sliced; | 1| size_t nth = 4; 1| auto x = [ 2, 12, 10, 8, 1, 20, 19, 1, 2, 7].sliced; 1| auto frontI = x._iterator; 1| auto lastI = frontI + x.elementCount - 1; 46| partitionAtImpl!((a, b) => (a < b))(frontI, lastI, nth, true); 1| assert(x[nth] == 7); |} | |version(mir_test) |@trusted pure nothrow |unittest { | import mir.ndslice.slice: sliced; | 1| size_t nth = 5; 1| auto x = [ 2, 12, 10, 8, 1, 20, 19, 1, 2, 7].sliced; 1| auto frontI = x._iterator; 1| auto lastI = frontI + x.elementCount - 1; 64| partitionAtImpl!((a, b) => (a < b))(frontI, lastI, nth, true); 1| assert(x[nth] == 8); |} | |version(mir_test) |@trusted pure nothrow |unittest { | import mir.ndslice.slice: sliced; | 1| size_t nth = 6; 1| auto x = [ 2, 12, 10, 8, 1, 20, 19, 1, 2, 7].sliced; 1| auto frontI = x._iterator; 1| auto lastI = frontI + x.elementCount - 1; 59| partitionAtImpl!((a, b) => (a < b))(frontI, lastI, nth, true); 1| assert(x[nth] == 10); |} | |// Check all partitionAt |version(mir_test) |@trusted pure nothrow |unittest { | import mir.ndslice.slice: sliced; | import mir.ndslice.allocation: slice; | | static immutable raw = [ 6, 7, 10, 25, 5, 10, 9, 0, 2, 15, 7, 9, 11, 8, 13, 18, 17, 13, 25, 22]; | | static void fill(T)(T x) { 882| for (size_t i = 0; i < x.length; i++) { 420| x[i] = raw[i]; | } | } 1| auto x = slice!int(raw.length); 1| fill(x); 1| auto x_sort = x.dup; 1| x_sort = x_sort.sort; 1| size_t i = 0; 21| while (i < raw.length) { 20| auto frontI = x._iterator; 20| auto lastI = frontI + x.length - 1; 1615| partitionAtImpl!((a, b) => (a < b))(frontI, lastI, i, true); 20| assert(x[i] == x_sort[i]); 20| fill(x); 20| i++; | } |} | |private @trusted pure nothrow @nogc |Iterator partitionAtPartition(alias less, Iterator)( | ref Iterator frontI, | ref Iterator lastI, | size_t n, | bool useSampling) |{ 17| size_t len = lastI - frontI + 1; | 34| assert(len >= 9 && n < len, "partitionAtPartition: length must be longer than 9 and n must be less than r.length"); | 17| size_t ninth = len / 9; 17| size_t pivot = ninth / 2; | // Position subrange r[loI .. hiI] to have length equal to ninth and its upper | // median r[loI .. hiI][$ / 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. 17| auto loI = frontI; 17| loI += len / 2 - pivot; 17| auto hiI = loI; 17| hiI += ninth; | | // We have either one straggler on the left, one on the right, or none. 17| assert(loI - frontI <= lastI - hiI + 1 || lastI - hiI <= loI - frontI + 1, "partitionAtPartition: straggler check failed for loI, len, hiI"); 17| assert(loI - frontI >= ninth * 4, "partitionAtPartition: loI - frontI >= ninth * 4"); 17| assert((lastI + 1) - hiI >= ninth * 4, "partitionAtPartition: (lastI + 1) - hiI >= ninth * 4"); | | // Partition in groups of 3, and the mid tertile again in groups of 3 17| if (!useSampling) { 1| auto loI_ = loI; 1| loI_ -= ninth; 1| auto hiI_ = hiI; 1| hiI_ += ninth; 1| p3!(less, Iterator)(frontI, lastI, loI_, hiI_); | } 17| p3!(less, Iterator)(frontI, lastI, loI, hiI); | | // Get the median of medians of medians | // Map the full interval of n to the full interval of the ninth 17| pivot = (n * (ninth - 1)) / (len - 1); 17| if (hiI > loI) { 17| auto hiI_minus = hiI; 17| --hiI_minus; 17| partitionAtImpl!less(loI, hiI_minus, pivot, useSampling); | } | 17| auto pivotI = loI; 17| pivotI += pivot; | 17| return expandPartition!less(frontI, lastI, loI, pivotI, hiI); |} | |version(mir_test) |@trusted pure nothrow |unittest { | import mir.ndslice.slice: sliced; 1| auto x = [ 6, 7, 10, 25, 5, 10, 9, 0, 2, 15, 7, 9, 11, 8, 13, 18, 17, 13, 25, 22].sliced; 1| auto x_sort = x.dup; 1| x_sort = x_sort.sort; 1| auto frontI = x._iterator; 1| auto lastI = frontI + x.length - 1; 1| size_t n = x.length / 2; 66| partitionAtPartition!((a, b) => (a < b))(frontI, lastI, n, true); 1| assert(x[n - 1] == x_sort[n - 1]); |} | |private @trusted pure nothrow @nogc |Iterator partitionAtPartitionOffMedian(alias less, bool leanRight, Iterator)( | ref Iterator frontI, | ref Iterator lastI, | size_t n, | bool useSampling) |{ 124| size_t len = lastI - frontI + 1; | 124| assert(len >= 12, "partitionAtPartitionOffMedian: len must be greater than 11"); 124| assert(n < len, "partitionAtPartitionOffMedian: n must be less than len"); 124| auto _4 = len / 4; 124| auto leftLimitI = frontI; | static if (leanRight) 62| leftLimitI += 2 * _4; | else 62| leftLimitI += _4; | // Partition in groups of 4, and the left quartile again in groups of 3 124| if (!useSampling) | { 18| auto leftLimit_plus_4 = leftLimitI; 18| leftLimit_plus_4 += _4; 18| p4!(less, leanRight)(frontI, lastI, leftLimitI, leftLimit_plus_4); | } 124| auto _12 = _4 / 3; 124| auto loI = leftLimitI; 124| loI += _12; 124| auto hiI = loI; 124| hiI += _12; 124| p3!less(frontI, lastI, loI, hiI); | | // Get the median of medians of medians | // Map the full interval of n to the full interval of the ninth 124| auto pivot = (n * (_12 - 1)) / (len - 1); 124| if (hiI > loI) { 124| auto hiI_minus = hiI; 124| --hiI_minus; 124| partitionAtImpl!less(loI, hiI_minus, pivot, useSampling); | } 124| auto pivotI = loI; 124| pivotI += pivot; 124| return expandPartition!less(frontI, lastI, loI, pivotI, hiI); |} | |version(mir_test) |@trusted pure nothrow |unittest { | import mir.ndslice.slice: sliced; | import mir.algorithm.iteration: equal; | 1| auto x = [ 6, 7, 10, 25, 5, 10, 9, 0, 2, 15, 7, 9, 11, 8, 13, 18, 17, 13, 25, 22].sliced; 1| auto frontI = x._iterator; 1| auto lastI = frontI + x.length - 1; 63| partitionAtPartitionOffMedian!((a, b) => (a < b), false)(frontI, lastI, 5, true); 1| assert(x.equal([6, 7, 8, 9, 5, 0, 2, 7, 9, 15, 10, 25, 11, 10, 13, 18, 17, 13, 25, 22])); |} | |version(mir_test) |@trusted pure nothrow |unittest { | import mir.ndslice.slice: sliced; | import mir.algorithm.iteration: equal; | 1| auto x = [ 6, 7, 10, 25, 5, 10, 9, 0, 2, 15, 7, 9, 11, 8, 13, 18, 17, 13, 25, 22].sliced; 1| auto frontI = x._iterator; 1| auto lastI = frontI + x.length - 1; 59| partitionAtPartitionOffMedian!((a, b) => (a < b), true)(frontI, lastI, 15, true); 1| assert(x.equal([6, 7, 8, 7, 5, 2, 9, 0, 9, 15, 25, 10, 11, 10, 13, 18, 17, 13, 25, 22])); |} | |private @trusted |void p3(alias less, Iterator)( | Iterator frontI, | Iterator lastI, | Iterator loI, | Iterator hiI) |{ 286| assert(loI <= hiI && hiI <= lastI, "p3: loI must be less than or equal to hiI and hiI must be less than or equal to lastI"); 143| immutable diffI = hiI - loI; 143| Iterator lo_loI; 143| Iterator hi_loI; 467| for (; loI < hiI; ++loI) | { 162| lo_loI = loI; 162| lo_loI -= diffI; 162| hi_loI = loI; 162| hi_loI += diffI; 162| assert(lo_loI >= frontI, "p3: lo_loI must be greater than or equal to frontI"); 162| assert(hi_loI <= lastI, "p3: hi_loI must be less than or equal to lastI"); 162| medianOf!less(lo_loI, loI, hi_loI); | } |} | |version(mir_test) |@trusted pure nothrow |unittest { | import mir.ndslice.slice: sliced; | import mir.algorithm.iteration: equal; | 1| auto x = [3, 4, 0, 5, 2, 1].sliced; 1| auto frontI = x._iterator; 1| auto lastI = frontI + x.length - 1; 1| auto loI = frontI + 2; 1| auto hiI = frontI + 4; 10| p3!((a, b) => (a < b))(frontI, lastI, loI, hiI); 1| assert(x.equal([0, 1, 2, 4, 3, 5])); |} | |private @trusted |template p4(alias less, bool leanRight) |{ | void p4(Iterator)( | Iterator frontI, | Iterator lastI, | Iterator loI, | Iterator hiI) | { 40| assert(loI <= hiI && hiI <= lastI, "p4: loI must be less than or equal to hiI and hiI must be less than or equal to lastI"); | 20| immutable diffI = hiI - loI; 20| immutable diffI2 = diffI * 2; | 20| Iterator lo_loI; 20| Iterator hi_loI; | | static if (leanRight) 13| Iterator lo2_loI; | else 7| Iterator hi2_loI; | 168| for (; loI < hiI; ++loI) | { 74| lo_loI = loI - diffI; 74| hi_loI = loI + diffI; | 74| assert(lo_loI >= frontI, "p4: lo_loI must be greater than or equal to frontI"); 74| assert(hi_loI <= lastI, "p4: hi_loI must be less than or equal to lastI"); | | static if (leanRight) { 50| lo2_loI = loI - diffI2; 50| assert(lo2_loI >= frontI, "lo2_loI must be greater than or equal to frontI"); 50| medianOf!(less, leanRight)(lo2_loI, lo_loI, loI, hi_loI); | } else { 24| hi2_loI = loI + diffI2; 24| assert(hi2_loI <= lastI, "hi2_loI must be less than or equal to lastI"); 24| medianOf!(less, leanRight)(lo_loI, loI, hi_loI, hi2_loI); | } | } | } |} | |version(mir_test) |@trusted pure nothrow |unittest { | import mir.ndslice.slice: sliced; | import mir.algorithm.iteration: equal; | 1| auto x = [3, 4, 0, 7, 2, 6, 5, 1, 4].sliced; 1| auto frontI = x._iterator; 1| auto lastI = frontI + x.length - 1; 1| auto loI = frontI + 3; 1| auto hiI = frontI + 5; 14| p4!((a, b) => (a < b), false)(frontI, lastI, loI, hiI); 1| assert(x.equal([3, 1, 0, 4, 2, 6, 4, 7, 5])); |} | |version(mir_test) |@trusted pure nothrow |unittest { | import mir.ndslice.slice: sliced; | import mir.algorithm.iteration: equal; | 1| auto x = [3, 4, 0, 8, 2, 7, 5, 1, 4, 3].sliced; 1| auto frontI = x._iterator; 1| auto lastI = frontI + x.length - 1; 1| auto loI = frontI + 4; 1| auto hiI = frontI + 6; 14| p4!((a, b) => (a < b), true)(frontI, lastI, loI, hiI); 1| assert(x.equal([0, 4, 2, 1, 3, 7, 5, 8, 4, 3])); |} | |private @trusted |template expandPartition(alias less) |{ | Iterator expandPartition(Iterator)( | ref Iterator frontI, | ref Iterator lastI, | ref Iterator loI, | ref Iterator pivotI, | ref Iterator hiI) | { | import mir.algorithm.iteration: all; | 142| assert(frontI <= loI, "expandPartition: frontI must be less than or equal to loI"); 142| assert(loI <= pivotI, "expandPartition: loI must be less than or equal pivotI"); 142| assert(pivotI < hiI, "expandPartition: pivotI must be less than hiI"); 142| assert(hiI <= lastI, "expandPartition: hiI must be less than or equal to lastI"); | 855| foreach(x; loI .. (pivotI + 1)) 143| assert(!less(*pivotI, *x), "expandPartition: loI .. (pivotI + 1) failed test"); 474| foreach(x; (pivotI + 1) .. hiI) 16| assert(!less(*x, *pivotI), "expandPartition: (pivotI + 1) .. hiI failed test"); | | import mir.utility: swapStars; | import mir.algorithm.iteration: all; | // We work with closed intervals! 142| --hiI; | 142| auto leftI = frontI; 142| auto rightI = lastI; 191| loop: for (;; ++leftI, --rightI) | { 596| for (;; ++leftI) | { 1015| if (leftI == loI) break loop; 1090| if (!less(*leftI, *pivotI)) break; | } 738| for (;; --rightI) | { 1041| if (rightI == hiI) break loop; 1120| if (!less(*pivotI, *rightI)) break; | } 191| swapStars(leftI, rightI); | } | 855| foreach(x; loI .. (pivotI + 1)) 143| assert(!less(*pivotI, *x), "expandPartition: loI .. (pivotI + 1) failed less than test"); 474| foreach(x; (pivotI + 1) .. (hiI + 1)) 16| assert(!less(*x, *pivotI), "expandPartition: (pivotI + 1) .. (hiI + 1) failed less than test"); 2787| foreach(x; frontI .. leftI) 787| assert(!less(*pivotI, *x), "expandPartition: frontI .. leftI failed less than test"); 3213| foreach(x; (rightI + 1) .. (lastI + 1)) 929| assert(!less(*x, *pivotI), "expandPartition: (rightI + 1) .. (lastI + 1) failed less than test"); | 142| auto oldPivotI = pivotI; | 142| if (leftI < loI) | { | // First loop: spend r[loI .. pivot] 56| for (; loI < pivotI; ++leftI) | { 0000000| if (leftI == loI) goto done; 0000000| if (!less(*oldPivotI, *leftI)) continue; 0000000| --pivotI; 0000000| assert(!less(*oldPivotI, *pivotI), "expandPartition: less check failed"); 0000000| swapStars(leftI, pivotI); | } | // Second loop: make leftI and pivot meet 169| for (;; ++leftI) | { 257| if (leftI == pivotI) goto done; 284| if (!less(*oldPivotI, *leftI)) continue; | for (;;) | { 249| if (leftI == pivotI) goto done; 201| --pivotI; 201| if (less(*pivotI, *oldPivotI)) | { 78| swapStars(leftI, pivotI); 78| break; | } | } | } | } | | // First loop: spend r[lo .. pivot] 142| for (; hiI != pivotI; --rightI) | { 34| if (rightI == hiI) goto done; 54| if (!less(*rightI, *oldPivotI)) continue; 2| ++pivotI; 2| assert(!less(*pivotI, *oldPivotI), "expandPartition: less check failed"); 2| swapStars(rightI, pivotI); | } | // Second loop: make leftI and pivotI meet 713| for (; rightI > pivotI; --rightI) | { 520| if (!less(*rightI, *oldPivotI)) continue; 190| while (rightI > pivotI) | { 155| ++pivotI; 155| if (less(*oldPivotI, *pivotI)) | { 75| swapStars(rightI, pivotI); 75| break; | } | } | } | | done: 142| swapStars(oldPivotI, pivotI); | | 4194| foreach(x; frontI .. (pivotI + 1)) 1256| assert(!less(*pivotI, *x), "expandPartition: frontI .. (pivotI + 1) failed test"); 4782| foreach(x; (pivotI + 1) .. (lastI + 1)) 1452| assert(!less(*x, *pivotI), "expandPartition: (pivotI + 1) .. (lastI + 1) failed test"); 142| return pivotI; | } |} | |version(mir_test) |@trusted pure nothrow |unittest |{ | import mir.ndslice.slice: sliced; | 1| auto a = [ 10, 5, 3, 4, 8, 11, 13, 3, 9, 4, 10 ].sliced; 1| auto frontI = a._iterator; 1| auto lastI = frontI + a.length - 1; 1| auto loI = frontI + 4; 1| auto pivotI = frontI + 5; 1| auto hiI = frontI + 6; 30| assert(expandPartition!((a, b) => a < b)(frontI, lastI, loI, pivotI, hiI) == (frontI + 9)); |} source/mir/ndslice/sorting.d is 92% covered <<<<<< EOF # path=./source-mir-ndslice-ndfield.lst |/++ |This is a submodule of $(MREF mir,ndslice). | |NdField is a type with `opIndex(size_t[N] index...)` primitive. |An ndslice can be created on top of a ndField using $(SUBREF slice, slicedNdField). | |$(BOOKTABLE $(H2 NdFields), |$(TR $(TH NdField Name) $(TH Used By)) |$(T2 Cartesian, $(SUBREF topology, cartesian)) |$(T2 Kronecker, $(SUBREF topology, kronecker)) |) | |See_also: $(SUBREF concatenation, concatenation). | |License: $(HTTP www.apache.org/licenses/LICENSE-2.0, Apache-2.0) |Copyright: 2020 Ilya Yaroshenko, Kaleidic Associates Advisory Limited, Symmetry Investments |Authors: Ilya Yaroshenko | |Macros: |SUBREF = $(REF_ALTTEXT $(TT $2), $2, mir, ndslice, $1)$(NBSP) |T2=$(TR $(TDNW $(LREF $1)) $(TD $+)) |+/ |module mir.ndslice.ndfield; | |import mir.qualifier; |import mir.internal.utility; |import mir.ndslice.internal; |import mir.ndslice.slice; |import mir.primitives; |import std.meta; | |private template _indices(NdFields...) |{ | static if (NdFields.length == 0) | enum _indices = ""; | else | { | alias Next = NdFields[0 .. $ - 1]; | enum i = Next.length; | enum _indices = ._indices!Next ~ | "_fields[" ~ i.stringof ~ "][" ~ _indices_range!([staticMap!(DimensionCount, Next)].sum, DimensionCount!(NdFields[$ - 1])) ~ "], "; | } |} | |private template _indices_range(size_t begin, size_t count) |{ | static if (count == 0) | enum _indices_range = ""; | else | { | enum next = count - 1; | enum elem = begin + next; | enum _indices_range = ._indices_range!(begin, next) ~ "indices[" ~ elem.stringof ~ "], "; | } |} | |/// |struct Cartesian(NdFields...) | if (NdFields.length > 1) |{ | /// | NdFields _fields; | | package(mir) enum size_t M(size_t f) = [staticMap!(DimensionCount, NdFields[0..f])].sum; | package(mir) enum size_t N = M!(NdFields.length); | | /// | auto lightConst()() const @property | { | import std.format; | import mir.ndslice.topology: iota; 35| return mixin("Cartesian!(staticMap!(LightConstOf, NdFields))(%(_fields[%s].lightConst,%)].lightConst)".format(_fields.length.iota)); | } | | /// | auto lightImmutable()() immutable @property | { | import std.format; | import mir.ndslice.topology: iota; | return mixin("Cartesian!(staticMap!(LightImmutableOf, NdFields))(%(_fields[%s].lightImmutable,%)].lightImmutable)".format(_fields.length.iota)); | } | | /// | size_t length(size_t d = 0)() @safe scope const @property | { | foreach(f, NdField; NdFields) | static if (M!f <= d && M!(f + 1) > d) | { | enum d = d - M!f; | static if (d) | return _fields[f].length!(d - M!f); | else 0000000| return _fields[f].length; | } | } | | /// | size_t[N] shape()() @safe scope const @property | { 30| typeof(return) ret; | foreach(f, NdField; NdFields) | { | static if (hasShape!NdField) | { 70| auto s = _fields[f].shape; | foreach(j; Iota!(s.length)) 72| ret[M!f + j] = s[j]; | } | else | { | ret[M!f] = _fields[f].length; | } | } 30| return ret; | } | | /// | size_t elementCount()() @safe scope const @property | { 0000000| size_t ret = 1; | foreach (f, NdField; NdFields) 0000000| ret *= _fields[f].elementCount; 0000000| return ret; | } | | /// | auto opIndex(size_t[N] indices...) | { | import mir.functional : refTuple; 592| return mixin("refTuple(" ~ _indices!(NdFields) ~ ")"); | } |} | |private template _kr_indices(size_t n) |{ | static if (n == 0) | enum _kr_indices = ""; | else | { | enum i = n - 1; | enum _kr_indices = ._kr_indices!i ~ "_fields[" ~ i.stringof ~ "][ind[" ~ i.stringof ~ "]], "; | } |} | |/// |struct Kronecker(alias fun, NdFields...) | if (NdFields.length > 1 && allSatisfy!(templateOr!(hasShape, hasLength), NdFields[1 .. $])) |{ | /// | NdFields _fields; | | /// | auto lightConst()() const @property | { | import std.format; | import mir.ndslice.topology: iota; 19| return mixin("Kronecker!(fun, staticMap!(LightConstOf, NdFields))(%(_fields[%s].lightConst,%)].lightConst)".format(_fields.length.iota)); | } | | /// | auto lightImmutable()() immutable @property | { | import std.format; | import mir.ndslice.topology: iota; | return mixin("Kronecker!(fun, staticMap!(LightImmutableOf, NdFields))(%(_fields[%s].lightImmutable,%)].lightImmutable)".format(_fields.length.iota)); | } | | private enum N = DimensionCount!(NdFields[$-1]); | | /// | size_t length(size_t d = 0)() scope const @property | { | static if (d == 0) | { | size_t ret = 1; | foreach (f, NdField; NdFields) | ret *= _fields[f].length; | } | else | { | size_t ret = 1; | foreach (f, NdField; NdFields) | ret *= _fields[f].length!d; | } | return ret; | } | | | /// | size_t[N] shape()() scope const @property | { | static if (N > 1) | { 4| size_t[N] ret = 1; | foreach (f, NdField; NdFields) | { 10| auto s = _fields[f].shape; | foreach(i; Iota!N) 20| ret[i] *= s[i]; | } 4| return ret; | } | else | { 2| size_t[1] ret = 1; | foreach (f, NdField; NdFields) 4| ret[0] *= _fields[f].length; 2| return ret; | } | } | | /// | size_t elementCount()() scope const @property | { | size_t ret = 1; | foreach (f, NdField; NdFields) | ret *= _fields[f].elementCount; | ret; | } | | /// | auto ref opIndex()(size_t[N] indices...) | { | static if (N > 1) 128| size_t[N][NdFields.length] ind; | else 6| size_t[NdFields.length] ind; | foreach_reverse (f, NdField; NdFields) | { | static if (f) | { | static if (hasShape!(NdFields[f])) | { 198| auto s = _fields[f].shape; | } | else | { | size_t[1] s; | s[0] = _fields[f].length; | } | static if (N > 1) | { | foreach(i; Iota!N) | { 384| ind[f][i] = indices[i] % s[i]; 384| indices[i] /= s[i]; | } | } | else | { 6| ind[f] = indices[0] % s[0]; 6| indices[0] /= s[0]; | } | } | else | { | static if (N > 1) | { | foreach(i; Iota!N) 256| ind[f][i] = indices[i]; | } | else | { 6| ind[f] = indices[0]; | } | } | } 134| return mixin("fun(" ~ _kr_indices!(ind.length) ~ ")"); | } |} source/mir/ndslice/ndfield.d is 85% covered <<<<<< EOF # path=./..-..-..-.dub-packages-mir-core-1.1.83-mir-core-source-mir-internal-utility.lst |/// |module mir.internal.utility; | |private alias AliasSeq(T...) = T; | |/// |alias Iota(size_t j) = Iota!(0, j); | |/// |template Iota(size_t i, size_t j) |{ | static assert(i <= j, "Iota: i should be less than or equal to j"); | static if (i == j) | alias Iota = AliasSeq!(); | else | alias Iota = AliasSeq!(i, Iota!(i + 1, j)); |} | |/// |template realType(C) | if (__traits(isFloating, C) || isComplex!C) |{ | import std.traits: Unqual; | static if (isComplex!C) | alias realType = typeof(Unqual!C.init.re); | else | alias realType = Unqual!C; |} | |/// |template isComplex(C) |{ | static if (is(C == struct) || is(C == enum)) | { | static if (hasField!(C, "re") && hasField!(C, "im") && C.init.tupleof.length == 2) | enum isComplex = isFloatingPoint!(typeof(C.init.tupleof[0])); | else | enum isComplex = false; | } | else | { | // for backward compatability with cfloat, cdouble and creal | enum isComplex = __traits(isFloating, C) && !isFloatingPoint!C && !is(C : __vector(F[N]), F, size_t N); | } |} | |version(LDC) |version(mir_core_test) |unittest |{ | static assert(!isComplex!(__vector(double[2]))); |} | |/// |template isComplexOf(C, F) | if (isFloatingPoint!F) |{ | static if (isComplex!C) | enum isComplexOf = is(typeof(C.init.re) == F); | else | enum isComplexOf = false; |} | |/// |template isFloatingPoint(C) |{ | import std.traits: Unqual; | alias U = Unqual!C; | enum isFloatingPoint = is(U == double) || is(U == float) || is(U == real); |} | |// copy to reduce imports |enum bool hasField(T, string member) = __traits(compiles, (ref T aggregate) { return __traits(getMember, aggregate, member).offsetof; }); ../../../.dub/packages/mir-core-1.1.83/mir-core/source/mir/internal/utility.d has no code <<<<<< EOF # path=./..-..-..-.dub-packages-mir-core-1.1.83-mir-core-source-mir-enums.lst |/++ |Enum utilities. | |License: $(HTTP www.apache.org/licenses/LICENSE-2.0, Apache-2.0) |Authors: Ilya Yaroshenko |Macros: |+/ |module mir.enums; | |private bool hasSeqGrow(T)(T[] elems) | if (__traits(isIntegral, T)) |{ | assert(elems.length); | auto min = elems[0]; | foreach (i, e; elems) | if (i != e - min) | return false; | return true; |} | |/++ |Enum index that corresponds of the list returned by `std.traits.EnumMembers`. |Returns: | enum member position index in the enum definition that corresponds the `value`. |+/ |bool getEnumIndex(T)(const T value, ref uint index) | @safe pure nothrow @nogc | if (is(T == enum)) |{ | import std.traits: EnumMembers, isSomeString; | import mir.utility: _expect; | | static if (__traits(isFloating, T)) | { | // TODO: index based binary searach | foreach (i, member; enumMembers!T) | { | if (value == member) | { | index = cast(uint) i; | return true; | } | } | return false; | } | else | static if (!__traits(isIntegral, T)) //strings | { | enum string[1] stringEnumValue(alias symbol) = [symbol]; | return getEnumIndexFromKey!(T, false, stringEnumValue)(value, index); | } | else | static if (hasSeqGrow(enumMembers!T)) | { | import std.traits: Unsigned; | const shifted = cast(Unsigned!(typeof(value - T.min)))(value - T.min); | if (_expect(shifted < enumMembers!T.length, true)) | { | index = cast(uint) shifted; | return true; | } | return false; | } | else | static if (is(T : bool)) | { | index = !value; | return true; | } | else | { | import std.traits: Unsigned; | alias U = Unsigned!(typeof(T.max - T.min + 1)); | | enum length = cast(size_t)cast(U)(T.max - T.min + 1); | | const shifted = cast(size_t)cast(U)(value - T.min); | | static if (length <= 255) | { | static immutable ubyte[length] table = (){ | ubyte[length] ret; | foreach (i, member; enumMembers!T) | { | ret[member - T.min] = cast(ubyte)(i + 1); | } | return ret; | }(); | | if (_expect(shifted < length, true)) | { | int id = table[shifted] - 1; | if (_expect(id >= 0, true)) | { | index = id; | return true; | } | } | return false; | } | else | { | switch (value) | { | foreach (i, member; EnumMembers!T) | { | case member: | index = i; | return true; | } | default: return false; | } | } | } |} | |/// |@safe pure nothrow @nogc |version(mir_core_test) unittest |{ | import std.meta: AliasSeq; | | enum Common { a, b, c } | enum Reversed { a = 1, b = 0, c = -1 } | enum Shifted { a = -4, b, c } | enum Small { a = -4, b, c = 10 } | enum Big { a = -4, b, c = 1000 } | enum InverseBool { True = true, False = false } | enum FP : float { a = -4, b, c } | enum S : string { a = "а", b = "б", c = "ц" } | | uint index = -1; | foreach (E; AliasSeq!(Common, Reversed, Shifted, Small, Big, FP, S)) | { | assert(getEnumIndex(E.a, index) && index == 0); | assert(getEnumIndex(E.b, index) && index == 1); | assert(getEnumIndex(E.c, index) && index == 2); | } | | assert(getEnumIndex(InverseBool.True, index) && index == 0); | assert(getEnumIndex(InverseBool.False, index) && index == 1); |} | |/++ |Static immutable instance of `[std.traits.EnumMembers!T]`. |+/ |template enumMembers(T) | if (is(T == enum)) |{ | import std.traits: EnumMembers; | /// | static immutable T[EnumMembers!T.length] enumMembers = [EnumMembers!T]; |} | |/// |version(mir_core_test) unittest |{ | enum E {a = 1, b = -1, c} | static assert(enumMembers!E == [E.a, E.b, E.c]); |} | |/++ |Static immutable instance of Enum Identifiers. |+/ |template enumIdentifiers(T) | if (is(T == enum)) |{ | import std.traits: EnumMembers; | static immutable string[EnumMembers!T.length] enumIdentifiers = () { | string[EnumMembers!T.length] identifiers; | static foreach(i, member; EnumMembers!T) | identifiers[i] = __traits(identifier, EnumMembers!T[i]); | return identifiers; | } (); |} | |/// |version(mir_core_test) unittest |{ | enum E {z = 1, b = -1, c} | static assert(enumIdentifiers!E == ["z", "b", "c"]); |} | |/++ |Aliases itself to $(LREF enumMembers) for string enums and |$(LREF enumIdentifiers) for integral and floating point enums. |+/ |template enumStrings(T) | if (is(T == enum)) |{ | static if (is(T : C[], C)) | alias enumStrings = enumMembers!T; | else | alias enumStrings = enumIdentifiers!T; |} | |/// |version(mir_core_test) unittest |{ | enum E {z = 1, b = -1, c} | static assert(enumStrings!E == ["z", "b", "c"]); | | enum S {a = "A", b = "B", c = ""} | static assert(enumStrings!S == [S.a, S.b, S.c]); |} | |/++ |Params: | index = enum index `std.traits.EnumMembers!T` |Returns: | A enum value that corresponds to the index. |Note: | The function doesn't check that index is less then `EnumMembers!T.length`. |+/ |T unsafeEnumFromIndex(T)(size_t index) | @trusted pure nothrow @nogc | if (is(T == enum)) |{ | static if (__traits(isIntegral, T)) | enum bool fastConv = hasSeqGrow(enumMembers!T); | else | enum bool fastConv = false; | | assert(index < enumMembers!T.length); | | static if (fastConv) | { | return cast(T) index; | } | else | { | return enumMembers!T[index]; | } |} | |/++ |+/ |template getEnumIndexFromKey(T, bool caseInsesetive = true, getKeysTemplate...) | if (is(T == enum) && getKeysTemplate.length <= 1) |{ | /// | bool getEnumIndexFromKey(C)(scope const(C)[] key, ref uint index) | @safe pure nothrow @nogc | if (is(C == char) || is(C == wchar) || is(C == dchar)) | { | import mir.string_table; | import mir.utility: simpleSort, _expect; | import std.traits: EnumMembers; | import std.meta: staticIndexOf; | | alias String = immutable(C)[]; | | static if (getKeysTemplate.length) | { | alias keysOfImpl = getKeysTemplate[0]; | enum String[] keysOf(alias symbol) = keysOfImpl!symbol; | } | else | static if (is(T : W[], W)) | enum String[1] keysOf(alias symbol) = [cast(String)symbol]; | else | enum String[1] keysOf(alias symbol) = [__traits(identifier, symbol)]; | | enum keys = () { | String[] keys; | foreach(i, member; EnumMembers!T) | keys ~= keysOf!(EnumMembers!T[i]); | return keys; | } (); | | static if (keys.length == 0) | { | return false; | } | else | { | enum indexLength = keys.length + 1; | alias ct = createTable!C; | static immutable table = ct!(keys, caseInsesetive); | static immutable indices = () | { | minimalSignedIndexType!indexLength[indexLength] indices; | | foreach (i, member; EnumMembers!T) | foreach (key; keysOf!(EnumMembers!T[i])) | { | static if (caseInsesetive) | { | key = key.dup.fastToUpperInPlace; | } | indices[table[key]] = i; | } | | return indices; | } (); | | static if (!caseInsesetive && keys.length <= 6 && keys[$ - 1].length <= 32) | { | | } | else | { | | } | | uint stringId = void; | if (_expect(table.get(key, stringId), true)) | { | index = indices[stringId]; | return true; | } | return false; | } | } |} ../../../.dub/packages/mir-core-1.1.83/mir-core/source/mir/enums.d has no code <<<<<< EOF # path=./source-mir-ndslice-filling.lst |/++ |This is a submodule of $(MREF mir,ndslice). | |Initialisation routines. | |License: $(HTTP www.apache.org/licenses/LICENSE-2.0, Apache-2.0) |Copyright: 2020 Ilya Yaroshenko, Kaleidic Associates Advisory Limited, Symmetry Investments |Authors: Ilya Yaroshenko | |Macros: |SUBREF = $(REF_ALTTEXT $(TT $2), $2, mir, ndslice, $1)$(NBSP) |T2=$(TR $(TDNW $(LREF $1)) $(TD $+)) |+/ |module mir.ndslice.filling; | |import mir.ndslice.slice: Slice, SliceKind; | |/++ |Fills a matrix with the terms of a geometric progression in each row. |Params: | matrix = `m × n` matrix to fill | vec = vector of progression coefficients length of `m` |See_also: $(LINK2 https://en.wikipedia.org/wiki/Vandermonde_matrix, Vandermonde matrix) |+/ |void fillVandermonde(F, SliceKind matrixKind, SliceKind kind)(Slice!(F*, 2, matrixKind) matrix, Slice!(const(F)*, 1, kind) vec) |in { 1| assert(matrix.length == vec.length); |} |do { | import mir.conv: to; | 17| foreach (v; matrix) | { 5| F a = vec.front; 5| vec.popFront; 5| F x = to!F(1); 85| foreach (ref e; v) | { 25| e = x; 25| x *= a; | } | } |} | |/// |@safe pure nothrow version(mir_test) unittest |{ | import mir.ndslice.slice: sliced; | import mir.ndslice.allocation: uninitSlice; 1| auto x = [1.0, 2, 3, 4, 5].sliced; 1| auto v = uninitSlice!double(x.length, x.length); 1| v.fillVandermonde(x); 1| assert(v == | [[ 1.0, 1, 1, 1, 1], | [ 1.0, 2, 4, 8, 16], | [ 1.0, 3, 9, 27, 81], | [ 1.0, 4, 16, 64, 256], | [ 1.0, 5, 25, 125, 625]]); |} source/mir/ndslice/filling.d is 100% covered <<<<<< EOF # path=./source-mir-interpolate-linear.lst |/++ |$(H2 Linear Interpolation) | |See_also: $(REF_ALTTEXT $(TT interp1), interp1, mir, interpolate) | |License: $(HTTP www.apache.org/licenses/LICENSE-2.0, Apache-2.0) |Copyright: 2020 Ilya Yaroshenko, Kaleidic Associates Advisory Limited, Symmetry Investments |Authors: Ilya Yaroshenko | |Macros: |SUBREF = $(REF_ALTTEXT $(TT $2), $2, mir, interpolate, $1)$(NBSP) |T2=$(TR $(TDNW $(LREF $1)) $(TD $+)) |+/ |module mir.interpolate.linear; | |import core.lifetime: move; |import mir.functional; |import mir.internal.utility; |import mir.interpolate; |import mir.math.common: optmath; |import mir.ndslice.slice; |import mir.primitives; |import mir.rc.array; |import mir.utility: min, max; |import std.meta: AliasSeq, staticMap; |import std.traits; |public import mir.interpolate: atInterval; | |@optmath: | |/++ |Constructs multivariate linear interpolant with nodes on rectilinear grid. | |Params: | grid = `x` values for interpolant | values = `f(x)` values for interpolant | |Constraints: | `grid`, `values` must have the same length >= 2 | |Returns: $(LREF Linear) |+/ |Linear!(F, N, X) linear(F, size_t N = 1, X = F) | (Repeat!(N, Slice!(RCI!(immutable X))) grid, Slice!(RCI!(const F), N) values) |{ 5| return typeof(return)(forward!grid, values.move); |} | |/// R -> R: Linear interpolation |version(mir_test) |@safe pure @nogc unittest |{ | import mir.algorithm.iteration; | import mir.ndslice; | import mir.math.common: approxEqual; | | static immutable x = [0, 1, 2, 3, 5.00274, 7.00274, 10.0055, 20.0137, 30.0192]; | static immutable y = [0.0011, 0.0011, 0.0030, 0.0064, 0.0144, 0.0207, 0.0261, 0.0329, 0.0356,]; | static immutable xs = [1, 2, 3, 4.00274, 5.00274, 6.00274, 7.00274, 8.00548, 9.00548, 10.0055, 11.0055, 12.0082, 13.0082, 14.0082, 15.0082, 16.011, 17.011, 18.011, 19.011, 20.0137, 21.0137, 22.0137, 23.0137, 24.0164, 25.0164, 26.0164, 27.0164, 28.0192, 29.0192, 30.0192]; | 2| auto interpolant = linear!double(x.rcslice!(immutable double), y.rcslice!(const double)); | | static immutable data = [0.0011, 0.0030, 0.0064, 0.0104, 0.0144, 0.0176, 0.0207, 0.0225, 0.0243, 0.0261, 0.0268, 0.0274, 0.0281, 0.0288, 0.0295, 0.0302, 0.0309, 0.0316, 0.0322, 0.0329, 0.0332, 0.0335, 0.0337, 0.0340, 0.0342, 0.0345, 0.0348, 0.0350, 0.0353, 0.0356]; | 31| assert(xs.sliced.vmap(interpolant).all!((a, b) => approxEqual(a, b, 1e-4, 1e-4))(data)); |} | |/// R^2 -> R: Bilinear interpolation |version(mir_test) |@safe pure @nogc unittest |{ | import mir.math.common: approxEqual; | import mir.ndslice; 25| alias appreq = (a, b) => approxEqual(a, b, 10e-10, 10e-10); | | //// set test function //// | enum y_x0 = 2; | enum y_x1 = -7; | enum y_x0x1 = 3; | | // this function should be approximated very well 42| alias f = (x0, x1) => y_x0 * x0 + y_x1 * x1 + y_x0x1 * x0 * x1 - 11; | | ///// set interpolant //// | static immutable x0 = [-1.0, 2, 8, 15]; | static immutable x1 = [-4.0, 2, 5, 10, 13]; | 2| auto grid = cartesian(x0, x1) | .map!f | .rcslice | .lightConst; | 2| auto interpolant = | linear!(double, 2)( | x0.rcslice!(immutable double), | x1.rcslice!(immutable double), | grid | ); | | ///// compute test data //// 1| auto test_grid = cartesian(x0.sliced + 1.23, x1.sliced + 3.23); 1| auto real_data = test_grid.map!f; 2| auto interp_data = test_grid.vmap(interpolant); | ///// verify result //// 1| assert(all!appreq(interp_data, real_data)); | | //// check derivatives //// 1| auto z0 = 1.23; 1| auto z1 = 3.21; 1| auto d = interpolant.withDerivative(z0, z1); 1| assert(appreq(interpolant(z0, z1), f(z0, z1))); 1| assert(appreq(d[0][0], f(z0, z1))); 1| assert(appreq(d[1][0], y_x0 + y_x0x1 * z1)); 1| assert(appreq(d[0][1], y_x1 + y_x0x1 * z0)); 1| assert(appreq(d[1][1], y_x0x1)); |} | |/// R^3 -> R: Trilinear interpolation |version(mir_test) |@safe pure unittest |{ | import mir.math.common: approxEqual; | import mir.ndslice; 66| alias appreq = (a, b) => approxEqual(a, b, 10e-10, 10e-10); | | ///// set test function //// | enum y_x0 = 2; | enum y_x1 = -7; | enum y_x2 = 5; | enum y_x0x1 = 10; | enum y_x0x1x2 = 3; | | // this function should be approximated very well | static auto f(double x0, double x1, double x2) | { 122| return y_x0 * x0 + y_x1 * x1 + y_x2 * x2 + y_x0x1 * x0 * x1 + y_x0x1x2 * x0 * x1 * x2 - 11; | } | | ///// set interpolant //// | static immutable x0 = [-1.0, 2, 8, 15]; | static immutable x1 = [-4.0, 2, 5, 10, 13]; | static immutable x2 = [3, 3.7, 5]; 2| auto grid = cartesian(x0, x1, x2) | .map!f | .as!(const double) | .rcslice; | 2| auto interpolant = linear!(double, 3)( | x0.rcslice!(immutable double), | x1.rcslice!(immutable double), | x2.rcslice!(immutable double), | grid); | | ///// compute test data //// 1| auto test_grid = cartesian(x0.sliced + 1.23, x1.sliced + 3.23, x2.sliced - 3); 1| auto real_data = test_grid.map!f; 2| auto interp_data = test_grid.vmap(interpolant); | ///// verify result //// 1| assert(all!appreq(interp_data, real_data)); | | //// check derivatives //// 1| auto z0 = 1.23; 1| auto z1 = 3.21; 1| auto z2 = 4; 1| auto d = interpolant.withDerivative(z0, z1, z2); 1| assert(appreq(interpolant(z0, z1, z2), f(z0, z1, z2))); 1| assert(appreq(d[0][0][0], f(z0, z1, z2))); 1| assert(appreq(d[1][0][0], y_x0 + y_x0x1 * z1 + y_x0x1x2 * z1 * z2)); 1| assert(appreq(d[0][1][0], y_x1 + y_x0x1 * z0 + y_x0x1x2 * z0 * z2)); 1| assert(appreq(d[1][1][0], y_x0x1 + y_x0x1x2 * z2)); 1| assert(appreq(d[1][1][1], y_x0x1x2)); |} | |/++ |Multivariate linear interpolant with nodes on rectilinear grid. |+/ 13|struct Linear(F, size_t N = 1, X = F) | if (N && N <= 6) |{ | /// Aligned buffer allocated with `mir.internal.memory`. $(RED For internal use.) | Slice!(RCI!(const F), N) _data; | /// Grid iterators. $(RED For internal use.) | Repeat!(N, RCI!(immutable X)) _grid; | |@optmath extern(D): | | /++ | +/ 5| this(Repeat!(N, Slice!(RCI!(immutable X))) grid, Slice!(RCI!(const F), N) data) @safe @nogc | { | enum msg_min = "linear interpolant: minimal allowed length for the grid equals 2."; | enum msg_eq = "linear interpolant: X and Y values length should be equal."; | version(D_Exceptions) | { | static immutable exc_min = new Exception(msg_min); | static immutable exc_eq = new Exception(msg_eq); | } 8| foreach(i, ref x; grid) | { 8| if (x.length < 2) | { 0000000| version(D_Exceptions) throw exc_min; | else assert(0, msg_min); | } 8| if (x.length != data._lengths[i]) | { 0000000| version(D_Exceptions) throw exc_eq; | else assert(0, msg_eq); | } 8| _grid[i] = x._iterator.move; | } 5| _data = data.move; | } | |@trusted: | | /// 0000000| Linear lightConst()() const @property { return *cast(Linear*)&this; } | | /// | Slice!(RCI!(immutable X)) grid(size_t dimension = 0)() scope return const @property | if (dimension < N) | { | return _grid[dimension].lightConst.sliced(_data._lengths[dimension]); | } | | /// | immutable(X)[] gridScopeView(size_t dimension = 0)() scope return const @property @trusted | if (dimension < N) | { 291| return _grid[dimension]._iterator[0 .. _data._lengths[dimension]]; | } | | /++ | Returns: intervals count. | +/ | size_t intervalCount(size_t dimension = 0)() scope const @property | { 291| assert(_data._lengths[dimension] > 1); 291| return _data._lengths[dimension] - 1; | } | | /// | size_t[N] gridShape()() scope const @property | { | return _data.shape; | } | | /// | enum uint derivativeOrder = 1; | | /// | template opCall(uint derivative = 0) | if (derivative <= derivativeOrder) | { | /++ | `(x)` operator. | Complexity: | `O(log(grid.length))` | +/ | auto opCall(X...)(in X xs) scope const @trusted | if (X.length == N) | { | import mir.functional: AliasCall; | import mir.ndslice.topology: iota; | alias Kernel = AliasCall!(LinearKernel!F, "opCall", derivative); | 144| size_t[N] indices; 144| Kernel[N] kernels; | | enum rp2d = derivative; | | foreach(i; Iota!N) | { | static if (isInterval!(typeof(xs[i]))) | { 30| indices[i] = xs[i][1]; 30| auto x = xs[i][0]; | } | else | { | alias x = xs[i]; 260| indices[i] = this.findInterval!i(x); | } 290| kernels[i] = LinearKernel!F(_grid[i][indices[i]], _grid[i][indices[i] + 1], x); | } | | align(64) F[2 ^^ N][derivative + 1] local; 144| immutable strides = _data._lengths.iota.strides; | | void load(sizediff_t i)(F* from, F* to) | { | version(LDC) pragma(inline, true); | static if (i == -1) | { 704| *to = *from; | } | else | { 560| from += strides[i] * indices[i]; 560| load!(i - 1)(from, to); 560| from += strides[i]; | enum s = 2 ^^ (N - 1 - i); 560| to += s; 560| load!(i - 1)(from, to); | } | } | 144| load!(N - 1)(cast(F*) _data.ptr, cast(F*)local[0].ptr); | | foreach(i; Iota!N) | { | enum P = 2 ^^ (N - 1 - i); | enum L = 2 ^^ (N - i * (1 - rp2d)) / 2; 290| vectorize(kernels[i], local[0][0 * L .. 1 * L], local[0][1 * L .. 2 * L], *cast(F[L][2 ^^ rp2d]*)local[rp2d].ptr); | static if (rp2d == 1) 5| shuffle3!1(local[1][0 .. L], local[1][L .. 2 * L], local[0][0 .. L], local[0][L .. 2 * L]); | static if (i + 1 == N) 144| return *cast(SplineReturnType!(F, N, 2 ^^ rp2d)*) local[0].ptr; | } | } | } | | /// | alias withDerivative = opCall!1; |} | |/// |struct LinearKernel(X) |{ | X step = 0; | X w0 = 0; | X w1 = 0; | | /// | auto lightConst()() const @property | { | return LinearKernel!X(step, w0, w1); | } | | /// | auto lightImmutable()() immutable @property | { | return LinearKernel!X(step, w0, w1); | } | | /// 290| this()(X x0, X x1, X x) | { 290| step = x1 - x0; 290| auto c0 = x - x0; 290| auto c1 = x1 - x; 290| w0 = c0 / step; 290| w1 = c1 / step; | } | | /// | template opCall(uint derivative = 0) | if (derivative <= 1) | { | /// | auto opCall(Y)(in Y y0, in Y y1) | { 566| auto r0 = y0 * w1; 566| auto r1 = y1 * w0; 566| auto y = r0 + r1; | static if (derivative) | { 16| auto diff = y1 - y0; 16| Y[derivative + 1] ret = 0; 16| ret[0] = y; 16| ret[1] = diff / step; 16| return ret; | } | else | { 550| return y; | } | } | } | | /// | alias withDerivative = opCall!1; |} source/mir/interpolate/linear.d is 96% covered <<<<<< EOF # path=./source-mir-serde.lst |/++ |This implements common de/serialization routines. | |License: $(HTTP www.apache.org/licenses/LICENSE-2.0, Apache-2.0) |Copyright: 2020 Ilya Yaroshenko, Kaleidic Associates Advisory Limited, Symmetry Investments |Authors: Ilya Yaroshenko | |Macros: |T2=$(TR $(TDNW $(LREF $1)) $(TD $+)) |T4=$(TR $(TDNW $(LREF $1)) $(TD $2) $(TD $3) $(TD $4)) |+/ |module mir.serde; | |import mir.functional: naryFun; |import mir.reflection; |import std.traits: TemplateArgsOf, EnumMembers, hasUDA, isAggregateType; | |version (D_Exceptions) |{ | /++ | Serde Exception | +/ | class SerdeException : Exception | { | /// 0000000| this( | string msg, | string file = __FILE__, | size_t line = __LINE__, | Throwable next = null) pure nothrow @nogc @safe | { 0000000| super(msg, file, line, next); | } | | /// 0000000| this( | string msg, | Throwable next, | string file = __FILE__, | size_t line = __LINE__, | ) pure nothrow @nogc @safe | { 0000000| this(msg, file, line, next); | } | | SerdeException toMutable() @trusted pure nothrow @nogc const | { 0000000| return cast() this; | } | | alias toMutable this; | } | | /++ | Serde Exception with formatting support | +/ | class SerdeMirException : SerdeException | { | import mir.exception: MirThrowableImpl, mirExceptionInitilizePayloadImpl; | | enum maxMsgLen = 447; | | /// | mixin MirThrowableImpl; | } |} | |/++ |Constructs annotated type. |+/ |template SerdeAnnotated(T, string annotation) |{ | /// | @serdeAlgebraicAnnotation(annotation) | @serdeProxy!T | struct SerdeAnnotated | { | /// | T value; | /// | alias value this; | } |} | |/++ |Helper enumeration for for serializer . |Use negative `int` values for user defined targets. |+/ |enum SerdeTarget : int |{ | /// | ion, | /// | json, | /// | cbor, | /// | msgpack, | /// | yaml, | /// | csv, | /// | excel, | /// | bloomberg, | /// | typedJson, |} | |/++ |Attribute for key overloading during Serialization and Deserialization. |The first argument overloads the key value during serialization unless `serdeKeyOut` is given. |+/ |struct serdeKeys |{ | /// | immutable(string)[] keys; | |@trusted pure nothrow @nogc: | /// 0000000| this(immutable(string)[] keys...) { this.keys = keys; } |} | |/++ |Attribute for key overloading during serialization. |+/ |struct serdeKeyOut |{ | /// | string key; | |@safe pure nothrow @nogc: | /// 0000000| this(string key) { this.key = key; } |} | |/++ |The attribute should be used as a hind for scripting languages to register type deserializer in the type system. | |The attribute should be applied to a type definition. |+/ |enum serdeRegister; | |/++ |The attribute should be applied to a string-like member that should be de/serialized as an annotation / attribute. | |This feature is used in $(MIR_PACKAGE mir-ion). |+/ |enum serdeAnnotation; | | |private template serdeIsAnnotationMemberIn(T) |{ | enum bool serdeIsAnnotationMemberIn(string member) | = hasUDA!(__traits(getMember, T, member), serdeAnnotation) | && !hasUDA!(__traits(getMember, T, member), serdeIgnore) | && !hasUDA!(__traits(getMember, T, member), serdeIgnoreIn); |} | |/++ |+/ |template serdeGetAnnotationMembersIn(T) |{ | import std.meta: aliasSeqOf, Filter; | static if (isAggregateType!T) | enum string[] serdeGetAnnotationMembersIn = [Filter!(serdeIsAnnotationMemberIn!T, aliasSeqOf!(DeserializableMembers!T))]; | else | enum string[] serdeGetAnnotationMembersIn = null; |} | | |/// |version(mir_test) unittest |{ | struct S | { | double data; | | @serdeAnnotation | string a; | @serdeAnnotation @serdeIgnoreIn | string b; | @serdeAnnotation @serdeIgnoreOut | string c; | @serdeAnnotation @serdeIgnore | string d; | } | | static assert(serdeGetAnnotationMembersIn!int == []); | static assert(serdeGetAnnotationMembersIn!S == ["a", "c"]); |} | |private template serdeIsAnnotationMemberOut(T) |{ | enum bool serdeIsAnnotationMemberOut(string member) | = hasUDA!(__traits(getMember, T, member), serdeAnnotation) | && !hasUDA!(__traits(getMember, T, member), serdeIgnore) | && !hasUDA!(__traits(getMember, T, member), serdeIgnoreOut); |} | |/++ |+/ |template serdeGetAnnotationMembersOut(T) |{ | import std.meta: aliasSeqOf, Filter; | static if (isAggregateType!T) | enum string[] serdeGetAnnotationMembersOut = [Filter!(serdeIsAnnotationMemberOut!T, aliasSeqOf!(DeserializableMembers!T))]; | else | enum string[] serdeGetAnnotationMembersOut = null; |} | |/// |version(mir_test) unittest |{ | struct S | { | double data; | | @serdeAnnotation | string a; | @serdeAnnotation @serdeIgnoreIn | string b; | @serdeAnnotation @serdeIgnoreOut | string c; | @serdeAnnotation @serdeIgnore | string d; | } | | static assert(serdeGetAnnotationMembersOut!int == []); | static assert(serdeGetAnnotationMembersOut!S == ["a", "b"]); |} | |/++ |An annotation / attribute for algebraic types deserialization. | |This feature is used in $(MIR_PACKAGE mir-ion) for $(GMREF mir-core, mir,algebraic). |+/ |struct serdeAlgebraicAnnotation |{ | /// | string annotation; | |@safe pure nothrow @nogc: | /// 0000000| this(string annotation) { this.annotation = annotation; } |} | |/++ |+/ |template serdeHasAlgebraicAnnotation(T) |{ | static if (isAggregateType!T || is(T == enum)) | { | static if (hasUDA!(T, serdeAlgebraicAnnotation)) | { | enum serdeHasAlgebraicAnnotation = true; | } | else | static if (__traits(getAliasThis, T).length) | { | T* aggregate; | alias A = typeof(__traits(getMember, aggregate, __traits(getAliasThis, T))); | enum serdeHasAlgebraicAnnotation = .serdeHasAlgebraicAnnotation!A; | } | else | { | enum serdeHasAlgebraicAnnotation = false; | } | } | else | { | enum serdeHasAlgebraicAnnotation = false; | } |} | |/++ |+/ |template serdeGetAlgebraicAnnotation(T) |{ | static if (hasUDA!(T, serdeAlgebraicAnnotation)) | { | enum string serdeGetAlgebraicAnnotation = getUDA!(T, serdeAlgebraicAnnotation).annotation; | } | else | { | T* aggregate; | alias A = typeof(__traits(getMember, aggregate, __traits(getAliasThis, T))); | enum serdeGetAlgebraicAnnotation = .serdeGetAlgebraicAnnotation!A; | } |} | |/++ |Returns: | immutable array of the input keys for the symbol or enum value |+/ |template serdeGetKeysIn(alias symbol) |{ | static if (hasUDA!(symbol, serdeAnnotation) || hasUDA!(symbol, serdeIgnore) || hasUDA!(symbol, serdeIgnoreIn)) | enum immutable(string)[] serdeGetKeysIn = null; | else | static if (hasUDA!(symbol, serdeKeys)) | enum immutable(string)[] serdeGetKeysIn = getUDA!(symbol, serdeKeys).keys; | else | enum immutable(string)[] serdeGetKeysIn = [__traits(identifier, symbol)]; |} | |/// ditto |immutable(string)[] serdeGetKeysIn(T)(const T value) @trusted pure nothrow @nogc | if (is(T == enum)) |{ | foreach (i, member; EnumMembers!T) | {{ | alias all = __traits(getAttributes, EnumMembers!T[i]); | }} | | import std.meta: staticMap; | static immutable ret = [staticMap!(.serdeGetKeysIn, EnumMembers!T)]; | static if (__VERSION__ < 2093) | { | final switch (value) | { | foreach (i, member; EnumMembers!T) | { | case member: | return ret[i]; | } | } | } | else | { | import mir.enums: getEnumIndex; | uint index = void; | if (getEnumIndex(value, index)) | return ret[index]; | assert(0); | } |} | |/// |version(mir_test) unittest |{ | struct S | { | int f; | | @serdeKeys("D", "t") | int d; | | @serdeIgnore | int i; | | @serdeIgnoreIn | int ii; | | @serdeIgnoreOut | int io; | | void p(int) @property {} | } | | static assert(serdeGetKeysIn!(S.f) == ["f"]); | static assert(serdeGetKeysIn!(S.d) == ["D", "t"]); | static assert(serdeGetKeysIn!(S.i) == null); | static assert(serdeGetKeysIn!(S.ii) == null); | static assert(serdeGetKeysIn!(S.io) == ["io"]); | static assert(serdeGetKeysIn!(S.p) == ["p"]); |} | |/// |version(mir_test) unittest |{ | enum E | { | @serdeKeys("A", "alpha") | a, | @serdeKeys("B", "beta") | b, | c, | } | | static assert (serdeGetKeysIn(E.a) == ["A", "alpha"], serdeGetKeysIn(E.a)); | static assert (serdeGetKeysIn(E.c) == ["c"]); |} | |/++ |Returns: | output key for the symbol or enum value |+/ |template serdeGetKeyOut(alias symbol) |{ | static if (hasUDA!(symbol, serdeAnnotation) || hasUDA!(symbol, serdeIgnore) || hasUDA!(symbol, serdeIgnoreOut)) | enum string serdeGetKeyOut = null; | else | static if (hasUDA!(symbol, serdeKeyOut)) | enum string serdeGetKeyOut = getUDA!(symbol, serdeKeyOut).key; | else | static if (hasUDA!(symbol, serdeKeys)) | enum string serdeGetKeyOut = getUDA!(symbol, serdeKeys).keys[0]; | else | enum string serdeGetKeyOut = __traits(identifier, symbol); |} | |///ditto |@safe pure nothrow @nogc |string serdeGetKeyOut(T)(const T value) | if (is(T == enum)) |{ | foreach (i, member; EnumMembers!T) | {{ | alias all = __traits(getAttributes, EnumMembers!T[i]); | }} | | static if (__VERSION__ < 2093) | { | import std.meta: staticMap; | static immutable ret = [staticMap!(.serdeGetKeyOut, EnumMembers!T)]; | final switch (value) | { | foreach (i, member; EnumMembers!T) | { | case member: | return ret[i]; | } | } | } | else | { | import std.meta: staticMap; | import mir.enums: getEnumIndex; | static immutable ret = [staticMap!(.serdeGetKeyOut, EnumMembers!T)]; | uint index = void; | if (getEnumIndex(value, index)) | return ret[index]; | assert(0); | } |} | |/// |version(mir_test) unittest |{ | struct S | { | int f; | | @serdeKeys("D", "t") | int d; | | @serdeIgnore | int i; | | @serdeIgnoreIn | int ii; | | @serdeIgnoreOut | int io; | | @serdeKeys("P") | @serdeKeyOut("") | void p(int) @property {} | } | | static assert(serdeGetKeyOut!(S.f) == "f"); | static assert(serdeGetKeyOut!(S.d) == "D"); | static assert(serdeGetKeyOut!(S.i) is null); | static assert(serdeGetKeyOut!(S.ii) == "ii"); | static assert(serdeGetKeyOut!(S.io) is null); | static assert(serdeGetKeyOut!(S.p) !is null); | static assert(serdeGetKeyOut!(S.p) == ""); |} | |/// |version(mir_test) unittest |{ | enum E | { | @serdeKeys("A", "alpha") | a, | @serdeKeys("B", "beta") | @serdeKeyOut("o") | b, | c, | } | | static assert (serdeGetKeyOut(E.a) == "A"); | static assert (serdeGetKeyOut(E.b) == "o"); | static assert (serdeGetKeyOut(E.c) == "c"); |} | |/++ |Attribute to ignore field. | |See_also: $(LREF serdeIgnoreIn) $(LREF serdeIgnoreOut) |+/ |enum serdeIgnore; | |/++ |Attribute to ignore field during deserialization. | |See_also: $(LREF serdeIgnoreInIfAggregate) |+/ |enum serdeIgnoreIn; | |/++ |Attribute to ignore field during serialization. |+/ |enum serdeIgnoreOut; | |/++ |Attribute to ignore a field during deserialization when equals to its default value. |Do not use it on void initialized fields or aggregates with void initialized fields, recursively. |+/ |enum serdeIgnoreDefault; | |/// |version(mir_test) unittest |{ | struct S | { | @serdeIgnoreDefault | double d = 0; // skips field if 0 during deserialization | } | | import std.traits: hasUDA; | | static assert(hasUDA!(S.d, serdeIgnoreDefault)); |} | |/++ |+/ | |/++ |Serialization proxy. |+/ |struct serdeProxy(T); | |/// |version(mir_test) unittest |{ | import mir.small_string; | | struct S | { | @serdeProxy!(SmallString!32) | double d; | } | | import std.traits: hasUDA; | | static assert(hasUDA!(S.d, serdeProxy)); | static assert(hasUDA!(S.d, serdeProxy!(SmallString!32))); | static assert(is(serdeGetProxy!(S.d) == SmallString!32)); |} | |/++ |+/ |alias serdeGetProxy(alias symbol) = TemplateArgsOf!(getUDA!(symbol, serdeProxy))[0]; | |/++ |Can be applied only to fields that can be constructed from strings. |Does not allocate new data when deserializeing. Raw data is used for strings instead of new memory allocation. |Use this attributes only for strings that would not be used after the input data deallocation. |+/ |deprecated("use @serdeScoped @serdeProxy!(const(char)[]) instead") enum serdeScopeStringProxy; | |/++ |Attributes to conditional ignore field during serialization. | |The predicate should be aplied to the member, to the aggregate type. | |See_also: $(LREF serdeIgnoreOutIfAggregate) |+/ |struct serdeIgnoreOutIf(alias pred); | |/++ |+/ |alias serdeGetIgnoreOutIf(alias symbol) = naryFun!(TemplateArgsOf!(getUDA!(symbol, serdeIgnoreOutIf))[0]); | |/++ |Attributes to conditional ignore field during serialization. | |The predicate should be aplied to the aggregate value, not to the member. | |See_also: $(LREF serdeIgnoreIfAggregate) $(LREF serdeIgnoreOutIf), $(LREF serdeIgnoreInIfAggregate) |+/ |struct serdeIgnoreOutIfAggregate(alias pred); | |/++ |+/ |alias serdeGetIgnoreOutIfAggregate(alias symbol) = naryFun!(TemplateArgsOf!(getUDA!(symbol, serdeIgnoreOutIfAggregate))[0]); | |/++ |Attributes to conditional ignore field during deserialization. | |The attribute should be combined with $(LREF serdeRealOrderedIn) applied on the aggregate. | |See_also: $(LREF serdeIgnoreIfAggregate) $(LREF serdeIgnoreOutIfAggregate) $(LREF serdeIgnoreIn) |+/ |struct serdeIgnoreInIfAggregate(alias pred); | |/++ |+/ |alias serdeGetIgnoreInIfAggregate(alias symbol) = naryFun!(TemplateArgsOf!(getUDA!(symbol, serdeIgnoreInIfAggregate))[0]); | |/++ |Attributes to conditional ignore field during serialization and deserialization. | |The attribute should be combined with $(LREF serdeRealOrderedIn) applied on the aggregate. | |The predicate should be aplied to the aggregate value, not to the member. | |See_also: $(LREF serdeIgnoreOutIfAggregate) $(LREF serdeIgnoreInIfAggregate) $ $(LREF serdeIgnore) |+/ |struct serdeIgnoreIfAggregate(alias pred); | |/++ |+/ |alias serdeGetIgnoreIfAggregate(alias symbol) = naryFun!(TemplateArgsOf!(getUDA!(symbol, serdeIgnoreIfAggregate))[0]); | |/++ |Allows to use flexible deserialization rules such as conversion from input string to numeric types. |+/ |enum serdeFlexible; | |/++ |Allows serialize / deserialize fields like arrays. | |A range or a container should be iterable for serialization. |Following code should compile: |------ |foreach(ref value; yourRangeOrContainer) |{ | ... |} |------ | |`put(value)` method is used for deserialization. | |See_also: $(MREF serdeIgnoreOut), $(MREF serdeIgnoreIn) |+/ |enum serdeLikeList; | |/++ |Allows serialize / deserialize fields like objects. | |Object should have `opApply` method to allow serialization. |Following code should compile: |------ |foreach(key, value; yourObject) |{ | ... |} |------ |Object should have only one `opApply` method with 2 argument to allow automatic value type deduction. | |`opIndexAssign` or `opIndex` is used for deserialization to support required syntax: |----- |yourObject["key"] = value; |----- |Multiple value types is supported for deserialization. | |See_also: $(MREF serdeIgnoreOut), $(MREF serdeIgnoreIn) |+/ |enum serdeLikeStruct; | |/++ |Ignore keys for object and enum members. |Should be applied to members or enum type itself. |+/ |enum serdeIgnoreCase; | |/// |bool hasSerdeIgnoreCase(T)(T value) | if (is(T == enum)) |{ | static if (hasUDA!(T, serdeIgnoreCase)) | { | return true; | } | else | { | foreach (i, member; EnumMembers!T) | { | alias all = __traits(getAttributes, EnumMembers!T[i]); | if (value == member) | return hasUDA!(EnumMembers!T[i], serdeIgnoreCase); | } | assert(0); | } |} | |/// |version(mir_test) unittest |{ | enum E | { | @serdeIgnoreCase | a, | b, | @serdeIgnoreCase | c, | d, | } | | static assert(hasSerdeIgnoreCase(E.a)); | static assert(!hasSerdeIgnoreCase(E.b)); | static assert(hasSerdeIgnoreCase(E.c)); | static assert(!hasSerdeIgnoreCase(E.d)); |} | |/// |version(mir_test) unittest |{ | @serdeIgnoreCase | enum E | { | a, | b, | c, | d, | } | | static assert(hasSerdeIgnoreCase(E.a)); | static assert(hasSerdeIgnoreCase(E.b)); | static assert(hasSerdeIgnoreCase(E.c)); | static assert(hasSerdeIgnoreCase(E.d)); |} | |/++ |Can be applied only to strings fields. |Does not allocate new data when deserializeing. Raw data is used for strings instead of new memory allocation. |Use this attributes only for strings or arrays that would not be used after deallocation. |+/ |enum serdeScoped; | |/++ |Attribute that force deserializer to throw an exception that the field hasn't been not found in the input. |+/ |enum serdeRequired; | |/++ |Attribute that allow deserializer to do not throw an exception if the field hasn't been not found in the input. |+/ |enum serdeOptional; | |/++ |Attribute that allow deserializer to don't throw an exception that the field matches multiple keys in the object. |+/ |enum serdeAllowMultiple; | |/++ |Attributes for in transformation. |Return type of in transformation must be implicitly convertable to the type of the field. |In transformation would be applied after serialization proxy if any. | |+/ |struct serdeTransformIn(alias fun) {} | |/++ |Returns: unary function of underlaying alias of $(LREF serdeTransformIn) |+/ |alias serdeGetTransformIn(alias value) = naryFun!(TemplateArgsOf!(getUDA!(value, serdeTransformIn))[0]); | |/++ |Attributes for out transformation. |Return type of out transformation may be differ from the type of the field. |Out transformation would be applied before serialization proxy if any. |+/ |struct serdeTransformOut(alias fun) {} | |/++ |Returns: unary function of underlaying alias of $(LREF serdeTransformOut) |+/ |alias serdeGetTransformOut(alias value) = naryFun!(TemplateArgsOf!(getUDA!(value, serdeTransformOut))[0]); | |/++ |+/ |bool serdeParseEnum(E)(const char[] str, ref E res) | @safe pure nothrow @nogc |{ | static if (__VERSION__ < 2093) | { | static if (hasUDA!(E, serdeIgnoreCase)) | { | import mir.format: stringBuf; | stringBuf buf; | buf << str; | auto ustr = buf.data.fastToUpperInPlace; | } | else | { | alias ustr = str; | } | switch(ustr) | { | foreach(i, member; EnumMembers!E) | {{ | enum initKeys = serdeGetKeysIn(EnumMembers!E[i]); | static if (hasUDA!(E, serdeIgnoreCase)) | { | import mir.ndslice.topology: map; | import mir.array.allocation: array; | enum keys = initKeys.map!fastLazyToUpper.map!array.array; | } | else | { | enum keys = initKeys; | } | static assert (keys.length, "At least one input enum key is required"); | static foreach (key; keys) | { | case key: | } | res = member; | return true; | }} | default: | return false; | } | } | else | { | import mir.enums: getEnumIndexFromKey, unsafeEnumFromIndex; | import mir.utility: _expect; | 12| uint index = void; 12| if (getEnumIndexFromKey!(E, hasUDA!(E, serdeIgnoreCase), serdeGetKeysIn)(str, index)._expect(true)) | { 10| res = unsafeEnumFromIndex!E(index); 10| return true; | } 2| return false; | } |} | |/// |version(mir_test) unittest |{ | enum E | { | @serdeKeys("A", "alpha") | a, | @serdeKeys("B", "beta") | b, | c, | } | 1| auto e = E.c; 1| assert(serdeParseEnum("A", e)); 1| assert(e == E.a); 1| assert(serdeParseEnum("alpha", e)); 1| assert(e == E.a); 1| assert(serdeParseEnum("beta", e)); 1| assert(e == E.b); 1| assert(serdeParseEnum("B", e)); 1| assert(e == E.b); 1| assert(serdeParseEnum("c", e)); 1| assert(e == E.c); | 1| assert(!serdeParseEnum("C", e)); 1| assert(!serdeParseEnum("Alpha", e)); |} | |/// Case insensitive |version(mir_test) unittest |{ | @serdeIgnoreCase // supported for the whole type | enum E | { | @serdeKeys("A", "alpha") | a, | @serdeKeys("B", "beta") | b, | c, | } | 1| auto e = E.c; 1| assert(serdeParseEnum("a", e)); 1| assert(e == E.a); 1| assert(serdeParseEnum("alpha", e)); 1| assert(e == E.a); 1| assert(serdeParseEnum("BETA", e)); 1| assert(e == E.b); 1| assert(serdeParseEnum("b", e)); 1| assert(e == E.b); 1| assert(serdeParseEnum("C", e)); 1| assert(e == E.c); |} | |/++ |Deserialization member type |+/ |template serdeDeserializationMemberType(T, string member) |{ | import std.traits: Unqual, Parameters; | T* aggregate; | static if (hasField!(T, member)) | { | alias serdeDeserializationMemberType = typeof(__traits(getMember, *aggregate, member)); | } | else | static if (__traits(compiles, &__traits(getMember, *aggregate, member)()) || __traits(getOverloads, *aggregate, member).length > 1) | { | alias serdeDeserializationMemberType = typeof(__traits(getMember, *aggregate, member)()); | } | else | { | alias serdeDeserializationMemberType = Unqual!(Parameters!(__traits(getMember, *aggregate, member))[0]); | } |} | |/// ditto |template serdeDeserializationMemberType(T) |{ | /// | alias serdeDeserializationMemberType(string member) = .serdeDeserializationMemberType!(T, member); |} | | |/++ |Is deserializable member |+/ |template serdeIsDeserializable(T) |{ | /// | enum bool serdeIsDeserializable(string member) = serdeGetKeysIn!(__traits(getMember, T, member)).length > 0; |} | |/// |version(mir_test) unittest |{ | | static struct S | { | @serdeIgnore | int i; | | @serdeKeys("a", "b") | int a; | } | | alias serdeIsDeserializableInS = serdeIsDeserializable!S; | static assert (!serdeIsDeserializableInS!"i"); | static assert (serdeIsDeserializableInS!"a"); |} | |/++ |Serialization member type |+/ |template serdeSerializationMemberType(T, string member) |{ | import std.traits: Unqual, Parameters; | T* aggregate; | static if (hasField!(T, member)) | { | alias serdeSerializationMemberType = typeof(__traits(getMember, *aggregate, member)); | } | else | { | alias serdeSerializationMemberType = typeof(__traits(getMember, *aggregate, member)()); | } |} | |/// ditto |template serdeSerializationMemberType(T) |{ | /// | alias serdeSerializationMemberType(string member) = .serdeSerializationMemberType!(T, member); |} | | |/++ |Is deserializable member |+/ |template serdeIsSerializable(T) |{ | /// | enum bool serdeIsSerializable(string member) = serdeGetKeyOut!(__traits(getMember, T, member)) !is null; |} | |/// |version(mir_test) unittest |{ | | static struct S | { | @serdeIgnore | int i; | | @serdeKeys("a", "b") | int a; | } | | alias serdeIsSerializableInS = serdeIsSerializable!S; | static assert (!serdeIsSerializableInS!"i"); | static assert (serdeIsSerializableInS!"a"); |} | |/++ |Final proxy type |+/ |template serdeGetFinalProxy(T) |{ | import mir.timestamp: Timestamp; | import std.traits: hasUDA, isAggregateType; | static if (isAggregateType!T || is(T == enum)) | { | static if (hasUDA!(T, serdeProxy)) | { | alias serdeGetFinalProxy = .serdeGetFinalProxy!(serdeGetProxy!T); | } | else | static if (isAggregateType!T && is(typeof(Timestamp(T.init)))) | { | alias serdeGetFinalProxy = string; | } | else | { | alias serdeGetFinalProxy = T; | } | } | else | { | alias serdeGetFinalProxy = T; | } |} | |/// |version(mir_test) unittest |{ | | @serdeProxy!string | static struct A {} | | @serdeProxy!A | static struct B {} | | @serdeProxy!B | static struct C {} | | static assert (is(serdeGetFinalProxy!C == string), serdeGetFinalProxy!C.stringof); | static assert (is(serdeGetFinalProxy!string == string)); |} | |/++ |Final deep proxy type |+/ |template serdeGetFinalDeepProxy(T) |{ | import mir.timestamp: Timestamp; | import std.traits: Unqual, hasUDA, isAggregateType, isArray, ForeachType; | static if (isAggregateType!T || is(T == enum)) | { | static if (hasUDA!(T, serdeProxy)) | { | alias serdeGetFinalDeepProxy = .serdeGetFinalDeepProxy!(serdeGetProxy!T); | } | else | static if (isAggregateType!T && is(typeof(Timestamp(T.init)))) | { | alias serdeGetFinalDeepProxy = string; | } | else | static if (__traits(hasMember, T, "serdeKeysProxy")) | { | alias serdeGetFinalDeepProxy = .serdeGetFinalDeepProxy!(T.serdeKeysProxy); | } | else | // static if (is(T == enum)) | // { | // alias serdeGetFinalDeepProxy = typeof(null); | // } | // else | { | alias serdeGetFinalDeepProxy = T; | } | } | else | static if (isArray!T) | { | alias E = Unqual!(ForeachType!T); | static if (isAggregateType!E || is(E == enum)) | alias serdeGetFinalDeepProxy = .serdeGetFinalDeepProxy!E; | else | alias serdeGetFinalDeepProxy = T; | } | else | static if (is(immutable T == immutable V[K], K, V)) | { | alias E = serdeGetFinalDeepProxy!(Unqual!V); | static if (isAggregateType!E || is(E == enum)) | alias serdeGetFinalDeepProxy = E; | else | alias serdeGetFinalDeepProxy = T; | } | else | { | alias serdeGetFinalDeepProxy = T; | } |} | |/// |version(mir_test) unittest |{ | | @serdeProxy!string | static struct A {} | | enum E {a,b,c} | | @serdeProxy!(A[E]) | static struct B {} | | @serdeProxy!(B[]) | static struct C {} | | static assert (is(serdeGetFinalDeepProxy!C == A[E])); | static assert (is(serdeGetFinalDeepProxy!string == string)); |} | |/++ |Final proxy type deserializable members |+/ |template serdeFinalProxyDeserializableMembers(T) |{ | import std.meta: Filter, aliasSeqOf; | alias P = serdeGetFinalProxy!T; | static if (isAggregateType!P || is(P == enum)) | enum string[] serdeFinalProxyDeserializableMembers = [Filter!(serdeIsDeserializable!P, aliasSeqOf!(DeserializableMembers!P))]; | else | // static if (is(P == enum)) | // enum string[] serdeFinalProxyDeserializableMembers = serdeGetKeysIn!P; | // else | enum string[] serdeFinalProxyDeserializableMembers = null; |} | |/// |version(mir_test) unittest |{ | | static struct A | { | @serdeIgnore | int i; | | @serdeKeys("a", "b") | int m; | } | | @serdeProxy!A | static struct B {} | | @serdeProxy!B | static struct C {} | | static assert (serdeFinalProxyDeserializableMembers!C == ["m"]); |} | |/++ |Final deep proxy type serializable members |+/ |template serdeFinalDeepProxySerializableMembers(T) |{ | import std.traits: isAggregateType; | import std.meta: Filter, aliasSeqOf; | alias P = serdeGetFinalDeepProxy!T; | static if (isAggregateType!P || is(P == enum)) | enum string[] serdeFinalDeepProxySerializableMembers = [Filter!(serdeIsSerializable!P, aliasSeqOf!(SerializableMembers!P))]; | else | // static if (is(P == enum)) | // enum string[] serdeFinalDeepProxySerializableMembers = [serdeGetKeyOut!P]; | // else | enum string[] serdeFinalDeepProxySerializableMembers = null; |} | |/// |version(mir_test) unittest |{ | | static struct A | { | @serdeIgnore | int i; | | @serdeKeys("a", "b") | int m; | } | | @serdeProxy!(A[string]) | static struct B {} | | @serdeProxy!(B[]) | static struct C {} | | static assert (serdeFinalDeepProxySerializableMembers!C == ["m"]); |} | |/++ |Final proxy type deserializable members |+/ |template serdeFinalProxySerializableMembers(T) |{ | import std.meta: Filter, aliasSeqOf; | alias P = serdeGetFinalProxy!T; | static if (isAggregateType!P || is(P == enum)) | enum string[] serdeFinalProxySerializableMembers = [Filter!(serdeIsSerializable!P, aliasSeqOf!(SerializableMembers!P))]; | else | // static if (is(P == enum)) | // enum string[] serdeFinalProxySerializableMembers = [serdeGetKeyOut!P]; | // else | enum string[] serdeFinalProxySerializableMembers = null; |} | |/// |version(mir_test) unittest |{ | | static struct A | { | @serdeIgnore | int i; | | @serdeKeys("a", "b") | int m; | } | | @serdeProxy!A | static struct B {} | | @serdeProxy!B | static struct C {} | | static assert (serdeFinalProxySerializableMembers!C == ["m"]); |} | |/++ |Final deep proxy type serializable members |+/ |template serdeFinalDeepProxyDeserializableMembers(T) |{ | import std.traits: isAggregateType; | import std.meta: Filter, aliasSeqOf; | alias P = serdeGetFinalDeepProxy!T; | static if (isAggregateType!P || is(P == enum)) | enum string[] serdeFinalDeepProxyDeserializableMembers = [Filter!(serdeIsDeserializable!P, aliasSeqOf!(DeserializableMembers!P))]; | else | // static if (is(P == enum)) | // enum string[] serdeFinalDeepProxyDeserializableMembers = serdeGetKeysIn!P; | // else | enum string[] serdeFinalDeepProxyDeserializableMembers = null; |} | |/// |version(mir_test) unittest |{ | static struct A | { | @serdeIgnore | int i; | | @serdeKeys("a", "b") | int m; | } | | @serdeProxy!(A[string]) | static struct B {} | | @serdeProxy!(B[]) | static struct C {} | | static assert (serdeFinalDeepProxyDeserializableMembers!C == ["m"]); |} | |/++ |Deserialization member final proxy type |+/ |template serdeFinalDeserializationMemberType(T, string member) |{ | import std.traits: hasUDA; | static if (hasUDA!(__traits(getMember, T, member), serdeProxy)) | { | alias serdeFinalDeserializationMemberType = serdeGetFinalProxy!(serdeGetProxy!(__traits(getMember, T, member))); | } | else | { | alias serdeFinalDeserializationMemberType = serdeGetFinalProxy!(serdeDeserializationMemberType!(T, member)); | } |} | |/// ditto |template serdeFinalDeserializationMemberType(T) |{ | /// | alias serdeFinalDeserializationMemberType(string member) = .serdeFinalDeserializationMemberType!(T, member); |} | |/// |version(mir_test) unittest |{ | | static struct A | { | | } | | @serdeProxy!A | static struct B {} | | @serdeProxy!B | static struct C {} | | | @serdeProxy!double | struct E {} | | struct D | { | C c; | | @serdeProxy!E | int d; | } | | static assert (is(serdeFinalDeserializationMemberType!(D, "c") == A)); | static assert (is(serdeFinalDeserializationMemberType!(D, "d") == double)); |} | |/++ |Deserialization members final proxy types |+/ |template serdeDeserializationFinalProxyMemberTypes(T) |{ | import std.meta: NoDuplicates, staticMap, aliasSeqOf; | alias serdeDeserializationFinalProxyMemberTypes = NoDuplicates!(staticMap!(serdeGetFinalProxy, staticMap!(serdeFinalDeserializationMemberType!T, aliasSeqOf!(serdeFinalProxyDeserializableMembers!T)))); |} | |/// |version(mir_test) unittest |{ | | static struct A {} | | @serdeProxy!A | static struct B {} | | @serdeProxy!B | static struct C {} | | @serdeProxy!B | static struct E {} | | static struct D | { | C c; | | @serdeProxy!E | int d; | } | | import std.meta: AliasSeq; | static assert (is(serdeDeserializationFinalProxyMemberTypes!D == AliasSeq!A)); |} | |/++ |Serialization member final proxy type |+/ |template serdeFinalSerializationMemberType(T, string member) |{ | import std.traits: hasUDA; | static if (hasUDA!(__traits(getMember, T, member), serdeProxy)) | { | alias serdeFinalSerializationMemberType = serdeGetFinalProxy!(serdeGetProxy!(__traits(getMember, T, member))); | } | else | { | alias serdeFinalSerializationMemberType = serdeGetFinalProxy!(serdeSerializationMemberType!(T, member)); | } |} | |/// ditto |template serdeFinalSerializationMemberType(T) |{ | /// | alias serdeFinalSerializationMemberType(string member) = .serdeFinalSerializationMemberType!(T, member); |} | |/// |version(mir_test) unittest |{ | | static struct A | { | | } | | @serdeProxy!A | static struct B {} | | @serdeProxy!B | static struct C {} | | | @serdeProxy!double | struct E {} | | struct D | { | C c; | | @serdeProxy!E | int d; | } | | static assert (is(serdeFinalSerializationMemberType!(D, "c") == A), serdeFinalSerializationMemberType!(D, "c")); | static assert (is(serdeFinalSerializationMemberType!(D, "d") == double)); |} | |/++ |Serialization members final proxy types |+/ |template serdeSerializationFinalProxyMemberTypes(T) |{ | import std.meta: NoDuplicates, staticMap, aliasSeqOf; | alias serdeSerializationFinalProxyMemberTypes = NoDuplicates!(staticMap!(serdeGetFinalProxy, staticMap!(serdeFinalSerializationMemberType!T, aliasSeqOf!(serdeFinalProxySerializableMembers!T)))); |} | |/// |version(mir_test) unittest |{ | | static struct A {} | | @serdeProxy!A | static struct B {} | | @serdeProxy!B | static struct C {} | | @serdeProxy!B | static struct E {} | | static struct D | { | C c; | | @serdeProxy!E | int d; | } | | import std.meta: AliasSeq; | static assert (is(serdeSerializationFinalProxyMemberTypes!D == AliasSeq!A)); |} | |/++ |Deserialization members final deep proxy types |+/ |template serdeDeserializationFinalDeepProxyMemberTypes(T) |{ | import std.meta: NoDuplicates, staticMap, aliasSeqOf; | import mir.algebraic: isVariant; | static if (isVariant!T) | alias serdeDeserializationFinalDeepProxyMemberTypes = NoDuplicates!(T, staticMap!(serdeGetFinalDeepProxy, T.AllowedTypes)); | else | static if (isAlgebraicAliasThis!T) | { | T* aggregate; | alias A = typeof(__traits(getMember, aggregate, __traits(getAliasThis, T))); | alias serdeDeserializationFinalDeepProxyMemberTypes = .serdeDeserializationFinalDeepProxyMemberTypes!A; | } | else | alias serdeDeserializationFinalDeepProxyMemberTypes = NoDuplicates!(staticMap!(serdeGetFinalDeepProxy, staticMap!(serdeFinalDeserializationMemberType!T, aliasSeqOf!(serdeFinalDeepProxyDeserializableMembers!T)))); |} | |/// |version(mir_test) unittest |{ | | static struct A {} | | @serdeProxy!(A[]) | static struct B {} | | enum R {a, b, c} | | @serdeProxy!(B[R]) | static struct C {} | | @serdeProxy!(B[string]) | static struct E {} | | static struct D | { | C c; | | @serdeProxy!E | int d; | } | | import std.meta: AliasSeq; | static assert (is(serdeDeserializationFinalDeepProxyMemberTypes!D == AliasSeq!A), serdeDeserializationFinalDeepProxyMemberTypes!D); |} | |/++ |Serialization members final deep proxy types |+/ |template serdeSerializationFinalDeepProxyMemberTypes(T) |{ | import std.meta: NoDuplicates, staticMap, aliasSeqOf; | import mir.algebraic: isVariant; | static if (isVariant!T) | alias serdeSerializationFinalDeepProxyMemberTypes = NoDuplicates!(T, staticMap!(serdeGetFinalDeepProxy, T.AllowedTypes)); | else | static if (isAlgebraicAliasThis!T) | { | T* aggregate; | alias A = typeof(__traits(getMember, aggregate, __traits(getAliasThis, T))); | alias serdeSerializationFinalDeepProxyMemberTypes = .serdeSerializationFinalDeepProxyMemberTypes!A; | } | else | alias serdeSerializationFinalDeepProxyMemberTypes = NoDuplicates!(staticMap!(serdeGetFinalDeepProxy, staticMap!(serdeFinalSerializationMemberType!T, aliasSeqOf!(serdeFinalDeepProxySerializableMembers!T)))); |} | |/// |version(mir_test) unittest |{ | | static struct A {} | | @serdeProxy!(A[]) | static struct B {} | | enum R {a, b, c} | | @serdeProxy!(B[R]) | static struct C {} | | @serdeProxy!(B[string]) | static struct E {} | | static struct D | { | C c; | | @serdeProxy!E | int d; | } | | import std.meta: AliasSeq; | static assert (is(serdeSerializationFinalDeepProxyMemberTypes!D == AliasSeq!A), serdeSerializationFinalDeepProxyMemberTypes!D); |} | |private template serdeDeserializationFinalProxyMemberTypesRecurseImpl(T...) |{ | import std.meta: NoDuplicates, staticMap; | alias F = NoDuplicates!(T, staticMap!(serdeDeserializationFinalProxyMemberTypes, T)); | static if (F.length == T.length) | alias serdeDeserializationFinalProxyMemberTypesRecurseImpl = T; | else | alias serdeDeserializationFinalProxyMemberTypesRecurseImpl = .serdeDeserializationFinalProxyMemberTypesRecurseImpl!F; |} | |/++ |Deserialization members final proxy types (recursive) |+/ |alias serdeDeserializationFinalProxyMemberTypesRecurse(T) = serdeDeserializationFinalProxyMemberTypesRecurseImpl!(serdeGetFinalProxy!T); | |/// |version(mir_test) unittest |{ | | static struct A { double g; } | | @serdeProxy!A | static struct B {} | | @serdeProxy!B | static struct C {} | | @serdeProxy!B | static struct E {} | | static struct D | { | C c; | | @serdeProxy!E | int d; | } | | @serdeProxy!D | static struct F {} | | import std.meta: AliasSeq; | static assert (is(serdeDeserializationFinalProxyMemberTypesRecurse!F == AliasSeq!(D, A, double))); |} | |private template serdeSerializationFinalDeepProxyMemberTypesRecurseImpl(T...) |{ | import std.meta: NoDuplicates, staticMap; | alias F = NoDuplicates!(T, staticMap!(serdeSerializationFinalDeepProxyMemberTypes, T)); | static if (F.length == T.length) | alias serdeSerializationFinalDeepProxyMemberTypesRecurseImpl = T; | else | alias serdeSerializationFinalDeepProxyMemberTypesRecurseImpl = .serdeSerializationFinalDeepProxyMemberTypesRecurseImpl!F; |} | |private template serdeDeserializationFinalDeepProxyMemberTypesRecurseImpl(T...) |{ | import std.meta: NoDuplicates, staticMap; | alias F = NoDuplicates!(T, staticMap!(serdeDeserializationFinalDeepProxyMemberTypes, T)); | static if (F.length == T.length) | alias serdeDeserializationFinalDeepProxyMemberTypesRecurseImpl = T; | else | alias serdeDeserializationFinalDeepProxyMemberTypesRecurseImpl = .serdeDeserializationFinalDeepProxyMemberTypesRecurseImpl!F; |} | |/++ |Deserialization members final deep proxy types (recursive) |+/ |alias serdeDeserializationFinalDeepProxyMemberTypesRecurse(T) = serdeDeserializationFinalDeepProxyMemberTypesRecurseImpl!(serdeGetFinalDeepProxy!T); | |/// |version(mir_test) unittest |{ | | static struct A { double g; } | | @serdeProxy!(A[]) | static struct B {} | | @serdeProxy!(B[string]) | static struct C {} | | @serdeProxy!B | static struct E {} | | static struct D | { | C c; | | @serdeProxy!(E[]) | int d; | } | | @serdeProxy!D | static struct F {} | | import std.meta: AliasSeq; | static assert (is(serdeDeserializationFinalDeepProxyMemberTypesRecurse!F == AliasSeq!(D, A, double))); |} | |/++ |Serialization members final deep proxy types (recursive) |+/ |alias serdeSerializationFinalDeepProxyMemberTypesRecurse(T) = serdeSerializationFinalDeepProxyMemberTypesRecurseImpl!(serdeGetFinalDeepProxy!T); | |/// |version(mir_test) unittest |{ | | static struct A { double g; } | | @serdeProxy!(A[]) | static struct B {} | | @serdeProxy!(B[string]) | static struct C {} | | @serdeProxy!B | static struct E {} | | static struct D | { | C c; | | @serdeProxy!(E[]) | int d; | } | | @serdeProxy!D | static struct F {} | | import std.meta: AliasSeq; | static assert (is(serdeSerializationFinalDeepProxyMemberTypesRecurse!F == AliasSeq!(D, A, double)), serdeSerializationFinalDeepProxyMemberTypesRecurse!F); |} | |package string[] sortUniqKeys()(string[] keys) | @safe pure nothrow |{ | import mir.algorithm.iteration: uniq; | import mir.array.allocation: array; | import mir.ndslice.sorting: sort; | | return keys | .sort!((a, b) { | if (sizediff_t d = a.length - b.length) | return d < 0; | return a < b; | }) | .uniq | .array; |} | | |private template serdeGetKeysIn2(T) |{ | // T* value; | enum string[] serdeGetKeysIn2(string member) = serdeGetKeysIn!(__traits(getMember, T, member)); |} | |private template serdeGetKeyOut2(T) |{ | enum string[] serdeGetKeyOut2(string member) = serdeGetKeyOut!(__traits(getMember, T, member)) is null ? null : [serdeGetKeyOut!(__traits(getMember, T, member))]; |} | |private template serdeFinalDeepProxyDeserializableMemberKeys(T) |{ | import std.meta: staticMap, aliasSeqOf; | import std.traits: isAggregateType; | | static if (isAggregateType!T) | { | import mir.algebraic: isVariant; | static if (isVariant!T) | enum string[] serdeFinalDeepProxyDeserializableMemberKeys = getAlgebraicAnnotationsOfVariant!T; | else | enum string[] serdeFinalDeepProxyDeserializableMemberKeys = [staticMap!(aliasSeqOf, staticMap!(serdeGetKeysIn2!T, aliasSeqOf!(serdeFinalDeepProxyDeserializableMembers!T)))]; | } | else | static if (is(T == enum)) | { | enum string[] serdeFinalDeepProxyDeserializableMemberKeys = enumAllKeysIn!T; | } | else | enum string[] serdeFinalDeepProxyDeserializableMemberKeys = null; |} | |package template getAlgebraicAnnotationsOfVariant(T) |{ | import std.meta: staticMap, Filter; | enum string[] getAlgebraicAnnotationsOfVariant = [staticMap!(serdeGetAlgebraicAnnotation, Filter!(serdeHasAlgebraicAnnotation, T.AllowedTypes))]; |} | |private template serdeFinalDeepProxySerializableMemberKeys(T) |{ | import std.meta: staticMap, aliasSeqOf; | import std.traits: isAggregateType; | | static if (isAggregateType!T) | { | import mir.algebraic: isVariant; | static if (isVariant!T) | enum string[] serdeFinalDeepProxySerializableMemberKeys = getAlgebraicAnnotationsOfVariant!T; | else | enum string[] serdeFinalDeepProxySerializableMemberKeys = [staticMap!(aliasSeqOf, staticMap!(serdeGetKeyOut2!T, aliasSeqOf!(serdeFinalDeepProxySerializableMembers!T)))]; | } | else | static if (is(T == enum)) | { | enum string[] serdeFinalDeepProxySerializableMemberKeys = enumAllKeysOut!T; | } | else | enum string[] serdeFinalDeepProxySerializableMemberKeys = null; |} | |private template serdeGetAlgebraicAnnotations(T) |{ | static if (isAggregateType!T || is(T == enum)) | static if (hasUDA!(T, serdeAlgebraicAnnotation)) | enum string[] serdeGetAlgebraicAnnotations = [getUDA!(T, serdeAlgebraicAnnotation).annotation]; | else | enum string[] serdeGetAlgebraicAnnotations = null; | else | enum string[] serdeGetAlgebraicAnnotations = null; |} | |package template serdeIsComplexVariant(T) |{ | import mir.algebraic: isVariant, isNullable; | static if (isVariant!T) | { | enum serdeIsComplexVariant = (T.AllowedTypes.length - isNullable!T) > 1; | } | else | { | enum bool serdeIsComplexVariant = false; | } |} | |package template isAlgebraicAliasThis(T) |{ | static if (__traits(getAliasThis, T).length) | { | import mir.algebraic: isVariant; | T* aggregate; | alias A = typeof(__traits(getMember, aggregate, __traits(getAliasThis, T))); | enum isAlgebraicAliasThis = isVariant!A; | } | else | { | enum isAlgebraicAliasThis = false; | } |} | |/++ |Serialization members final proxy keys (recursive) |+/ |template serdeGetSerializationKeysRecurse(T) |{ | import std.meta: staticMap, aliasSeqOf; | enum string[] serdeGetSerializationKeysRecurse = [staticMap!(aliasSeqOf, staticMap!(serdeFinalDeepProxySerializableMemberKeys, serdeSerializationFinalDeepProxyMemberTypesRecurse!T))].sortUniqKeys; |} | |/// |version(mir_test) unittest |{ | enum Y | { | a, | b, | c, | } | | static struct A { double g; float d; } | | @serdeProxy!A | static struct B { int f; } | | @serdeProxy!(B[Y][string]) | static union C { int f; } | | @serdeProxy!(B[]) | static interface E { int f() @property; } | | enum N { a, b } | | static class D | { | C c; | | @serdeProxy!(E[]) | int d; | | N e; | } | | @serdeAlgebraicAnnotation("$F") | @serdeProxy!D | static struct F { int f; } | | static assert (serdeGetSerializationKeysRecurse!F == ["a", "b", "c", "d", "e", "g"]); | | import mir.algebraic; | static assert (serdeGetSerializationKeysRecurse!(Nullable!(F, int)) == ["a", "b", "c", "d", "e", "g", "$F"]); |} | |/++ |Deserialization members final proxy keys (recursive) |+/ |template serdeGetDeserializationKeysRecurse(T) |{ | import std.meta: staticMap, aliasSeqOf; | enum string[] serdeGetDeserializationKeysRecurse = [staticMap!(aliasSeqOf, staticMap!(serdeFinalDeepProxyDeserializableMemberKeys, serdeDeserializationFinalDeepProxyMemberTypesRecurse!T))].sortUniqKeys; |} | |/// |version(mir_test) unittest |{ | | static struct A { double g; float d; } | | @serdeProxy!A | static struct B { int f; } | | @serdeProxy!(B[string]) | static union C { int f; } | | @serdeProxy!(B[]) | static interface E { int f() @property; } | | enum N { a, b } | | static class D | { | C c; | | @serdeProxy!(E[]) | int d; | | N e; | } | | @serdeAlgebraicAnnotation("$F") | @serdeProxy!D | static struct F { int f; } | | static assert (serdeGetDeserializationKeysRecurse!N == ["a", "b"], serdeGetDeserializationKeysRecurse!N); | | static assert (serdeGetDeserializationKeysRecurse!F == ["a", "b", "c", "d", "e", "g"]); | | import mir.algebraic; | static assert (serdeGetDeserializationKeysRecurse!(Nullable!(F, int)) == ["a", "b", "c", "d", "e", "g", "$F"]); |} | |/++ |UDA used to force deserializer to initilize members in the order of their definition in the target object/structure. | |The attribute force deserializer to create a dummy type (recursively), initializer its fields and then assign them to |to the object members (fields and setters) in the order of their definition. | |See_also: $(LREF SerdeOrderedDummy), $(LREF serdeRealOrderedIn), $(LREF serdeIgnoreInIfAggregate). |+/ |enum serdeOrderedIn; | |/++ |UDA used to force deserializer to initilize members in the order of their definition in the target object/structure. | |Unlike $(LREF serdeOrderedIn) `serdeRealOrderedDummy` force deserialzier to iterate all DOM keys for each object deserialization member. |It is slower but more universal approach. | |See_also: $(LREF serdeOrderedIn), $(LREF serdeIgnoreInIfAggregate) |+/ |enum serdeRealOrderedIn; | |/++ |UDA used to force deserializer to skip the member final deserialization. |A user should finalize the member deserialize using the dummy object provided in `serdeFinalizeWithDummy(ref SerdeOrderedDummy!(typeof(this)) dummy)` struct method |and dummy method `serdeFinalizeTargetMember`. |+/ |enum serdeFromDummyByUser; | |/++ |UDA used to force serializer to output members in the alphabetical order of their output keys. |+/ |enum serdeAlphabetOut; | |/++ |A dummy structure usefull $(LREF serdeOrderedIn) support. |+/ |struct SerdeOrderedDummy(T, bool __optionalByDefault = false) | if (is(serdeGetFinalProxy!T == T) && isAggregateType!T) |{ | import std.traits: hasUDA; | | @serdeIgnore | SerdeFlags!(typeof(this)) __serdeFlags; | | static if (__optionalByDefault) | alias __serdeOptionalRequired = serdeRequired; | else | alias __serdeOptionalRequired = serdeOptional; | | this()(T value) | { | static foreach (member; serdeFinalProxyDeserializableMembers!T) | { | static if (hasField!(T, member)) | { | static if (__traits(compiles, {__traits(getMember, this, member) = __traits(getMember, value, member);})) | __traits(getMember, this, member) = __traits(getMember, value, member); | } | } | } | |public: | | static foreach (i, member; serdeFinalProxyDeserializableMembers!T) | { | static if (hasField!(T, member)) | { | static if (hasUDA!(__traits(getMember, T, member), serdeProxy)) | { | mixin("@(__traits(getAttributes, T." ~ member ~ ")) serdeDeserializationMemberType!(T, `" ~ member ~ "`) " ~ member ~ ";"); | } | else | static if (isAggregateType!(typeof(__traits(getMember, T, member)))) | { | static if (hasUDA!(typeof(__traits(getMember, T, member)), serdeProxy)) | { | mixin("@(__traits(getAttributes, T." ~ member ~ ")) serdeDeserializationMemberType!(T, `" ~ member ~ "`) " ~ member ~ " = T.init." ~ member ~ ";"); | } | else | static if (__traits(compiles, { | mixin("enum SerdeOrderedDummy!(serdeDeserializationMemberType!(T, `" ~ member ~ "`)) " ~ member ~ " = SerdeOrderedDummy!(serdeDeserializationMemberType!(T, `" ~ member ~ "`))(T.init." ~ member ~ ");"); | })) | { | mixin("@(__traits(getAttributes, T." ~ member ~ ")) SerdeOrderedDummy!(serdeDeserializationMemberType!(T, `" ~ member ~ "`)) " ~ member ~ " = SerdeOrderedDummy!(serdeDeserializationMemberType!(T, `" ~ member ~ "`))(T.init." ~ member ~ ");"); | } | else | { | mixin("@(__traits(getAttributes, T." ~ member ~ ")) SerdeOrderedDummy!(serdeDeserializationMemberType!(T, `" ~ member ~ "`)) " ~ member ~ ";"); | } | } | else | { | mixin("@(__traits(getAttributes, T." ~ member ~ ")) serdeDeserializationMemberType!(T, `" ~ member ~ "`) " ~ member ~ " = T.init." ~ member ~ ";"); | } | } | else | { | mixin("@(__traits(getAttributes, T." ~ member ~ ")) serdeDeserializationMemberType!(T, `" ~ member ~ "`) " ~ member ~ ";"); | } | } | | /// Initialize target members | void serdeFinalizeWithFlags(ref scope const SerdeFlags!(typeof(this)) flags) | { | __serdeFlags = flags; | } | | /// Initialize target members | void serdeFinalizeTarget(ref T value, ref scope SerdeFlags!T flags) | { | import std.traits: hasElaborateAssign; | static foreach (member; serdeFinalProxyDeserializableMembers!T) | __traits(getMember, flags, member) = __traits(getMember, __serdeFlags, member); | static foreach (member; serdeFinalProxyDeserializableMembers!T) | static if (!hasUDA!(__traits(getMember, T, member), serdeFromDummyByUser)) | {{ | if (hasUDA!(__traits(getMember, T, member), __serdeOptionalRequired) == __optionalByDefault || __traits(getMember, __serdeFlags, member)) | { | static if (is(typeof(__traits(getMember, this, member)) : SerdeOrderedDummy!I, I)) | { | alias M = typeof(__traits(getMember, value, member)); | SerdeFlags!M memberFlags; | __traits(getMember, this, member).serdeFinalizeTarget(__traits(getMember, value, member), memberFlags); | static if (__traits(hasMember, M, "serdeFinalizeWithFlags")) | { | __traits(getMember, value, member).serdeFinalizeWithFlags(memberFlags); | } | static if (__traits(hasMember, M, "serdeFinalize")) | { | __traits(getMember, value, member).serdeFinalize(); | } | } | else | { | static if (hasElaborateAssign!(typeof(__traits(getMember, this, member)))) | { | import core.lifetime: move; | __traits(getMember, value, member) = move(__traits(getMember, this, member)); | } | else | __traits(getMember, value, member) = __traits(getMember, this, member); | } | } | }} | static if (__traits(hasMember, T, "serdeFinalizeWithDummy")) | { | value.serdeFinalizeWithDummy(this); | } | } | | /// Initialize target member | void serdeFinalizeTargetMember(string member)(ref T value) | { | if (hasUDA!(__traits(getMember, T, member), __serdeOptionalRequired) == __optionalByDefault || __traits(getMember, __serdeFlags, member)) | { | static if (is(typeof(__traits(getMember, this, member)) : SerdeOrderedDummy!I, I)) | { | alias M = typeof(__traits(getMember, value, member)); | SerdeFlags!M memberFlags; | __traits(getMember, this, member).serdeFinalizeTarget(__traits(getMember, value, member), memberFlags); | static if (__traits(hasMember, M, "serdeFinalizeWithFlags")) | { | __traits(getMember, value, member).serdeFinalizeWithFlags(memberFlags); | } | static if (__traits(hasMember, M, "serdeFinalize")) | { | __traits(getMember, value, member).serdeFinalize(); | } | } | else | { | static if (hasElaborateAssign!(typeof(__traits(getMember, this, member)))) | { | import core.lifetime: move; | __traits(getMember, value, member) = move(__traits(getMember, this, member)); | } | else | __traits(getMember, value, member) = __traits(getMember, this, member); | } | } | } |} | |/// |version(mir_test) unittest |{ | import std.traits; | | static struct S | { | private double _d; | | @serdeProxy!int 0000000| void d(double v) @property { _d = v; } | | string s; | } | | static assert(is(typeof(SerdeOrderedDummy!S.init.d) == double), SerdeOrderedDummy!S.init.d); | static assert(is(typeof(SerdeOrderedDummy!S.init.s) == string)); | static assert(hasUDA!(S.d, serdeProxy)); | static assert(hasUDA!(SerdeOrderedDummy!S.d, serdeProxy)); |} | |/++ |A dummy structure passed to `.serdeFinalizeWithFlags` finalizer method. |+/ |struct SerdeFlags(T) |{ | static if (is(T : SerdeOrderedDummy!I, I)) | static foreach(member; serdeFinalProxyDeserializableMembers!I) | mixin("bool " ~ member ~ ";"); | else | static foreach(member; serdeFinalProxyDeserializableMembers!T) | mixin("bool " ~ member ~ ";"); |} | |template deserializeValueMemberImpl(alias deserializeValue, alias deserializeScoped) |{ | /// | SerdeException deserializeValueMemberImpl(string member, Data, T, Context...)(Data data, ref T value, ref SerdeFlags!T requiredFlags, ref Context context) | { | import core.lifetime: move; | import mir.conv: to; | | enum likeList = hasUDA!(__traits(getMember, value, member), serdeLikeList); | enum likeStruct = hasUDA!(__traits(getMember, value, member), serdeLikeStruct); | enum hasProxy = hasUDA!(__traits(getMember, value, member), serdeProxy); | enum hasScoped = hasUDA!(__traits(getMember, value, member), serdeScoped); | | static assert (likeList + likeStruct <= 1, T.stringof ~ "." ~ member ~ " can't have both @serdeLikeStruct and @serdeLikeList attributes"); | static assert (hasProxy >= likeStruct, T.stringof ~ "." ~ member ~ " should have a Proxy type for deserialization"); | static assert (hasProxy >= likeList, T.stringof ~ "." ~ member ~ " should have a Proxy type for deserialization"); | | alias Member = serdeDeserializationMemberType!(T, member); | | static if (hasProxy) | alias Temporal = serdeGetProxy!(__traits(getMember, value, member)); | else | alias Temporal = Member; | | static if (hasScoped) | static if (__traits(compiles, { Temporal temporal; deserializeScoped(data, temporal); })) | alias impl = deserializeScoped; | else | alias impl = deserializeValue; | else | alias impl = deserializeValue; | | static immutable excm(string member) = new SerdeException("ASDF deserialisation: multiple keys for member '" ~ member ~ "' in " ~ T.stringof ~ " are not allowed."); | | static if (!hasUDA!(__traits(getMember, value, member), serdeAllowMultiple)) | if (__traits(getMember, requiredFlags, member)) | return excm!member; | | __traits(getMember, requiredFlags, member) = true; | | static if (likeList) | { | foreach(elem; data.byElement) | { | Temporal temporal; | if (auto exc = impl(elem, temporal, context)) | return exc; | __traits(getMember, value, member).put(move(temporal)); | } | } | else | static if (likeStruct) | { | foreach(v; data.byKeyValue(context)) | { | Temporal temporal; | if (auto exc = impl(v.value, temporal, context)) | return exc; | __traits(getMember, value, member)[v.key.idup] = move(temporal); | } | } | else | static if (hasProxy) | { | Temporal temporal; | if (auto exc = impl(data, temporal, context)) | return exc; | __traits(getMember, value, member) = to!(serdeDeserializationMemberType!(T, member))(move(temporal)); | } | else | static if (hasField!(T, member)) | { | if (auto exc = impl(data, __traits(getMember, value, member), context)) | return exc; | } | else | { | Member temporal; | if (auto exc = impl(data, temporal, context)) | return exc; | __traits(getMember, value, member) = move(temporal); | } | | static if (hasUDA!(__traits(getMember, value, member), serdeTransformIn)) | { | alias transform = serdeGetTransformIn!(__traits(getMember, value, member)); | static if (hasField!(T, member)) | { | transform(__traits(getMember, value, member)); | } | else | { | auto temporal = __traits(getMember, value, member); | transform(temporal); | __traits(getMember, value, member) = move(temporal); | } | } | | return null; | } |} | |private: | |auto fastLazyToUpper()(const(char)[] name) |{ | import mir.ndslice.topology: map; | return name.map!fastToUpper; |} | |auto fastToUpper()(char a) |{ // std.ascii may not be inlined | return 'a' <= a && a <= 'z' ? cast(char)(a ^ 0x20) : a; |} | |@safe pure nothrow @nogc |char[] fastToUpperInPlace()(scope return char[] a) |{ | foreach(ref char e; a) | e = e.fastToUpper; | return a; |} | |template enumAllKeysIn(T) | if (is(T == enum)) |{ | import std.traits: EnumMembers; | import std.meta: staticMap, aliasSeqOf; | enum string[] enumAllKeysIn = [staticMap!(aliasSeqOf, staticMap!(.serdeGetKeysIn, EnumMembers!T))]; |} | |template enumAllKeysOut(T) | if (is(T == enum)) |{ | import std.traits: EnumMembers; | import std.meta: staticMap, aliasSeqOf; | enum string[] enumAllKeysOut = [staticMap!(.serdeGetKeyOut, EnumMembers!T)]; |} source/mir/serde.d is 76% covered <<<<<< EOF # path=./source-mir-rc-package.lst |/++ |$(H1 Thread-safe reference-counted arrays and pointers) | |Mir provides two kinds of ref-counting pointers and two kinds of ref-counted arrays. | |The first kind pointer is `RCPtr`, which consists of a pointer to the context and pointer to the value.`RCPtr` supports structural and object polymorphism. It allows getting members with the same context as the root. |The second kind is `SlimRCPtr`, which consist only from a pointer to the value. The context for `SlimRCPtr`is computed using a fixed-length memory shift from the pointer to the value. |`SlimRCPtr` can be converted to an `RCPtr` and to an `RCArray` of the one element. | |`RCArray` is an array type without range primitives. It's length can't be reduced after construction.In the other hand, `Slice!(RCI!(T))` is an ndslice with all random-access range primitives.`RCI` is an iterator, which consists of `RCArray` and the pointer to the current element. |`RCArray!T` can be converted or moved to `Slice!(RCI!(T))` using `.asSlice` or `.moveToSlice` methods respectively. | |$(RED `RCArray!T` aliases itself to a common D array slice. This feature may cause a segmentation fault in safe code if used without DIP1000.) | |`RCPtr!T` can be constructed from an element index and `RCArray!T` / `Slice!(RCI!(T))`. | |The package publicly imports $(MREF mir,rc,array), $(MREF mir,rc,ptr), and $(MREF mir,rc,slim_ptr). | |See_also: $(MREF mir,ndslice). |+/ |module mir.rc; | |public import mir.rc.array; |public import mir.rc.ptr; |public import mir.rc.slim_ptr; | |import mir.ndslice.slice; | |/++ |Returns: shared pointer constructed from the slim shared pointer. | |The function has zero computation cost. |+/ |RCPtr!F toRCPtr(F)(return SlimRCPtr!F contextAndValue) @trusted |{ 1| typeof(return) ret; 1| ret._value = contextAndValue._value; 1| ret._context = &contextAndValue.context(); 1| contextAndValue._value = null; 1| return ret; |} | |/// |version(mir_test) |@safe pure @nogc nothrow |unittest |{ | import core.lifetime: move; | struct S | { | double e; | } | struct C | { | int i; | S s; | } | 2| auto a = createSlimRC!C(10, S(3)); 2| auto s = a.move.toRCPtr.shareMember!"s"; 1| assert(s._counter == 1); 1| assert(s.e == 3); |} | |/++ |Returns: shared pointer constructed with the `array`'s context and the value points to `array[index]`. | |The function has zero computation cost. |+/ |RCPtr!F toRCPtrAt(F)(return RCArray!F array, size_t index) @trusted | if (!is(R == class) && !is(R == interface)) |in { 1| assert(index < array.length, "toRCPtrAt: index should be less then array.length"); |} |do { 1| typeof(return) ret; 1| ret._value = array._payload + index; 1| ret._context = &array.context(); 1| array._payload = null; 1| return ret; |} | |/// |version(mir_test) |@safe pure @nogc nothrow |unittest |{ | struct S { double e; } | 2| auto a = RCArray!S(10); 1| a[3].e = 4; | 2| auto s = a.toRCPtrAt(3); | 1| assert(s._counter == 2); 1| assert(s.e == 4); |} | |/// ditto |RCPtr!F toRCPtrAt(F)(return Slice!(RCI!F) array, size_t index) @trusted | if (!is(R == class) && !is(R == interface)) |in { 1| assert(index < array.length, "toRCPtrAt: index should be less then array.length"); |} |do { 1| typeof(return) ret; 1| ret._value = array._iterator._iterator + index; 1| ret._context = &array._iterator._array.context(); 1| array._iterator._array._payload = null; 1| return ret; |} | |/// |version(mir_test) |@safe pure @nogc nothrow |unittest |{ | struct S { double e; } | 2| auto a = RCArray!S(10).asSlice[5 .. $]; 1| a[3].e = 4; | 2| auto s = a.toRCPtrAt(3); | 1| assert(s._counter == 2); 1| assert(s.e == 4); |} | |/++ |Returns: RC array length of one constructed from the slim shared pointer. | |The function has zero computation cost. |+/ |RCArray!F toRCArray(F)(return SlimRCPtr!F context) @trusted |{ 1| typeof(return) ret; 1| ret._payload = context._value; 1| context._value = null; 1| return ret; |} | |/// |version(mir_test) |@safe pure @nogc nothrow |unittest |{ | struct S { double e; } | 2| auto a = createSlimRC!S(4).toRCArray; 1| assert(a._counter == 1); 1| assert(a.length == 1); 1| assert(a[0].e == 4); |} source/mir/rc/package.d is 100% covered <<<<<< EOF # path=./source-mir-bignum-internal-ryu-generic_128.lst |// Converted and then optimised from generic_128.h and generic_128.c |// Copyright 2018 Ulf Adams (original code https://github.com/ulfjack/ryu) |// Copyright 2020 Ilya Yaroshenko (2020 D conversion and optimisation) |// License: $(HTTP www.apache.org/licenses/LICENSE-2.0, Apache-2.0) | |// This is a generic 128-bit implementation of float to shortest conversion |// using the Ryu algorithm. It can handle any IEEE-compatible floating-point |// type up to 128 bits. In order to use this correctly, you must use the |// appropriate *_to_fd128 function for the underlying type - DO NOT CAST your |// input to another floating-point type, doing so will result in incorrect |// output! |// |// For any floating-point type that is not natively defined by the compiler, |// you can use genericBinaryToDecimal to work directly on the underlying bit |// representation. | |module mir.bignum.internal.ryu.generic_128; | |version(BigEndian) | static assert (0, "Let us know if you are using Mir on BigEndian target and we will add support for this module."); | |debug(ryu) import core.stdc.stdio; | |import mir.bignum.decimal: Decimal; |import mir.bignum.fixed: UInt, extendedMulHigh, extendedMul; | |@safe pure nothrow @nogc: | |// Returns e == 0 ? 1 : ceil(log_2(5^e)); requires 0 <= e <= 32768. |uint pow5bits(const int e) |{ | version(LDC) pragma(inline, true); 253| assert(e >= 0); 253| assert(e <= 1 << 15); 253| return cast(uint) (((e * 163391164108059UL) >> 46) + 1); |} | |void mul_128_256_shift(const UInt!128 a, const UInt!256 b, const uint shift, const uint corr, ref UInt!256 result) |{ | version(LDC) pragma(inline, true); 90| assert(shift > 0); 90| assert(shift < 256); 90| result = (extendedMul(a, b) >> shift).toSize!256 + corr; |} | |// Computes 5^i in the form required by Ryu, and stores it in the given pointer. |void generic_computePow5(const uint i, ref UInt!256 result) |{ | version(LDC) pragma(inline, true); 81| const uint base = i / POW5_TABLE_SIZE; 81| const uint base2 = base * POW5_TABLE_SIZE; 81| const mul = UInt!256(GENERIC_POW5_SPLIT[base]); 81| if (i == base2) | { 1| result = mul; | } | else | { 80| const uint offset = i - base2; 80| const m = UInt!128(GENERIC_POW5_TABLE[offset]); 80| const uint delta = pow5bits(i) - pow5bits(base2); 80| const uint corr = cast(uint) ((POW5_ERRORS[i / 32] >> (2 * (i % 32))) & 3); 80| mul_128_256_shift(m, mul, delta, corr, result); | } |} | |version(mir_bignum_test) unittest |{ | // We only test a few entries - we could test the fUL table instead, but should we? | static immutable uint[10] EXACT_POW5_IDS = [1, 10, 55, 56, 300, 1000, 2345, 3210, 4968 - 3, 4968 - 1]; | | static immutable ulong[4][10] EXACT_POW5 = [ | [ 0u, 0u, 0u, 90071992547409920u], | [ 0u, 0u, 0u, 83886080000000000u], | [ 0u, 15708555500268290048u, 14699724349295723422u, 117549435082228750u], | [ 0u, 5206161169240293376u, 4575641699882439235u, 73468396926392969u], | [ 2042133660145364371u, 9702060195405861314u, 6467325284806654637u, 107597969523956154u], | [15128847313296546509u, 11916317791371073891u, 788593023170869613u, 137108429762886488u], | [10998857860460266920u, 858411415306315808u, 12732466392391605111u, 136471991906002539u], | [ 5404652432687674341u, 18039986361557197657u, 2228774284272261859u, 94370442653226447u], | [15313487127299642753u, 9780770376910163681u, 15213531439620567348u, 93317108016191349u], | [ 7928436552078881485u, 723697829319983520u, 932817143438521969u, 72903990637649492u], | ]; | 22| for (int i = 0; i < 10; i++) | { 10| UInt!256 result; 10| generic_computePow5(EXACT_POW5_IDS[i], result); 10| assert(UInt!256(EXACT_POW5[i]) == result); | } |} | |// Computes 5^-i in the form required by Ryu, and stores it in the given pointer. |void generic_computeInvPow5(const uint i, ref UInt!256 result) |{ | version(LDC) pragma(inline, true); 11| const uint base = (i + POW5_TABLE_SIZE - 1) / POW5_TABLE_SIZE; 11| const uint base2 = base * POW5_TABLE_SIZE; 11| const mul = UInt!256(GENERIC_POW5_INV_SPLIT[base]); // 1/5^base2 11| if (i == base2) | { 1| result = mul + 1; | } | else | { 10| const uint offset = base2 - i; 10| const m = UInt!128(GENERIC_POW5_TABLE[offset]); // 5^offset 10| const uint delta = pow5bits(base2) - pow5bits(i); 10| const uint corr = cast(uint) ((POW5_INV_ERRORS[i / 32] >> (2 * (i % 32))) & 3) + 1; 10| mul_128_256_shift(m, mul, delta, corr, result); | } |} | |version(mir_bignum_test) unittest |{ | static immutable uint[9] EXACT_INV_POW5_IDS = [10, 55, 56, 300, 1000, 2345, 3210, 4897 - 3, 4897 - 1]; | | static immutable ulong[4][10] EXACT_INV_POW5 = [ | [13362655651931650467u, 3917988799323120213u, 9037289074543890586u, 123794003928538027u], | [ 983662216614650042u, 15516934687640009097u, 8839031818921249472u, 88342353238919216u], | [ 1573859546583440066u, 2691002611772552616u, 6763753280790178510u, 141347765182270746u], | [ 1607391579053635167u, 943946735193622172u, 10726301928680150504u, 96512915280967053u], | [ 7238603269427345471u, 17319296798264127544u, 14852913523241959878u, 75740009093741608u], | [ 2734564866961569744u, 13277212449690943834u, 17231454566843360565u, 76093223027199785u], | [ 5348945211244460332u, 14119936934335594321u, 15647307321253222579u, 110040743956546595u], | [ 2848579222248330872u, 15087265905644220040u, 4449739884766224405u, 100774177495370196u], | [ 1432572115632717323u, 9719393440895634811u, 3482057763655621045u, 128990947194073851u], | ]; | 20| for (int i = 0; i < 9; i++) | { 9| UInt!256 result; 9| generic_computeInvPow5(EXACT_INV_POW5_IDS[i], result); 9| assert(UInt!256(EXACT_INV_POW5[i]) == result); | } |} | |version(LittleEndian) | enum fiveReciprocal = UInt!128([0xCCCCCCCCCCCCCCCD, 0xCCCCCCCCCCCCCCCC]); |else | enum fiveReciprocal = UInt!128([0xCCCCCCCCCCCCCCCC, 0xCCCCCCCCCCCCCCCD]); | |enum baseDiv5 = UInt!128([0x3333333333333333, 0x3333333333333333]); | |uint divRem5(size_t size)(ref UInt!size value) |{ 0000000| auto q = div5(value); 0000000| auto r = cast(uint)(value - q * 5); 0000000| value = q; 0000000| return r; |} | |uint divRem10(size_t size)(ref UInt!size value) |{ 874| auto q = div10(value); 874| auto r = cast(uint)(value - q * 10); 874| value = q; 874| return r; |} | |uint rem5(size_t size)(UInt!size value) |{ 0000000| return divRem5(value); |} | |uint rem10(size_t size)(UInt!size value) |{ | return divRem10(value); |} | |UInt!size div5(size_t size)(UInt!size value) |{ 0000000| return extendedMulHigh(value, fiveReciprocal.toSize!size) >> 2; |} | |UInt!size div10(size_t size)(UInt!size value) |{ 2768| return extendedMulHigh(value, fiveReciprocal.toSize!size) >> 3; |} | |// Returns true if value is divisible by 5^p. |bool multipleOfPowerOf5(size_t size)(UInt!size value, const uint p) |{ | enum fiveReciprocal = .fiveReciprocal.toSize!size; | enum baseDiv5 = .baseDiv5.toSize!size; | version(LDC) pragma(inline, true); 10| assert(value); 19| for (uint count = 0;; ++count) | { 19| value *= fiveReciprocal; 19| if (value > baseDiv5) 10| return count >= p; | } |} | |version(mir_bignum_test) unittest |{ 1| assert(multipleOfPowerOf5(UInt!128(1), 0) == true); 1| assert(multipleOfPowerOf5(UInt!128(1), 1) == false); 1| assert(multipleOfPowerOf5(UInt!128(5), 1) == true); 1| assert(multipleOfPowerOf5(UInt!128(25), 2) == true); 1| assert(multipleOfPowerOf5(UInt!128(75), 2) == true); 1| assert(multipleOfPowerOf5(UInt!128(50), 2) == true); 1| assert(multipleOfPowerOf5(UInt!128(51), 2) == false); 1| assert(multipleOfPowerOf5(UInt!128(75), 4) == false); |} | |// Returns true if value is divisible by 2^p. |bool multipleOfPowerOf2(size_t size)(const UInt!size value, const uint p) |{ | version(LDC) pragma(inline, true); 77| return (value & ((UInt!size(1) << p) - 1)) == 0; |} | |version(mir_bignum_test) unittest |{ 1| assert(multipleOfPowerOf5(UInt!128(1), 0) == true); 1| assert(multipleOfPowerOf5(UInt!128(1), 1) == false); 1| assert(multipleOfPowerOf2(UInt!128(2), 1) == true); 1| assert(multipleOfPowerOf2(UInt!128(4), 2) == true); 1| assert(multipleOfPowerOf2(UInt!128(8), 2) == true); 1| assert(multipleOfPowerOf2(UInt!128(12), 2) == true); 1| assert(multipleOfPowerOf2(UInt!128(13), 2) == false); 1| assert(multipleOfPowerOf2(UInt!128(8), 4) == false); |} | |UInt!size mulShift(size_t size)(const UInt!size m, const UInt!256 mul, const uint j) |{ | version(LDC) pragma(inline, true); 222| assert(j > 128); 222| return (extendedMul(mul, m) >> 128 >> (j - 128)).toSize!size; |} | |version(mir_bignum_test) unittest |{ 1| UInt!256 m = cast(ulong[4])[0, 0, 2, 0]; 1| assert(mulShift(UInt!128(1), m, 129) == 1u); 1| assert(mulShift(UInt!128(12345), m, 129) == 12345u); |} | |version(mir_bignum_test) unittest |{ 1| UInt!256 m = cast(ulong[4])[0, 0, 8, 0]; 1| UInt!128 f = (UInt!128(123) << 64) | 321; 1| assert(mulShift(f, m, 131) == f); |} | |// Returns floor(log_10(2^e)). |uint log10Pow2(const int e) |{ | version(LDC) pragma(inline, true); | // The first value this approximation fails for is 2^1651 which is just greater than 10^297. 5| assert(e >= 0); 5| assert(e <= 1 << 15); 5| return (e * 0x9A209A84FBCFUL) >> 49; |} | |version(mir_bignum_test) unittest |{ 1| assert(log10Pow2(1) == 0u); 1| assert(log10Pow2(5) == 1u); 1| assert(log10Pow2(1 << 15) == 9864u); |} | |// Returns floor(log_10(5^e)). |uint log10Pow5(const int e) |{ | version(LDC) pragma(inline, true); | // The first value this approximation fails for is 5^2621 which is just greater than 10^1832. 75| assert(e >= 0); 75| assert(e <= 1 << 15); 75| return (e * 0xB2EFB2BD8218UL) >> 48; |} | |version(mir_bignum_test) unittest |{ 1| assert(log10Pow5(1) == 0u); 1| assert(log10Pow5(2) == 1u); 1| assert(log10Pow5(3) == 2u); 1| assert(log10Pow5(1 << 15) == 22903u); |} | |debug(ryu) |private char* s(UInt!128 v) |{ | import mir.conv: to; | return (v.to!string ~ "\0").ptr; |} | |// Converts the given binary floating point number to the shortest decimal floating point number |// that still accurately represents it. |Decimal!(T.mant_dig < 64 ? 1 : 2) genericBinaryToDecimal(T)(const T x) |{ | import mir.utility: _expect; | import mir.math: signbit, fabs; | enum coefficientSize = T.mant_dig <= 64 ? 64 : 128; | enum workSize = T.mant_dig < 64 ? 64 : 128; | enum wordCount = workSize / 64; | 88| Decimal!wordCount fd; 88| if (_expect(x != x, false)) | { 4| fd.coefficient = 1u; 4| fd.exponent = fd.exponent.max; | } | else 84| if (_expect(x.fabs == T.infinity, false)) | { 4| fd.exponent = fd.exponent.max; | } | else 80| if (x) | { | import mir.bignum.fp: Fp; 73| const fp = Fp!coefficientSize(x, false); 73| int e2 = cast(int) fp.exponent - 2; 73| UInt!workSize m2 = fp.coefficient; | 73| const bool even = (fp.coefficient & 1) == 0; 73| const bool acceptBounds = even; | | debug(ryu) if (!__ctfe) | { | printf("-> %s %s * 2^%d\n", (fp.sign ? "-" : "+").ptr, s(m2), e2 + 2); | } | | // Step 2: Determine the interval of legal decimal representations. 73| const UInt!workSize mv = m2 << 2; | // Implicit bool -> int conversion. True is 1, false is 0. 73| const bool mmShift = fp.coefficient != (UInt!coefficientSize(1) << (T.mant_dig - 1)); | | // Step 3: Convert to a decimal power base using 128-bit arithmetic. 219| UInt!workSize vr, vp, vm; 73| int e10; 73| bool vmIsTrailingZeros = false; 73| bool vrIsTrailingZeros = false; 73| if (e2 >= 0) | { | // I tried special-casing q == 0, but there was no effect on performance. | // This expression is slightly faster than max(0, log10Pow2(e2) - 1). 2| const uint q = log10Pow2(e2) - (e2 > 3); 2| e10 = q; 2| const int k = FLOAT_128_POW5_INV_BITCOUNT + pow5bits(q) - 1; 2| const int i = -e2 + q + k; 2| UInt!256 pow5; 2| generic_computeInvPow5(q, pow5); 2| vr = mulShift(mv, pow5, i); 2| vp = mulShift(mv + 2, pow5, i); 2| vm = mulShift(mv - 1 - mmShift, pow5, i); | debug(ryu) if (!__ctfe) | { | printf("%s * 2^%d / 10^%d\n", s(mv), e2, q); | printf("V+=%s\nV =%s\nV-=%s\n", s(vp), s(vr), s(vm)); | } | // floor(log_5(2^128)) = 55, this is very conservative 2| if (q <= 55) | { | // Only one of mp, mv, and mm can be a multiple of 5, if any. 0000000| if (rem5(mv) == 0) | { 0000000| vrIsTrailingZeros = multipleOfPowerOf5(mv, q - 1); | } | else 0000000| if (acceptBounds) | { | // Same as min(e2 + (~mm & 1), pow5Factor(mm)) >= q | // <=> e2 + (~mm & 1) >= q && pow5Factor(mm) >= q | // <=> true && pow5Factor(mm) >= q, since e2 >= q. 0000000| vmIsTrailingZeros = multipleOfPowerOf5(mv - 1 - mmShift, q); | } | else | { | // Same as min(e2 + 1, pow5Factor(mp)) >= q. 0000000| vp -= multipleOfPowerOf5(mv + 2, q); | } | } | } | else | { | // This expression is slightly faster than max(0, log10Pow5(-e2) - 1). 71| const uint q = log10Pow5(-e2) - (-e2 > 1); 71| e10 = q + e2; 71| const int i = -e2 - q; 71| const int k = pow5bits(i) - FLOAT_128_POW5_BITCOUNT; 71| const int j = q - k; 71| UInt!256 pow5; 71| generic_computePow5(i, pow5); 71| vr = mulShift(mv, pow5, j); 71| vp = mulShift(mv + 2, pow5, j); 71| vm = mulShift(mv - 1 - mmShift, pow5, j); | debug(ryu) if (!__ctfe) | { | printf("%s * 5^%d / 10^%d\n", s(mv), -e2, q); | printf("%d %d %d %d\n", q, i, k, j); | printf("V+=%s\nV =%s\nV-=%s\n", s(vp), s(vr), s(vm)); | } 71| if (q <= 1) | { | // {vr,vp,vm} is trailing zeros if {mv,mp,mm} has at least q trailing 0 bits. | // mv = 4 m2, so it always has at least two trailing 0 bits. 0000000| vrIsTrailingZeros = true; 0000000| if (acceptBounds) | { | // mm = mv - 1 - mmShift, so it has 1 trailing 0 bit iff mmShift == 1. 0000000| vmIsTrailingZeros = mmShift == 1; | } | else | { | // mp = mv + 2, so it always has at least one trailing 0 bit. 0000000| --vp; | } | } | else 71| if (q < workSize - 1) | { | // TODO(ulfjack): Use a tighter bound here. | // We need to compute min(ntz(mv), pow5Factor(mv) - e2) >= q-1 | // <=> ntz(mv) >= q-1 && pow5Factor(mv) - e2 >= q-1 | // <=> ntz(mv) >= q-1 (e2 is negative and -e2 >= q) | // <=> (mv & ((1 << (q-1)) - 1)) == 0 | // We also need to make sure that the left shift does not overflow. 71| vrIsTrailingZeros = multipleOfPowerOf2(mv, q - 1); | debug(ryu) if (!__ctfe) | { | printf("vr is trailing zeros=%s\n", (vrIsTrailingZeros ? "true" : "false").ptr); | } | } | } | debug(ryu) if (!__ctfe) | { | printf("e10=%d\n", e10); | printf("V+=%s\nV =%s\nV-=%s\n", s(vp), s(vr), s(vm)); | printf("vm is trailing zeros=%s\n", (vmIsTrailingZeros ? "true" : "false").ptr); | printf("vr is trailing zeros=%s\n", (vrIsTrailingZeros ? "true" : "false").ptr); | } | | // Step 4: Find the shortest decimal representation in the interval of legal representations. 73| uint removed = 0; 73| uint lastRemovedDigit = 0; 73| UInt!workSize output; | | for (;;) | { 947| auto div10vp = div10(vp); 947| auto div10vm = div10(vm); 947| if (div10vp == div10vm) 73| break; 874| vmIsTrailingZeros &= vm - div10vm * 10 == 0; 874| vrIsTrailingZeros &= lastRemovedDigit == 0; 874| lastRemovedDigit = vr.divRem10; 874| vp = div10vp; 874| vm = div10vm; 874| ++removed; | } | debug(ryu) if (!__ctfe) | { | printf("V+=%s\nV =%s\nV-=%s\n", s(vp), s(vr), s(vm)); | printf("d-10=%s\n", (vmIsTrailingZeros ? "true" : "false").ptr); | printf("lastRemovedDigit=%d\n", lastRemovedDigit); | } 73| if (vmIsTrailingZeros) | { | for (;;) | { 0000000| auto div10vm = div10(vm); 0000000| if (vm - div10vm * 10) 0000000| break; 0000000| vrIsTrailingZeros &= lastRemovedDigit == 0; 0000000| lastRemovedDigit = cast(uint) (vr - div10vm * 10); 0000000| vr = vp = vm = div10vm; 0000000| ++removed; | } | } | debug(ryu) if (!__ctfe) | { | printf("%s %d\n", s(vr), lastRemovedDigit); | printf("vr is trailing zeros=%s\n", (vrIsTrailingZeros ? "true" : "false").ptr); | printf("lastRemovedDigit=%d\n", lastRemovedDigit); | } 99| if (vrIsTrailingZeros && (lastRemovedDigit == 5) && ((vr & 1) == 0)) | { | // Round even if the exact numbers is .....50..0. 0000000| lastRemovedDigit = 4; | } | // We need to take vr+1 if vr is outside bounds or we need to round up. 159| output = vr + ((vr == vm && (!acceptBounds || !vmIsTrailingZeros)) || (lastRemovedDigit >= 5)); | 73| const int exp = e10 + removed; | | debug(ryu) if (!__ctfe) | { | printf("V+=%s\nV =%s\nV-=%s\n", s(vp), s(vr), s(vm)); | printf("acceptBounds=%d\n", acceptBounds); | printf("vmIsTrailingZeros=%d\n", vmIsTrailingZeros); | printf("lastRemovedDigit=%d\n", lastRemovedDigit); | printf("vrIsTrailingZeros=%d\n", vrIsTrailingZeros); | printf("O=%s\n", s(output)); | printf("EXP=%d\n", exp); | } | | import mir.bignum.integer: BigInt; 73| fd.coefficient.__ctor(output); 73| fd.exponent = exp; | } 88| fd.coefficient.sign = x.signbit; 88| return fd; |} | |private enum FLOAT_128_POW5_INV_BITCOUNT = 249; |private enum FLOAT_128_POW5_BITCOUNT = 249; |private enum POW5_TABLE_SIZE = 56; | |// These tables are ~4.5 kByte total, compared to ~160 kByte for the fUL tables. | |// There's no way to define 128-bit constants in C, so we use little-endian |// pairs of 64-bit constants. |private static immutable ulong[2] [POW5_TABLE_SIZE] GENERIC_POW5_TABLE = [ | [ 1u, 0u], | [ 5u, 0u], | [ 25u, 0u], | [ 125u, 0u], | [ 625u, 0u], | [ 3125u, 0u], | [ 15625u, 0u], | [ 78125u, 0u], | [ 390625u, 0u], | [ 1953125u, 0u], | [ 9765625u, 0u], | [ 48828125u, 0u], | [ 244140625u, 0u], | [ 1220703125u, 0u], | [ 6103515625u, 0u], | [ 30517578125u, 0u], | [ 152587890625u, 0u], | [ 762939453125u, 0u], | [ 3814697265625u, 0u], | [ 19073486328125u, 0u], | [ 95367431640625u, 0u], | [ 476837158203125u, 0u], | [ 2384185791015625u, 0u], | [ 11920928955078125u, 0u], | [ 59604644775390625u, 0u], | [ 298023223876953125u, 0u], | [ 1490116119384765625u, 0u], | [ 7450580596923828125u, 0u], | [ 359414837200037393u, 2u], | [ 1797074186000186965u, 10u], | [ 8985370930000934825u, 50u], | [ 8033366502585570893u, 252u], | [ 3273344365508751233u, 1262u], | [16366721827543756165u, 6310u], | [ 8046632842880574361u, 31554u], | [ 3339676066983768573u, 157772u], | [16698380334918842865u, 788860u], | [ 9704925379756007861u, 3944304u], | [11631138751360936073u, 19721522u], | [ 2815461535676025517u, 98607613u], | [14077307678380127585u, 493038065u], | [15046306170771983077u, 2465190328u], | [ 1444554559021708921u, 12325951644u], | [ 7222772795108544605u, 61629758220u], | [17667119901833171409u, 308148791101u], | [14548623214327650581u, 1540743955509u], | [17402883850509598057u, 7703719777548u], | [13227442957709783821u, 38518598887744u], | [10796982567420264257u, 192592994438723u], | [17091424689682218053u, 962964972193617u], | [11670147153572883801u, 4814824860968089u], | [ 3010503546735764157u, 24074124304840448u], | [15052517733678820785u, 120370621524202240u], | [ 1475612373555897461u, 601853107621011204u], | [ 7378061867779487305u, 3009265538105056020u], | [18443565265187884909u, 15046327690525280101u], |]; | |private static immutable ulong[4][89] GENERIC_POW5_SPLIT = [ | [ 0u, 0u, 0u, 72057594037927936u], | [ 0u, 5206161169240293376u, 4575641699882439235u, 73468396926392969u], | [ 3360510775605221349u, 6983200512169538081u, 4325643253124434363u, 74906821675075173u], | [ 11917660854915489451u, 9652941469841108803u, 946308467778435600u, 76373409087490117u], | [ 1994853395185689235u, 16102657350889591545u, 6847013871814915412u, 77868710555449746u], | [ 958415760277438274u, 15059347134713823592u, 7329070255463483331u, 79393288266368765u], | [ 2065144883315240188u, 7145278325844925976u, 14718454754511147343u, 80947715414629833u], | [ 8980391188862868935u, 13709057401304208685u, 8230434828742694591u, 82532576417087045u], | [ 432148644612782575u, 7960151582448466064u, 12056089168559840552u, 84148467132788711u], | [ 484109300864744403u, 15010663910730448582u, 16824949663447227068u, 85795995087002057u], | [ 14793711725276144220u, 16494403799991899904u, 10145107106505865967u, 87475779699624060u], | [ 15427548291869817042u, 12330588654550505203u, 13980791795114552342u, 89188452518064298u], | [ 9979404135116626552u, 13477446383271537499u, 14459862802511591337u, 90934657454687378u], | [ 12385121150303452775u, 9097130814231585614u, 6523855782339765207u, 92715051028904201u], | [ 1822931022538209743u, 16062974719797586441u, 3619180286173516788u, 94530302614003091u], | [ 12318611738248470829u, 13330752208259324507u, 10986694768744162601u, 96381094688813589u], | [ 13684493829640282333u, 7674802078297225834u, 15208116197624593182u, 98268123094297527u], | [ 5408877057066295332u, 6470124174091971006u, 15112713923117703147u, 100192097295163851u], | [ 11407083166564425062u, 18189998238742408185u, 4337638702446708282u, 102153740646605557u], | [ 4112405898036935485u, 924624216579956435u, 14251108172073737125u, 104153790666259019u], | [ 16996739107011444789u, 10015944118339042475u, 2395188869672266257u, 106192999311487969u], | [ 4588314690421337879u, 5339991768263654604u, 15441007590670620066u, 108272133262096356u], | [ 2286159977890359825u, 14329706763185060248u, 5980012964059367667u, 110391974208576409u], | [ 9654767503237031099u, 11293544302844823188u, 11739932712678287805u, 112553319146000238u], | [ 11362964448496095896u, 7990659682315657680u, 251480263940996374u, 114756980673665505u], | [ 1423410421096377129u, 14274395557581462179u, 16553482793602208894u, 117003787300607788u], | [ 2070444190619093137u, 11517140404712147401u, 11657844572835578076u, 119294583757094535u], | [ 7648316884775828921u, 15264332483297977688u, 247182277434709002u, 121630231312217685u], | [ 17410896758132241352u, 10923914482914417070u, 13976383996795783649u, 124011608097704390u], | [ 9542674537907272703u, 3079432708831728956u, 14235189590642919676u, 126439609438067572u], | [ 10364666969937261816u, 8464573184892924210u, 12758646866025101190u, 128915148187220428u], | [ 14720354822146013883u, 11480204489231511423u, 7449876034836187038u, 131439155071681461u], | [ 1692907053653558553u, 17835392458598425233u, 1754856712536736598u, 134012579040499057u], | [ 5620591334531458755u, 11361776175667106627u, 13350215315297937856u, 136636387622027174u], | [ 17455759733928092601u, 10362573084069962561u, 11246018728801810510u, 139311567287686283u], | [ 2465404073814044982u, 17694822665274381860u, 1509954037718722697u, 142039123822846312u], | [ 2152236053329638369u, 11202280800589637091u, 16388426812920420176u, 72410041352485523u], | [ 17319024055671609028u, 10944982848661280484u, 2457150158022562661u, 73827744744583080u], | [ 17511219308535248024u, 5122059497846768077u, 2089605804219668451u, 75273205100637900u], | [ 10082673333144031533u, 14429008783411894887u, 12842832230171903890u, 76746965869337783u], | [ 16196653406315961184u, 10260180891682904501u, 10537411930446752461u, 78249581139456266u], | [ 15084422041749743389u, 234835370106753111u, 16662517110286225617u, 79781615848172976u], | [ 8199644021067702606u, 3787318116274991885u, 7438130039325743106u, 81343645993472659u], | [ 12039493937039359765u, 9773822153580393709u, 5945428874398357806u, 82936258850702722u], | [ 984543865091303961u, 7975107621689454830u, 6556665988501773347u, 84560053193370726u], | [ 9633317878125234244u, 16099592426808915028u, 9706674539190598200u, 86215639518264828u], | [ 6860695058870476186u, 4471839111886709592u, 7828342285492709568u, 87903640274981819u], | [ 14583324717644598331u, 4496120889473451238u, 5290040788305728466u, 89624690099949049u], | [ 18093669366515003715u, 12879506572606942994u, 18005739787089675377u, 91379436055028227u], | [ 17997493966862379937u, 14646222655265145582u, 10265023312844161858u, 93168537870790806u], | [ 12283848109039722318u, 11290258077250314935u, 9878160025624946825u, 94992668194556404u], | [ 8087752761883078164u, 5262596608437575693u, 11093553063763274413u, 96852512843287537u], | [ 15027787746776840781u, 12250273651168257752u, 9290470558712181914u, 98748771061435726u], | [ 15003915578366724489u, 2937334162439764327u, 5404085603526796602u, 100682155783835929u], | [ 5225610465224746757u, 14932114897406142027u, 2774647558180708010u, 102653393903748137u], | [ 17112957703385190360u, 12069082008339002412u, 3901112447086388439u, 104663226546146909u], | [ 4062324464323300238u, 3992768146772240329u, 15757196565593695724u, 106712409346361594u], | [ 5525364615810306701u, 11855206026704935156u, 11344868740897365300u, 108801712734172003u], | [ 9274143661888462646u, 4478365862348432381u, 18010077872551661771u, 110931922223466333u], | [ 12604141221930060148u, 8930937759942591500u, 9382183116147201338u, 113103838707570263u], | [ 14513929377491886653u, 1410646149696279084u, 587092196850797612u, 115318278760358235u], | [ 2226851524999454362u, 7717102471110805679u, 7187441550995571734u, 117576074943260147u], | [ 5527526061344932763u, 2347100676188369132u, 16976241418824030445u, 119878076118278875u], | [ 6088479778147221611u, 17669593130014777580u, 10991124207197663546u, 122225147767136307u], | [ 11107734086759692041u, 3391795220306863431u, 17233960908859089158u, 124618172316667879u], | [ 7913172514655155198u, 17726879005381242552u, 641069866244011540u, 127058049470587962u], | [ 12596991768458713949u, 15714785522479904446u, 6035972567136116512u, 129545696547750811u], | [ 16901996933781815980u, 4275085211437148707u, 14091642539965169063u, 132082048827034281u], | [ 7524574627987869240u, 15661204384239316051u, 2444526454225712267u, 134668059898975949u], | [ 8199251625090479942u, 6803282222165044067u, 16064817666437851504u, 137304702024293857u], | [ 4453256673338111920u, 15269922543084434181u, 3139961729834750852u, 139992966499426682u], | [ 15841763546372731299u, 3013174075437671812u, 4383755396295695606u, 142733864029230733u], | [ 9771896230907310329u, 4900659362437687569u, 12386126719044266361u, 72764212553486967u], | [ 9420455527449565190u, 1859606122611023693u, 6555040298902684281u, 74188850200884818u], | [ 5146105983135678095u, 2287300449992174951u, 4325371679080264751u, 75641380576797959u], | [ 11019359372592553360u, 8422686425957443718u, 7175176077944048210u, 77122349788024458u], | [ 11005742969399620716u, 4132174559240043701u, 9372258443096612118u, 78632314633490790u], | [ 8887589641394725840u, 8029899502466543662u, 14582206497241572853u, 80171842813591127u], | [ 360247523705545899u, 12568341805293354211u, 14653258284762517866u, 81741513143625247u], | [ 12314272731984275834u, 4740745023227177044u, 6141631472368337539u, 83341915771415304u], | [ 441052047733984759u, 7940090120939869826u, 11750200619921094248u, 84973652399183278u], | [ 3436657868127012749u, 9187006432149937667u, 16389726097323041290u, 86637336509772529u], | [ 13490220260784534044u, 15339072891382896702u, 8846102360835316895u, 88333593597298497u], | [ 4125672032094859833u, 158347675704003277u, 10592598512749774447u, 90063061402315272u], | [ 12189928252974395775u, 2386931199439295891u, 7009030566469913276u, 91826390151586454u], | [ 9256479608339282969u, 2844900158963599229u, 11148388908923225596u, 93624242802550437u], | [ 11584393507658707408u, 2863659090805147914u, 9873421561981063551u, 95457295292572042u], | [ 13984297296943171390u, 1931468383973130608u, 12905719743235082319u, 97326236793074198u], | [ 5837045222254987499u, 10213498696735864176u, 14893951506257020749u, 99231769968645227u], |]; | |// Unfortunately, the results are sometimes off by one or two. We use an additional |// lookup table to store those cases and adjust the result. |private static immutable ulong[156] POW5_ERRORS = [ | 0x0000000000000000u, 0x0000000000000000u, 0x0000000000000000u, 0x9555596400000000u, | 0x65a6569525565555u, 0x4415551445449655u, 0x5105015504144541u, 0x65a69969a6965964u, | 0x5054955969959656u, 0x5105154515554145u, 0x4055511051591555u, 0x5500514455550115u, | 0x0041140014145515u, 0x1005440545511051u, 0x0014405450411004u, 0x0414440010500000u, | 0x0044000440010040u, 0x5551155000004001u, 0x4554555454544114u, 0x5150045544005441u, | 0x0001111400054501u, 0x6550955555554554u, 0x1504159645559559u, 0x4105055141454545u, | 0x1411541410405454u, 0x0415555044545555u, 0x0014154115405550u, 0x1540055040411445u, | 0x0000000500000000u, 0x5644000000000000u, 0x1155555591596555u, 0x0410440054569565u, | 0x5145100010010005u, 0x0555041405500150u, 0x4141450455140450u, 0x0000000144000140u, | 0x5114004001105410u, 0x4444100404005504u, 0x0414014410001015u, 0x5145055155555015u, | 0x0141041444445540u, 0x0000100451541414u, 0x4105041104155550u, 0x0500501150451145u, | 0x1001050000004114u, 0x5551504400141045u, 0x5110545410151454u, 0x0100001400004040u, | 0x5040010111040000u, 0x0140000150541100u, 0x4400140400104110u, 0x5011014405545004u, | 0x0000000044155440u, 0x0000000010000000u, 0x1100401444440001u, 0x0040401010055111u, | 0x5155155551405454u, 0x0444440015514411u, 0x0054505054014101u, 0x0451015441115511u, | 0x1541411401140551u, 0x4155104514445110u, 0x4141145450145515u, 0x5451445055155050u, | 0x4400515554110054u, 0x5111145104501151u, 0x565a655455500501u, 0x5565555555525955u, | 0x0550511500405695u, 0x4415504051054544u, 0x6555595965555554u, 0x0100915915555655u, | 0x5540001510001001u, 0x5450051414000544u, 0x1405010555555551u, 0x5555515555644155u, | 0x5555055595496555u, 0x5451045004415000u, 0x5450510144040144u, 0x5554155555556455u, | 0x5051555495415555u, 0x5555554555555545u, 0x0000000010005455u, 0x4000005000040000u, | 0x5565555555555954u, 0x5554559555555505u, 0x9645545495552555u, 0x4000400055955564u, | 0x0040000000000001u, 0x4004100100000000u, 0x5540040440000411u, 0x4565555955545644u, | 0x1140659549651556u, 0x0100000410010000u, 0x5555515400004001u, 0x5955545555155255u, | 0x5151055545505556u, 0x5051454510554515u, 0x0501500050415554u, 0x5044154005441005u, | 0x1455445450550455u, 0x0010144055144545u, 0x0000401100000004u, 0x1050145050000010u, | 0x0415004554011540u, 0x1000510100151150u, 0x0100040400001144u, 0x0000000000000000u, | 0x0550004400000100u, 0x0151145041451151u, 0x0000400400005450u, 0x0000100044010004u, | 0x0100054100050040u, 0x0504400005410010u, 0x4011410445500105u, 0x0000404000144411u, | 0x0101504404500000u, 0x0000005044400400u, 0x0000000014000100u, 0x0404440414000000u, | 0x5554100410000140u, 0x4555455544505555u, 0x5454105055455455u, 0x0115454155454015u, | 0x4404110000045100u, 0x4400001100101501u, 0x6596955956966a94u, 0x0040655955665965u, | 0x5554144400100155u, 0xa549495401011041u, 0x5596555565955555u, 0x5569965959549555u, | 0x969565a655555456u, 0x0000001000000000u, 0x0000000040000140u, 0x0000040100000000u, | 0x1415454400000000u, 0x5410415411454114u, 0x0400040104000154u, 0x0504045000000411u, | 0x0000001000000010u, 0x5554000000001040u, 0x5549155551556595u, 0x1455541055515555u, | 0x0510555454554541u, 0x9555555555540455u, 0x6455456555556465u, 0x4524565555654514u, | 0x5554655255559545u, 0x9555455441155556u, 0x0000000051515555u, 0x0010005040000550u, | 0x5044044040000000u, 0x1045040440010500u, 0x0000400000040000u, 0x0000000000000000u, |]; | |private static immutable ulong[4][89] GENERIC_POW5_INV_SPLIT = [ | [ 0u, 0u, 0u, 144115188075855872u ], | [ 1573859546583440065u, 2691002611772552616u, 6763753280790178510u, 141347765182270746u ], | [ 12960290449513840412u, 12345512957918226762u, 18057899791198622765u, 138633484706040742u ], | [ 7615871757716765416u, 9507132263365501332u, 4879801712092008245u, 135971326161092377u ], | [ 7869961150745287587u, 5804035291554591636u, 8883897266325833928u, 133360288657597085u ], | [ 2942118023529634767u, 15128191429820565086u, 10638459445243230718u, 130799390525667397u ], | [ 14188759758411913794u, 5362791266439207815u, 8068821289119264054u, 128287668946279217u ], | [ 7183196927902545212u, 1952291723540117099u, 12075928209936341512u, 125824179589281448u ], | [ 5672588001402349748u, 17892323620748423487u, 9874578446960390364u, 123407996258356868u ], | [ 4442590541217566325u, 4558254706293456445u, 10343828952663182727u, 121038210542800766u ], | [ 3005560928406962566u, 2082271027139057888u, 13961184524927245081u, 118713931475986426u ], | [ 13299058168408384786u, 17834349496131278595u, 9029906103900731664u, 116434285200389047u ], | [ 5414878118283973035u, 13079825470227392078u, 17897304791683760280u, 114198414639042157u ], | [ 14609755883382484834u, 14991702445765844156u, 3269802549772755411u, 112005479173303009u ], | [ 15967774957605076027u, 2511532636717499923u, 16221038267832563171u, 109854654326805788u ], | [ 9269330061621627145u, 3332501053426257392u, 16223281189403734630u, 107745131455483836u ], | [ 16739559299223642282u, 1873986623300664530u, 6546709159471442872u, 105676117443544318u ], | [ 17116435360051202055u, 1359075105581853924u, 2038341371621886470u, 103646834405281051u ], | [ 17144715798009627550u, 3201623802661132408u, 9757551605154622431u, 101656519392613377u ], | [ 17580479792687825857u, 6546633380567327312u, 15099972427870912398u, 99704424108241124u ], | [ 9726477118325522902u, 14578369026754005435u, 11728055595254428803u, 97789814624307808u ], | [ 134593949518343635u, 5715151379816901985u, 1660163707976377376u, 95911971106466306u ], | [ 5515914027713859358u, 7124354893273815720u, 5548463282858794077u, 94070187543243255u ], | [ 6188403395862945512u, 5681264392632320838u, 15417410852121406654u, 92263771480600430u ], | [ 15908890877468271457u, 10398888261125597540u, 4817794962769172309u, 90492043761593298u ], | [ 1413077535082201005u, 12675058125384151580u, 7731426132303759597u, 88754338271028867u ], | [ 1486733163972670293u, 11369385300195092554u, 11610016711694864110u, 87050001685026843u ], | [ 8788596583757589684u, 3978580923851924802u, 9255162428306775812u, 85378393225389919u ], | [ 7203518319660962120u, 15044736224407683725u, 2488132019818199792u, 83738884418690858u ], | [ 4004175967662388707u, 18236988667757575407u, 15613100370957482671u, 82130858859985791u ], | [ 18371903370586036463u, 53497579022921640u, 16465963977267203307u, 80553711981064899u ], | [ 10170778323887491315u, 1999668801648976001u, 10209763593579456445u, 79006850823153334u ], | [ 17108131712433974546u, 16825784443029944237u, 2078700786753338945u, 77489693813976938u ], | [ 17221789422665858532u, 12145427517550446164u, 5391414622238668005u, 76001670549108934u ], | [ 4859588996898795878u, 1715798948121313204u, 3950858167455137171u, 74542221577515387u ], | [ 13513469241795711526u, 631367850494860526u, 10517278915021816160u, 73110798191218799u ], | [ 11757513142672073111u, 2581974932255022228u, 17498959383193606459u, 143413724438001539u ], | [ 14524355192525042817u, 5640643347559376447u, 1309659274756813016u, 140659771648132296u ], | [ 2765095348461978538u, 11021111021896007722u, 3224303603779962366u, 137958702611185230u ], | [ 12373410389187981037u, 13679193545685856195u, 11644609038462631561u, 135309501808182158u ], | [ 12813176257562780151u, 3754199046160268020u, 9954691079802960722u, 132711173221007413u ], | [ 17557452279667723458u, 3237799193992485824u, 17893947919029030695u, 130162739957935629u ], | [ 14634200999559435155u, 4123869946105211004u, 6955301747350769239u, 127663243886350468u ], | [ 2185352760627740240u, 2864813346878886844u, 13049218671329690184u, 125211745272516185u ], | [ 6143438674322183002u, 10464733336980678750u, 6982925169933978309u, 122807322428266620u ], | [ 1099509117817174576u, 10202656147550524081u, 754997032816608484u, 120449071364478757u ], | [ 2410631293559367023u, 17407273750261453804u, 15307291918933463037u, 118136105451200587u ], | [ 12224968375134586697u, 1664436604907828062u, 11506086230137787358u, 115867555084305488u ], | [ 3495926216898000888u, 18392536965197424288u, 10992889188570643156u, 113642567358547782u ], | [ 8744506286256259680u, 3966568369496879937u, 18342264969761820037u, 111460305746896569u ], | [ 7689600520560455039u, 5254331190877624630u, 9628558080573245556u, 109319949786027263u ], | [ 11862637625618819436u, 3456120362318976488u, 14690471063106001082u, 107220694767852583u ], | [ 5697330450030126444u, 12424082405392918899u, 358204170751754904u, 105161751436977040u ], | [ 11257457505097373622u, 15373192700214208870u, 671619062372033814u, 103142345693961148u ], | [ 16850355018477166700u, 1913910419361963966u, 4550257919755970531u, 101161718304283822u ], | [ 9670835567561997011u, 10584031339132130638u, 3060560222974851757u, 99219124612893520u ], | [ 7698686577353054710u, 11689292838639130817u, 11806331021588878241u, 97313834264240819u ], | [ 12233569599615692137u, 3347791226108469959u, 10333904326094451110u, 95445130927687169u ], | [ 13049400362825383933u, 17142621313007799680u, 3790542585289224168u, 93612312028186576u ], | [ 12430457242474442072u, 5625077542189557960u, 14765055286236672238u, 91814688482138969u ], | [ 4759444137752473128u, 2230562561567025078u, 4954443037339580076u, 90051584438315940u ], | [ 7246913525170274758u, 8910297835195760709u, 4015904029508858381u, 88322337023761438u ], | [ 12854430245836432067u, 8135139748065431455u, 11548083631386317976u, 86626296094571907u ], | [ 4848827254502687803u, 4789491250196085625u, 3988192420450664125u, 84962823991462151u ], | [ 7435538409611286684u, 904061756819742353u, 14598026519493048444u, 83331295300025028u ], | [ 11042616160352530997u, 8948390828345326218u, 10052651191118271927u, 81731096615594853u ], | [ 11059348291563778943u, 11696515766184685544u, 3783210511290897367u, 80161626312626082u ], | [ 7020010856491885826u, 5025093219346041680u, 8960210401638911765u, 78622294318500592u ], | [ 17732844474490699984u, 7820866704994446502u, 6088373186798844243u, 77112521891678506u ], | [ 688278527545590501u, 3045610706602776618u, 8684243536999567610u, 75631741404109150u ], | [ 2734573255120657297u, 3903146411440697663u, 9470794821691856713u, 74179396127820347u ], | [ 15996457521023071259u, 4776627823451271680u, 12394856457265744744u, 72754940025605801u ], | [ 13492065758834518331u, 7390517611012222399u, 1630485387832860230u, 142715675091463768u ], | [ 13665021627282055864u, 9897834675523659302u, 17907668136755296849u, 139975126841173266u ], | [ 9603773719399446181u, 10771916301484339398u, 10672699855989487527u, 137287204938390542u ], | [ 3630218541553511265u, 8139010004241080614u, 2876479648932814543u, 134650898807055963u ], | [ 8318835909686377084u, 9525369258927993371u, 2796120270400437057u, 132065217277054270u ], | [ 11190003059043290163u, 12424345635599592110u, 12539346395388933763u, 129529188211565064u ], | [ 8701968833973242276u, 820569587086330727u, 2315591597351480110u, 127041858141569228u ], | [ 5115113890115690487u, 16906305245394587826u, 9899749468931071388u, 124602291907373862u ], | [ 15543535488939245974u, 10945189844466391399u, 3553863472349432246u, 122209572307020975u ], | [ 7709257252608325038u, 1191832167690640880u, 15077137020234258537u, 119862799751447719u ], | [ 7541333244210021737u, 9790054727902174575u, 5160944773155322014u, 117561091926268545u ], | [ 12297384708782857832u, 1281328873123467374u, 4827925254630475769u, 115303583460052092u ], | [ 13243237906232367265u, 15873887428139547641u, 3607993172301799599u, 113089425598968120u ], | [ 11384616453739611114u, 15184114243769211033u, 13148448124803481057u, 110917785887682141u ], | [ 17727970963596660683u, 1196965221832671990u, 14537830463956404138u, 108787847856377790u ], | [ 17241367586707330931u, 8880584684128262874u, 11173506540726547818u, 106698810713789254u ], | [ 7184427196661305643u, 14332510582433188173u, 14230167953789677901u, 104649889046128358u ], |]; | |private static immutable ulong[154] POW5_INV_ERRORS = [ | 0x1144155514145504u, 0x0000541555401141u, 0x0000000000000000u, 0x0154454000000000u, | 0x4114105515544440u, 0x0001001111500415u, 0x4041411410011000u, 0x5550114515155014u, | 0x1404100041554551u, 0x0515000450404410u, 0x5054544401140004u, 0x5155501005555105u, | 0x1144141000105515u, 0x0541500000500000u, 0x1104105540444140u, 0x4000015055514110u, | 0x0054010450004005u, 0x4155515404100005u, 0x5155145045155555u, 0x1511555515440558u, | 0x5558544555515555u, 0x0000000000000010u, 0x5004000000000050u, 0x1415510100000010u, | 0x4545555444514500u, 0x5155151555555551u, 0x1441540144044554u, 0x5150104045544400u, | 0x5450545401444040u, 0x5554455045501400u, 0x4655155555555145u, 0x1000010055455055u, | 0x1000004000055004u, 0x4455405104000005u, 0x4500114504150545u, 0x0000000014000000u, | 0x5450000000000000u, 0x5514551511445555u, 0x4111501040555451u, 0x4515445500054444u, | 0x5101500104100441u, 0x1545115155545055u, 0x0000000000000000u, 0x1554000000100000u, | 0x5555545595551555u, 0x5555051851455955u, 0x5555555555555559u, 0x0000400011001555u, | 0x0000004400040000u, 0x5455511555554554u, 0x5614555544115445u, 0x6455156145555155u, | 0x5455855455415455u, 0x5515555144555545u, 0x0114400000145155u, 0x0000051000450511u, | 0x4455154554445100u, 0x4554150141544455u, 0x65955555559a5965u, 0x5555555854559559u, | 0x9569654559616595u, 0x1040044040005565u, 0x1010010500011044u, 0x1554015545154540u, | 0x4440555401545441u, 0x1014441450550105u, 0x4545400410504145u, 0x5015111541040151u, | 0x5145051154000410u, 0x1040001044545044u, 0x4001400000151410u, 0x0540000044040000u, | 0x0510555454411544u, 0x0400054054141550u, 0x1001041145001100u, 0x0000000140000000u, | 0x0000000014100000u, 0x1544005454000140u, 0x4050055505445145u, 0x0011511104504155u, | 0x5505544415045055u, 0x1155154445515554u, 0x0000000000004555u, 0x0000000000000000u, | 0x5101010510400004u, 0x1514045044440400u, 0x5515519555515555u, 0x4554545441555545u, | 0x1551055955551515u, 0x0150000011505515u, 0x0044005040400000u, 0x0004001004010050u, | 0x0000051004450414u, 0x0114001101001144u, 0x0401000001000001u, 0x4500010001000401u, | 0x0004100000005000u, 0x0105000441101100u, 0x0455455550454540u, 0x5404050144105505u, | 0x4101510540555455u, 0x1055541411451555u, 0x5451445110115505u, 0x1154110010101545u, | 0x1145140450054055u, 0x5555565415551554u, 0x1550559555555555u, 0x5555541545045141u, | 0x4555455450500100u, 0x5510454545554555u, 0x1510140115045455u, 0x1001050040111510u, | 0x5555454555555504u, 0x9954155545515554u, 0x6596656555555555u, 0x0140410051555559u, | 0x0011104010001544u, 0x965669659a680501u, 0x5655a55955556955u, 0x4015111014404514u, | 0x1414155554505145u, 0x0540040011051404u, 0x1010000000015005u, 0x0010054050004410u, | 0x5041104014000100u, 0x4440010500100001u, 0x1155510504545554u, 0x0450151545115541u, | 0x4000100400110440u, 0x1004440010514440u, 0x0000115050450000u, 0x0545404455541500u, | 0x1051051555505101u, 0x5505144554544144u, 0x4550545555515550u, 0x0015400450045445u, | 0x4514155400554415u, 0x4555055051050151u, 0x1511441450001014u, 0x4544554510404414u, | 0x4115115545545450u, 0x5500541555551555u, 0x5550010544155015u, 0x0144414045545500u, | 0x4154050001050150u, 0x5550511111000145u, 0x1114504055000151u, 0x5104041101451040u, | 0x0010501401051441u, 0x0010501450504401u, 0x4554585440044444u, 0x5155555951450455u, | 0x0040000400105555u, 0x0000000000000001u, |]; source/mir/bignum/internal/ryu/generic_128.d is 86% covered <<<<<< EOF # path=./..-..-..-.dub-packages-mir-core-1.1.83-mir-core-source-mir-complex.lst |/++ |Complex numbers | |Authors: Ilya Yaroshenko |+/ |module mir.complex; | |import mir.math.common: optmath; | |private alias CommonType(A, B) = typeof(A.init + B.init); | |@optmath: | |/++ |Generic complex number type |+/ |struct Complex(T) | if (is(T == float) || is(T == double) || is(T == real)) |{ | import mir.internal.utility: isComplex; | import std.traits: isNumeric; | |@optmath: | | /++ | Real part. Default value is zero. | +/ | T re = 0; | /++ | Imaginary part. Default value is zero. | +/ | T im = 0; | | /// | ref Complex opAssign(R)(Complex!R rhs) | if (!is(R == T)) | { | this.re = rhs.re; | this.im = rhs.im; | return this; | } | | /// | ref Complex opAssign(F)(const F rhs) | if (isNumeric!F) | { | this.re = rhs; | this.im = 0; | return this; | } | | /// | ref Complex opOpAssign(string op : "+", R)(Complex!R rhs) return | { | re += rhs.re; | im += rhs.im; | return this; | } | | /// | ref Complex opOpAssign(string op : "-", R)(Complex!R rhs) return | { | re -= rhs.re; | im -= rhs.im; | return this; | } | | /// | ref Complex opOpAssign(string op, R)(Complex!R rhs) return | if (op == "*" || op == "/") | { | return this = this.opBinary!op(rhs); | } | | /// | ref Complex opOpAssign(string op : "+", R)(const R rhs) return | if (isNumeric!R) | { | re += rhs; | return this; | } | | /// | ref Complex opOpAssign(string op : "-", R)(const R rhs) return | if (isNumeric!R) | { | re -= rhs; | return this; | } | | /// | ref Complex opOpAssign(string op : "*", R)(const R rhs) return | if (isNumeric!R) | { | re *= rhs; | return this; | } | | /// | ref Complex opOpAssign(string op : "/", R)(const R rhs) return | if (isNumeric!R) | { | re /= rhs; | return this; | } | |const: | | /// | bool opEquals(const Complex rhs) | { 0000000| return re == rhs.re && im == rhs.im; | } | | /// | size_t toHash() | { 0000000| T[2] val = [re, im]; 0000000| return hashOf(val) ; | } | |scope: | | /// | bool opEquals(R)(Complex!R rhs) | if (!is(R == T)) | { | return re == rhs.re && im == rhs.im; | } | | /// | bool opEquals(F)(const F rhs) | if (isNumeric!F) | { | return re == rhs && im == 0; | } | | /// | Complex opUnary(string op : "+")() | { | return this; | } | | /// | Complex opUnary(string op : "-")() | { | return typeof(return)(-re, -im); | } | | /// | Complex!(CommonType!(T, R)) opBinary(string op : "+", R)(Complex!R rhs) | { | return typeof(return)(re + rhs.re, im + rhs.im); | } | | /// | Complex!(CommonType!(T, R)) opBinary(string op : "-", R)(Complex!R rhs) | { | return typeof(return)(re - rhs.re, im - rhs.im); | } | | /// | Complex!(CommonType!(T, R)) opBinary(string op : "*", R)(Complex!R rhs) | { | return typeof(return)(re * rhs.re - im * rhs.im, re * rhs.im + im * rhs.re); | } | | /// | Complex!(CommonType!(T, R)) opBinary(string op : "/", R)(Complex!R rhs) | { | // TODO: use more precise algorithm | auto norm = rhs.re * rhs.re + rhs.im * rhs.im; | return typeof(return)( | (re * rhs.re + im * rhs.im) / norm, | (im * rhs.re - re * rhs.im) / norm, | ); | } | | /// | Complex!(CommonType!(T, R)) opBinary(string op : "+", R)(const R rhs) | if (isNumeric!R) | { | return typeof(return)(re + rhs, im); | } | | /// | Complex!(CommonType!(T, R)) opBinary(string op : "-", R)(const R rhs) | if (isNumeric!R) | { | return typeof(return)(re - rhs, im); | } | | /// | Complex!(CommonType!(T, R)) opBinary(string op : "*", R)(const R rhs) | if (isNumeric!R) | { | return typeof(return)(re * rhs, im * rhs); | } | | /// | Complex!(CommonType!(T, R)) opBinary(string op : "/", R)(const R rhs) | if (isNumeric!R) | { | return typeof(return)(re / rhs, im / rhs); | } | | | /// | Complex!(CommonType!(T, R)) opBinaryRight(string op : "+", R)(const R rhs) | if (isNumeric!R) | { | return typeof(return)(rhs + re, im); | } | | /// | Complex!(CommonType!(T, R)) opBinaryRight(string op : "-", R)(const R rhs) | if (isNumeric!R) | { | return typeof(return)(rhs - re, -im); | } | | /// | Complex!(CommonType!(T, R)) opBinaryRight(string op : "*", R)(const R rhs) | if (isNumeric!R) | { | return typeof(return)(rhs * re, rhs * im); | } | | /// | Complex!(CommonType!(T, R)) opBinaryRight(string op : "/", R)(const R rhs) | if (isNumeric!R) | { | // TODO: use more precise algorithm | auto norm = this.re * this.re + this.im * this.im; | return typeof(return)( | rhs * (this.re / norm), | -rhs * (this.im / norm), | ); | } | | /// | R opCast(R)() | if (isNumeric!R || isComplex!R) | { | static if (isNumeric!R) | return cast(R) re; | else | return R(re, im); | } |} | |/// ditto |Complex!T complex(T)(const T re, const T im) | if (is(T == float) || is(T == double) || is(T == real)) |{ | return typeof(return)(re, im); |} | |private alias _cdouble_ = Complex!double; |private alias _cfloat_ = Complex!float; |private alias _creal_ = Complex!real; | |/// |unittest |{ | auto a = complex(1.0, 3); | auto b = a; | b.re += 3; | a = b; | assert(a == b); | | a = Complex!float(5, 6); | assert(a == Complex!real(5, 6)); | | a += b; | a -= b; | a *= b; | a /= b; | | a = a + b; | a = a - b; | a = a * b; | a = a / b; | | a += 2; | a -= 2; | a *= 2; | a /= 2; | | a = a + 2; | a = a - 2; | a = a * 2; | a = a / 2; | | a = 2 + a; | a = 2 - a; | a = 2 * a; | a = 2 / a; | | a = -a; | a = +a; | | assert(a != 4.0); | a = 4; | assert(a == 4); | assert(cast(int)a == 4); | assert(cast(Complex!float)a == 4); | | import std.complex : StdComplex = Complex; | assert(cast(StdComplex!double)a == StdComplex!double(4, 0)); |} ../../../.dub/packages/mir-core-1.1.83/mir-core/source/mir/complex.d is 0% covered <<<<<< EOF # path=./source-mir-ndslice-field.lst |/++ |This is a submodule of $(MREF mir,ndslice). | |Field is a type with `opIndex()(ptrdiff_t index)` primitive. |An iterator can be created on top of a field using $(SUBREF iterator, FieldIterator). |An ndslice can be created on top of a field using $(SUBREF slice, slicedField). | |$(BOOKTABLE $(H2 Fields), |$(TR $(TH Field Name) $(TH Used By)) |$(T2 BitField, $(SUBREF topology, bitwise)) |$(T2 BitpackField, $(SUBREF topology, bitpack)) |$(T2 CycleField, $(SUBREF topology, cycle) (2 kinds)) |$(T2 LinspaceField, $(SUBREF topology, linspace)) |$(T2 MagicField, $(SUBREF topology, magic)) |$(T2 MapField, $(SUBREF topology, map) and $(SUBREF topology, mapField)) |$(T2 ndIotaField, $(SUBREF topology, ndiota)) |$(T2 OrthogonalReduceField, $(SUBREF topology, orthogonalReduceField)) |$(T2 RepeatField, $(SUBREF topology, repeat)) |$(T2 SparseField, Used for mutable DOK sparse matrixes ) |) | | | |License: $(HTTP www.apache.org/licenses/LICENSE-2.0, Apache-2.0) |Copyright: 2020 Ilya Yaroshenko, Kaleidic Associates Advisory Limited, Symmetry Investments |Authors: Ilya Yaroshenko | |Macros: |SUBREF = $(REF_ALTTEXT $(TT $2), $2, mir, ndslice, $1)$(NBSP) |T2=$(TR $(TDNW $(LREF $1)) $(TD $+)) |+/ |module mir.ndslice.field; | |import mir.internal.utility: Iota; |import mir.math.common: optmath; |import mir.ndslice.internal; |import mir.qualifier; | |@optmath: | |package template ZeroShiftField(T) |{ | static if (hasZeroShiftFieldMember!T) | alias ZeroShiftField = typeof(T.init.assumeFieldsHaveZeroShift()); | else | alias ZeroShiftField = T; |} | |package enum hasZeroShiftFieldMember(T) = __traits(hasMember, T, "assumeFieldsHaveZeroShift"); | |package auto applyAssumeZeroShift(Types...)() |{ | import mir.ndslice.topology; 0000000| string str; | foreach(i, T; Types) | static if (hasZeroShiftFieldMember!T) 0000000| str ~= "_fields[" ~ i.stringof ~ "].assumeFieldsHaveZeroShift, "; | else 0000000| str ~= "_fields[" ~ i.stringof ~ "], "; 0000000| return str; |} | |auto MapField__map(Field, alias fun, alias fun1)(ref MapField!(Field, fun) f) |{ | import core.lifetime: move; | import mir.functional: pipe; 3| return MapField!(Field, pipe!(fun, fun1))(move(f._field)); |} | | |/++ |`MapField` is used by $(SUBREF topology, map). |+/ |struct MapField(Field, alias _fun) |{ |@optmath: | /// | Field _field; | | /// | auto lightConst()() const @property | { 32| return MapField!(LightConstOf!Field, _fun)(.lightConst(_field)); | } | | /// | auto lightImmutable()() immutable @property | { | return MapField!(LightImmutableOf!Field, _fun)(.lightImmutable(_field)); | } | | /++ | User defined constructor used by $(LREF mapField). | +/ | static alias __map(alias fun1) = MapField__map!(Field, _fun, fun1); | | auto ref opIndex(T...)(auto ref T index) | { | import mir.functional: RefTuple, unref; | static if (is(typeof(_field[index]) : RefTuple!K, K...)) | { 199| auto t = _field[index]; 199| return mixin("_fun(" ~ _iotaArgs!(K.length, "t.expand[", "].unref, ") ~ ")"); | } | else 1284| return _fun(_field[index]); | } | | static if (__traits(hasMember, Field, "length")) | auto length() const @property | { 0000000| return _field.length; | } | | static if (__traits(hasMember, Field, "shape")) | auto shape() const @property | { 0000000| return _field.shape; | } | | static if (__traits(hasMember, Field, "elementCount")) | auto elementCount() const @property | { 0000000| return _field.elementCount; | } | | static if (hasZeroShiftFieldMember!Field) | /// Defined if `Field` has member `assumeFieldsHaveZeroShift`. | auto assumeFieldsHaveZeroShift() @property | { | return _mapField!_fun(_field.assumeFieldsHaveZeroShift); | } |} | |/++ |`VmapField` is used by $(SUBREF topology, map). |+/ |struct VmapField(Field, Fun) |{ |@optmath: | /// | Field _field; | /// | Fun _fun; | | /// | auto lightConst()() const @property | { | return VmapField!(LightConstOf!Field, _fun)(.lightConst(_field)); | } | | /// | auto lightImmutable()() immutable @property | { | return VmapField!(LightImmutableOf!Field, _fun)(.lightImmutable(_field)); | } | | auto ref opIndex(T...)(auto ref T index) | { | import mir.functional: RefTuple, unref; | static if (is(typeof(_field[index]) : RefTuple!K, K...)) | { | auto t = _field[index]; | return mixin("_fun(" ~ _iotaArgs!(K.length, "t.expand[", "].unref, ") ~ ")"); | } | else | return _fun(_field[index]); | } | | static if (__traits(hasMember, Field, "length")) | auto length() const @property | { | return _field.length; | } | | static if (__traits(hasMember, Field, "shape")) | auto shape() const @property | { | return _field.shape; | } | | static if (__traits(hasMember, Field, "elementCount")) | auto elementCount()const @property | { | return _field.elementCount; | } | | static if (hasZeroShiftFieldMember!Field) | /// Defined if `Field` has member `assumeFieldsHaveZeroShift`. | auto assumeFieldsHaveZeroShift() @property | { | return _vmapField(_field.assumeFieldsHaveZeroShift, _fun); | } |} | |/+ |Creates a mapped field. Uses `__map` if possible. |+/ |auto _mapField(alias fun, Field)(Field field) |{ | import mir.functional: naryFun; | static if (( | __traits(isSame, fun, naryFun!"a|b") || | __traits(isSame, fun, naryFun!"a^b") || | __traits(isSame, fun, naryFun!"a&b") || | __traits(isSame, fun, naryFun!"a | b") || | __traits(isSame, fun, naryFun!"a ^ b") || | __traits(isSame, fun, naryFun!"a & b")) && | is(Field : ZipField!(BitField!(LeftField, I), BitField!(RightField, I)), LeftField, RightField, I)) | { | import mir.ndslice.topology: bitwiseField; 1| auto f = ZipField!(LeftField, RightField)(field._fields[0]._field, field._fields[1]._field)._mapField!fun; 1| return f.bitwiseField!(typeof(f), I); | } | else | static if (__traits(hasMember, Field, "__map")) 9| return Field.__map!fun(field); | else 20| return MapField!(Field, fun)(field); |} | |/+ |Creates a mapped field. Uses `__vmap` if possible. |+/ |auto _vmapField(Field, Fun)(Field field, Fun fun) |{ | static if (__traits(hasMember, Field, "__vmap")) | return Field.__vmap(field, fun); | else | return VmapField!(Field, Fun)(field, fun); |} | |/++ |Iterates multiple fields in lockstep. | |`ZipField` is used by $(SUBREF topology, zipFields). |+/ |struct ZipField(Fields...) | if (Fields.length > 1) |{ |@optmath: | import mir.functional: RefTuple, Ref, _ref; | import std.meta: anySatisfy; | | /// | Fields _fields; | | /// | auto lightConst()() const @property | { | import std.format; | import mir.ndslice.topology: iota; | import std.meta: staticMap; 0000000| return mixin("ZipField!(staticMap!(LightConstOf, Fields))(%(_fields[%s].lightConst,%)].lightConst)".format(_fields.length.iota)); | } | | /// | auto lightImmutable()() immutable @property | { | import std.format; | import mir.ndslice.topology: iota; | import std.meta: staticMap; | return mixin("ZipField!(staticMap!(LightImmutableOf, Fields))(%(_fields[%s].lightImmutable,%)].lightImmutable)".format(_fields.length.iota)); | } | | auto opIndex()(ptrdiff_t index) | { | alias Iterators = Fields; | alias _iterators = _fields; | import mir.ndslice.iterator: _zip_types, _zip_index; 0000000| return mixin("RefTuple!(_zip_types!Fields)(" ~ _zip_index!Fields ~ ")"); | } | | auto opIndexAssign(Types...)(RefTuple!(Types) value, ptrdiff_t index) | if (Types.length == Fields.length) | { | foreach(i, ref val; value.expand) | { | _fields[i][index] = val; | } | return opIndex(index); | } | | static if (anySatisfy!(hasZeroShiftFieldMember, Fields)) | /// Defined if at least one of `Fields` has member `assumeFieldsHaveZeroShift`. | auto assumeFieldsHaveZeroShift() @property | { | import std.meta: staticMap; | return mixin("ZipField!(staticMap!(ZeroShiftField, Fields))(" ~ applyAssumeZeroShift!Fields ~ ")"); | } |} | |/++ |`RepeatField` is used by $(SUBREF topology, repeat). |+/ |struct RepeatField(T) |{ | import std.traits: Unqual; | |@optmath: | alias UT = Unqual!T; | | /// | UT _value; | | /// | auto lightConst()() const @property @trusted | { 55| return RepeatField!(const T)(cast(UT) _value); | } | | /// | auto lightImmutable()() immutable @property @trusted | { | return RepeatField!(immutable T)(cast(UT) _value); | } | | auto ref T opIndex()(ptrdiff_t) @trusted 906| { return cast(T) _value; } |} | |/++ |`BitField` is used by $(SUBREF topology, bitwise). |+/ 2|struct BitField(Field, I = typeof(cast()Field.init[size_t.init])) | if (__traits(isUnsigned, I)) |{ |@optmath: | import mir.bitop: ctlz; | package(mir) alias E = I; | package(mir) enum shift = ctlz(I.sizeof) + 3; | | /// | Field _field; | | /// optimization for bitwise operations | auto __vmap(Fun : LeftOp!(op, bool), string op)(Fun fun) | if (op == "|" || op == "&" || op == "^") | { | import mir.ndslice.topology: bitwiseField; | return _vmapField(_field, RightOp!(op, I)(I(0) - fun.value)).bitwiseField; | } | | /// ditto | auto __vmap(Fun : RightOp!(op, bool), string op)(Fun fun) | if (op == "|" || op == "&" || op == "^") | { | import mir.ndslice.topology: bitwiseField; | return _vmapField(_field, RightOp!(op, I)(I(0) - fun.value)).bitwiseField; | } | | /// ditto | auto __vmap(Fun)(Fun fun) | { | return VmapField!(typeof(this), Fun)(this, fun); | } | | /// ditto | alias __map(alias fun) = BitField__map!(Field, I, fun); | | /// | auto lightConst()() const @property | { 8| return BitField!(LightConstOf!Field, I)(mir.qualifier.lightConst(_field)); | } | | /// | auto lightImmutable()() immutable @property | { | return BitField!(LightImmutableOf!Field, I)(mir.qualifier.lightImmutable(_field)); | } | | bool opIndex()(size_t index) | { | import mir.bitop: bt; 4202| return bt!(Field, I)(_field, index) != 0; | } | | bool opIndexAssign()(bool value, size_t index) | { | import mir.bitop: bta; 1210| bta!(Field, I)(_field, index, value); 1210| return value; | } | | static if (hasZeroShiftFieldMember!Field) | /// Defined if `Field` has member `assumeFieldsHaveZeroShift`. | auto assumeFieldsHaveZeroShift() @property | { | return BitField!(ZeroShiftField!Field, I)(_field.assumeFieldsHaveZeroShift); | } |} | |/// |version(mir_test) unittest |{ | import mir.ndslice.iterator: FieldIterator; 1| ushort[10] data; 1| auto f = FieldIterator!(BitField!(ushort*))(0, BitField!(ushort*)(data.ptr)); 1| f[123] = true; 1| f++; 1| assert(f[122]); |} | |auto BitField__map(Field, I, alias fun)(BitField!(Field, I) field) |{ | import core.lifetime: move; | import mir.functional: naryFun; | static if (__traits(isSame, fun, naryFun!"~a") || __traits(isSame, fun, naryFun!"!a")) | { | import mir.ndslice.topology: bitwiseField; 5| auto f = _mapField!(naryFun!"~a")(move(field._field)); 5| return f.bitwiseField!(typeof(f), I); | } | else | { 1| return MapField!(BitField!(Field, I), fun)(move(field)); | } |} | |/++ |`BitpackField` is used by $(SUBREF topology, bitpack). |+/ |struct BitpackField(Field, uint pack, I = typeof(cast()Field.init[size_t.init])) | if (__traits(isUnsigned, I)) |{ | //static assert(); |@optmath: | package(mir) alias E = I; | package(mir) enum mask = (I(1) << pack) - 1; | package(mir) enum bits = I.sizeof * 8; | | /// | Field _field; | | /// | auto lightConst()() const @property | { 0000000| return BitpackField!(LightConstOf!Field, pack)(.lightConst(_field)); | } | | /// | auto lightImmutable()() immutable @property | { | return BitpackField!(LightImmutableOf!Field, pack)(.lightImmutable(_field)); | } | | I opIndex()(size_t index) | { 25| index *= pack; 25| size_t start = index % bits; 25| index /= bits; 25| auto ret = (_field[index] >>> start) & mask; | static if (bits % pack) | { 25| sizediff_t end = start - (bits - pack); 25| if (end > 0) 7| ret ^= cast(I)(_field[index + 1] << (bits - end)) >>> (bits - pack); | } 25| return cast(I) ret; | } | | I opIndexAssign()(I value, size_t index) | { | import std.traits: Unsigned; 18| assert(cast(Unsigned!I)value <= mask); 18| index *= pack; 18| size_t start = index % bits; 18| index /= bits; 18| _field[index] = cast(I)((_field[index] & ~(mask << start)) ^ (value << start)); | static if (bits % pack) | { 18| sizediff_t end = start - (bits - pack); 18| if (end > 0) 5| _field[index + 1] = cast(I)((_field[index + 1] & ~((I(1) << end) - 1)) ^ (value >>> (pack - end))); | } 18| return value; | } | | static if (hasZeroShiftFieldMember!Field) | /// Defined if `Field` has member `assumeFieldsHaveZeroShift`. | auto assumeFieldsHaveZeroShift() @property | { | return BitpackField!(ZeroShiftField!Field, pack, I)(_field.assumeFieldsHaveZeroShift); | } |} | |/// |unittest |{ | import mir.ndslice.iterator: FieldIterator; 1| ushort[10] data; 1| auto f = FieldIterator!(BitpackField!(ushort*, 6))(0, BitpackField!(ushort*, 6)(data.ptr)); 1| f[0] = cast(ushort) 31; 1| f[1] = cast(ushort) 13; 1| f[2] = cast(ushort) 8; 1| f[3] = cast(ushort) 43; 1| f[4] = cast(ushort) 28; 1| f[5] = cast(ushort) 63; 1| f[6] = cast(ushort) 39; 1| f[7] = cast(ushort) 23; 1| f[8] = cast(ushort) 44; | 1| assert(f[0] == 31); 1| assert(f[1] == 13); 1| assert(f[2] == 8); 1| assert(f[3] == 43); 1| assert(f[4] == 28); 1| assert(f[5] == 63); 1| assert(f[6] == 39); 1| assert(f[7] == 23); 1| assert(f[8] == 44); 1| assert(f[9] == 0); 1| assert(f[10] == 0); 1| assert(f[11] == 0); |} | |unittest |{ | import mir.ndslice.slice; | import mir.ndslice.topology; | import mir.ndslice.sorting; 1| uint[2] data; 1| auto packed = data[].sliced.bitpack!18; 1| assert(packed.length == 3); 1| packed[0] = 5; 1| packed[1] = 3; 1| packed[2] = 2; 1| packed.sort; 1| assert(packed[0] == 2); 1| assert(packed[1] == 3); 1| assert(packed[2] == 5); |} | |/// |struct OrthogonalReduceField(FieldsIterator, alias fun, T) |{ | import mir.ndslice.slice: Slice; | |@optmath: | /// non empty slice | | Slice!FieldsIterator _fields; | | /// | T _initialValue; | | /// | auto lightConst()() const @property | { 1| auto fields = _fields.lightConst; 1| return OrthogonalReduceField!(fields.Iterator, fun, T)(fields, _initialValue); | } | | /// | auto lightImmutable()() immutable @property | { | auto fields = _fields.lightImmutable; | return OrthogonalReduceField!(fields.Iterator, fun, T)(fields, _initialValue); | } | | /// `r = fun(r, fields[i][index]);` reduction by `i` | auto opIndex()(size_t index) | { | import std.traits: Unqual; 100| auto fields = _fields; 100| T r = _initialValue; 100| if (!fields.empty) do | { 300| r = cast(T) fun(r, fields.front[index]); 300| fields.popFront; | } 300| while(!fields.empty); 100| return r; | } |} | |/// |struct CycleField(Field) |{ | import mir.ndslice.slice: Slice; | |@optmath: | /// Cycle length | size_t _length; | /// | Field _field; | | /// | auto lightConst()() const @property | { 2| auto field = .lightConst(_field); 2| return CycleField!(typeof(field))(_length, field); | } | | /// | auto lightImmutable()() immutable @property | { | auto field = .lightImmutable(_field); | return CycleField!(typeof(field))(_length, field); | } | | /// | auto ref opIndex()(size_t index) | { 14| return _field[index % _length]; | } | | /// | static if (!__traits(compiles, &opIndex(size_t.init))) | { | auto ref opIndexAssign(T)(auto ref T value, size_t index) | { | return _field[index % _length] = value; | } | } | | static if (hasZeroShiftFieldMember!Field) | /// Defined if `Field` has member `assumeFieldsHaveZeroShift`. | auto assumeFieldsHaveZeroShift() @property | { | return CycleField!(ZeroShiftField!Field)(_length, _field.assumeFieldsHaveZeroShift); | } |} | |/// |struct CycleField(Field, size_t length) |{ | import mir.ndslice.slice: Slice; | |@optmath: | /// Cycle length | enum _length = length; | /// | Field _field; | | /// | auto lightConst()() const @property | { 2| auto field = .lightConst(_field); 2| return CycleField!(typeof(field), _length)(field); | } | | /// | auto lightImmutable()() immutable @property | { | auto field = .lightImmutable(_field); | return CycleField!(typeof(field), _length)(field); | } | | /// | auto ref opIndex()(size_t index) | { 14| return _field[index % _length]; | } | | /// | static if (!__traits(compiles, &opIndex(size_t.init))) | { | auto ref opIndexAssign(T)(auto ref T value, size_t index) | { | return _field[index % _length] = value; | } | } | | static if (hasZeroShiftFieldMember!Field) | /// Defined if `Field` has member `assumeFieldsHaveZeroShift`. | auto assumeFieldsHaveZeroShift() @property | { | return CycleField!(ZeroShiftField!Field, _length)(_field.assumeFieldsHaveZeroShift); | } |} | |/++ |`ndIotaField` is used by $(SUBREF topology, ndiota). |+/ |struct ndIotaField(size_t N) | if (N) |{ |@optmath: | /// | size_t[N - 1] _lengths; | | /// | auto lightConst()() const @property | { 59| return ndIotaField!N(_lengths); | } | | /// | auto lightImmutable()() const @property | { | return ndIotaField!N(_lengths); | } | | /// | size_t[N] opIndex()(size_t index) const | { 777| size_t[N] indices; | foreach_reverse (i; Iota!(N - 1)) | { 1151| indices[i + 1] = index % _lengths[i]; 1151| index /= _lengths[i]; | } 777| indices[0] = index; 777| return indices; | } |} | |/++ |`LinspaceField` is used by $(SUBREF topology, linspace). |+/ |struct LinspaceField(T) |{ | /// | size_t _length; | | /// | T _start = cast(T) 0, _stop = cast(T) 0; | | /// | auto lightConst()() scope const @property | { 29| return LinspaceField!T(_length, _start, _stop); | } | | /// | auto lightImmutable()() scope const @property | { | return LinspaceField!T(_length, _start, _stop); | } | | // no fastmath | /// | T opIndex()(sizediff_t index) scope const | { 1148| sizediff_t d = _length - 1; 1148| auto v = typeof(T.init.re)(d - index); 1148| auto w = typeof(T.init.re)(index); 1148| v /= d; 1148| w /= d; 1148| auto a = v * _start; 1148| auto b = w * _stop; 1148| return a + b; | } | |@optmath: | | /// | size_t length(size_t dimension = 0)() scope const @property | if (dimension == 0) | { 26| return _length; | } | | /// | size_t[1] shape()() scope const @property @nogc | { 8| return [_length]; | } |} | |/++ |Magic square field. |+/ |struct MagicField |{ |@optmath: |@safe pure nothrow @nogc: | | /++ | Magic Square size. | +/ | size_t _n; | |scope const: | | /// | MagicField lightConst()() @property | { 45| return this; | } | | /// | MagicField lightImmutable()() @property | { | return this; | } | | /// | size_t length(size_t dimension = 0)() @property | if(dimension <= 2) | { 25| return _n * _n; | } | | /// | size_t[1] shape() @property | { 0000000| return [_n * _n]; | } | | /// | size_t opIndex(size_t index) | { | pragma(inline, false); 9224| auto d = index / _n; 9224| auto m = index % _n; 9224| if (_n & 1) | { | //d = _n - 1 - d; // MATLAB synchronization | //index = d * _n + m; // ditto 4922| auto r = (index + 1 - d + (_n - 3) / 2) % _n; 4922| auto c = (_n * _n - index + 2 * d) % _n; 4922| return r * _n + c + 1; | } | else 4302| if ((_n & 2) == 0) | { 1880| auto a = (d + 1) & 2; 1880| auto b = (m + 1) & 2; 3760| return a != b ? index + 1: _n * _n - index; | } | else | { 2422| auto n = _n / 2 ; 2422| size_t shift; 2422| ptrdiff_t q; 2422| ptrdiff_t p = m - n; 2422| if (p >= 0) | { 1211| m = p; 1211| shift = n * n; 1211| auto mul = m <= n / 2 + 1; 1211| q = d - n; 1211| if (q >= 0) | { 605| d = q; 605| mul = !mul; | } 1211| if (mul) | { 606| shift *= 2; | } | } | else | { 1211| auto mul = m < n / 2; 1211| q = d - n; 1211| if (q >= 0) | { 605| d = q; 605| mul = !mul; | } 1492| if (d == n / 2 && (m == 0 || m == n / 2)) | { 51| mul = !mul; | } 1211| if (mul) | { 606| shift = n * n * 3; | } | } 2422| index = d * n + m; 2422| auto r = (index + 1 - d + (n - 3) / 2) % n; 2422| auto c = (n * n - index + 2 * d) % n; 2422| return r * n + c + 1 + shift; | } | } |} | |/++ |`SparseField` is used to represent Sparse ndarrays in mutable DOK format. |+/ |struct SparseField(T) |{ | /// | T[size_t] _table; | | /// | auto lightConst()() const @trusted | { | return SparseField!(const T)(cast(const(T)[size_t])_table); | } | | /// | auto lightImmutable()() immutable @trusted | { | return SparseField!(immutable T)(cast(immutable(T)[size_t])_table); | } | | /// | T opIndex()(size_t index) | { | import std.traits: isScalarType; | static if (isScalarType!T) | return _table.get(index, cast(T)0); | else | return _table.get(index, null); | } | | /// | T opIndexAssign()(T value, size_t index) | { | import std.traits: isScalarType; | static if (isScalarType!T) | { | if (value != 0) | _table[index] = value; | else | _table.remove(index); | } | else | { | if (value !is null) | _table[index] = value; | else | _table.remove(index); | } | return value; | } | | /// | T opIndexUnary(string op)(size_t index) | if (op == `++` || op == `--`) | { | import std.traits: isScalarType; | mixin (`auto value = ` ~ op ~ `_table[index];`); | static if (isScalarType!T) | { | if (value == 0) | _table.remove(index); | } | else | { | if (value is null) | _table.remove(index); | } | return value; | } | | /// | T opIndexOpAssign(string op)(T value, size_t index) | if (op == `+` || op == `-`) | { | import std.traits: isScalarType; | mixin (`value = _table[index] ` ~ op ~ `= value;`); // this works | static if (isScalarType!T) | { | if (value == 0) | _table.remove(index); | } | else | { | if (value is null) | _table.remove(index); | } | return value; | } |} source/mir/ndslice/field.d is 92% covered <<<<<< EOF # path=./source-mir-algebraic_alias-json.lst |/++ |$(H1 Mutable JSON value) | |This module contains a single alias definition and doesn't provide JSON serialization API. | |See_also: JSON libraries $(MIR_PACKAGE mir-ion) and $(MIR_PACKAGE asdf); | |License: $(HTTP www.apache.org/licenses/LICENSE-2.0, Apache-2.0) |Authors: Ilya Yaroshenko |Macros: |+/ |module mir.algebraic_alias.json; | |import mir.algebraic: TaggedVariant, This; |public import mir.string_map: StringMap; | |/++ |Definition union for $(LREF JsonAlgebraic). |+/ |union JsonAlgebraicUnion |{ | /// | typeof(null) null_; | /// | bool boolean; | /// | long integer; | /// | double float_; | /// | immutable(char)[] string; | /// Self alias in array. | This[] array; | /// Self alias in $(MREF mir,string_map). | StringMap!This object; |} | |/++ |JSON tagged algebraic alias. | |The example below shows only the basic features. Advanced API to work with algebraic types can be found at $(GMREF mir-core, mir,algebraic). |See also $(MREF mir,string_map) - ordered string-value associative array. |+/ |alias JsonAlgebraic = TaggedVariant!JsonAlgebraicUnion; | |/// |unittest |{ | import mir.ndslice.topology: map; | import mir.array.allocation: array; | 1| JsonAlgebraic value; | 1| StringMap!JsonAlgebraic object; | | // Default 1| assert(value.isNull); 1| assert(value.kind == JsonAlgebraic.Kind.null_); | | // Boolean 1| value = object["bool"] = true; 1| assert(!value.isNull); 1| assert(value == true); 1| assert(value.kind == JsonAlgebraic.Kind.boolean); 1| assert(value.get!bool == true); 1| assert(value.get!(JsonAlgebraic.Kind.boolean) == true); | | // Null 1| value = object["null"] = null; 1| assert(value.isNull); 1| assert(value == null); 1| assert(value.kind == JsonAlgebraic.Kind.null_); 1| assert(value.get!(typeof(null)) == null); 1| assert(value.get!(JsonAlgebraic.Kind.null_) == null); | | // String 1| value = object["string"] = "s"; 1| assert(value.kind == JsonAlgebraic.Kind.string); 1| assert(value == "s"); 1| assert(value.get!string == "s"); 1| assert(value.get!(JsonAlgebraic.Kind.string) == "s"); | | // Integer 1| value = object["integer"] = 4; 1| assert(value.kind == JsonAlgebraic.Kind.integer); 1| assert(value == 4); 1| assert(value != 4.0); 1| assert(value.get!long == 4); 1| assert(value.get!(JsonAlgebraic.Kind.integer) == 4); | | // Float 1| value = object["float"] = 3.0; 1| assert(value.kind == JsonAlgebraic.Kind.float_); 1| assert(value != 3); 1| assert(value == 3.0); 1| assert(value.get!double == 3.0); 1| assert(value.get!(JsonAlgebraic.Kind.float_) == 3.0); | | // Array 1| JsonAlgebraic[] arr = [0, 1, 2, 3, 4].map!JsonAlgebraic.array; | 1| value = object["array"] = arr; 1| assert(value.kind == JsonAlgebraic.Kind.array); 1| assert(value == arr); 1| assert(value.get!(JsonAlgebraic[])[3] == 3); | | // Object 1| assert(object.keys == ["bool", "null", "string", "integer", "float", "array"]); 1| object.values[0] = "false"; 1| assert(object["bool"] == "false"); // it is a string now 1| object.remove("bool"); // remove the member | 1| value = object["array"] = object; 1| assert(value.kind == JsonAlgebraic.Kind.object); 1| assert(value.get!(StringMap!JsonAlgebraic).keys is object.keys); 1| assert(value.get!(StringMap!JsonAlgebraic).values is object.values); | 1| JsonAlgebraic[string] aa = object.toAA; 1| object = StringMap!JsonAlgebraic(aa); | 1| JsonAlgebraic fromAA = ["a" : JsonAlgebraic(3), "b" : JsonAlgebraic("b")]; 1| assert(fromAA.get!(StringMap!JsonAlgebraic)["a"] == 3); 1| assert(fromAA.get!(StringMap!JsonAlgebraic)["b"] == "b"); |} source/mir/algebraic_alias/json.d is 100% covered <<<<<< EOF # path=./..-..-..-.dub-packages-mir-core-1.1.83-mir-core-source-mir-conv.lst |/++ |Conversion utilities. | |License: $(HTTP www.apache.org/licenses/LICENSE-2.0, Apache-2.0) |Authors: Ilya Yaroshenko |+/ |module mir.conv; | |public import core.lifetime: emplace; | |import std.traits; | |private template isMirString(T) |{ | static if (isSomeString!T) | { | enum isMirString = true; | } | else | { | static if (__traits(compiles, {import mir.small_string;})) | { | import mir.small_string; | enum isMirString = is(T : SmallString!size, size_t size); | } | else | { | enum isMirString = false; | } | } |} | |/++ |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 "unsafe", i.e., |it does not check for overflow. |+/ |template to(T) |{ | /// | auto ref T to(A...)(auto ref A args) | if (A.length > 0) | { | import mir.utility; | import mir.functional: forward; | static if (A.length == 1 && isImplicitlyConvertible!(A[0], T)) | return args[0]; | else | static if (is(T == class) && is(typeof(new T(forward!args)))) | return new T(forward!args); | else | static if (is(typeof(T(args)))) | return T(forward!args); | else | static if (A.length == 1) | { | alias I = A[0]; | alias arg = args[0]; | static if (is(typeof(cast(T) arg)) && !(isDynamicArray!T && isDynamicArray!I) && !isSomeString!T) | return cast(T) forward!arg; | else | static if (isSomeString!I && is(T == enum)) | { | import mir.enums; | uint index = void; | if (getEnumIndexFromKey!T(arg, index)._expect(true)) | return index.unsafeEnumFromIndex!T; | static immutable msg = "Can not convert string to the enum " ~ T.stringof; | version (D_Exceptions) | { | static immutable Exception exc = new Exception(msg); | throw exc; | } | else | { | assert(0, msg); | } | } | else | static if (is(I == enum) && isSomeString!T) | { | import mir.enums; | uint id = void; | if (getEnumIndex(arg, id)._expect(true)) | return enumStrings!I[id]; | assert(0); | } | else | static if (isMirString!I && !isSomeString!T) | { | static assert (__traits(compiles, { import mir.parse: fromString; })); | import mir.parse: fromString; | return fromString!(Unqual!T)(arg[]); | } | else | static if (!isSomeString!I && isMirString!T) | { | // static if (is(Unqual!I == typeof(null))) | // { | // enum immutable(T) ret = "null"; | // static if (isImplicitlyConvertible!(immutable T, T)) | // return ret; | // else | // return .to!T(ret[]); | // } | // else | static if (is(Unqual!I == bool)) | { | enum immutable(T) t = "true"; | enum immutable(T) f = "false"; | auto ret = arg ? t : f; | static if (isImplicitlyConvertible!(immutable T, T)) | return ret; | else | return .to!T(ret[]); | } | else | { | static if (isImplicitlyConvertible!(T, string) && __traits(compiles, () @safe {const(char)[] s = arg.toString; return s;})) | { | auto ret = arg.toString; | static if (is(typeof(ret) : string)) | return ret; | else | return ret.idup; | } | else | { | static assert (__traits(compiles, { import mir.format: print; })); | import mir.format: print; | static if (isSomeString!T) | { | static if (isNumeric!I) | { | import mir.appender: UnsafeArrayBuffer; | alias C = Unqual!(ForeachType!T); | C[64] array = '\0'; | auto buffer = UnsafeArrayBuffer!C(array); | } | else | { | import mir.appender: ScopedBuffer; | ScopedBuffer!(Unqual!(ForeachType!T)) buffer; | } | buffer.print(arg); | static if (isMutable!(ForeachType!(T))) | return buffer.data.dup; | else | return buffer.data.idup; | } | else | { | Unqual!T buffer; | buffer.print(arg); | return buffer; | } | } | } | } | else | static if (is(I : const(C)[], C) && is(T : immutable(C)[])) | { | static if (is(I : immutable(C)[])) | return arg; | else | return idup(arg); | } | else | static if (is(I : const(D)[], D) && is(T : D[])) | { | static if (is(I : D[])) | return arg; | else | return dup(arg); | } | else | static assert(0); | } | else | static assert(0); | } |} | |/// |@safe pure @nogc |version(mir_core_test) unittest |{ | enum E | { | A, | B, | C, | } | | assert(to!E("B") == E.B); | assert(to!string(E.B) == "B"); | assert(to!string(null) is null); | assert(to!string(true) == "true"); | assert(to!string(false) == "false"); | | enum S : wstring | { | a = "A", | b = "B", | } | | assert(to!wstring(S.b) == "B"w); | assert(to!S("B"w) == S.b); |} | |/++ |Emplace helper function. |+/ |void emplaceInitializer(T)(scope ref T chunk) @trusted pure nothrow | |{ | // Emplace T.init. | // Previously, an immutable static and memcpy were used to hold an initializer. | // With improved unions, this is no longer needed. | union UntypedInit | { | T dummy; | } | static struct UntypedStorage | { | align(T.alignof) void[T.sizeof] dummy; | } | | () @trusted { | *cast(UntypedStorage*) &chunk = cast(UntypedStorage) UntypedInit.init; | } (); |} | |/++ |+/ |T[] uninitializedFillDefault(T)(return scope T[] array) nothrow @nogc |{ | static if (__VERSION__ < 2083) | { | static if (__traits(isIntegral, T) && 0 == cast(T) (T.init + 1)) | { | import core.stdc.string : memset; | memset(array.ptr, 0xff, T.sizeof * array.length); | return array; | } | else | { | pragma(inline, false); | foreach(ref e; array) | emplaceInitializer(e); | return array; | } | } | else | { | static if (__traits(isZeroInit, T)) | { | import core.stdc.string : memset; | memset(array.ptr, 0, T.sizeof * array.length); | return array; | } | else static if (__traits(isIntegral, T) && 0 == cast(T) (T.init + 1)) | { | import core.stdc.string : memset; | memset(array.ptr, 0xff, T.sizeof * array.length); | return array; | } | else | { | pragma(inline, false); | foreach(ref e; array) | emplaceInitializer(e); | return array; | } | } |} | |/// |pure nothrow @nogc @system |version(mir_core_test) 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 |version(mir_core_test) unittest |{ | int[] a = [1, 2, 4]; | uninitializedFillDefault(a); | assert(a == [0, 0, 0]); |} | |/++ |Destroy structs and unions usnig `__xdtor` member if any. |Do nothing for other types. |+/ |void xdestroy(T)(scope T[] ar) |{ | static if ((is(T == struct) || is(T == union)) && __traits(hasMember, T, "__xdtor")) | { | static if (__traits(isSame, T, __traits(parent, ar[0].__xdtor))) | { | pragma(inline, false); | foreach_reverse (ref e; ar) | e.__xdtor(); | } | } |} | |/// |nothrow @nogc version(mir_core_test) unittest |{ | __gshared int d; | __gshared int c; | struct D { ~this() nothrow @nogc {d++;} } | extern(C++) | struct C { ~this() nothrow @nogc {c++;} } | C[2] carray; | D[2] darray; | carray.xdestroy; | darray.xdestroy; | assert(c == 2); | assert(d == 2); | c = 0; | d = 0; |} | | |template emplaceRef(T) |{ | void emplaceRef(UT, Args...)(ref UT chunk, auto ref Args args) | { | import core.lifetime: forward; | import core.internal.lifetime: emplaceRef; | return emplaceRef!T(chunk, forward!args); | } |} | |void emplaceRef(UT, Args...)(ref UT chunk, auto ref Args args) |if (is(UT == Unqual!UT)) |{ | import core.lifetime: forward; | emplaceRef!UT(chunk, forward!args); |} ../../../.dub/packages/mir-core-1.1.83/mir-core/source/mir/conv.d has no code <<<<<< EOF # path=./source-mir-ndslice-iterator.lst |/++ |This is a submodule of $(MREF mir,ndslice). | |Iterator is a type with a pointer like behavior. |An ndslice can be created on top of an iterator using $(SUBREF slice, sliced). | |$(BOOKTABLE $(H2 Iterators), |$(TR $(TH Iterator Name) $(TH Used By)) |$(T2 BytegroupIterator, $(SUBREF topology, bytegroup).) |$(T2 CachedIterator, $(SUBREF topology, cached), $(SUBREF topology, cachedGC).) |$(T2 ChopIterator, $(SUBREF topology, chopped)) |$(T2 FieldIterator, $(SUBREF slice, slicedField), $(SUBREF topology, bitwise), $(SUBREF topology, ndiota), and others.) |$(T2 FlattenedIterator, $(SUBREF topology, flattened)) |$(T2 IndexIterator, $(SUBREF topology, indexed)) |$(T2 IotaIterator, $(SUBREF topology, iota)) |$(T2 MapIterator, $(SUBREF topology, map)) |$(T2 MemberIterator, $(SUBREF topology, member)) |$(T2 NeighboursIterator, $(SUBREF topology, withNeighboursSum)) |$(T2 RetroIterator, $(SUBREF topology, retro)) |$(T2 SliceIterator, $(SUBREF topology, map) in composition with $(LREF MapIterator) for packed slices.) |$(T2 SlideIterator, $(SUBREF topology, diff), $(SUBREF topology, pairwise), and $(SUBREF topology, slide).) |$(T2 StairsIterator, $(SUBREF topology, stairs)) |$(T2 StrideIterator, $(SUBREF topology, stride)) |$(T2 SubSliceIterator, $(SUBREF topology, subSlices)) |$(T2 TripletIterator, $(SUBREF topology, triplets)) |$(T2 ZipIterator, $(SUBREF topology, zip)) |) | |License: $(HTTP www.apache.org/licenses/LICENSE-2.0, Apache-2.0) |Copyright: 2020 Ilya Yaroshenko, Kaleidic Associates Advisory Limited, Symmetry Investments |Authors: Ilya Yaroshenko | |Macros: |SUBREF = $(REF_ALTTEXT $(TT $2), $2, mir, ndslice, $1)$(NBSP) |T2=$(TR $(TDNW $(LREF $1)) $(TD $+)) |+/ |module mir.ndslice.iterator; | |import mir.internal.utility: Iota; |import mir.math.common: optmath; |import mir.ndslice.field; |import mir.ndslice.internal; |import mir.ndslice.slice: SliceKind, Slice, Universal, Canonical, Contiguous, isSlice; |import mir.qualifier; |import mir.conv; |import std.traits; | |private static immutable assumeZeroShiftExceptionMsg = "*.assumeFieldsHaveZeroShift: shift is not zero!"; |version(D_Exceptions) | private static immutable assumeZeroShiftException = new Exception(assumeZeroShiftExceptionMsg); | |@optmath: | |enum std_ops = q{ | void opUnary(string op)() scope | if (op == "--" || op == "++") | { mixin(op ~ "_iterator;"); } | | void opOpAssign(string op)(ptrdiff_t index) scope | if (op == "-" || op == "+") | { mixin("_iterator " ~ op ~ "= index;"); } | | auto opBinary(string op)(ptrdiff_t index) | if (op == "+" || op == "-") | { | auto ret = this; | mixin(`ret ` ~ op ~ `= index;`); | return ret; | } | | ptrdiff_t opBinary(string op : "-")(scope ref const typeof(this) right) scope const | { return this._iterator - right._iterator; } | | bool opEquals()(scope ref const typeof(this) right) scope const | { return this._iterator == right._iterator; } | | ptrdiff_t opCmp()(scope ref const typeof(this) right) scope const | { | static if (isPointer!Iterator) | return this._iterator - right._iterator; | else | return this._iterator.opCmp(right._iterator); | } |}; | |/++ |Step counter. | |`IotaIterator` is used by $(SUBREF topology, iota). |+/ |struct IotaIterator(I) | if (isIntegral!I || isPointer!I) |{ |@optmath: | | /// | I _index; | | static if (isPointer!I) | /// | auto lightConst()() const @property | { | static if (isIntegral!I) | return IotaIterator!I(_index); | else 0000000| return IotaIterator!(LightConstOf!I)(_index); | } | | static if (isPointer!I) | /// | auto lightImmutable()() immutable @property | { | static if (isIntegral!I) | return IotaIterator!I(_index); | else | return IotaIterator!(LightImmutableOf!I)(_index); | } | |pure: | | I opUnary(string op : "*")() 56646| { return _index; } | | void opUnary(string op)() | if (op == "--" || op == "++") | { mixin(op ~ `_index;`); } | | I opIndex()(ptrdiff_t index) const 5696| { return cast(I)(_index + index); } | | void opOpAssign(string op)(ptrdiff_t index) | if (op == `+` || op == `-`) | { mixin(`_index ` ~ op ~ `= index;`); } | | auto opBinary(string op)(ptrdiff_t index) | if (op == "+" || op == "-") | { 153| auto ret = this; | mixin(`ret ` ~ op ~ `= index;`); 153| return ret; | } | | ptrdiff_t opBinary(string op : "-")(const typeof(this) right) const 6| { return cast(ptrdiff_t)(this._index - right._index); } | | bool opEquals()(const typeof(this) right) const 166| { return this._index == right._index; } | | auto opCmp()(const typeof(this) right) const 6| { return this._index - right._index; } |} | |/// |@safe pure nothrow @nogc version(mir_test) unittest |{ 1| IotaIterator!int iota; 1| assert(*iota == 0); | | // iteration 1| ++iota; 1| assert(*iota == 1); | 1| assert(iota[2] == 3); 1| assert(iota[-1] == 0); | 1| --iota; 1| assert(*iota == 0); | | // opBinary 1| assert(*(iota + 2) == 2); 1| assert(*(iota - 3) == -3); 1| assert((iota - 3) - iota == -3); | | // construction 1| assert(*IotaIterator!int(3) == 3); 1| assert(iota - 1 < iota); |} | |/// |pure nothrow @nogc version(mir_test) unittest |{ 1| int[32] data; 1| auto iota = IotaIterator!(int*)(data.ptr); 1| assert(*iota == data.ptr); | | // iteration 1| ++iota; 1| assert(*iota == 1 + data.ptr); | 1| assert(iota[2] == 3 + data.ptr); 1| assert(iota[-1] == 0 + data.ptr); | 1| --iota; 1| assert(*iota == 0 + data.ptr); | | // opBinary 1| assert(*(iota + 2) == 2 + data.ptr); 1| assert(*(iota - 3) == -3 + data.ptr); 1| assert((iota - 3) - iota == -3); | | // construction 1| assert(*IotaIterator!(int*)(data.ptr) == data.ptr); 1| assert(iota - 1 < iota); |} | |auto RetroIterator__map(Iterator, alias fun)(ref RetroIterator!Iterator it) |{ 4| auto iterator = it._iterator._mapIterator!fun; 4| return RetroIterator!(typeof(iterator))(iterator); |} | |version(mir_test) unittest |{ | import mir.ndslice.topology; | import mir.ndslice.allocation; 10| auto v = iota(9).retro.map!(a => a).slice; 1| uint r; 19| auto w = iota(9).retro.map!(a => a).map!(a => a * r).slice; |} | |/++ |Reverse directions for an iterator. | |`RetroIterator` is used by $(SUBREF topology, retro). |+/ 4|struct RetroIterator(Iterator) |{ |@optmath: | /// | Iterator _iterator; | | /// | auto lightConst()() const @property | { 81| return RetroIterator!(LightConstOf!Iterator)(.lightConst(_iterator)); | } | | /// | auto lightImmutable()() immutable @property | { | return RetroIterator!(LightImmutableOf!Iterator)(.lightImmutable(_iterator)); | } | | /// | static alias __map(alias fun) = RetroIterator__map!(Iterator, fun); | | auto ref opUnary(string op : "*")() 4504| { return *_iterator; } | | void opUnary(string op : "--")() 1| { ++_iterator; } | | void opUnary(string op : "++")() pure 4163| { --_iterator; } | | auto ref opIndex()(ptrdiff_t index) 483| { return _iterator[-index]; } | | void opOpAssign(string op : "-")(ptrdiff_t index) scope 3| { _iterator += index; } | | void opOpAssign(string op : "+")(ptrdiff_t index) scope 83| { _iterator -= index; } | | auto opBinary(string op)(ptrdiff_t index) | if (op == "+" || op == "-") | { 37| auto ret = this; | mixin(`ret ` ~ op ~ `= index;`); 37| return ret; | } | | ptrdiff_t opBinary(string op : "-")(scope ref const typeof(this) right) scope const 1| { return right._iterator - this._iterator; } | | bool opEquals()(scope ref const typeof(this) right) scope const 0000000| { return right._iterator == this._iterator; } | | ptrdiff_t opCmp()(scope ref const typeof(this) right) scope const | { | static if (isPointer!Iterator) 0000000| return right._iterator - this._iterator; | else 1| return right._iterator.opCmp(this._iterator); | } |} | |/// |@safe pure nothrow @nogc version(mir_test) unittest |{ 1| IotaIterator!int iota; 1| RetroIterator!(IotaIterator!int) retro; | 1| ++iota; 1| --retro; 1| assert(*retro == *iota); | 1| --iota; 1| ++retro; 1| assert(*retro == *iota); | 1| assert(retro[-7] == iota[7]); | 1| iota += 100; 1| retro -= 100; 1| assert(*retro == *iota); | 1| iota -= 100; 1| retro += 100; 1| assert(*retro == *iota); | 1| assert(*(retro + 10) == *(iota - 10)); | 1| assert(retro - 1 < retro); | 1| assert((retro - 5) - retro == -5); | 1| iota = IotaIterator!int(3); 1| retro = RetroIterator!(IotaIterator!int)(iota); 1| assert(*retro == *iota); |} | |auto StrideIterator__map(Iterator, alias fun)(StrideIterator!Iterator it) |{ 3| auto iterator = it._iterator._mapIterator!fun; 3| return StrideIterator!(typeof(iterator))(it._stride, iterator); |} | |version(mir_test) unittest |{ | import mir.ndslice.topology; | import mir.ndslice.allocation; 4| auto v = iota([3], 0, 3).map!(a => a).slice; 1| uint r; 7| auto w = iota([3], 0, 3).map!(a => a).map!(a => a * r).slice; |} | |/++ |Iterates an iterator with a fixed strides. | |`StrideIterator` is used by $(SUBREF topology, stride). |+/ |struct StrideIterator(Iterator) |{ |@optmath: | /// | ptrdiff_t _stride; | /// | Iterator _iterator; | | /// | auto lightConst()() const @property | { 42| return StrideIterator!(LightConstOf!Iterator)(_stride, .lightConst(_iterator)); | } | | /// | auto lightImmutable()() immutable @property | { | return StrideIterator!(LightImmutableOf!Iterator)(_stride, .lightImmutable(_iterator)); | } | | /// | static alias __map(alias fun) = StrideIterator__map!(Iterator, fun); | | auto ref opUnary(string op : "*")() 267| { return *_iterator; } | | void opUnary(string op)() scope | if (op == "--" || op == "++") | { mixin("_iterator " ~ op[0] ~ "= _stride;"); } | | auto ref opIndex()(ptrdiff_t index) 2675| { return _iterator[index * _stride]; } | | void opOpAssign(string op)(ptrdiff_t index) scope | if (op == "-" || op == "+") | { mixin("_iterator " ~ op ~ "= index * _stride;"); } | | auto opBinary(string op)(ptrdiff_t index) | if (op == "+" || op == "-") | { 3| auto ret = this; | mixin(`ret ` ~ op ~ `= index;`); 3| return ret; | } | | ptrdiff_t opBinary(string op : "-")(scope ref const typeof(this) right) scope const 1| { return (this._iterator - right._iterator) / _stride; } | | bool opEquals()(scope ref const typeof(this) right) scope const 0000000| { return this._iterator == right._iterator; } | | ptrdiff_t opCmp()(scope ref const typeof(this) right) scope const | { | static if (isPointer!Iterator) 0000000| ptrdiff_t ret = this._iterator - right._iterator; | else 1| ptrdiff_t ret = this._iterator.opCmp(right._iterator); 2| return _stride >= 0 ? ret : -ret; | } |} | |/// |@safe pure nothrow @nogc version(mir_test) unittest |{ 1| IotaIterator!int iota; 1| StrideIterator!(IotaIterator!int) stride; 1| stride._stride = -3; | 1| iota -= stride._stride; 1| --stride; 1| assert(*stride == *iota); | 1| iota += stride._stride; 1| ++stride; 1| assert(*stride == *iota); | 1| assert(stride[7] == iota[7 * stride._stride]); | 1| iota -= 100 * stride._stride; 1| stride -= 100; 1| assert(*stride == *iota); | 1| iota += 100 * stride._stride; 1| stride += 100; 1| assert(*stride == *iota); | 1| assert(*(stride + 10) == *(iota + 10 * stride._stride)); | 1| assert(stride - 1 < stride); | 1| assert((stride - 5) - stride == -5); | 1| iota = IotaIterator!int(3); 1| stride = StrideIterator!(IotaIterator!int)(3, iota); 1| assert(*stride == *iota); |} | |auto StrideIterator__map(Iterator, size_t factor, alias fun)(StrideIterator!(Iterator, factor) it) |{ | auto iterator = it._iterator._mapIterator!fun; | return StrideIterator!(typeof(iterator), factor)(iterator); |} | |/++ |Iterates an iterator with a fixed strides. | |`StrideIterator` is used by $(SUBREF topology, stride). |+/ |struct StrideIterator(Iterator, ptrdiff_t factor) |{ |@optmath: | /// | enum _stride = factor; | | /// | Iterator _iterator; | | /// | auto lightConst()() const @property | { 6| return StrideIterator!(LightConstOf!Iterator, _stride)(.lightConst(_iterator)); | } | | /// | auto lightImmutable()() immutable @property | { | return StrideIterator!(LightImmutableOf!Iterator, _stride)(.lightImmutable(_iterator)); | } | | /// | static alias __map(alias fun) = StrideIterator__map!(Iterator, _stride, fun); | | auto ref opUnary(string op : "*")() 43| { return *_iterator; } | | void opUnary(string op)() scope | if (op == "--" || op == "++") | { mixin("_iterator " ~ op[0] ~ "= _stride;"); } | | auto ref opIndex()(ptrdiff_t index) 1| { return _iterator[index * _stride]; } | | void opOpAssign(string op)(ptrdiff_t index) scope | if (op == "-" || op == "+") | { mixin("_iterator " ~ op ~ "= index * _stride;"); } | | auto opBinary(string op)(ptrdiff_t index) | if (op == "+" || op == "-") | { 3| auto ret = this; | mixin(`ret ` ~ op ~ `= index;`); 3| return ret; | } | | ptrdiff_t opBinary(string op : "-")(scope ref const typeof(this) right) scope const 1| { return (this._iterator - right._iterator) / _stride; } | | bool opEquals()(scope ref const typeof(this) right) scope const 0000000| { return this._iterator == right._iterator; } | | ptrdiff_t opCmp()(scope ref const typeof(this) right) scope const | { | static if (isPointer!Iterator) 0000000| ptrdiff_t ret = this._iterator - right._iterator; | else 1| ptrdiff_t ret = this._iterator.opCmp(right._iterator); 1| return _stride >= 0 ? ret : -ret; | } |} | |/// |@safe pure nothrow @nogc version(mir_test) unittest |{ 1| IotaIterator!int iota; 1| StrideIterator!(IotaIterator!int, -3) stride; | 1| iota -= stride._stride; 1| --stride; 1| assert(*stride == *iota); | 1| iota += stride._stride; 1| ++stride; 1| assert(*stride == *iota); | 1| assert(stride[7] == iota[7 * stride._stride]); | 1| iota -= 100 * stride._stride; 1| stride -= 100; 1| assert(*stride == *iota); | 1| iota += 100 * stride._stride; 1| stride += 100; 1| assert(*stride == *iota); | 1| assert(*(stride + 10) == *(iota + 10 * stride._stride)); | 1| assert(stride - 1 < stride); | 1| assert((stride - 5) - stride == -5); |} | |package template _zip_types(Iterators...) |{ | alias AliasSeq(T...) = T; | static if (Iterators.length) | { | enum i = Iterators.length - 1; | alias T = typeof(Iterators[i].init[sizediff_t.init]); | static if (__traits(compiles, &Iterators[i].init[sizediff_t.init])) | { | import mir.functional: Ref; | alias _zip_types = AliasSeq!(_zip_types!(Iterators[0 .. i]), Ref!T); | } | else | alias _zip_types = AliasSeq!(_zip_types!(Iterators[0 .. i]), T); | } | else | alias _zip_types = AliasSeq!(); |} | |package template _zip_fronts(Iterators...) |{ | static if (Iterators.length) | { | enum i = Iterators.length - 1; | static if (__traits(compiles, &Iterators[i].init[sizediff_t.init])) | enum _zip_fronts = _zip_fronts!(Iterators[0 .. i]) ~ "_ref(*_iterators[" ~ i.stringof ~ "]), "; | else | enum _zip_fronts = _zip_fronts!(Iterators[0 .. i]) ~ "*_iterators[" ~ i.stringof ~ "], "; | } | else | enum _zip_fronts = ""; |} | |package template _zip_index(Iterators...) |{ | static if (Iterators.length) | { | enum i = Iterators.length - 1; | static if (__traits(compiles, &Iterators[i].init[sizediff_t.init])) | enum _zip_index = _zip_index!(Iterators[0 .. i]) ~ "_ref(_iterators[" ~ i.stringof ~ "][index]), "; | else | enum _zip_index = _zip_index!(Iterators[0 .. i]) ~ "_iterators[" ~ i.stringof ~ "][index], "; | } | else | enum _zip_index = ""; |} | |/++ |Iterates multiple iterators in lockstep. | |`ZipIterator` is used by $(SUBREF topology, zip). |+/ 0000000|struct ZipIterator(Iterators...) | if (Iterators.length > 1) |{ |@optmath: | import std.traits: ConstOf, ImmutableOf; | import std.meta: staticMap; | import mir.functional: RefTuple, Ref, _ref; | /// | Iterators _iterators; | | /// | auto lightConst()() const @property | { | import std.format; | import mir.ndslice.topology: iota; | import std.meta: staticMap; | alias Ret = ZipIterator!(staticMap!(LightConstOf, Iterators)); | enum ret = "Ret(%(.lightConst(_iterators[%s]),%)]))".format(_iterators.length.iota); 28| return mixin(ret); | } | | /// | auto lightImmutable()() immutable @property | { | import std.format; | import mir.ndslice.topology: iota; | import std.meta: staticMap; | alias Ret = ZipIterator!(staticMap!(LightImmutableOf, Iterators)); | enum ret = "Ret(%(.lightImmutable(_iterators[%s]),%)]))".format(_iterators.length.iota); | return mixin(ret); | } | | auto opUnary(string op : "*")() 756| { return mixin("RefTuple!(_zip_types!Iterators)(" ~ _zip_fronts!Iterators ~ ")"); } | | | auto opUnary(string op : "*")() const | { return mixin("RefTuple!(_zip_types!Iterators)(" ~ _zip_fronts!Iterators ~ ")"); } | | auto opUnary(string op : "*")() immutable | { return mixin("RefTuple!(_zip_types!Iterators)(" ~ _zip_fronts!Iterators ~ ")"); } | | void opUnary(string op)() scope | if (op == "++" || op == "--") | { 1612| foreach (ref _iterator; _iterators) | mixin(op ~ `_iterator;`); | } | | auto opIndex()(ptrdiff_t index) 199| { return mixin("RefTuple!(_zip_types!Iterators)(" ~ _zip_index!Iterators ~ ")"); } | | auto opIndexAssign(Types...)(RefTuple!(Types) value, ptrdiff_t index) | if (Types.length == Iterators.length) | { 246| foreach(i, ref val; value.expand) | { | import mir.functional: unref; 246| _iterators[i][index] = unref(val); | } 123| return opIndex(index); | } | | void opOpAssign(string op)(ptrdiff_t index) scope | if (op == "+" || op == "-") | { 198| foreach (ref _iterator; _iterators) | mixin(`_iterator ` ~ op ~ `= index;`); | } | | auto opBinary(string op)(ptrdiff_t index) | if (op == "+" || op == "-") | { 7| auto ret = this; | mixin(`ret ` ~ op ~ `= index;`); 7| return ret; | } | | ptrdiff_t opBinary(string op : "-")(scope ref const typeof(this) right) scope const 1| { return this._iterators[0] - right._iterators[0]; } | | bool opEquals()(scope ref const typeof(this) right) scope const 167| { return this._iterators[0] == right._iterators[0]; } | | ptrdiff_t opCmp()(scope ref const typeof(this) right) scope const | { | static if (isPointer!(Iterators[0])) 0000000| return this._iterators[0] - right._iterators[0]; | else 1| return this._iterators[0].opCmp(right._iterators[0]); | } | | import std.meta: anySatisfy; | static if (anySatisfy!(hasZeroShiftFieldMember, Iterators)) | /// Defined if at least one of `Iterators` has member `assumeFieldsHaveZeroShift`. | auto assumeFieldsHaveZeroShift() @property | { | import std.meta: staticMap; | alias _fields = _iterators; 1| return mixin("ZipField!(staticMap!(ZeroShiftField, Iterators))(" ~ applyAssumeZeroShift!Iterators ~ ")"); | } |} | |/// |pure nothrow @nogc version(mir_test) unittest |{ | import mir.ndslice.traits: isIterator; | 1| double[10] data = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]; | alias ItA = IotaIterator!int; | alias ItB = double*; | alias ItZ = ZipIterator!(ItA, ItB); 1| auto zip = ItZ(ItA(3), data.ptr); 1| assert((*zip).a == 3); 1| assert((*zip).b == 1); | | // iteration 1| ++zip; 1| assert((*zip).a == 3 + 1); 1| assert((*zip).b == 1 + 1); 1| assert(&(*zip).b() == data.ptr + 1); | 1| assert(zip[4].a == 3 + 5); 1| assert(zip[4].b == 1 + 5); 1| assert(&zip[4].b() == data.ptr + 5); | 1| --zip; 1| assert((*zip).a == 3); 1| assert((*zip).b == 1); | 1| assert((*(zip + 2)).a == 3 + 2); 1| assert((*(zip - 3)).a == 3 + -3); 1| assert((*(zip + 2)).b == 1 + 2); 1| assert((*(zip + 3 - 3)).b == 1); 1| assert((zip - 3).opBinary!"-"(zip) == -3); | 1| assert(zip == zip); 1| assert(zip - 1 < zip); | | static assert(isIterator!(ZipIterator!(double*, int*))); | static assert(isIterator!(ZipIterator!(immutable(double)*, immutable(int)*))); |} | |/// |struct CachedIterator(Iterator, CacheIterator, FlagIterator) |{ | /// | Iterator _iterator; | /// | CacheIterator _caches; | /// | FlagIterator _flags; | |@optmath: | | /// | auto lightScope()() scope @property | { 2| return CachedIterator!(LightScopeOf!Iterator, LightScopeOf!CacheIterator, LightScopeOf!FlagIterator)( | .lightScope(_iterator), | .lightScope(_caches), | .lightScope(_flags), | ); | } | | /// | auto lightScope()() scope const @property | { 0000000| return lightConst.lightScope; | } | | /// | auto lightScope()() scope immutable @property | { | return lightImmutable.lightScope; | } | | /// | auto lightConst()() const @property | { 0000000| return CachedIterator!(LightConstOf!Iterator, CacheIterator, FlagIterator)( | .lightConst(_iterator), | *cast(CacheIterator*)&_caches, | *cast(FlagIterator*)&_flags, | ); | } | | /// | auto lightImmutable()() immutable @property @trusted | { | return CachedIterator!(LightImmutableOf!Iterator, CacheIterator, FlagIterator)( | .lightImmutable(_iterator), | *cast(CacheIterator*)&_caches, | *cast(FlagIterator*)&_flags, | ); | } | | private alias T = typeof(Iterator.init[0]); | private alias UT = Unqual!T; | | auto opUnary(string op : "*")() | { 0000000| if (_expect(!*_flags, false)) | { 0000000| _flags[0] = true; 0000000| emplaceRef!T(*cast(UT*)&*_caches, *_iterator); | } 0000000| return *_caches; | } | | auto opIndex()(ptrdiff_t index) | { 16| if (_expect(!_flags[index], false)) | { 8| _flags[index] = true; 8| emplaceRef!T(*cast(UT*)&(_caches[index]), _iterator[index]); | } 16| return _caches[index]; | } | | auto ref opIndexAssign(T)(auto ref T val, ptrdiff_t index) | { 4| _flags[index] = true; 4| return _caches[index] = val; | } | | void opUnary(string op)() scope | if (op == "--" || op == "++") | { | mixin(op ~ "_iterator;"); | mixin(op ~ "_caches;"); | mixin(op ~ "_flags;"); | } | | void opOpAssign(string op)(ptrdiff_t index) scope | if (op == "-" || op == "+") | { | mixin("_iterator" ~ op ~ "= index;"); | mixin("_caches" ~ op ~ "= index;"); | mixin("_flags" ~ op ~ "= index;"); | } | | auto opBinary(string op)(ptrdiff_t index) | if (op == "+" || op == "-") | { 2| auto ret = this; | mixin(`ret ` ~ op ~ `= index;`); 2| return ret; | } | | ptrdiff_t opBinary(string op : "-")(scope ref const typeof(this) right) scope const | { return this._iterator - right._iterator; } | | bool opEquals()(scope ref const typeof(this) right) scope const 0000000| { return this._iterator == right._iterator; } | | ptrdiff_t opCmp()(scope ref const typeof(this) right) scope const | { | static if (isPointer!Iterator) | return this._iterator - right._iterator; | else 0000000| return this._iterator.opCmp(right._iterator); | } |} | |private enum map_primitives = q{ | | import mir.functional: RefTuple, unref; | | auto ref opUnary(string op : "*")() | { | static if (is(typeof(*_iterator) : RefTuple!T, T...)) | { | auto t = *_iterator; | return mixin("_fun(" ~ _iotaArgs!(T.length, "t.expand[", "].unref, ") ~ ")"); | } | else | return _fun(*_iterator); | } | | auto ref opIndex(ptrdiff_t index) scope | { | static if (is(typeof(_iterator[0]) : RefTuple!T, T...)) | { | auto t = _iterator[index]; | return mixin("_fun(" ~ _iotaArgs!(T.length, "t.expand[", "].unref, ") ~ ")"); | } | else | return _fun(_iterator[index]); | } | | static if (!__traits(compiles, &opIndex(ptrdiff_t.init))) | { | auto ref opIndexAssign(T)(auto ref T value, ptrdiff_t index) scope | { | static if (is(typeof(_iterator[0]) : RefTuple!T, T...)) | { | auto t = _iterator[index]; | return mixin("_fun(" ~ _iotaArgs!(T.length, "t.expand[", "].unref, ") ~ ") = value"); | } | else | return _fun(_iterator[index]) = value; | } | | auto ref opIndexUnary(string op)(ptrdiff_t index) | { | static if (is(typeof(_iterator[0]) : RefTuple!T, T...)) | { | auto t = _iterator[index]; | return mixin(op ~ "_fun(" ~ _iotaArgs!(T.length, "t.expand[", "].unref, ") ~ ")"); | } | else | return mixin(op ~ "_fun(_iterator[index])"); | } | | auto ref opIndexOpAssign(string op, T)(T value, ptrdiff_t index) | { | static if (is(typeof(_iterator[0]) : RefTuple!T, T...)) | { | auto t = _iterator[index]; | return mixin("_fun(" ~ _iotaArgs!(T.length, "t.expand[", "].unref, ") ~ ")" ~ op ~ "= value"); | } | else | return mixin("_fun(_iterator[index])" ~ op ~ "= value"); | } | } |}; | |/++ |`VmapIterator` is used by $(SUBREF topology, map). |+/ 65|struct VmapIterator(Iterator, Fun) |{ |@optmath: | | /// | Iterator _iterator; | /// | Fun _fun; | | /// | auto lightConst()() const @property | { 35| return VmapIterator!(LightConstOf!Iterator, LightConstOf!Fun)(.lightConst(_iterator), .lightConst(_fun)); | } | | /// | auto lightImmutable()() immutable @property | { | return VmapIterator!(LightImmutableOf!Iterator, LightImmutableOf!Fun)(.lightImmutable(_iterator), .lightImmutable(_fun)); | } | | import mir.functional: RefTuple, unref; | | auto ref opUnary(string op : "*")() | { | static if (is(typeof(*_iterator) : RefTuple!T, T...)) | { 166| auto t = *_iterator; 166| return mixin("_fun(" ~ _iotaArgs!(T.length, "t.expand[", "].unref, ") ~ ")"); | } | else 967| return _fun(*_iterator); | } | | auto ref opIndex(ptrdiff_t index) scope | { | static if (is(typeof(_iterator[0]) : RefTuple!T, T...)) | { 0000000| auto t = _iterator[index]; 0000000| return mixin("_fun(" ~ _iotaArgs!(T.length, "t.expand[", "].unref, ") ~ ")"); | } | else 880| return _fun(_iterator[index]); | } | | static if (!__traits(compiles, &opIndex(ptrdiff_t.init))) | { | auto ref opIndexAssign(T)(auto ref T value, ptrdiff_t index) scope | { | static if (is(typeof(_iterator[0]) : RefTuple!T, T...)) | { | auto t = _iterator[index]; | return mixin("_fun(" ~ _iotaArgs!(T.length, "t.expand[", "].unref, ") ~ ") = value"); | } | else | return _fun(_iterator[index]) = value; | } | | auto ref opIndexUnary(string op)(ptrdiff_t index) | { | static if (is(typeof(_iterator[0]) : RefTuple!T, T...)) | { | auto t = _iterator[index]; | return mixin(op ~ "_fun(" ~ _iotaArgs!(T.length, "t.expand[", "].unref, ") ~ ")"); | } | else | return mixin(op ~ "_fun(_iterator[index])"); | } | | auto ref opIndexOpAssign(string op, T)(T value, ptrdiff_t index) | { | static if (is(typeof(_iterator[0]) : RefTuple!T, T...)) | { | auto t = _iterator[index]; | return mixin("_fun(" ~ _iotaArgs!(T.length, "t.expand[", "].unref, ") ~ ")" ~ op ~ "= value"); | } | else | return mixin("_fun(_iterator[index])" ~ op ~ "= value"); | } | } | | mixin(std_ops); | | static if (hasZeroShiftFieldMember!Iterator) | /// | auto assumeFieldsHaveZeroShift() @property | { | return _vmapField(_iterator.assumeFieldsHaveZeroShift, _fun); | } |} | |auto MapIterator__map(Iterator, alias fun0, alias fun)(ref MapIterator!(Iterator, fun0) it) |{ 9| return MapIterator!(Iterator, fun)(it._iterator); |} | |/++ |`MapIterator` is used by $(SUBREF topology, map). |+/ 7|struct MapIterator(Iterator, alias _fun) |{ |@optmath: | /// | Iterator _iterator; | | /// | auto lightConst()() const @property | { 143| return MapIterator!(LightConstOf!Iterator, _fun)(.lightConst(_iterator)); | } | | /// | auto lightImmutable()() immutable @property | { | return MapIterator!(LightImmutableOf!Iterator, _fun)(.lightImmutable(_iterator)); | } | | import mir.functional: pipe; | /// | static alias __map(alias fun1) = MapIterator__map!(Iterator, _fun, pipe!(_fun, fun1)); | | import mir.functional: RefTuple, unref; | | auto ref opUnary(string op : "*")() | { | static if (is(typeof(*_iterator) : RefTuple!T, T...)) | { 493| auto t = *_iterator; 493| return mixin("_fun(" ~ _iotaArgs!(T.length, "t.expand[", "].unref, ") ~ ")"); | } | else 3539| return _fun(*_iterator); | } | | auto ref opIndex(ptrdiff_t index) scope | { | static if (is(typeof(_iterator[0]) : RefTuple!T, T...)) | { 49| auto t = _iterator[index]; 49| return mixin("_fun(" ~ _iotaArgs!(T.length, "t.expand[", "].unref, ") ~ ")"); | } | else 46| return _fun(_iterator[index]); | } | | static if (!__traits(compiles, &opIndex(ptrdiff_t.init))) | { | auto ref opIndexAssign(T)(auto ref T value, ptrdiff_t index) scope | { | static if (is(typeof(_iterator[0]) : RefTuple!T, T...)) | { | auto t = _iterator[index]; | return mixin("_fun(" ~ _iotaArgs!(T.length, "t.expand[", "].unref, ") ~ ") = value"); | } | else | return _fun(_iterator[index]) = value; | } | | auto ref opIndexUnary(string op)(ptrdiff_t index) | { | static if (is(typeof(_iterator[0]) : RefTuple!T, T...)) | { | auto t = _iterator[index]; | return mixin(op ~ "_fun(" ~ _iotaArgs!(T.length, "t.expand[", "].unref, ") ~ ")"); | } | else | return mixin(op ~ "_fun(_iterator[index])"); | } | | auto ref opIndexOpAssign(string op, T)(T value, ptrdiff_t index) | { | static if (is(typeof(_iterator[0]) : RefTuple!T, T...)) | { | auto t = _iterator[index]; | return mixin("_fun(" ~ _iotaArgs!(T.length, "t.expand[", "].unref, ") ~ ")" ~ op ~ "= value"); | } | else | return mixin("_fun(_iterator[index])" ~ op ~ "= value"); | } | } | | mixin(std_ops); | | static if (hasZeroShiftFieldMember!Iterator) | /// | auto assumeFieldsHaveZeroShift() @property | { 1| return _mapField!_fun(_iterator.assumeFieldsHaveZeroShift); | } |} | |/+ |Creates a mapped iterator. Uses `__map` if possible. |+/ |auto _mapIterator(alias fun, Iterator)(Iterator iterator) |{ | import core.lifetime: move; | static if (__traits(hasMember, Iterator, "__map")) | { | static if (is(Iterator : MapIterator!(Iter0, fun0), Iter0, alias fun0) | && !__traits(compiles, Iterator.__map!fun(iterator))) | { | // https://github.com/libmir/mir-algorithm/issues/111 | debug(mir) pragma(msg, __FUNCTION__~" not coalescing chained map calls into a single lambda, possibly because of multiple embedded context pointers"); 3| return MapIterator!(Iterator, fun)(move(iterator)); | } | else 43| return Iterator.__map!fun(iterator); | } | else 541| return MapIterator!(Iterator, fun)(move(iterator)); |} | | |/+ |Creates a mapped iterator. Uses `__vmap` if possible. |+/ |auto _vmapIterator(Iterator, Fun)(Iterator iterator, Fun fun) |{ | static if (__traits(hasMember, Iterator, "__vmap")) | return Iterator.__vmap(iterator, fun); | else | return MapIterator!(Iterator, fun)(iterator); |} | |@safe pure nothrow @nogc version(mir_test) unittest |{ | // https://github.com/libmir/mir-algorithm/issues/111 | import mir.ndslice.topology : iota, map; | import mir.functional : pipe; | | static auto foo(T)(T x) | { 6| return x.map!(a => a + 1); | } | | static auto bar(T)(T x) | { 6| return foo(x).map!(a => a + 2); | } | 1| auto data = iota(5); 1| auto result = iota([5], 3); | 11| auto x = data.map!(a => a + 1).map!(a => a + 2); 1| assert(x == result); | 1| auto y = bar(data); 1| assert(y == result); |} | |/++ |`NeighboursIterator` is used by $(SUBREF topology, map). |+/ |struct NeighboursIterator(Iterator, size_t N, alias _fun, bool around) |{ | import std.meta: AliasSeq; |@optmath: | /// | Iterator _iterator; | static if (N) | Iterator[2][N] _neighbours; | else alias _neighbours = AliasSeq!(); | | /// | auto lightConst()() const @property | { 4| LightConstOf!Iterator[2][N] neighbours; 24| foreach (i; 0 .. N) | { 4| neighbours[i][0] = .lightConst(_neighbours[i][0]); 4| neighbours[i][1] = .lightConst(_neighbours[i][1]); | } 4| return NeighboursIterator!(LightConstOf!Iterator, N, _fun, around)(.lightConst(_iterator), neighbours); | } | | /// | auto lightImmutable()() immutable @property | { | LightImmutableOf!Iterator[2][N] neighbours; | foreach (i; 0 .. N) | { | neighbours[i][0] = .lightImmutable(_neighbours[i][0]); | neighbours[i][1] = .lightImmutable(_neighbours[i][1]); | } | return NeighboursIterator!(LightImmutableOf!Iterator, N, _fun, around)(.lightImmutable(_iterator), neighbours); | } | | import mir.functional: RefTuple, _ref; | | private alias RA = Unqual!(typeof(_fun(_iterator[-1], _iterator[+1]))); | private alias Result = RefTuple!(_zip_types!Iterator, RA); | | auto ref opUnary(string op : "*")() | { 36| return opIndex(0); | } | | auto ref opIndex(ptrdiff_t index) scope | { | static if (around) 36| RA result = _fun(_iterator[index - 1], _iterator[index + 1]); | | foreach (i; Iota!N) | { | static if (i == 0 && !around) | RA result = _fun(_neighbours[i][0][index], _neighbours[i][1][index]); | else 36| result = _fun(result, _fun(_neighbours[i][0][index], _neighbours[i][1][index])); | } | static if (__traits(compiles, &_iterator[index])) | return Result(_ref(_iterator[index]), result); | else 36| return Result(_iterator[index], result); | } | | void opUnary(string op)() scope | if (op == "--" || op == "++") | { | mixin(op ~ "_iterator;"); | foreach (i; Iota!N) | { | mixin(op ~ "_neighbours[i][0];"); | mixin(op ~ "_neighbours[i][1];"); | } | } | | void opOpAssign(string op)(ptrdiff_t index) scope | if (op == "-" || op == "+") | { | | mixin("_iterator " ~ op ~ "= index;"); | foreach (i; Iota!N) | { | mixin("_neighbours[i][0] " ~ op ~ "= index;"); | mixin("_neighbours[i][1] " ~ op ~ "= index;"); | } | } | | auto opBinary(string op)(ptrdiff_t index) | if (op == "+" || op == "-") | { | auto ret = this; | mixin(`ret ` ~ op ~ `= index;`); | return ret; | } | | ptrdiff_t opBinary(string op : "-")(scope ref const typeof(this) right) scope const | { return this._iterator - right._iterator; } | | bool opEquals()(scope ref const typeof(this) right) scope const 0000000| { return this._iterator == right._iterator; } | | ptrdiff_t opCmp()(scope ref const typeof(this) right) scope const | { | static if (isPointer!Iterator) | return this._iterator - right._iterator; | else 0000000| return this._iterator.opCmp(right._iterator); | } |} | |/++ |`MemberIterator` is used by $(SUBREF topology, member). |+/ |struct MemberIterator(Iterator, string member) |{ |@optmath: | /// | Iterator _iterator; | | /// | auto lightConst()() const @property | { 2| return MemberIterator!(LightConstOf!Iterator, member)(.lightConst(_iterator)); | } | | /// | auto lightImmutable()() immutable @property | { | return MemberIterator!(LightImmutableOf!Iterator, member)(.lightImmutable(_iterator)); | } | | auto ref opUnary(string op : "*")() | { 30| return __traits(getMember, *_iterator, member); | } | | auto ref opIndex()(ptrdiff_t index) | { 0000000| return __traits(getMember, _iterator[index], member); | } | | static if (!__traits(compiles, &opIndex(ptrdiff_t.init))) | { | auto ref opIndexAssign(T)(auto ref T value, ptrdiff_t index) scope | { 12| return __traits(getMember, _iterator[index], member) = value; | } | | auto ref opIndexUnary(string op)(ptrdiff_t index) | { | return mixin(op ~ "__traits(getMember, _iterator[index], member)"); | } | | auto ref opIndexOpAssign(string op, T)(T value, ptrdiff_t index) | { | return mixin("__traits(getMember, _iterator[index], member)" ~ op ~ "= value"); | } | } | | mixin(std_ops); |} | |/++ |`BytegroupIterator` is used by $(SUBREF topology, Bytegroup) and $(SUBREF topology, bytegroup). |+/ |struct BytegroupIterator(Iterator, size_t count, DestinationType) | if (count) |{ |@optmath: | /// | Iterator _iterator; | | /// | auto lightConst()() const @property | { 0000000| return BytegroupIterator!(LightConstOf!Iterator, count, DestinationType)(.lightConst(_iterator)); | } | | /// | auto lightImmutable()() immutable @property | { | return BytegroupIterator!(LightImmutableOf!Iterator, count, DestinationType)(.lightImmutable(_iterator)); | } | | package(mir) alias Byte = Unqual!(typeof(_iterator[0])); | | version(LittleEndian) | private enum BE = false; | else | private enum BE = true; | | private union U | { | DestinationType value; | static if (DestinationType.sizeof > Byte[count].sizeof && BE && isScalarType!DestinationType) | { | struct | { | ubyte[DestinationType.sizeof - Byte[count].sizeof] shiftPayload; | Byte[count] bytes; | } | } | else | { | Byte[count] bytes; | } | } | | DestinationType opUnary(string op : "*")() | { 4| U ret = { value: DestinationType.init }; | foreach (i; Iota!count) 12| ret.bytes[i] = _iterator[i]; 4| return ret.value; | } | | DestinationType opIndex()(ptrdiff_t index) | { 4| return *(this + index); | } | | DestinationType opIndexAssign(T)(T val, ptrdiff_t index) scope | { 2| auto it = this + index; 2| U ret = { value: val }; | foreach (i; Iota!count) 6| it._iterator[i] = ret.bytes[i]; 2| return ret.value; | } | | void opUnary(string op)() scope | if (op == "--" || op == "++") | { mixin("_iterator " ~ op[0] ~ "= count;"); } | | void opOpAssign(string op)(ptrdiff_t index) scope | if (op == "-" || op == "+") | { mixin("_iterator " ~ op ~ "= index * count;"); } | | auto opBinary(string op)(ptrdiff_t index) | if (op == "+" || op == "-") | { 6| auto ret = this; | mixin(`ret ` ~ op ~ `= index;`); 6| return ret; | } | | ptrdiff_t opBinary(string op : "-")(scope ref const typeof(this) right) scope const | { return (this._iterator - right._iterator) / count; } | | bool opEquals()(scope ref const typeof(this) right) scope const 0000000| { return this._iterator == right._iterator; } | | ptrdiff_t opCmp()(scope ref const typeof(this) right) scope const | { | static if (isPointer!Iterator) 0000000| return this._iterator - right._iterator; | else | return this._iterator.opCmp(right._iterator); | } |} | |auto SlideIterator__map(Iterator, size_t params, alias fun0, alias fun)(SlideIterator!(Iterator, params, fun0) it) |{ 1| return SlideIterator!(Iterator, params, fun)(it._iterator); |} | |/++ |`SlideIterator` is used by $(SUBREF topology, diff) and $(SUBREF topology, slide). |+/ 399|struct SlideIterator(Iterator, size_t params, alias fun) | if (params > 1) |{ |@optmath: | /// | Iterator _iterator; | | /// | auto lightConst()() const @property | { 18| return SlideIterator!(LightConstOf!Iterator, params, fun)(.lightConst(_iterator)); | } | | /// | auto lightImmutable()() immutable @property | { | return SlideIterator!(LightImmutableOf!Iterator, params, fun)(.lightImmutable(_iterator)); | } | | import mir.functional: pipe; | /// | static alias __map(alias fun1) = SlideIterator__map!(Iterator, params, fun, pipe!(fun, fun1)); | | auto ref opUnary(string op : "*")() | { 137| return mixin("fun(" ~ _iotaArgs!(params, "_iterator[", "], ") ~ ")"); | } | | auto ref opIndex()(ptrdiff_t index) | { 3016| return mixin("fun(" ~ _iotaArgs!(params, "_iterator[index + ", "], ") ~ ")"); | } | | mixin(std_ops); |} | |/// |version(mir_test) unittest |{ | import mir.functional: naryFun; 1| auto data = [1, 3, 8, 18]; 1| auto diff = SlideIterator!(int*, 2, naryFun!"b - a")(data.ptr); 1| assert(*diff == 2); 1| assert(diff[1] == 5); 1| assert(diff[2] == 10); |} | |auto IndexIterator__map(Iterator, Field, alias fun)(ref IndexIterator!(Iterator, Field) it) |{ 11| auto field = it._field._mapField!fun; 11| return IndexIterator!(Iterator, typeof(field))(it._iterator, field); |} | |version(mir_test) unittest |{ | import mir.ndslice.topology; | import mir.ndslice.allocation; | import mir.ndslice.slice; 1| auto indices = [4, 3, 1, 2, 0, 4].sliced; 7| auto v = iota(5).indexed(indices).map!(a => a).slice; 1| uint r; 13| auto w = iota(5).indexed(indices).map!(a => a).map!(a => a * r).slice; |} | |/++ |Iterates a field using an iterator. | |`IndexIterator` is used by $(SUBREF topology, indexed). |+/ |struct IndexIterator(Iterator, Field) |{ | import mir.functional: RefTuple, unref; | |@optmath: | /// | Iterator _iterator; | /// | Field _field; | | /// | auto lightConst()() const @property | { 76| return IndexIterator!(LightConstOf!Iterator, LightConstOf!Field)(.lightConst(_iterator), .lightConst(_field)); | } | | /// | auto lightImmutable()() immutable @property | { | return IndexIterator!(LightImmutableOf!Iterator, LightImmutableOf!Field)(.lightImmutable(_iterator), _field.lightImmutable); | } | | /// | static alias __map(alias fun) = IndexIterator__map!(Iterator, Field, fun); | | auto ref opUnary(string op : "*")() | { | static if (is(typeof(_iterator[0]) : RefTuple!T, T...)) | { 18| auto t = *_iterator; 18| return mixin("_field[" ~ _iotaArgs!(T.length, "t.expand[", "].unref, ") ~ "]"); | } | else 1809| return _field[*_iterator]; | } | | auto ref opIndex()(ptrdiff_t index) | { | static if (is(typeof(_iterator[0]) : RefTuple!T, T...)) | { 0000000| auto t = _iterator[index]; 0000000| return mixin("_field[" ~ _iotaArgs!(T.length, "t.expand[", "].unref, ") ~ "]"); | } | else 63| return _field[_iterator[index]]; | } | | static if (!__traits(compiles, &opIndex(ptrdiff_t.init))) | { | auto ref opIndexAssign(T)(auto ref T value, ptrdiff_t index) scope | { | static if (is(typeof(_iterator[0]) : RefTuple!T, T...)) | { | auto t = _iterator[index]; | return mixin("_field[" ~ _iotaArgs!(T.length, "t.expand[", "].unref, ") ~ "] = value"); | } | else | return _field[_iterator[index]] = value; | } | | auto ref opIndexUnary(string op)(ptrdiff_t index) | { | static if (is(typeof(_iterator[0]) : RefTuple!T, T...)) | { | auto t = _iterator[index]; | return mixin(op ~ "_field[" ~ _iotaArgs!(T.length, "t.expand[", "].unref, ") ~ "]"); | } | else | return mixin(op ~ "_field[_iterator[index]]"); | } | | auto ref opIndexOpAssign(string op, T)(T value, ptrdiff_t index) | { | static if (is(typeof(_iterator[0]) : RefTuple!T, T...)) | { | auto t = _iterator[index]; | return mixin("_field[" ~ _iotaArgs!(T.length, "t.expand[", "].unref, ") ~ "]" ~ op ~ "= value"); | } | else | return mixin("_field[_iterator[index]]" ~ op ~ "= value"); | } | } | | mixin(std_ops); |} | |/++ |Iterates chunks in a sliceable using an iterator composed of indices. | |Definition: |---- |auto index = iterator[i]; |auto elem = sliceable[index[0] .. index[1]]; |---- |+/ |struct SubSliceIterator(Iterator, Sliceable) |{ |@optmath: | /// | Iterator _iterator; | /// | Sliceable _sliceable; | | /// | auto lightConst()() const @property | { 1| return SubSliceIterator!(LightConstOf!Iterator, LightConstOf!Sliceable)(.lightConst(_iterator), _sliceable.lightConst); | } | | /// | auto lightImmutable()() immutable @property | { | return SubSliceIterator!(LightImmutableOf!Iterator, LightImmutableOf!Sliceable)(.lightImmutable(_iterator), _sliceable.lightImmutable); | } | | auto ref opUnary(string op : "*")() | { 2| auto i = *_iterator; 2| return _sliceable[i[0] .. i[1]]; | } | | auto ref opIndex()(ptrdiff_t index) | { 0000000| auto i = _iterator[index]; 0000000| return _sliceable[i[0] .. i[1]]; | } | | mixin(std_ops); |} | |/++ |Iterates chunks in a sliceable using an iterator composed of indices stored consequently. | |Definition: |---- |auto elem = _sliceable[_iterator[index] .. _iterator[index + 1]]; |---- |+/ 0000000|struct ChopIterator(Iterator, Sliceable) |{ |@optmath: | /// | Iterator _iterator; | /// | Sliceable _sliceable; | | /// | auto lightConst()() const @property | { 9| return ChopIterator!(LightConstOf!Iterator, LightConstOf!Sliceable)(.lightConst(_iterator), _sliceable.lightConst); | } | | /// | auto lightImmutable()() immutable @property | { | return ChopIterator!(LightImmutableOf!Iterator, LightImmutableOf!Sliceable)(.lightImmutable(_iterator), _sliceable.lightImmutable); | } | | auto ref opUnary(string op : "*")() | { 29| return _sliceable[*_iterator .. _iterator[1]]; | } | | auto ref opIndex()(ptrdiff_t index) | { 113| return _sliceable[_iterator[index] .. _iterator[index + 1]]; | } | | mixin(std_ops); |} | |/++ |Iterates on top of another iterator and returns a slice |as a multidimensional window at the current position. | |`SliceIterator` is used by $(SUBREF topology, map) for packed slices. |+/ |struct SliceIterator(Iterator, size_t N = 1, SliceKind kind = Contiguous) |{ |@optmath: | /// | alias Element = Slice!(Iterator, N, kind); | /// | Element._Structure _structure; | /// | Iterator _iterator; | | /// | auto lightConst()() const @property | { 124| return SliceIterator!(LightConstOf!Iterator, N, kind)(_structure, .lightConst(_iterator)); | } | | /// | auto lightImmutable()() immutable @property | { | return SliceIterator!(LightImmutableOf!Iterator, N, kind)(_structure, .lightImmutable(_iterator)); | } | | auto opUnary(string op : "*")() | { 1446| return Element(_structure, _iterator); | } | | auto opIndex()(ptrdiff_t index) | { 34| return Element(_structure, _iterator + index); | } | | mixin(std_ops); |} | |public auto FieldIterator__map(Field, alias fun)(FieldIterator!(Field) it) |{ | import mir.ndslice.field: _mapField; 12| auto field = it._field._mapField!fun; 12| return FieldIterator!(typeof(field))(it._index, field); |} | |version(mir_test) unittest |{ | import mir.ndslice.topology; | import mir.ndslice.allocation; 10| auto v = ndiota(3, 3).map!(a => a).slice; 1| uint r; 19| auto w = ndiota(3, 3).map!(a => a).map!(a => a[0] * r).slice; |} | |/++ |Creates an iterator on top of a field. | |`FieldIterator` is used by $(SUBREF slice, slicedField), $(SUBREF topology, bitwise), $(SUBREF topology, ndiota), and others. |+/ 2|struct FieldIterator(Field) |{ |@optmath: | /// | ptrdiff_t _index; | /// | Field _field; | | /// | auto lightConst()() const @property | { 226| return FieldIterator!(LightConstOf!Field)(_index, .lightConst(_field)); | } | | /// | auto lightImmutable()() immutable @property | { 2| return FieldIterator!(LightImmutableOf!Field)(_index, .lightImmutable(_field)); | } | | /// | static alias __map(alias fun) = FieldIterator__map!(Field, fun); | | /// | Slice!(IotaIterator!size_t) opSlice(size_t dimension)(size_t i, size_t j) scope const | { 8| assert(i <= j); 8| return typeof(return)(j - i, typeof(return).Iterator(i)); | } | | /++ | Returns: | `_field[_index + sl.i .. _index + sl.j]`. | +/ | auto opIndex()(Slice!(IotaIterator!size_t) sl) | { 8| auto idx = _index + sl._iterator._index; 8| return _field[idx .. idx + sl.length]; | } | | auto ref opUnary(string op : "*")() 16395| { return _field[_index]; } | | void opUnary(string op)() scope | if (op == "++" || op == "--") | { mixin(op ~ `_index;`); } | | auto ref opIndex()(ptrdiff_t index) 1532| { return _field[_index + index]; } | | static if (!__traits(compiles, &_field[_index])) | { | auto ref opIndexAssign(T)(auto ref T value, ptrdiff_t index) 1228| { return _field[_index + index] = value; } | | auto ref opIndexUnary(string op)(ptrdiff_t index) | { mixin (`return ` ~ op ~ `_field[_index + index];`); } | | auto ref opIndexOpAssign(string op, T)(T value, ptrdiff_t index) | { mixin (`return _field[_index + index] ` ~ op ~ `= value;`); } | } | | void opOpAssign(string op)(ptrdiff_t index) scope | if (op == "+" || op == "-") | { mixin(`_index ` ~ op ~ `= index;`); } | | auto opBinary(string op)(ptrdiff_t index) | if (op == "+" || op == "-") | { 63| auto ret = this; | mixin(`ret ` ~ op ~ `= index;`); 63| return ret; | } | | ptrdiff_t opBinary(string op : "-")(scope ref const typeof(this) right) scope const 0000000| { return this._index - right._index; } | | bool opEquals()(scope ref const typeof(this) right) scope const 8| { return this._index == right._index; } | | ptrdiff_t opCmp()(scope ref const typeof(this) right) scope const 0000000| { return this._index - right._index; } | | /// | auto assumeFieldsHaveZeroShift() @property | { 2| if (_expect(_index != 0, false)) | { | version (D_Exceptions) 0000000| throw assumeZeroShiftException; | else | assert(0, assumeZeroShiftExceptionMsg); | } | static if (hasZeroShiftFieldMember!Field) | return _field.assumeFieldsHaveZeroShift; | else 2| return _field; | } |} | |auto FlattenedIterator__map(Iterator, size_t N, SliceKind kind, alias fun)(FlattenedIterator!(Iterator, N, kind) it) |{ | import mir.ndslice.topology: map; 3| auto slice = it._slice.map!fun; 3| return FlattenedIterator!(TemplateArgsOf!(typeof(slice)))(it._indices, slice); |} | |version(mir_test) unittest |{ | import mir.ndslice.topology; | import mir.ndslice.allocation; 10| auto v = iota(3, 3).universal.flattened.map!(a => a).slice; 1| uint r; 19| auto w = iota(3, 3).universal.flattened.map!(a => a).map!(a => a * r).slice; |} | |/++ |Creates an iterator on top of all elements in a slice. | |`FieldIterator` is used by $(SUBREF topology, bitwise), $(SUBREF topology, ndiota), and others. |+/ |struct FlattenedIterator(Iterator, size_t N, SliceKind kind) | if (N > 1 && (kind == Universal || kind == Canonical)) |{ |@optmath: | /// | ptrdiff_t[N] _indices; | /// | Slice!(Iterator, N, kind) _slice; | | /// | auto lightConst()() const @property | { 3| return FlattenedIterator!(LightConstOf!Iterator, N, kind)(_indices, _slice.lightConst); | } | | /// | auto lightImmutable()() immutable @property | { | return FlattenedIterator!(LightImmutableOf!Iterator, N, kind)(_indices, _slice.lightImmutable); | } | | /// | static alias __map(alias fun) = FlattenedIterator__map!(Iterator, N, kind, fun); | | private ptrdiff_t getShift()(ptrdiff_t n) | { 137| ptrdiff_t _shift; 137| n += _indices[$ - 1]; | foreach_reverse (i; Iota!(1, N)) | { 242| immutable v = n / ptrdiff_t(_slice._lengths[i]); 242| n %= ptrdiff_t(_slice._lengths[i]); | static if (i == _slice.S) 27| _shift += (n - _indices[i]); | else 215| _shift += (n - _indices[i]) * _slice._strides[i]; 242| n = _indices[i - 1] + v; | } 137| _shift += (n - _indices[0]) * _slice._strides[0]; 137| return _shift; | } | | auto ref opUnary(string op : "*")() | { 505| return *_slice._iterator; | } | | void opUnary(string op)() scope | if (op == "--" || op == "++") | { | foreach_reverse (i; Iota!N) | { | static if (i == _slice.S) | mixin(op ~ `_slice._iterator;`); | else | mixin(`_slice._iterator ` ~ op[0] ~ `= _slice._strides[i];`); | mixin (op ~ `_indices[i];`); | static if (i) | { | static if (op == "++") | { 486| if (_indices[i] < _slice._lengths[i]) 356| return; | static if (i == _slice.S) 4| _slice._iterator -= _slice._lengths[i]; | else 126| _slice._iterator -= _slice._lengths[i] * _slice._strides[i]; 130| _indices[i] = 0; | } | else | { 82| if (_indices[i] >= 0) 64| return; | static if (i == _slice.S) | _slice._iterator += _slice._lengths[i]; | else 18| _slice._iterator += _slice._lengths[i] * _slice._strides[i]; 18| _indices[i] = _slice._lengths[i] - 1; | } | } | } | } | | auto ref opIndex()(ptrdiff_t index) | { 137| return _slice._iterator[getShift(index)]; | } | | static if (isMutable!(_slice.DeepElement) && !_slice.hasAccessByRef) | /// | auto ref opIndexAssign(E)(scope ref E elem, size_t index) scope return | { | return _slice._iterator[getShift(index)] = elem; | } | | void opOpAssign(string op : "+")(ptrdiff_t n) scope | { 10| ptrdiff_t _shift; 10| n += _indices[$ - 1]; | foreach_reverse (i; Iota!(1, N)) | { 14| immutable v = n / ptrdiff_t(_slice._lengths[i]); 14| n %= ptrdiff_t(_slice._lengths[i]); | static if (i == _slice.S) | _shift += (n - _indices[i]); | else 14| _shift += (n - _indices[i]) * _slice._strides[i]; 14| _indices[i] = n; 14| n = _indices[i - 1] + v; | } 10| _shift += (n - _indices[0]) * _slice._strides[0]; 10| _indices[0] = n; | foreach_reverse (i; Iota!(1, N)) | { 10| if (_indices[i] >= 0) 9| break; 1| _indices[i] += _slice._lengths[i]; 1| _indices[i - 1]--; | } 10| _slice._iterator += _shift; | } | | void opOpAssign(string op : "-")(ptrdiff_t n) scope 2| { this += -n; } | | auto opBinary(string op)(ptrdiff_t index) | if (op == "+" || op == "-") | { 1| auto ret = this; | mixin(`ret ` ~ op ~ `= index;`); 1| return ret; | } | | ptrdiff_t opBinary(string op : "-")(scope ref const typeof(this) right) scope const | { 6| ptrdiff_t ret = this._indices[0] - right._indices[0]; | foreach (i; Iota!(1, N)) | { 6| ret *= _slice._lengths[i]; 6| ret += this._indices[i] - right._indices[i]; | } 6| return ret; | } | | bool opEquals()(scope ref const typeof(this) right) scope const | { | foreach_reverse (i; Iota!N) 8| if (this._indices[i] != right._indices[i]) 0000000| return false; 4| return true; | } | | ptrdiff_t opCmp()(scope ref const typeof(this) right) scope const | { | foreach (i; Iota!(N - 1)) 6| if (auto ret = this._indices[i] - right._indices[i]) 1| return ret; 5| return this._indices[$ - 1] - right._indices[$ - 1]; | } |} | |version(mir_test) unittest |{ | import mir.ndslice.topology; | import mir.ndslice.slice; | 1| auto it0 = iota(3, 4).universal.flattened._iterator; 1| auto it1 = it0; 1| assert(it0 == it1); 1| it0 += 5; 1| assert(it0 > it1); 1| it0 -= 5; 1| assert(*it0 == *it1); 1| assert(it0 == it1); 1| it0 += 5; 1| it0 += 7; 1| it0 -= 9; 1| assert(it0 > it1); 1| it1 += 3; 1| assert(*it0 == *it1); 1| assert(it0 == it1); 1| assert(it0 <= it1); 1| assert(it0 >= it1); | 1| ++it0; 1| ++it0; 1| ++it0; 1| ++it0; 1| ++it0; 1| ++it0; 1| ++it0; 1| ++it0; 1| ++it0; | 1| assert(it0 - it1 == 9); 1| assert(it1 - it0 == -9); | 1| ++it0; | 1| assert(it0 - it1 == 10); 1| assert(it1 - it0 == -10); | 1| --it0; | 1| assert(it0 - it1 == 9); 1| assert(it1 - it0 == -9); 1| assert(it0[-9] == *it1); 1| assert(*it0 == it1[9]); | 1| --it0; 1| --it0; 1| --it0; 1| --it0; 1| --it0; 1| --it0; 1| --it0; 1| --it0; 1| --it0; 1| assert(*it0 == *it1); 1| assert(it0 == it1); 1| assert(it0 <= it1); 1| assert(it0 >= it1); |} | |/++ |`StairsIterator` is used by $(SUBREF topology, stairs). |+/ |struct StairsIterator(Iterator, string direction) | if (direction == "+" || direction == "-") |{ | /// | size_t _length; | | /// | Iterator _iterator; | | /// | auto lightConst()() const @property | { 2| return StairsIterator!(LightConstOf!Iterator, direction)(_length, .lightConst(_iterator)); | } | | /// | auto lightImmutable()() immutable @property | { | return StairsIterator!(LightImmutableOf!Iterator, direction)(_length, .lightImmutable(_iterator)); | } | |@optmath: | | /// | Slice!Iterator opUnary(string op : "*")() | { | import mir.ndslice.slice: sliced; 28| return _iterator.sliced(_length); | } | | /// | Slice!Iterator opIndex()(ptrdiff_t index) | { | import mir.ndslice.slice: sliced; | static if (direction == "+") | { 4| auto newLength = _length + index; 4| auto shift = ptrdiff_t(_length + newLength - 1) * index / 2; | } | else | { 4| auto newLength = _length - index; 4| auto shift = ptrdiff_t(_length + newLength + 1) * index / 2; | } 8| assert(ptrdiff_t(newLength) >= 0); 8| return (_iterator + shift).sliced(newLength); | } | | void opUnary(string op)() scope | if (op == "--" || op == "++") | { | static if (op == "++") | { 18| _iterator += _length; | static if (direction == "+") 9| ++_length; | else 9| --_length; | } | else | { 2| assert(_length); | static if (direction == "+") 1| --_length; | else 1| ++_length; 2| _iterator -= _length; | } | } | | void opOpAssign(string op)(ptrdiff_t index) scope | if (op == "-" || op == "+") | { | static if (op == direction) 8| auto newLength = _length + index; | else 8| auto newLength = _length - index; | static if (direction == "+") 8| auto shift = ptrdiff_t(_length + newLength - 1) * index / 2; | else 8| auto shift = ptrdiff_t(_length + newLength + 1) * index / 2; 16| assert(ptrdiff_t(newLength) >= 0); 16| _length = newLength; | static if (op == "+") 10| _iterator += shift; | else 6| _iterator -= shift; | } | | auto opBinary(string op)(ptrdiff_t index) | if (op == "+" || op == "-") | { 14| auto ret = this; | mixin(`ret ` ~ op ~ `= index;`); 14| return ret; | } | | ptrdiff_t opBinary(string op : "-")(scope ref const typeof(this) right) scope const | { | static if (direction == "+") 2| return this._length - right._length; | else 2| return right._length - this._length; | } | | bool opEquals()(scope ref const typeof(this) right) scope const 2| { return this._length == right._length; } | | ptrdiff_t opCmp()(scope ref const typeof(this) right) scope const 2| { return this - right; } |} | |/// |version(mir_test) unittest |{ | // 0 | // 1 2 | // 3 4 5 | // 6 7 8 9 | // 10 11 12 13 14 1| auto it = StairsIterator!(IotaIterator!size_t, "+")(1, IotaIterator!size_t()); 1| assert(*it == [0]); 1| assert(it[4] == [10, 11, 12, 13, 14]); 1| assert(*(it + 4) == [10, 11, 12, 13, 14]); 1| ++it; 1| assert(*it == [1, 2]); 1| it += 3; 1| assert(*it == [10, 11, 12, 13, 14]); 1| assert(it[-3] == [1, 2]); 1| assert(*(it - 3) == [1, 2]); 1| assert(it + 1 > it); 1| assert(it + 1 - 1 == it); 1| assert(it - 3 - it == -3); 1| --it; 1| assert(*it == [6, 7, 8, 9]); |} | |/// |version(mir_test) unittest |{ | // [0, 1, 2, 3, 4], | // [5, 6, 7, 8], | // [9, 10, 11], | // [12, 13], | // [14]]); | 1| auto it = StairsIterator!(IotaIterator!size_t, "-")(5, IotaIterator!size_t()); 1| assert(*it == [0, 1, 2, 3, 4]); 1| assert(it[4] == [14]); 1| assert(*(it + 4) == [14]); 1| ++it; 1| assert(*it == [5, 6, 7, 8]); 1| it += 3; 1| assert(*it == [14]); 1| assert(it[-3] == [5, 6, 7, 8]); 1| assert(*(it - 3) == [5, 6, 7, 8]); 1| assert(it + 1 > it); 1| assert(it + 1 - 1 == it); 1| assert(it - 3 - it == -3); 1| --it; 1| assert(*it == [12, 13]); |} | |/++ |Element type of $(LREF TripletIterator). |+/ |struct Triplet(Iterator, SliceKind kind = Contiguous) |{ |@optmath: | /// | size_t _iterator; | /// | Slice!(Iterator, 1, kind) _slice; | | /// | auto lightConst()() const @property | { | return Triplet!(LightConstOf!Iterator, kind)(_iterator, slice.lightConst); | } | | /// | auto lightImmutable()() immutable @property | { | return Triplet!(LightImmutableOf!Iterator, kind)(_iterator, slice.lightImmutable); | } | | @property | { | /// | auto ref center() | { 67| assert(_iterator < _slice.length); 67| return _slice[_iterator]; | } | | /// | Slice!(Iterator, 1, kind) left() | { 22| assert(_iterator < _slice.length); 22| return _slice[0 .. _iterator]; | } | | /// | Slice!(Iterator, 1, kind) right() | { 18| assert(_iterator < _slice.length); 18| return _slice[_iterator + 1 .. $]; | } | } |} | |/++ |Iterates triplets position in a slice. | |`TripletIterator` is used by $(SUBREF topology, triplets). |+/ |struct TripletIterator(Iterator, SliceKind kind = Contiguous) |{ |@optmath: | | /// | size_t _iterator; | /// | Slice!(Iterator, 1, kind) _slice; | | /// | auto lightConst()() const @property | { 1| return TripletIterator!(LightConstOf!Iterator, kind)(_iterator, _slice.lightConst); | } | | /// | auto lightImmutable()() immutable @property | { | return TripletIterator!(LightImmutableOf!Iterator, kind)(_iterator, _slice.lightImmutable); | } | | /// | Triplet!(Iterator, kind) opUnary(string op : "*")() | { 16| return typeof(return)(_iterator, _slice); | } | | /// | Triplet!(Iterator, kind) opIndex()(ptrdiff_t index) | { 5| return typeof(return)(_iterator + index, _slice); | } | | mixin(std_ops); |} source/mir/ndslice/iterator.d is 92% covered <<<<<< EOF # path=./source-mir-interpolate-package.lst |/++ |$(H1 Interpolation Algorithms) | |$(BOOKTABLE $(H2 Interpolation modules), |$(TR $(TH Module) $(TH Interpolation kind)) |$(T2M constant, Constant Interpolant) |$(T2M generic, Generic Piecewise Interpolant) |$(T2M linear, Linear Interpolant) |$(T2M polynomial, Lagrange Barycentric Interpolant) |$(T2M spline, Piecewise Cubic Hermite Interpolant Spline: C2 (with contiguous second derivative), cardinal, monotone (aka PCHIP), double-quadratic, Akima) |) |] |Copyright: 2020 Ilya Yaroshenko, Kaleidic Associates Advisory Limited, Symmetry Investments |Authors: Ilya Yaroshenko | |Macros: |SUBREF = $(REF_ALTTEXT $(TT $2), $2, mir,interpolate, $1)$(NBSP) |T2M=$(TR $(TDNW $(MREF mir,interpolate,$1)) $(TD $+)) |T2=$(TR $(TDNW $(LREF $1)) $(TD $+)) |+/ |module mir.interpolate; | |import mir.functional: RefTuple; |import mir.math.common: optmath; |import mir.ndslice.slice: Slice, Contiguous; |import mir.primitives; |import mir.qualifier; |import std.traits: isInstanceOf; | |@optmath: | |package ref iter(alias s)() { return s._iterator; }; |package alias GridVector(It) = Slice!It; | |package enum bool isInterval(T) = isInstanceOf!(RefTuple, T); |package enum bool isInterval(alias T) = isInstanceOf!(RefTuple, T); | |package template Repeat(ulong n, L...) |{ | static if (n) | { | static import std.meta; | alias Repeat = std.meta.Repeat!(n, L); | } | else | alias Repeat = L[0 .. 0]; |} | |/++ |Interval index for x value given. |+/ |template findInterval(size_t dimension = 0) |{ | /++ | Interval index for x value given. | Params: | interpolant = interpolant | x = X value | +/ | size_t findInterval(Interpolant, X)(auto ref const Interpolant interpolant, in X x) @trusted | { | import mir.ndslice.slice: sliced; | import mir.ndslice.sorting: transitionIndex; | static if (dimension) | { 294| immutable sizediff_t len = interpolant.intervalCount!dimension - 1; 294| auto grid = interpolant.gridScopeView!dimension[1 .. $][0 .. len]; | } | else | { 1108| immutable sizediff_t len = interpolant.intervalCount - 1; 1108| auto grid = interpolant.gridScopeView[1 .. $][0 .. len]; | } 1402| assert(len >= 0); 1402| return grid.transitionIndex!"a <= b"(x); | } |} | |/// |version(mir_test) unittest |{ | import mir.ndslice.allocation: rcslice; | import mir.ndslice.topology: as; | import mir.ndslice.slice: sliced; | import mir.interpolate.linear; | | static immutable x = [0.0, 1, 2]; | static immutable y = [10.0, 2, 4]; 2| auto interpolation = linear!double(x.rcslice, y.as!(const double).rcslice); 1| assert(interpolation.findInterval(1.0) == 1); |} | |/++ |Lazy interpolation shell with linear complexity. | |Params: | range = sorted range | interpolant = interpolant structure with `.gridScopeView` method. |Complexity: | `O(range.length + interpolant.gridScopeView.length)` to evaluate all elements. |Returns: | Lazy input range. |See_also: | $(SUBREF linear, linear), | $(SUBREF spline, spline). |+/ |auto interp1(Range, Interpolant)(Range range, Interpolant interpolant, size_t interval = 0) |{ 2| return Interp1!(Range, Interpolant)(range, interpolant, interval); |} | |/// ditto 1|struct Interp1(Range, Interpolant) |{ | /// Sorted range (descending). $(RED For internal use.) | private Range _range; | /// Interpolant structure. $(RED For internal use.) | private Interpolant _interpolant; | /// Current interpolation interval. $(RED For internal use.) | private size_t _interval; | | /// | auto lightScope() | { 0000000| return Interp1!(LightScopeOf!Range, LightScopeOf!Interpolant)(.lightScope(_range), .lightScope(_interpolant), _interval); | } | | static if (hasLength!Range) | /// Length (optional) 1| size_t length()() const @property { return _range.length; } | /// Save primitive (optional) | auto save()() @property | { | auto ret = this; | ret._range = _range.save; | return ret; | } | /// Input range primitives 61| bool empty () const @property { return _range.empty; } | /// ditto 0000000| void popFront() { _range.popFront; } | /// ditto | auto front() @property | | { 30| assert(!empty); 30| auto x = _range.front; 30| return (x) @trusted { 30| auto points = _interpolant.gridScopeView; 30| sizediff_t len = _interpolant.intervalCount - 1; 30| assert(len >= 0); 44| while (x > points[_interval + 1] && _interval < len) 7| _interval++; 30| return _interpolant(x.atInterval(_interval)); | } (x); | } |} | |/++ |PCHIP interpolation. |+/ |version(mir_test) |@safe unittest |{ | import mir.math.common: approxEqual; | import mir.ndslice.slice: sliced; | import mir.ndslice.allocation: rcslice; | import mir.interpolate: interp1; | import mir.interpolate.spline; | | static immutable x = [1.0, 2, 4, 5, 8, 10, 12, 15, 19, 22]; | static immutable y = [17.0, 0, 16, 4, 10, 15, 19, 5, 18, 6]; 2| auto interpolation = spline!double(x.rcslice, y.sliced, SplineType.monotone); | 1| auto xs = x[0 .. $ - 1].sliced + 0.5; | 2| auto ys = xs.interp1(interpolation); |} | |@safe pure @nogc version(mir_test) unittest |{ | import mir.interpolate.linear; | import mir.ndslice; | import mir.math.common: approxEqual; | | static immutable x = [0, 1, 2, 3, 5.00274, 7.00274, 10.0055, 20.0137, 30.0192]; | static immutable y = [0.0011, 0.0011, 0.0030, 0.0064, 0.0144, 0.0207, 0.0261, 0.0329, 0.0356,]; | static immutable xs = [1, 2, 3, 4.00274, 5.00274, 6.00274, 7.00274, 8.00548, 9.00548, 10.0055, 11.0055, 12.0082, 13.0082, 14.0082, 15.0082, 16.011, 17.011, 18.011, 19.011, 20.0137, 21.0137, 22.0137, 23.0137, 24.0164, 25.0164, 26.0164, 27.0164, 28.0192, 29.0192, 30.0192]; | 2| auto interpolation = linear!double(x.rcslice, y.as!(const double).rcslice); | | static immutable data = [0.0011, 0.0030, 0.0064, 0.0104, 0.0144, 0.0176, 0.0207, 0.0225, 0.0243, 0.0261, 0.0268, 0.0274, 0.0281, 0.0288, 0.0295, 0.0302, 0.0309, 0.0316, 0.0322, 0.0329, 0.0332, 0.0335, 0.0337, 0.0340, 0.0342, 0.0345, 0.0348, 0.0350, 0.0353, 0.0356]; | 1| () @trusted { 31| assert(all!((a, b) => approxEqual(a, b, 1e-4, 1e-4))(xs.interp1(interpolation), data)); | }(); |} | |/++ |Optimization utility that can be used with interpolants if |x should be extrapolated at interval given. | |By default interpolants uses binary search to find appropriate interval, |it has `O(log(.gridScopeView.length))` complexity. |If an interval is given, interpolant uses it instead of binary search. |+/ |RefTuple!(T, size_t) atInterval(T)(in T value, size_t intervalIndex) |{ 32| return typeof(return)(value, intervalIndex); |} | |/// |version(mir_test) unittest |{ | import mir.ndslice.allocation; | import mir.ndslice.slice; | import mir.interpolate.spline; | static immutable x = [0.0, 1, 2]; | static immutable y = [3.0, 4, -10]; 2| auto interpolant = spline!double(x.rcslice, y.sliced); 1| assert(interpolant(1.3) != interpolant(1.3.atInterval(0))); 1| assert(interpolant(1.3) == interpolant(1.3.atInterval(1))); |} | |version(D_AVX2) | enum _avx = true; |else |version(D_AVX) | enum _avx = true; |else | enum _avx = false; | |version(LDC) | enum LDC = true; |else | enum LDC = false; | |version(X86_64) | enum x86_64 = true; |else | enum x86_64 = false; | |auto copyvec(F, size_t N)(ref const F[N] from, ref F[N] to) |{ | import mir.internal.utility; | | static if (LDC && F.mant_dig != 64 && is(__vector(F[N]))) | { | alias V = __vector(F[N]); // @FUTURE@ vector support | *cast(V*) to.ptr = *cast(V*) from.ptr; | } | else | static if (F.sizeof <= double.sizeof && F[N].sizeof >= (double[2]).sizeof && x86_64) | { | import mir.utility; | enum S = _avx ? 32u : 16u; | enum M = min(S, F[N].sizeof) / F.sizeof; | alias V = __vector(F[M]); // @FUTURE@ vector support | enum C = N / M; | foreach(i; Iota!C) | { 2752| *cast(V*)(to.ptr + i * M) = *cast(V*)(from.ptr + i * M); | } | } | else | { 1140| to = from; | } |} | |package template SplineReturnType(F, size_t N, size_t P) |{ | static if (P <= 1 || N == 0) | alias SplineReturnType = F; | else | alias SplineReturnType = .SplineReturnType!(F, N - 1, P)[P]; |} | |template generateShuffles3(size_t N, size_t P) |{ | enum size_t[N][2] generateShuffles3 = | () | { | auto ret = new size_t[](2 * N); | size_t[2] j; | bool f = 1; | foreach(i; 0 .. N) | { | ret[i * 2] = i; | ret[i * 2 + 1] = i + N; | } | return [ret[0 .. $ / 2], ret[$ / 2 .. $]]; | }(); |} | | |void shuffle3(size_t P, F, size_t N)(ref F[N] a, ref F[N] b, ref F[N] c, ref F[N] d) | if (P <= N && N) |{ | static if (P == 0 || N == 1) | { | copyvec(a, c); | copyvec(b, d); | } | else | version(LDC) | { | enum masks = generateShuffles3!(N, P); | import std.meta: aliasSeqOf; | import ldc.simd: shufflevector; | alias V = __vector(F[N]); // @FUTURE@ vector support | auto as = shufflevector!(V, aliasSeqOf!(masks[0]))(*cast(V*)a.ptr, *cast(V*)b.ptr); | auto bs = shufflevector!(V, aliasSeqOf!(masks[1]))(*cast(V*)a.ptr, *cast(V*)b.ptr); | *cast(V*)c.ptr = as; | *cast(V*)d.ptr = bs; | } | else | { | foreach(i; Iota!(N / P)) | { | enum j = 2 * i * P; | static if (j < N) | { 24| copyvec(a[i * P .. i * P + P], c[j .. j + P]); | static assert(j + 2 * P <= c.length); 24| copyvec(b[i * P .. i * P + P], c[j + P .. j + 2 * P]); | } | else | { 24| copyvec(a[i * P .. i * P + P], d[j - N .. j - N + P]); 24| copyvec(b[i * P .. i * P + P], d[j - N + P .. j - N + 2 * P]); | } | } | } |} | |void shuffle2(size_t P, F, size_t N)(ref F[N] a, ref F[N] b, ref F[N] c, ref F[N] d) | if (P <= N && N) |{ | static if (P == 0 || N == 1) | { 514| copyvec(a, c); 514| copyvec(b, d); | } | else | version(LDC) | { | enum masks = generateShuffles2!(N, P); | import std.meta: aliasSeqOf; | import ldc.simd: shufflevector; | alias V = __vector(F[N]); // @FUTURE@ vector support | auto as = shufflevector!(V, aliasSeqOf!(masks[0]))(*cast(V*)a.ptr, *cast(V*)b.ptr); | auto bs = shufflevector!(V, aliasSeqOf!(masks[1]))(*cast(V*)a.ptr, *cast(V*)b.ptr); | *cast(V*)c.ptr = as; | *cast(V*)d.ptr = bs; | } | else | { | foreach(i; Iota!(N / P)) | { | enum j = 2 * i * P; | static if (j < N) 429| copyvec(a[j .. j + P], c[i * P .. i * P + P]); | else 425| copyvec(b[j - N .. j - N + P], c[i * P .. i * P + P]); | } | foreach(i; Iota!(N / P)) | { | enum j = 2 * i * P + P; | static if (j < N) 425| copyvec(a[j .. j + P], d[i * P .. i * P + P]); | else 429| copyvec(b[j - N .. j - N + P], d[i * P .. i * P + P]); | } | } |} | |void shuffle1(size_t P, F, size_t N)(ref F[N] a, ref F[N] b, ref F[N] c, ref F[N] d) | if (P <= N && N) |{ | static if (P == 0 || N == 1) | { | copyvec(a, c); | copyvec(b, d); | } | else | version(LDC) | { | enum masks = generateShuffles1!(N, P); | import std.meta: aliasSeqOf; | import ldc.simd: shufflevector; | alias V = __vector(F[N]); // @FUTURE@ vector support | auto as = shufflevector!(V, aliasSeqOf!(masks[0]))(*cast(V*)a.ptr, *cast(V*)b.ptr); | auto bs = shufflevector!(V, aliasSeqOf!(masks[1]))(*cast(V*)a.ptr, *cast(V*)b.ptr); | *cast(V*)c.ptr = as; | *cast(V*)d.ptr = bs; | } | else | { | foreach(i; Iota!(N / P)) | { | enum j = i * P; | static if (i % 2 == 0) 3| copyvec(a[j .. j + P], c[i * P .. i * P + P]); | else 3| copyvec(b[j - P .. j], c[i * P .. i * P + P]); | } | foreach(i; Iota!(N / P)) | { | enum j = i * P + P; | static if (i % 2 == 0) 3| copyvec(a[j .. j + P], d[i * P .. i * P + P]); | else 3| copyvec(b[j - P .. j], d[i * P .. i * P + P]); | } | } |} | |template generateShuffles2(size_t N, size_t P) |{ | enum size_t[N][2] generateShuffles2 = | () | { | auto ret = new size_t[][](2, N); | size_t[2] j; | bool f = 1; | foreach(i; 0 .. 2 * N) | { | if (i % P == 0) | f = !f; | ret[f][j[f]++] = i; | } | return ret; | }(); |} | | |template generateShuffles1(size_t N, size_t P) |{ | enum size_t[N][2] generateShuffles1 = | () | { | auto ret = new size_t[][](2, N); | foreach(i; 0 .. N) | { | ret[0][i] = (i / P + 1) % 2 ? i : i + N - P; | ret[1][i] = ret[0][i] + P; | } | return ret; | }(); |} | |unittest |{ 1| assert(generateShuffles1!(4, 1) == [[0, 4, 2, 6], [1, 5, 3, 7]]); 1| assert(generateShuffles1!(4, 2) == [[0, 1, 4, 5], [2, 3, 6, 7]]); 1| assert(generateShuffles1!(4, 4) == [[0, 1, 2, 3], [4, 5, 6, 7]]); |} | |unittest |{ 1| assert(generateShuffles2!(4, 1) == [[0, 2, 4, 6], [1, 3, 5, 7]]); 1| assert(generateShuffles2!(4, 2) == [[0, 1, 4, 5], [2, 3, 6, 7]]); 1| assert(generateShuffles2!(4, 4) == [[0, 1, 2, 3], [4, 5, 6, 7]]); |} | |unittest |{ | enum ai = [0, 1, 2, 3]; | enum bi = [4, 5, 6, 7]; | align(32) | double[4] a = [0, 1, 2, 3], b = [4, 5, 6, 7], c, d; 1| shuffle3!1(a, b, c, d); 1| assert([c, d] == [[0.0, 4, 1, 5], [2.0, 6, 3, 7]]); |} | |unittest |{ | enum ai = [0, 1, 2, 3]; | enum bi = [4, 5, 6, 7]; | align(32) | double[4] a = [0, 1, 2, 3], b = [4, 5, 6, 7], c, d; 1| shuffle2!1(a, b, c, d); 1| assert([c, d] == [[0.0, 2, 4, 6], [1.0, 3, 5, 7]]); 1| shuffle2!2(a, b, c, d); 1| assert([c, d] == [[0.0, 1, 4, 5], [2.0, 3, 6, 7]]); | // shuffle2!4(a, b, c, d); | // assert([c, d] == [[0.0, 1, 2, 3], [4.0, 5, 6, 7]]); |} | |unittest |{ | enum ai = [0, 1, 2, 3]; | enum bi = [4, 5, 6, 7]; | align(32) | double[4] a = [0, 1, 2, 3], b = [4, 5, 6, 7], c, d; 1| shuffle1!1(a, b, c, d); 1| assert([c, d] == [[0, 4, 2, 6], [1, 5, 3, 7]]); 1| shuffle1!2(a, b, c, d); 1| assert([c, d] == [[0, 1, 4, 5], [2, 3, 6, 7]]); | // shuffle1!4(a, b, c, d); | // assert([c, d] == [[0, 1, 2, 3], [4, 5, 6, 7]]); |} | |import mir.internal.utility; | |auto vectorize(Kernel, F, size_t N, size_t R)(ref Kernel kernel, ref F[N] a0, ref F[N] b0, ref F[N] a1, ref F[N] b1, ref F[N][R] c) |{ | // static if (LDC && F.mant_dig != 64) | // { | // alias V = __vector(F[N]); // @FUTURE@ vector support | // *cast(V[R]*) c.ptr = kernel( | // *cast(V*)a0.ptr, *cast(V*)b0.ptr, | // *cast(V*)a1.ptr, *cast(V*)b1.ptr); | // } | // else | // static if (F.sizeof <= double.sizeof && F[N].sizeof >= (double[2]).sizeof) | // { | // import mir.utility; | // enum S = _avx ? 32u : 16u; | // enum M = min(S, F[N].sizeof) / F.sizeof; | // alias V = __vector(F[M]); // @FUTURE@ vector support | // enum C = N / M; | // foreach(i; Iota!C) | // { | // auto r = kernel( | // *cast(V*)(a0.ptr + i * M), *cast(V*)(b0.ptr + i * M), | // *cast(V*)(a1.ptr + i * M), *cast(V*)(b1.ptr + i * M), | // ); | // static if (R == 1) | // *cast(V*)(c[0].ptr + i * M) = r; | // else | // foreach(j; Iota!R) | // *cast(V*)(c[j].ptr + i * M) = r[j]; | // } | // } | // else | // { | foreach(i; Iota!N) | { 1615| auto r = kernel(a0[i], b0[i], a1[i], b1[i]); | static if (R == 1) 1551| c[0][i] = r; | else | foreach(j; Iota!R) 168| c[j][i] = r[j]; | } | // } |} | |auto vectorize(Kernel, F, size_t N, size_t R)(ref Kernel kernel, ref F[N] a, ref F[N] b, ref F[N][R] c) |{ | // static if (LDC && F.mant_dig != 64 && is(__vector(F[N]))) | // { | // alias V = __vector(F[N]); // @FUTURE@ vector support | // *cast(V[R]*) c.ptr = kernel(*cast(V*)a.ptr, *cast(V*)b.ptr); | // } | // else | // static if (F.sizeof <= double.sizeof && F[N].sizeof >= (double[2]).sizeof && x86_64) | // { | // import mir.utility; | // enum S = _avx ? 32u : 16u; | // enum M = min(S, F[N].sizeof) / F.sizeof; | // alias V = __vector(F[M]); // @FUTURE@ vector support | // enum C = N / M; | // foreach(i; Iota!C) | // { | // auto r = kernel( | // *cast(V*)(a.ptr + i * M), | // *cast(V*)(b.ptr + i * M), | // ); | // static if (R == 1) | // *cast(V*)(c[0].ptr + i * M) = r; | // else | // foreach(j; Iota!R) | // *cast(V*)(c[j].ptr + i * M) = r[j]; | // } | // } | // else | // { 290| F[N][R] _c;//Temporary array in case "c" overlaps "a" and/or "b". | foreach(i; Iota!N) | { 566| auto r = kernel(a[i], b[i]); | static if (R == 1) 550| _c[0][i] = r; | else | foreach(j; Iota!R) 32| _c[j][i] = r[j]; | } 290| c = _c; | // } |} | |// version(unittest) |// template _test_fun(size_t R) |// { |// auto _test_fun(T)(T a0, T b0, T a1, T b1) |// { |// static if (R == 4) |// { |// return cast(T[R])[a0, b0, a1, b1]; |// } |// else |// static if (R == 2) |// { |// return cast(T[R])[a0 + b0, a1 + b1]; |// } |// else |// return a0 + b0 + a1 + b1; |// } |// } | |// unittest |// { |// import std.meta; | |// foreach(F; AliasSeq!(float, double)) |// foreach(N; AliasSeq!(1, 2, 4, 8, 16)) |// { |// align(F[N].sizeof) F[N] a0 = 4; |// align(F[N].sizeof) F[N] b0 = 30; |// align(F[N].sizeof) F[N] a1 = 200; |// align(F[N].sizeof) F[N] b1 = 1000; |// align(F[N].sizeof) F[N][1] c1; |// align(F[N].sizeof) F[N][2] c2; |// align(F[N].sizeof) F[N][4] c4; |// vectorize!(_test_fun!(1))(a0, b0, a1, b1, c1); |// vectorize!(_test_fun!(2))(a0, b0, a1, b1, c2); |// vectorize!(_test_fun!(4))(a0, b0, a1, b1, c4); |// } |// } source/mir/interpolate/package.d is 97% covered <<<<<< EOF # path=./..-..-..-.dub-packages-mir-core-1.1.83-mir-core-source-mir-primitives.lst |/++ |Templates used to check primitives and |range primitives for arrays with multi-dimensional like API support. | |Note: |UTF strings behaves like common arrays in Mir. |`std.uni.byCodePoint` can be used to create a range of characters. | |License: $(HTTP www.apache.org/licenses/LICENSE-2.0, Apache-2.0) |Authors: Ilya Yaroshenko, $(HTTP erdani.com, Andrei Alexandrescu), David Simcha, and | $(HTTP jmdavisprog.com, Jonathan M Davis). Credit for some of the ideas | in building this module goes to | $(HTTP fantascienza.net/leonardo/so/, Leonardo Maffi) |+/ |module mir.primitives; | |import mir.internal.utility; |import mir.math.common: optmath; |import std.traits; | |@optmath: | |/++ |Returns: `true` if `R` has a `length` member that returns an |integral type implicitly convertible to `size_t`. | |`R` does not have to be a range. |+/ |enum bool hasLength(R) = is(typeof( |(const R r, inout int = 0) |{ | size_t l = r.length; |})); | |/// |@safe version(mir_core_test) unittest |{ | static assert(hasLength!(char[])); | static assert(hasLength!(int[])); | static assert(hasLength!(inout(int)[])); | | struct B { size_t length() const { return 0; } } | struct C { @property size_t length() const { return 0; } } | static assert(hasLength!(B)); | static assert(hasLength!(C)); |} | |/++ |Returns: `true` if `R` has a `shape` member that returns an static array type of size_t[N]. |+/ |enum bool hasShape(R) = is(typeof( |(const R r, inout int = 0) |{ | auto l = r.shape; | alias F = typeof(l); | import std.traits; | static assert(isStaticArray!F); | static assert(is(ForeachType!F == size_t)); |})); | |/// |@safe version(mir_core_test) unittest |{ | static assert(hasShape!(char[])); | static assert(hasShape!(int[])); | static assert(hasShape!(inout(int)[])); | | struct B { size_t length() const { return 0; } } | struct C { @property size_t length() const { return 0; } } | static assert(hasShape!(B)); | static assert(hasShape!(C)); |} | |/// |auto shape(Range)(scope const auto ref Range range) @property | if (hasLength!Range || hasShape!Range) |{ | static if (__traits(hasMember, Range, "shape")) | { | return range.shape; | } | else | { | size_t[1] ret; | ret[0] = range.length; | return ret; | } |} | |/// |version(mir_core_test) unittest |{ | static assert([2, 2, 2].shape == [3]); |} | |/// |template DimensionCount(T) |{ | import mir.ndslice.slice: Slice, SliceKind; | /// Extracts dimension count from a $(LREF Slice). Alias for $(LREF isSlice). | static if(is(T : Slice!(Iterator, N, kind), Iterator, size_t N, SliceKind kind)) | enum size_t DimensionCount = N; | else | static if (hasShape!T) | enum size_t DimensionCount = typeof(T.init.shape).length; | else | enum size_t DimensionCount = 1; |} | |package(mir) bool anyEmptyShape(size_t N)(scope const auto ref size_t[N] shape) @property |{ | foreach (i; Iota!N) | if (shape[i] == 0) | return true; | return false; |} | |/// |bool anyEmpty(Range)(scope const auto ref Range range) @property | if (hasShape!Range || __traits(hasMember, Range, "anyEmpty")) |{ | static if (__traits(hasMember, Range, "anyEmpty")) | { | return range.anyEmpty; | } | else | static if (__traits(hasMember, Range, "shape")) | { | return anyEmptyShape(range.shape); | } | else | { | return range.empty; | } |} | |/// |size_t elementCount(Range)(scope const auto ref Range range) @property | if (hasShape!Range || __traits(hasMember, Range, "elementCount")) |{ | static if (__traits(hasMember, Range, "elementCount")) | { | return range.elementCount; | } | else | { | auto sh = range.shape; | size_t ret = sh[0]; | foreach(i; Iota!(1, sh.length)) | { | ret *= sh[i]; | } | return ret; | } |} | |deprecated("use elementCount instead") |alias elementsCount = elementCount; | | |/++ |Returns the element type of a struct with `.DeepElement` inner alias or a type of common array. |Returns `ForeachType` if struct does not have `.DeepElement` member. |+/ |template DeepElementType(S) | if (is(S == struct) || is(S == class) || is(S == interface)) |{ | static if (__traits(hasMember, S, "DeepElement")) | alias DeepElementType = S.DeepElement; | else | alias DeepElementType = ForeachType!S; |} | |/// ditto |alias DeepElementType(S : T[], T) = T; | |/+ ARRAY PRIMITIVES +/ |pragma(inline, true): | |/// |bool empty(size_t dim = 0, T)(scope const T[] ar) | if (!dim) |{ | return !ar.length; |} | |/// |version(mir_core_test) unittest |{ | assert((int[]).init.empty); | assert(![1].empty!0); // Slice-like API |} | |/// |ref inout(T) front(size_t dim = 0, T)(scope return inout(T)[] ar) | if (!dim && !is(Unqual!T[] == void[])) |{ | assert(ar.length, "Accessing front of an empty array."); | return ar[0]; |} | |/// |version(mir_core_test) unittest |{ | assert(*&[3, 4].front == 3); // access be ref | assert([3, 4].front!0 == 3); // Slice-like API |} | | |/// |ref inout(T) back(size_t dim = 0, T)(scope return inout(T)[] ar) | if (!dim && !is(Unqual!T[] == void[])) |{ | assert(ar.length, "Accessing back of an empty array."); | return ar[$ - 1]; |} | |/// |version(mir_core_test) unittest |{ | assert(*&[3, 4].back == 4); // access be ref | assert([3, 4].back!0 == 4); // Slice-like API |} | |/// |void popFront(size_t dim = 0, T)(scope ref inout(T)[] ar) | if (!dim && !is(Unqual!T[] == void[])) |{ | assert(ar.length, "Evaluating popFront() on an empty array."); | ar = ar[1 .. $]; |} | |/// |version(mir_core_test) unittest |{ | auto ar = [3, 4]; | ar.popFront; | assert(ar == [4]); | ar.popFront!0; // Slice-like API | assert(ar == []); |} | |/// |void popBack(size_t dim = 0, T)(scope ref inout(T)[] ar) | if (!dim && !is(Unqual!T[] == void[])) |{ | assert(ar.length, "Evaluating popBack() on an empty array."); | ar = ar[0 .. $ - 1]; |} | |/// |version(mir_core_test) unittest |{ | auto ar = [3, 4]; | ar.popBack; | assert(ar == [3]); | ar.popBack!0; // Slice-like API | assert(ar == []); |} | |/// |size_t popFrontN(size_t dim = 0, T)(scope ref inout(T)[] ar, size_t n) | if (!dim && !is(Unqual!T[] == void[])) |{ | n = ar.length < n ? ar.length : n; | ar = ar[n .. $]; | return n; |} | |/// |version(mir_core_test) unittest |{ | auto ar = [3, 4]; | ar.popFrontN(1); | assert(ar == [4]); | ar.popFrontN!0(10); // Slice-like API | assert(ar == []); |} | |/// |size_t popBackN(size_t dim = 0, T)(scope ref inout(T)[] ar, size_t n) | if (!dim && !is(Unqual!T[] == void[])) |{ | n = ar.length < n ? ar.length : n; | ar = ar[0 .. $ - n]; | return n; |} | |/// |version(mir_core_test) unittest |{ | auto ar = [3, 4]; | ar.popBackN(1); | assert(ar == [3]); | ar.popBackN!0(10); // Slice-like API | assert(ar == []); |} | |/// |void popFrontExactly(size_t dim = 0, T)(scope ref inout(T)[] ar, size_t n) | if (!dim && !is(Unqual!T[] == void[])) |{ | assert(ar.length >= n, "Evaluating *.popFrontExactly(n) on an array with length less then n."); | ar = ar[n .. $]; |} | |/// |version(mir_core_test) unittest |{ | auto ar = [3, 4, 5]; | ar.popFrontExactly(2); | assert(ar == [5]); | ar.popFrontExactly!0(1); // Slice-like API | assert(ar == []); |} | |/// |void popBackExactly(size_t dim = 0, T)(scope ref inout(T)[] ar, size_t n) | if (!dim && !is(Unqual!T[] == void[])) |{ | assert(ar.length >= n, "Evaluating *.popBackExactly(n) on an array with length less then n."); | ar = ar[0 .. $ - n]; |} | |/// |version(mir_core_test) unittest |{ | auto ar = [3, 4, 5]; | ar.popBackExactly(2); | assert(ar == [3]); | ar.popBackExactly!0(1); // Slice-like API | assert(ar == []); |} | |/// |size_t length(size_t d : 0, T)(in T[] array) | if (d == 0) |{ | return array.length; |} | |/// |version(mir_core_test) unittest |{ | assert([1, 2].length!0 == 2); | assert([1, 2].elementCount == 2); |} | |/// |inout(T)[] save(T)(scope return inout(T)[] array) |{ | return array; |} | |/// |version(mir_core_test) unittest |{ | auto a = [1, 2]; | assert(a is a.save); |} | |/** |Returns `true` if `R` is an input range. An input range must |define the primitives `empty`, `popFront`, and `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 input range, `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)); | |/** |Returns `true` if `R` is an infinite input range. An |infinite input range is an input range that has a statically-defined |enumerated member called `empty` that is always `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; |} | | |/** |The element type of `R`. `R` does not have to be a range. The |element type is determined as the type yielded by `r.front` for an |object `r` of type `R`. For example, `ElementType!(T[])` is |`T` if `T[]` isn't a narrow string; if it is, the element type is |`dchar`. If `R` doesn't have `front`, `ElementType!R` is |`void`. | */ |template ElementType(R) |{ | static if (is(typeof(R.init.front.init) T)) | alias ElementType = T; | else | alias ElementType = void; |} | |/++ |This is a best-effort implementation of `length` for any kind of |range. |If `hasLength!Range`, simply returns `range.length` without |checking `upTo` (when specified). |Otherwise, walks the range through its length and returns the number |of elements seen. Performes $(BIGOH n) evaluations of `range.empty` |and `range.popFront()`, where `n` is the effective length of $(D |range). |+/ |auto walkLength(Range)(Range range) |if (isIterable!Range && !isInfinite!Range) |{ | static if (hasLength!Range) | return range.length; | else | static if (__traits(hasMember, Range, "walkLength")) | return range.walkLength; | static if (isInputRange!Range) | { | size_t result; | for ( ; !range.empty ; range.popFront() ) | ++result; | return result; | } | else | { | size_t result; | foreach (ref e; range) | ++result; | return result; | } |} | |/++ |Returns `true` if `R` is an output range for elements of type |`E`. An output range is defined functionally as a range that |supports the operation $(D r.put(e)). | +/ |enum bool isOutputRange(R, E) = | is(typeof(R.init.put(E.init))); ../../../.dub/packages/mir-core-1.1.83/mir-core/source/mir/primitives.d has no code <<<<<< EOF # path=./..-..-..-.dub-packages-mir-core-1.1.83-mir-core-source-mir-algebraic.lst |/++ |$(H2 Variant and Nullable types) | |This module implements a |$(HTTP erdani.org/publications/cuj-04-2002.php.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. | |The module defines generic $(LREF Algebraic) type that contains a payload. |The allowed types of the paylad are defined by the unordered $(LREF TypeSet). | |$(LREF Algebraic) template accepts two arguments: self type set id and a list of type sets. | |$(BOOKTABLE $(H3 $(LREF Algebraic) Aliases), |$(TR $(TH Name) $(TH Description)) |$(T2 Variant, an algebraic type) |$(T2 TaggedVariant, a tagged algebraic type) |$(T2 Nullable, an algebraic type with at least `typeof(null)`) |) | |$(BOOKTABLE $(H3 Visitor Handlers), |$(TR $(TH Name) $(TH Ensures can match) $(TH Throws if no match) $(TH Returns $(LREF Nullable)) $(TH Multiple dispatch) $(TH Argumments count) $(TH Algebraic first argument) $(TH Fuses Algebraic types on return)) |$(LEADINGROWN 8, Classic handlers) |$(T8 visit, Yes, N/A, No, No, 1+, Yes, No) |$(T8 optionalVisit, No, No, Yes, No, 1+, Yes, No) |$(T8 autoVisit, No, No, auto, No, 1+, Yes, No) |$(T8 tryVisit, No, Yes, No, No, 1+, Yes, No) |$(LEADINGROWN 8, Multiple dispatch and algebraic fusion on return) |$(T8 match, Yes, N/A, No, Yes, 0+, auto, Yes) |$(T8 optionalMatch, No, No, Yes, Yes, 0+, auto, Yes) |$(T8 autoMatch, No, No, auto, Yes, 0+, auto, Yes) |$(T8 tryMatch, No, Yes, No, Yes, 0+, auto, Yes) |$(LEADINGROWN 8, Member access) |$(T8 getMember, Yes, N/A, No, No, 1+, Yes, No) |$(T8 optionalGetMember, No, No, Yes, No, 1+, Yes, No) |$(T8 autoGetMember, No, No, auto, No, 1+, Yes, No) |$(T8 tryGetMember, No, Yes, No, No, 1+, Yes, No) |$(LEADINGROWN 8, Member access with algebraic fusion on return) |$(T8 matchMember, Yes, N/A, No, No, 1+, Yes, Yes) |$(T8 optionalMatchMember, No, No, Yes, No, 1+, Yes, Yes) |$(T8 autoMatchMember, No, No, auto, No, 1+, Yes, Yes) |$(T8 tryMatchMember, No, Yes, No, No, 1+, Yes, Yes) |) | |$(BOOKTABLE $(H3 Special Types), |$(TR $(TH Name) $(TH Description)) |$(T2plain `void`, It is usefull to indicate a possible return type of the visitor. Can't be accesed by reference. ) |$(T2plain `typeof(null)`, It is usefull for nullable types. Also, it is used to indicate that a visitor can't match the current value of the algebraic. Can't be accesed by reference. ) |$(T2 This, Dummy structure that is used to construct self-referencing algebraic types. Example: `Variant!(int, double, string, This*[2])`) |$(T2plain $(LREF SetAlias)`!setId`, Dummy structure that is used to construct cyclic-referencing lists of algebraic types. ) |$(T2 TaggedType, Dummy type used to associate tags with type. ) |) | |$(BOOKTABLE $(H3 $(LREF Algebraic) Traits), |$(TR $(TH Name) $(TH Description)) |$(T2 isVariant, Checks if the type is instance of $(LREF Algebraic).) |$(T2 isNullable, Checks if the type is instance of $(LREF Algebraic) with a self $(LREF TypeSet) that contains `typeof(null)`. ) |$(T2 isTaggedVariant, Checks if the type is instance of tagged $(LREF Algebraic).) |$(T2 isTypeSet, Checks if the types are the same as $(LREF TypeSet) of them. ) |$(T2 ValueTypeOfNullable, Gets type of $(LI $(LREF .Algebraic.get.2)) method. ) | |) | | |$(H3 Type Set) |$(UL |$(LI $(LREF TaggedTypeSet) is supported. Example:`TargetTypeSet!(["integer", "floating"], int, double)`). |$(LI Type set is unordered. Example:`TypeSet!(int, double)` and `TypeSet!(double, int)` are the same. ) |$(LI Duplicats are ignored. Example: `TypeSet!(float, int, float)` and `TypeSet!(int, float)` are the same. ) |$(LI Types are automatically unqualified if this operation can be performed implicitly. Example: `TypeSet!(const int) and `TypeSet!int` are the same. ) |$(LI Non trivial `TypeSet!(A, B, ..., etc)` is allowed.) |$(LI Trivial `TypeSet!T` is allowed.) |$(LI Empty `TypeSet!()` is allowed.) |) | |$(H3 Visitors) |$(UL |$(LI Visitors are allowed to return values of different types If there are more then one return type then the an $(LREF Algebraic) type is returned. ) |$(LI Visitors are allowed to accept additional arguments. The arguments can be passed to the visitor handler. ) |$(LI Multiple visitors can be passes to the visitor handler. ) |$(LI Visitors are matched according to the common $(HTTPS dlang.org/spec/function.html#function-overloading, Dlang Function Overloading) rules. ) |$(LI Visitors are allowed accept algebraic value by reference except the value of `typeof(null)`. ) |$(LI Visitors are called without algebraic value if its algebraic type is `void`. ) |$(LI If the visitors arguments has known types, then such visitors should be passed to a visitor handler before others to make the compiler happy. This includes visitors with no arguments, which is used to match `void` type. ) |) | |$(H3 Implementation Features) |$(UL |$(LI BetterC support. Runtime `TypeInfo` is not used.) |$(LI Copy-constructors and postblit constructors are supported. ) |$(LI `toHash`, `opCmp`. `opEquals`, and `toString` support. ) |$(LI No string or template mixins are used. ) |$(LI Optimised for fast execution. ) |) | |See_also: $(HTTPS en.wikipedia.org/wiki/Algebra_of_sets, Algebra of sets). | |License: $(HTTP www.apache.org/licenses/LICENSE-2.0, Apache-2.0) |Authors: Ilya Yaroshenko | |Macros: |T2plain=$(TR $(TDNW $1) $(TD $+)) |T2=$(TR $(TDNW $(LREF $1)) $(TD $+)) |T4=$(TR $(TDNW $(LREF $1)) $(TD $2) $(TD $3) $(TD $4)) |T8=$(TR $(TDNW $(LREF $1)) $(TD $2) $(TD $3) $(TD $4) $(TD $5) $(TD $6) $(TD $7) $(TD $8)) | |+/ |module mir.algebraic; | |import mir.internal.meta; |import mir.functional: naryFun; | |private static immutable variantExceptionMsg = "mir.algebraic: the algebraic stores other type then requested."; |private static immutable variantNullExceptionMsg = "mir.algebraic: the algebraic is empty and doesn't store any value."; |private static immutable variantMemberExceptionMsg = "mir.algebraic: the algebraic stores a type that isn't compatible with the user provided visitor and arguments."; | |version (D_Exceptions) |{ | private static immutable variantException = new Exception(variantExceptionMsg); | private static immutable variantNullException = new Exception(variantNullExceptionMsg); | private static immutable variantMemberException = new Exception(variantMemberExceptionMsg); |} | |private static struct _Null() |{ |@safe pure nothrow @nogc const: | int opCmp(_Null) { return 0; } | this(typeof(null)) inout {} | string toString() { return "null"; } |} | |private static struct _Void() |{ | @safe pure nothrow @nogc const: | int opCmp(_Void) { return 0; } | string toString() { return "void"; } |} | |/++ |Checks if the type is instance of $(LREF Algebraic). |+/ |enum bool isVariant(T) = is(T == Algebraic!Types, Types...); | |/// |@safe pure version(mir_core_test) unittest |{ | static assert(isVariant!(Variant!(int, string))); | static assert(isVariant!(const Variant!(int[], string))); | static assert(isVariant!(Nullable!(int, string))); | static assert(!isVariant!int); |} | |/++ |Checks if the type is instance of tagged $(LREF Algebraic). | |Tagged algebraics can be defined with $(LREF TaggedVariant). |+/ |enum bool isTaggedVariant(T) = isVariant!T && is(T.Kind == enum); | |/// |@safe pure version(mir_core_test) unittest |{ | static assert(!isTaggedVariant!int); | static assert(!isTaggedVariant!(Variant!(int, string))); | static assert(isTaggedVariant!(TaggedVariant!(["integer", "string"], int, string))); |} | |/++ |Checks if the type is instance of $(LREF Algebraic) with a self $(LREF TypeSet) that contains `typeof(null)`. |+/ |enum bool isNullable(T) = is(T == Algebraic!(typeof(null), Types), Types...); | |/// |@safe pure version(mir_core_test) unittest |{ | static assert(isNullable!(const Nullable!(int, string))); | static assert(isNullable!(Nullable!())); | | static assert(!isNullable!(Variant!())); | static assert(!isNullable!(Variant!string)); | static assert(!isNullable!int); | static assert(!isNullable!string); |} | |/++ |Gets type of $(LI $(LREF .Algebraic.get.2)) method. |+/ |template ValueTypeOfNullable(T : Algebraic!(typeof(null), Types), Types...) |{ | static if (Types.length == 1) | alias ValueTypeOfNullable = Types[0]; | else | alias ValueTypeOfNullable = Algebraic!Types; |} | |/// |@safe pure version(mir_core_test) unittest |{ | static assert(is(ValueTypeOfNullable!(const Nullable!(int, string)) == Algebraic!(int, string))); | static assert(is(ValueTypeOfNullable!(Nullable!()) == Algebraic!())); | static assert(is(typeof(Nullable!().get()) == Algebraic!())); |} | |/++ |Dummy type for $(LREF Variant) and $(LREF Nullable) self-referencing. |+/ |struct This |{ |@safe pure nothrow @nogc const: 0000000| int opCmp(typeof(this)) { return 0; } 0000000| string toString() { return typeof(this).stringof; } |} | |/++ |Dummy type used to associate tags with a type. |+/ |struct TaggedType(T, string name) | if (name.length) |{ | private enum tag = name; | private alias Type = T; | static if (!is(T == void) && !is(T == typeof(null))) | private T payload; |@safe pure nothrow @nogc const: | int opCmp(typeof(this)) { return 0; } | string toString() { return typeof(this).stringof; } |} | |/++ |Checks if `T` is $(LREF TaggedType) instance. |+/ |enum isTaggedType(T) = is(T == TaggedType!(I, name), I, string name); | |/++ |Gets $(LREF TaggedType) underlying type. |+/ |alias getTaggedTypeUnderlying(T : TaggedType!(I, name), I, string name) = I; | |/++ |Gets $(LREF TaggedType) tag name. |+/ |enum getTaggedTypeName(T : TaggedType!(I, name), I, string name) = name; | |// example from std.variant |/++ |$(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 $(LREF Variant) by using $(LREF This) as a placeholder whenever a |reference to the type being defined is needed. The $(LREF Variant) 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 $(LREF This) |with the self-referenced type. The structure of the type involving $(LREF This) may |be arbitrarily complex. |+/ |@safe pure version(mir_core_test) unittest |{ | import mir.functional: Tuple = RefTuple; | | // A tree is either a leaf or a branch of two others | alias Tree(Leaf) = Variant!(Leaf, Tuple!(This*, This*)); | alias Leafs = Tuple!(Tree!int*, Tree!int*); | | Tree!int tree = Leafs(new Tree!int(41), new Tree!int(43)); | Tree!int* right = tree.get!Leafs[1]; | assert(*right == 43); |} | |/// |@safe pure version(mir_core_test) unittest |{ | // An object is a double, a string, or a hash of objects | alias Obj = Variant!(double, string, This[string], This[]); | alias Map = Obj[string]; | | Obj obj = "hello"; | assert(obj._is!string); | assert(obj.trustedGet!string == "hello"); | obj = 42.0; | assert(obj.get!double == 42); | obj = ["customer": Obj("John"), "paid": Obj(23.95)]; | assert(obj.get!Map["customer"] == "John"); |} | | |/++ |Type set resolution template used to construct $(LREF Algebraic) . |+/ |template TypeSet(T...) |{ | import std.meta: staticSort, staticMap, allSatisfy, anySatisfy; | // sort types by sizeof and them mangleof | // but typeof(null) goes first | static if (is(staticMap!(TryRemoveConst, T) == T)) | static if (is(NoDuplicates!T == T)) | static if (staticIsSorted!(TypeCmp, T)) | { | static if (anySatisfy!(isTaggedType, T)) | { | import std.meta: Filter, templateNot; | static assert(Filter!(templateNot!isTaggedType, T).length == 0, | "Either all or none types must be tagged. Types that doesn't have tags: " ~ | Filter!(templateNot!isTaggedType, T).stringof); | } | alias TypeSet = T; | } | else | alias TypeSet = .TypeSet!(staticSort!(TypeCmp, T)); | else | alias TypeSet = TypeSet!(NoDuplicates!T); | else | alias TypeSet = TypeSet!(staticMap!(TryRemoveConst, T)); |} | |private template TypeCmp(A, B) |{ | enum bool TypeCmp = is(A == B) ? false: | is(A == typeof(null)) || is(A == TaggedType!(typeof(null), aname), string aname) ? true: | is(B == typeof(null)) || is(B == TaggedType!(typeof(null), bname), string bname) ? false: | is(A == void) || is(A == TaggedType!(void, aname), string aname) ? true: | is(B == void) || is(A == TaggedType!(void, bname), string bname) ? false: | A.sizeof < B.sizeof ? true: | A.sizeof > B.sizeof ? false: | A.mangleof < B.mangleof; |} | |/// |version(mir_core_test) unittest |{ | struct S {} | alias C = S; | alias Int = int; | static assert(is(TypeSet!(S, int) == TypeSet!(Int, C))); | static assert(is(TypeSet!(S, int, int) == TypeSet!(Int, C))); | static assert(!is(TypeSet!(uint, S) == TypeSet!(int, S))); |} | |private template applyTags(string[] tagNames, T...) | if (tagNames.length == T.length) |{ | import std.meta: AliasSeq; | static if (tagNames.length == 0) | alias applyTags = AliasSeq!(); | else | alias applyTags = AliasSeq!(TaggedType!(T[0], tagNames[0]), .applyTags!(tagNames[1 .. $], T[1 .. $])); |} | |/++ |Type set for tagged $(LREF Variants) self-referencing. |+/ |alias TaggedTypeSet(string[] tagNames, T...) = TypeSet!(applyTags!(tagNames, T)); | |/++ |Checks if the type list is $(LREF TypeSet). |+/ |enum bool isTypeSet(T...) = is(T == TypeSet!T); | |/// |@safe pure version(mir_core_test) unittest |{ | static assert(isTypeSet!(TypeSet!())); | static assert(isTypeSet!(TypeSet!void)); | static assert(isTypeSet!(TypeSet!(void, int, typeof(null)))); |} | |/++ |Variant Type (aka Algebraic Type). | |The impllementation is defined as |---- |alias Variant(T...) = Algebraic!(TypeSet!T); |---- | |Compatible with BetterC mode. |+/ |alias Variant(T...) = Algebraic!(TypeSet!T); | |/// |@safe pure @nogc |version(mir_core_test) unittest |{ | Variant!(int, double, string) v = 5; | assert(v.get!int == 5); | v = 3.14; | assert(v == 3.14); | // auto x = v.get!long; // won't compile, type long not allowed | // v = '1'; // won't compile, type char not allowed |} | |/// Single argument Variant |// and Type with copy constructor |@safe pure nothrow @nogc |version(mir_core_test) unittest |{ | static struct S | { | int n; | this(ref return scope inout S rhs) inout | { | this.n = rhs.n + 1; | } | } | | Variant!S a = S(); | auto b = a; | | import mir.conv; | assert(a.get!S.n == 0); | assert(b.n == 1); //direct access of a member in case of all algebraic types has this member |} | |/// Empty type set |@safe pure nothrow @nogc version(mir_core_test) unittest |{ | Variant!() a; | auto b = a; | assert(a.toHash == 0); | assert(a == b); | assert(a <= b && b >= a); | static assert(typeof(a).sizeof == 1); |} | |/// Small types |@safe pure nothrow @nogc version(mir_core_test) unittest |{ | struct S { ubyte d; } | static assert(Nullable!(byte, char, S).sizeof == 2); |} | |@safe pure nothrow @nogc version(mir_core_test) unittest |{ | struct S { ubyte[3] d; } | static assert(Nullable!(ushort, wchar, S).sizeof == 6); |} | |// /// opPostMove support |// @safe pure @nogc nothrow |// version(mir_core_test) unittest |// { |// import std.algorithm.mutation: move; | |// static struct S |// { |// uint s; | |// void opPostMove(const ref S old) nothrow |// { |// this.s = old.s + 1; |// } |// } | |// Variant!S a; | |// auto b = a.move; |// assert(b.s == 1); |// } | |/++ |Tagged Variant Type (aka Tagged Algebraic Type). | |Compatible with BetterC mode. | |Template has two declarations: |---- |alias TaggedVariant(string[] tags, T...) = Variant!(applyTags!(tags, T)); |// and |template TaggedVariant(T) | if (is(T == union)) |{ | ... |} |---- | |See_also: $(LREF Variant), $(LREF isTaggedVariant). |+/ |alias TaggedVariant(string[] tags, T...) = Variant!(applyTags!(tags, T)); | |/// ditto |template TaggedVariant(T) | if (is(T == union)) |{ | import std.meta: staticMap; | enum names = __traits(allMembers, T); | alias TypeOf(string member) = typeof(__traits(getMember, T, member)); | alias Types = staticMap!(TypeOf, names); | alias TaggedVariant = .TaggedVariant!([names], Types); |} | |/// Json Value |@safe pure |version(mir_core_test) unittest |{ | static union JsonUnion | { | long integer; | double floating; | bool boolean; | typeof(null) null_; | string string_; | This[] array; | This[string] object; | } | | alias JsonValue = TaggedVariant!JsonUnion; | | // typeof(null) has priority | static assert(JsonValue.Kind.init == JsonValue.Kind.null_); | static assert(JsonValue.Kind.null_ == 0); | | // Kind and AllowedTypes has the same order | static assert (is(JsonValue.AllowedTypes[JsonValue.Kind.array] == JsonValue[])); | static assert (is(JsonValue.AllowedTypes[JsonValue.Kind.boolean] == bool)); | static assert (is(JsonValue.AllowedTypes[JsonValue.Kind.floating] == double)); | static assert (is(JsonValue.AllowedTypes[JsonValue.Kind.integer] == long)); | static assert (is(JsonValue.AllowedTypes[JsonValue.Kind.null_] == typeof(null))); | static assert (is(JsonValue.AllowedTypes[JsonValue.Kind.object] == JsonValue[string])); | | JsonValue v; | assert(v.kind == JsonValue.Kind.null_); | | v = 1; | assert(v.kind == JsonValue.Kind.integer); | assert(v == 1); | v = JsonValue(1); | assert(v == 1); | v = v.get!(long, double); | | v = "Tagged!"; | assert(v.get !string == "Tagged!"); | assert(v.trustedGet!string == "Tagged!"); | | assert(v.kind == JsonValue.Kind.string_); | | assert(v.get !(JsonValue.Kind.string_) == "Tagged!"); // Kind-based get | assert(v.trustedGet!(JsonValue.Kind.string_) == "Tagged!"); // Kind-based trustedGet | | v = [JsonValue("str"), JsonValue(4.3)]; | | assert(v.kind == JsonValue.Kind.array); | assert(v.trustedGet!(JsonValue[])[1].kind == JsonValue.Kind.floating); | | v = null; | assert(v.kind == JsonValue.Kind.null_); |} | |/// Wrapped algebraic with propogated primitives |@safe pure |version(mir_core_test) unittest |{ | static struct Response | { | alias Union = TaggedVariant!( | ["double_", "string", "array", "table"], | double, | string, | Response[], | Response[string], | ); | | Union data; | alias Tag = Union.Kind; | // propogates opEquals, opAssign, and other primitives | alias data this; | | static foreach (T; Union.AllowedTypes) | this(T v) @safe pure nothrow @nogc { data = v; } | } | | Response v = 3.0; | assert(v.kind == Response.Tag.double_); | v = "str"; | assert(v == "str"); |} | |/++ |Nullable $(LREF Variant) Type (aka Algebraic Type). | |The impllementation is defined as |---- |alias Nullable(T...) = Variant!(typeof(null), T); |---- | |In additional to common algebraic API the following members can be accesssed: |$(UL |$(LI $(LREF .Algebraic.isNull)) |$(LI $(LREF .Algebraic.nullify)) |$(LI $(LREF .Algebraic.get.2)) |) | |Compatible with BetterC mode. |+/ |alias Nullable(T...) = Variant!(typeof(null), T); | |/// ditto |Nullable!T nullable(T)(T t) |{ | import core.lifetime: forward; | return Nullable!T(forward!t); |} | |/++ |Single type `Nullable` |+/ |@safe pure @nogc |version(mir_core_test) unittest |{ | static assert(is(Nullable!int == Variant!(typeof(null), int))); | | Nullable!int a = 5; | assert(a.get!int == 5); | | a.nullify; | assert(a.isNull); | | a = 4; | assert(!a.isNull); | assert(a.get == 4); | assert(a == 4); | a = 4; | | a = null; | assert(a == null); |} | |/// Empty nullable type set support |@safe pure nothrow @nogc version(mir_core_test) unittest |{ | Nullable!() a; | auto b = a; | assert(a.toHash == 0); | assert(a == b); | assert(a <= b && b >= a); | static assert(typeof(a).sizeof == 1); |} | |/++ |Algebraic implementation. |For more portable code, it is higly recommeded to don't use this template directly. |Instead, please use of $(LREF Variant) and $(LREF Nullable), which sort types. |+/ |struct Algebraic(_Types...) |{ | import core.lifetime: moveEmplace; | import mir.conv: emplaceRef; | import mir.reflection: isPublic, hasField, isProperty; | import std.meta: Filter, AliasSeq, ApplyRight, anySatisfy, allSatisfy, staticMap, templateOr, templateNot; | import std.traits: | hasElaborateAssign, | hasElaborateCopyConstructor, | hasElaborateDestructor, | hasMember, | isEqualityComparable, | isOrderingComparable, | Largest, | Unqual | ; | | private enum bool _variant_test_ = is(_Types == AliasSeq!(typeof(null), double)); | | static if (anySatisfy!(isTaggedType, _Types)) | { | private alias _UntaggedThisTypeSetList = staticMap!(getTaggedTypeUnderlying, _Types); | } | else | { | private alias _UntaggedThisTypeSetList = _Types; | } | | /++ | Allowed types list | See_also: $(LREF TypeSet) | +/ | alias AllowedTypes = AliasSeq!(ReplaceTypeUnless!(isVariant, This, Algebraic!_Types, _UntaggedThisTypeSetList)); | | version(mir_core_test) | static if (_variant_test_) | /// | unittest | { | import std.meta: AliasSeq; | | alias V = Nullable! | ( | This*, | string, | double, | bool, | ); | | static assert(is(V.AllowedTypes == TypeSet!( | typeof(null), | bool, | string, | double, | V*))); | } | | private alias _Payload = Replace!(void, _Void!(), Replace!(typeof(null), _Null!(), AllowedTypes)); | | private static union _Storage_ | { | _Payload payload; | | static foreach (int i, P; _Payload) | mixin(`alias _member_` ~ i.stringof ~ ` = payload[` ~ i.stringof ~ `];`); | | static if (AllowedTypes.length == 0 || is(AllowedTypes == AliasSeq!(typeof(null)))) | ubyte[0] bytes; | else | ubyte[Largest!_Payload.sizeof] bytes; | } | | private _Storage_ _storage_; | | static if (AllowedTypes.length > 1) | { | static if ((_Storage_.alignof & 1) && _Payload.length <= ubyte.max) | private alias _ID_ = ubyte; | else | static if ((_Storage_.alignof & 2) && _Payload.length <= ushort.max) | private alias _ID_ = ushort; | else | static if (_Storage_.alignof & 3) | private alias _ID_ = uint; | else | private alias _ID_ = ulong; | | _ID_ _identifier_; | } | else | { | alias _ID_ = uint; | enum _ID_ _identifier_ = 0; | } | | version (D_Ddoc) | { | /++ | Algebraic Kind. | | Defined as enum for tagged algebraics and as unsigned for common algebraics. | | The Kind enum contains the members defined using tag names. | | If the algebraic type is $(LREF Nullable) then the default Kind enum member has zero value and corresponds to `typeof(null)`. | | See_also: $(LREF TaggedVariant). | +/ | enum Kind { _not_me_but_tags_name_list_ } | } | | static if (anySatisfy!(isTaggedType, _Types)) | { | version (D_Ddoc){} | else | { | mixin(enumKindText([staticMap!(getTaggedTypeName, _Types)])); | | } | } | else | { | version (D_Ddoc){} | else | { | alias Kind = _ID_; | } | } | | /++ | Returns: $(LREF .Algebraic.Kind). | | Defined as enum for tagged algebraics and as unsigned for common algebraics. | See_also: $(LREF TaggedVariant). | +/ | Kind kind() const @safe pure nothrow @nogc @property | { | assert(_identifier_ <= Kind.max); | return cast(Kind) _identifier_; | } | | static if (anySatisfy!(hasElaborateDestructor, _Payload)) | ~this() @trusted | { | S: switch (_identifier_) | { | static foreach (i, T; AllowedTypes) | static if (hasElaborateDestructor!T) | { | case i: | (*cast(Unqual!(_Payload[i])*)&_storage_.payload[i]).__xdtor; | break S; | } | default: | } | version(mir_secure_memory) | _storage_.bytes = 0xCC; | } | | // static if (anySatisfy!(hasOpPostMove, _Payload)) | // void opPostMove(const ref typeof(this) old) | // { | // S: switch (_identifier_) | // { | // static foreach (i, T; AllowedTypes) | // static if (hasOpPostMove!T) | // { | // case i: | // this._storage_.payload[i].opPostMove(old._storage_.payload[i]); | // return; | // } | // default: return; | // } | // } | | static if (AllowedTypes.length) | { | static if (!__traits(compiles, (){ _Payload[0] arg; })) | { | @disable this(); | } | } | | /// Construct an algebraic type from its subset. | this(RhsTypes...)(Algebraic!RhsTypes rhs) | if (allSatisfy!(Contains!AllowedTypes, Algebraic!RhsTypes.AllowedTypes)) | { | import core.lifetime: move; | static if (is(RhsTypes == _Types)) | this = move(rhs); | else | { | switch (rhs._identifier_) | { | static foreach (i, T; Algebraic!RhsTypes.AllowedTypes) | { | case i: | static if (__traits(compiles, __ctor(move(rhs.trustedGet!T)))) | __ctor(move(rhs.trustedGet!T)); | else | __ctor(rhs.trustedGet!T); | return; | } | default: | assert(0, variantMemberExceptionMsg); | } | } | } | | version(mir_core_test) | static if (_variant_test_) | /// | unittest | { | alias Float = Variant!(float, double); | alias Int = Variant!(long, int); | alias Number = Variant!(Float.AllowedTypes, Int.AllowedTypes); | | Float fp = 3.0; | Number number = fp; // constructor call | assert(number == 3.0); | | Int integer = 12L; | number = Number(integer); | assert(number == 12L); | } | | static if (!allSatisfy!(isCopyable, AllowedTypes)) | @disable this(this); | else | static if (anySatisfy!(hasElaborateCopyConstructor, AllowedTypes)) | { | // private enum _allCanImplicitlyRemoveConst = allSatisfy!(canImplicitlyRemoveConst, AllowedTypes); | // private enum _allCanRemoveConst = allSatisfy!(canRemoveConst, AllowedTypes); | // private enum _allHaveImplicitSemiMutableConstruction = _allCanImplicitlyRemoveConst && _allHaveMutableConstruction; | | static if (__VERSION__ < 2094) | private static union _StorageI(uint i) | { | _Payload[i] payload; | ubyte[_Storage_.bytes.length] bytes; | } | | static if (allSatisfy!(hasInoutConstruction, AllowedTypes)) | this(return ref scope inout Algebraic rhs) inout | { | static if (AllowedTypes.length > 1) this._identifier_ = rhs._identifier_; | static foreach (int i, T; AllowedTypes) | static if (!is(T == typeof(null)) && !is(T == void)) | { | if (_identifier_ == i) | { | static if (__VERSION__ < 2094) | { | _storage_.bytes = () inout @trusted { | auto ret = inout _StorageI!i(rhs.trustedGet!T); | return ret.bytes; | } (); | return; | } | else | { | _storage_ = () inout { | mixin(`inout _Storage_ ret = { _member_` ~ i.stringof ~ ` : rhs.trustedGet!T };`); | return ret; | } (); | return; | } | } | } | } | else | { | static if (allSatisfy!(hasMutableConstruction, AllowedTypes)) | this(return ref scope Algebraic rhs) | { | static if (AllowedTypes.length > 1) this._identifier_ = rhs._identifier_; | static foreach (int i, T; AllowedTypes) | static if (!is(T == typeof(null)) && !is(T == void)) | { | if (_identifier_ == i) | { | _storage_ = () { | mixin(`_Storage_ ret = { _member_` ~ i.stringof ~ ` : rhs.trustedGet!T };`); | return ret; | } (); | return; | } | } | } | | static if (allSatisfy!(hasConstConstruction, AllowedTypes)) | this(return ref scope const Algebraic rhs) const | { | static if (AllowedTypes.length > 1) this._identifier_ = rhs._identifier_; | static foreach (int i, T; AllowedTypes) | static if (!is(T == typeof(null)) && !is(T == void)) | { | if (_identifier_ == i) | { | _storage_ = () const { | mixin(`const _Storage_ ret = { _member_` ~ i.stringof ~ ` : rhs.trustedGet!T };`); | return ret; | } (); | return; | } | } | } | | static if (allSatisfy!(hasImmutableConstruction, AllowedTypes)) | this(return ref scope immutable Algebraic rhs) immutable | { | static if (AllowedTypes.length > 1) this._identifier_ = rhs._identifier_; | static foreach (int i, T; AllowedTypes) | static if (!is(T == typeof(null)) && !is(T == void)) | { | if (_identifier_ == i) | { | _storage_ = () immutable { | mixin(`immutable _Storage_ ret = { _member_` ~ i.stringof ~ ` : rhs.trustedGet!T };`); | return ret; | } (); | return; | } | } | } | | static if (allSatisfy!(hasSemiImmutableConstruction, AllowedTypes)) | this(return ref scope const Algebraic rhs) immutable | { | static if (AllowedTypes.length > 1) this._identifier_ = rhs._identifier_; | static foreach (int i, T; AllowedTypes) | static if (!is(T == typeof(null)) && !is(T == void)) | { | if (_identifier_ == i) | { | _storage_ = () const { | mixin(`immutable _Storage_ ret = { _member_` ~ i.stringof ~ ` : rhs.trustedGet!T };`); | return ret; | } (); | return; | } | } | } | | static if (allSatisfy!(hasSemiMutableConstruction, AllowedTypes)) | this(return ref scope const Algebraic rhs) | { | static if (AllowedTypes.length > 1) this._identifier_ = rhs._identifier_; | static foreach (int i, T; AllowedTypes) | static if (!is(T == typeof(null)) && !is(T == void)) | { | if (_identifier_ == i) | { | _storage_ = () const { | mixin(`const _Storage_ ret = { _member_` ~ i.stringof ~ ` : rhs.trustedGet!T };`); | return ret; | } (); | return; | } | } | } | } | } | | /++ | +/ | size_t toHash() @trusted nothrow const | { | static if (AllowedTypes.length == 0 || is(AllowedTypes == AliasSeq!(typeof(null)))) | { | return 0; | } | else | switch (_identifier_) | { | static foreach (i, T; AllowedTypes) | { | case i: | static if (is(T == void)) | return i; | else | static if (is(T == typeof(null))) | return i; | else | static if (__traits(compiles, hashOf(trustedGet!T, cast(size_t)i))) | return hashOf(trustedGet!T, cast(size_t)i); | else | { | debug pragma(msg, "Mir warning: can't compute hash of " ~ (const T).stringof); | return i; | } | } | default: assert(0); | } | } | | /++ | +/ | bool opEquals()(auto ref const typeof(this) rhs) const @trusted | { | static if (AllowedTypes.length == 0) | { | return true; | } | else | { | if (this._identifier_ != rhs._identifier_) | return false; | switch (_identifier_) | { | static foreach (i, T; AllowedTypes) | { | case i: | return this.trustedGet!T == rhs.trustedGet!T; | } | default: assert(0); | } | } | } | | /++ | +/ | static if (is(AllowedTypes == _Types)) | auto opCmp()(auto ref const typeof(this) rhs) const @trusted | { | static if (AllowedTypes.length == 0) | { | return 0; | } | else | { | import mir.internal.utility: isFloatingPoint; | if (auto d = int(this._identifier_) - int(rhs._identifier_)) | return d; | switch (_identifier_) | { | static foreach (i, T; AllowedTypes) | { | case i: | static if (__traits(compiles, __cmp(trustedGet!T, rhs.trustedGet!T))) | return __cmp(trustedGet!T, rhs.trustedGet!T); | else | static if (__traits(hasMember, T, "opCmp") && !is(T == U*, U)) | return this.trustedGet!T.opCmp(rhs.trustedGet!T); | else | // static if (isFloatingPoint!T) | // return trustedGet!T == rhs ? 0 : trustedGet!T - rhs.trustedGet!T; | // else | return this.trustedGet!T < rhs.trustedGet!T ? -1 : | this.trustedGet!T > rhs.trustedGet!T ? +1 : 0; | } | default: assert(0); | } | } | } | | /// Requires mir-algorithm package | string toString()() const | { | static if (AllowedTypes.length == 0) | { | return "Algebraic"; | } | else | { | import mir.conv: to; | switch (_identifier_) | { | static foreach (i, T; AllowedTypes) | { | case i: | static if (is(T == void)) | return "void"; | else | static if (is(T == typeof(null))) | return "null"; | else | static if (__traits(compiles, { auto s = to!string(trustedGet!T);})) | return to!string(trustedGet!T); | else | return AllowedTypes[i].stringof; | } | default: assert(0); | } | } | } | | ///ditto | void toString(W)(scope ref W w) const | { | static if (AllowedTypes.length == 0) | { | return w.put("Algebraic"); | } | else | { | switch (_identifier_) | { | static foreach (i, T; AllowedTypes) | { | case i: | static if (is(T == void)) | return w.put("void"); | else | static if (is(T == typeof(null))) | return w.put("null"); | else | static if (__traits(compiles, { import mir.format: print; print(w, trustedGet!T); })) | { import mir.format: print; print(w, trustedGet!T); } | else | w.put(AllowedTypes[i].stringof); | return; | } | default: assert(0); | } | } | } | | static if (is(AllowedTypes[0] == typeof(null))) | { | /// | bool opCast(C)() const | if (is(C == bool)) | { | return _identifier_ != 0; | } | | /// | Algebraic opCast(C)() const | if (is(C == Algebraic)) | { | return this; | } | | /// Defined if the first type is `typeof(null)` | bool isNull() const @property { return _identifier_ == 0; } | /// ditto | void nullify() { this = null; } | | /// ditto | auto get()() | if (allSatisfy!(isCopyable, AllowedTypes[1 .. $]) && AllowedTypes.length != 2) | { | import mir.utility: _expect; | if (_expect(!_identifier_, false)) | { | throw variantNullException; | } | static if (AllowedTypes.length != 2) | { | Algebraic!(_Types[1 .. $]) ret; | | S: switch (_identifier_) | { | static foreach (i, T; AllowedTypes[1 .. $]) | { | { | case i + 1: | if (!hasElaborateCopyConstructor!T && !__ctfe) | goto default; | ret = this.trustedGet!T; | break S; | } | } | default: | ret._storage_.bytes = this._storage_.bytes; | static if (ret.AllowedTypes.length > 1) | ret._identifier_ = cast(typeof(ret._identifier_))(this._identifier_ - 1); | } | return ret; | } | } | | static if (AllowedTypes.length == 2) | { | /++ | Gets the value if not null. If `this` is in the null state, and the optional | parameter `fallback` was provided, it will be returned. Without `fallback`, | calling `get` with a null state is invalid. | | When the fallback type is different from the Nullable type, `get(T)` returns | the common type. | | Params: | fallback = the value to return in case the `Nullable` is null. | | Returns: | The value held internally by this `Nullable`. | +/ | auto ref inout(AllowedTypes[1]) get() return inout | { | assert(_identifier_, "Called `get' on null Nullable!(" ~ AllowedTypes[1].stringof ~ ")."); | return trustedGet!(AllowedTypes[1]); | } | | version(mir_core_test) | static if (_variant_test_) | /// | @safe pure nothrow @nogc | unittest | { | enum E { a = "a", b = "b" } | Nullable!E f = E.a; | auto e = f.get(); | static assert(is(typeof(e) == E), Nullable!E.AllowedTypes.stringof); | assert(e == E.a); | | assert(f.get(E.b) == E.a); | | f = null; | assert(f.get(E.b) == E.b); | } | | /// ditto | @property auto ref inout(AllowedTypes[1]) get()(auto ref inout(AllowedTypes[1]) fallback) return inout | { | return isNull ? fallback : get(); | } | } | } | | /++ | Checks if the underlaying type is an element of a user provided type set. | +/ | bool _is(R : Algebraic!RetTypes, RetTypes...)() @safe pure nothrow @nogc const @property | if (allSatisfy!(Contains!AllowedTypes, Algebraic!RetTypes.AllowedTypes)) | { | static if (is(RetTypes == _Types)) | return true; | else | { | import std.meta: staticIndexOf; | import std.traits: CopyTypeQualifiers; | alias RhsAllowedTypes = Algebraic!RetTypes.AllowedTypes; | alias Ret = CopyTypeQualifiers!(This, Algebraic!RetTypes); | // uint rhsTypeId; | switch (_identifier_) | { | foreach (i, T; AllowedTypes) | static if (staticIndexOf!(T, RhsAllowedTypes) >= 0) | { | case i: | return true; | } | default: | return false; | } | } | } | | /// ditto | bool _is(RetTypes...)() @safe pure nothrow @nogc const @property | if (RetTypes.length > 1) | { | return this._is!(Variant!RetTypes); | } | | /++ | `nothrow` $(LREF .Algebraic.get) alternative that returns an algebraic subset. | +/ | auto ref trustedGet(R : Algebraic!RetTypes, this This, RetTypes...)() return @property | if (allSatisfy!(Contains!AllowedTypes, Algebraic!RetTypes.AllowedTypes)) | { | static if (is(RetTypes == _Types)) | return this; | else | { | import std.meta: staticIndexOf; | import std.traits: CopyTypeQualifiers; | alias RhsAllowedTypes = Algebraic!RetTypes.AllowedTypes; | alias Ret = CopyTypeQualifiers!(This, Algebraic!RetTypes); | // uint rhsTypeId; | switch (_identifier_) | { | foreach (i, T; AllowedTypes) | static if (staticIndexOf!(T, RhsAllowedTypes) >= 0) | { | case i: | static if (is(T == void)) | return (()@trusted => cast(Ret) Ret._void)(); | else | return Ret(trustedGet!T); | } | default: | assert(0, variantMemberExceptionMsg); | } | } | } | | /// ditto | template trustedGet(RetTypes...) | if (RetTypes.length > 1) | { | /// | auto ref trustedGet(this This)() return | { | return this.trustedGet!(Variant!RetTypes); | } | } | | version(mir_core_test) | static if (_variant_test_) | /// | @safe pure nothrow @nogc | unittest | { | alias Float = Variant!(float, double); | alias Int = Variant!(long, int); | alias Number = Variant!(Float.AllowedTypes, Int.AllowedTypes); | | Number number = 3.0; | assert(number._is!Float); | auto fp = number.trustedGet!Float; | static assert(is(typeof(fp) == Float)); | assert(fp == 3.0); | | // type list overload | number = 12L; | assert(number._is!(int, long)); | auto integer = number.trustedGet!(int, long); | static assert(is(typeof(integer) == Int)); | assert(integer == 12L); | } | | static if (anySatisfy!(isTaggedType, _Types)) | /// `trustedGet` overload that accept $(LREF .Algebraic.Kind). | alias trustedGet(Kind kind) = trustedGet!(AllowedTypes[kind]); | | /++ | Gets an algebraic subset. | | Throws: Exception if the storage contains value of the type that isn't represented in the allowed type set of the requested algebraic. | +/ | auto ref get(R : Algebraic!RetTypes, this This, RetTypes...)() return @property | if (allSatisfy!(Contains!AllowedTypes, Algebraic!RetTypes.AllowedTypes)) | { | static if (is(RetTypes == _Types)) | return this; | else | { | import std.meta: staticIndexOf; | import std.traits: CopyTypeQualifiers; | alias RhsAllowedTypes = Algebraic!RetTypes.AllowedTypes; | alias Ret = CopyTypeQualifiers!(This, Algebraic!RetTypes); | // uint rhsTypeId; | switch (_identifier_) | { | foreach (i, T; AllowedTypes) | static if (staticIndexOf!(T, RhsAllowedTypes) >= 0) | { | case i: | static if (is(T == void)) | return (()@trusted => cast(Ret) Ret._void)(); | else | return Ret(trustedGet!T); | } | default: | throw variantMemberException; | } | } | } | | /// ditto | template get(RetTypes...) | if (RetTypes.length > 1) | { | /// | auto ref get(this This)() return | { | return this.get!(Variant!RetTypes); | } | } | | version(mir_core_test) | static if (_variant_test_) | /// | @safe pure @nogc | unittest | { | alias Float = Variant!(float, double); | alias Int = Variant!(long, int); | alias Number = Variant!(Float.AllowedTypes, Int.AllowedTypes); | | Number number = 3.0; | auto fp = number.get!Float; | static assert(is(typeof(fp) == Float)); | assert(fp == 3.0); | | // type list overload | number = 12L; | auto integer = number.get!(int, long); | static assert(is(typeof(integer) == Int)); | assert(integer == 12L); | } | | static if (anySatisfy!(isTaggedType, _Types)) | /// `get` overload that accept $(LREF .Algebraic.Kind). | alias get(Kind kind) = get!(AllowedTypes[kind]); | | private alias _ReflectionTypes = AllowedTypes[is(AllowedTypes[0] == typeof(null)) .. $]; | | static if (_ReflectionTypes.length) | this(this This, Args...)(auto ref Args args) | if (Args.length && (Args.length > 1 || !isVariant!(Args[0]))) | { | import std.traits: CopyTypeQualifiers; | import core.lifetime: forward; | | template CanCompile(T) | { | alias Q = CopyTypeQualifiers!(This, T); | enum CanCompile = __traits(compiles, new Q(forward!args)); | } | | alias TargetType = Filter!(CanCompile, _ReflectionTypes); | static if (TargetType.length == 0) | static assert(0, typeof(this).stringof ~ ".this: no types can be constructed with arguments " ~ Args.stringof); | static assert(TargetType.length == 1, typeof(this).stringof ~ ".this: multiple types " ~ TargetType.stringof ~ " can be constructed with arguments " ~ Args.stringof); | alias TT = TargetType[0]; | static if (is(TT == struct) || is(TT == union)) | this(CopyTypeQualifiers!(This, TT)(forward!args)); | else | this(new CopyTypeQualifiers!(This, TT)(forward!args)); | } | | static if (_ReflectionTypes.length && allSatisfy!(isSimpleAggregateType, _ReflectionTypes)) | { | static foreach (member; AllMembersRec!(_ReflectionTypes[0])) | static if ( | member != "_ID_" && | member != "_identifier_" && | member != "_is" && | member != "_storage_" && | member != "_Storage_" && | member != "_variant_test_" && | member != "_void" && | member != "AllowedTypes" && | member != "get" && | member != "isNull" && | member != "kind" && | member != "Kind" && | member != "nullify" && | member != "opAssign" && | member != "opCast" && | member != "opCmp" && | member != "opEquals" && | member != "opPostMove" && | member != "toHash" && | member != "toString" && | member != "trustedGet" && | member != "deserializeFromAsdf" && | member != "deserializeFromIon" && | !(member.length >= 2 && member[0 .. 2] == "__")) | static if (allSatisfy!(ApplyRight!(hasMember, member), _ReflectionTypes)) | static if (!anySatisfy!(ApplyRight!(isMemberType, member), _ReflectionTypes)) | static if (allSatisfy!(ApplyRight!(isSingleMember, member), _ReflectionTypes)) | static if (allSatisfy!(ApplyRight!(isPublic, member), _ReflectionTypes)) | { | static if (allSatisfy!(ApplyRight!(hasField, member), _ReflectionTypes) && NoDuplicates!(staticMap!(ApplyRight!(memberTypeOf, member), _ReflectionTypes)).length == 1) | { | mixin(`ref ` ~ member ~q{()() inout return @trusted pure nothrow @nogc @property { return this.getMember!member; }}); | } | else | static if (allSatisfy!(ApplyRight!(templateOr!(hasField, isProperty), member), _ReflectionTypes)) | { | mixin(`auto ref ` ~ member ~q{(this This, Args...)(auto ref Args args) @property { static if (args.length) { import core.lifetime: forward; return this.getMember!member = forward!args; } else return this.getMember!member; }}); | } | static if (allSatisfy!(ApplyRight!(templateNot!(templateOr!(hasField, isProperty)), member), _ReflectionTypes)) | { | mixin(`auto ref ` ~ member ~q{(this This, Args...)(auto ref Args args) { static if (args.length) { import core.lifetime: forward; return this.getMember!member(forward!args); } else return this.getMember!member; }}); | } | } | } | | /// | ref opAssign(RhsTypes...)(Algebraic!RhsTypes rhs) return @trusted | if (RhsTypes.length < AllowedTypes.length && allSatisfy!(Contains!AllowedTypes, Algebraic!RhsTypes.AllowedTypes)) | { | import core.lifetime: forward; | static if (anySatisfy!(hasElaborateDestructor, AllowedTypes)) | this.__dtor(); | __ctor(forward!rhs); | return this; | } | | static foreach (int i, T; AllowedTypes) | { | /// Zero cost always nothrow `get` alternative | auto ref trustedGet(E)() @trusted @property return inout nothrow | if (is(E == T)) | { | assert (i == _identifier_); | static if (is(T == typeof(null))) | return null; | else | static if (is(T == void)) | return; | else | return _storage_.payload[i]; | } | | /++ | Throws: Exception if the storage contains value of other type | +/ | auto ref get(E)() @property return inout | if (is(E == T)) | { | import mir.utility: _expect; | static if (AllowedTypes.length > 1) | { | if (_expect(i != _identifier_, false)) | { | throw variantException; | } | } | return trustedGet!T; | } | | /++ | Checks if the storage stores an allowed type. | +/ | bool _is(E)() const @property nothrow @nogc | if (is(E == T)) | { | return _identifier_ == i; | } | | static if (is(T == void)) | { | /// Defined if `AllowedTypes` contains `void` | static Algebraic _void() | { | Algebraic ret; | ret._storage_ = () { | import core.lifetime: forward; | mixin(`_Storage_ ret = { _member_` ~ i.stringof ~ ` : _Void!().init };`); | return ret; | } (); | ret._identifier_ = i; | return ret; | } | } | else | { | /// | static if (isCopyable!(const T) || is(Unqual!T == T)) | this(T value) | { | import core.lifetime: forward; | static if (is(T == typeof(null))) | auto rhs = _Null!()(); | else | alias rhs = forward!value; | | static if (__VERSION__ < 2094 && anySatisfy!(hasElaborateCopyConstructor, AllowedTypes)) | { | _storage_.bytes = () @trusted { | auto ret = _StorageI!i(rhs); | return ret.bytes; | } (); | } | else | { | _storage_ = () { | mixin(`_Storage_ ret = { _member_` ~ i.stringof ~ ` : rhs };`); | return ret; | } (); | } | static if (_Payload.length > 1) | _identifier_ = i; | } | | /// ditto | static if (isCopyable!(const T)) | this(const T value) const | { | static if (is(T == typeof(null))) | auto rhs = _Null!()(); | else | alias rhs = value; | static if (__VERSION__ < 2094 && anySatisfy!(hasElaborateCopyConstructor, AllowedTypes)) | { | _storage_.bytes = () const @trusted { | auto ret = const _StorageI!i(rhs); | return ret.bytes; | } (); | } | else | { | _storage_ = () { | mixin(`const _Storage_ ret = { _member_` ~ i.stringof ~ ` : rhs };`); | return ret; | } (); | } | static if (_Payload.length > 1) | _identifier_ = i; | } | | /// ditto | static if (isCopyable!(immutable T)) | this(immutable T value) immutable | { | static if (is(T == typeof(null))) | auto rhs = _Null!()(); | else | alias rhs = value; | static if (__VERSION__ < 2094 && anySatisfy!(hasElaborateCopyConstructor, AllowedTypes)) | { | _storage_.bytes = () const @trusted { | auto ret = immutable _StorageI!i(rhs); | return ret.bytes; | } (); | } | else | { | _storage_ = () { | mixin(`immutable _Storage_ ret = { _member_` ~ i.stringof ~ ` : rhs };`); | return ret; | } (); | } | static if (_Payload.length > 1) | _identifier_ = i; | } | | static if (__traits(compiles, (ref T a, ref T b) { moveEmplace(a, b); })) | /// | ref opAssign(T rhs) return @trusted | { | import core.lifetime: forward; | static if (anySatisfy!(hasElaborateDestructor, AllowedTypes)) | this.__dtor(); | __ctor(forward!rhs); | return this; | } | | /++ | +/ | auto opEquals()(auto ref const T rhs) const | { | static if (AllowedTypes.length > 1) | if (_identifier_ != i) | return false; | return trustedGet!T == rhs; | } | | /++ | +/ | auto opCmp()(auto ref const T rhs) const | { | import mir.internal.utility: isFloatingPoint; | static if (AllowedTypes.length > 1) | if (auto d = int(_identifier_) - int(i)) | return d; | static if (__traits(compiles, __cmp(trustedGet!T, rhs))) | return __cmp(trustedGet!T, rhs); | else | static if (__traits(hasMember, T, "opCmp") && !is(T == U*, U)) | return trustedGet!T.opCmp(rhs); | else | static if (isFloatingPoint!T) | return trustedGet!T == rhs ? 0 : trustedGet!T - rhs; | else | return trustedGet!T < rhs ? -1 : | trustedGet!T > rhs ? +1 : 0; | } | | static if (is(Unqual!T == bool)) | { | private alias contains = Contains!AllowedTypes; | static if (contains!long && !contains!int) | { | this(int value) | { | this(long(value)); | } | | this(int value) const | { | this(long(value)); | } | | this(int value) immutable | { | this(long(value)); | } | | ref opAssign(int rhs) return @trusted | { | return opAssign(long(rhs)); | } | | auto opEquals()(int rhs) const | { | return opEquals(long(rhs)); | } | | auto opCmp()(int rhs) const | { | return opCmp(long(rhs)); | } | } | | static if (contains!ulong && !contains!uint) | { | this(uint value) | { | this(ulong(value)); | } | | this(uint value) const | { | this(ulong(value)); | } | | this(uint value) immutable | { | this(ulong(value)); | } | | ref opAssign(uint rhs) return @trusted | { | return opAssign(ulong(rhs)); | } | | auto opEquals()(uint rhs) const | { | return opEquals(ulong(rhs)); | } | | auto opCmp()(uint rhs) const | { | return opCmp(ulong(rhs)); | } | } | } | } | } |} | |/++ |Constructor and methods propagation. |+/ |version(mir_core_test) |unittest |{ | static struct Base | { | double d; | } | | static class C | { | // alias this members are supported | Base base; | alias base this; | | int a; | private string _b; | | @safe pure nothrow @nogc: | | string b() const @property { return _b; } | void b(string b) @property { _b = b; } | | int retArg(int v) { return v; } | | this(int a, string b) | { | this.a = a; | this._b = b; | } | } | | static struct S | { | string b; | int a; | | double retArg(double v) { return v; } | | // alias this members are supported | Base base; | alias base this; | } | | static void inc(ref int a) { a++; } | | alias V = Nullable!(C, S); // or Variant! | | auto v = V(2, "str"); | assert(v._is!C); | assert(v.a == 2); | assert(v.b == "str"); | // members are returned by reference if possible | inc(v.a); | assert(v.a == 3); | v.b = "s"; | assert(v.b == "s"); | // alias this members are supported | v.d = 10; | assert(v.d == 10); | // method call support | assert(v.retArg(100)._is!int); | assert(v.retArg(100) == 100); | | v = V("S", 5); | assert(v._is!S); | assert(v.a == 5); | assert(v.b == "S"); | // members are returned by reference if possible | inc(v.a); | assert(v.a == 6); | v.b = "s"; | assert(v.b == "s"); | // alias this members are supported | v.d = 15; | assert(v.d == 15); | // method call support | assert(v.retArg(300)._is!double); | assert(v.retArg(300) == 300.0); | |} | |// test CTFE |unittest |{ | struct S { string s;} | alias V = Nullable!(double, S); | enum a = V(1.9); | static assert (a == 1.9); | enum b = V(S("str")); | static assert(b == S("str")); | static auto foo(int r) | { | auto s = V(S("str")); | s = r; | return s; | } | | static assert(foo(3) == 3); | static auto bar(int r) | { | auto s = V(S("str")); | s = r; | return s.visit!((double d) => d, (_)=> 0.0)(); | } | assert(bar(3) == 3); | static assert(bar(3) == 3); | | static auto bar3(int r) | { | auto s = V(S("str")); | s = r; | return s.match!((double d) => d, (_)=> "3")(); | } | assert(bar(3) == 3); | static assert(bar(3) == 3); |} | |@safe pure @nogc nothrow |version(mir_core_test) unittest |{ | import core.stdc.string: memcmp; | | static struct C(ubyte payloadSize, bool isPOD, bool hasToHash = true, bool hasOpEquals = true) | { | ubyte[payloadSize] _payload; | | const: | | static if (!isPOD) | { | this(this) {} | ~this() {} | } | | @safe pure nothrow @nogc: | | | static if (hasToHash) | size_t toHash() { return hashOf(_payload); } | | static if (hasOpEquals) | auto opEquals(ref const typeof(this) rhs) @trusted { return memcmp(_payload.ptr, rhs._payload.ptr, _payload.length); } | auto opCmp(ref const typeof(this) rhs) { return _payload == rhs._payload; } | } | | static foreach (size1; [1, 2, 4, 8, 10, 16, 20]) | static foreach (size2; [1, 2, 4, 8, 10, 16, 20]) | static if (size1 != size2) | static foreach (isPOD; [true, false]) | static foreach (hasToHash; [true, false]) | static foreach (hasOpEquals; [true, false]) | {{ | alias T = Variant!( | double, | C!(size1, isPOD, hasToHash, hasOpEquals), | C!(size2, isPOD, hasToHash, hasOpEquals)); | // static assert (__traits(compiles, T.init <= T.init)); | }} |} | |// const propogation |@safe pure nothrow @nogc |version(mir_core_test) unittest |{ | static struct S1 { immutable(ubyte)* value; } | static struct C1 { immutable(uint)* value; } | | alias V = Variant!(S1, C1); | const V v = S1(); | assert(v._is!S1); | V w = v; | w = v; | | immutable f = V(S1()); | auto t = immutable V(S1()); | // auto j = immutable V(t); | // auto i = const V(t); |} | |// ditto |@safe pure nothrow @nogc |version(mir_core_test) unittest |{ | static struct S2 { | uint* value; | this(return ref scope const typeof(this) rhs) {} | ref opAssign(typeof(this) rhs) return { return this; } | } | static struct C2 { const(uint)* value; } | | alias V = Variant!(S2, C2); | const V v = S2(); | V w = v; | w = S2(); | w = v; | w = cast(const) V.init; | | const f = V(S2()); | auto t = const V(f); |} | |@safe pure nothrow @nogc |version(mir_core_test) unittest |{ | static struct S3 { | uint* value; | this(return ref scope typeof(this) rhs) {} | this(return ref scope const typeof(this) rhs) const {} | this(return ref scope immutable typeof(this) rhs) immutable {} | } | static struct C3 { immutable(uint)* value; } | | S3 s; | S3 r = s; | r = s; | r = S3.init; | | alias V = Variant!(S3, C3); | V v = S3(); | V w = v; | w = S3(); | w = V.init; | w = v; | | immutable V e = S3(); | auto t = immutable V(S3()); | auto j = const V(t); | auto h = t; | | immutable V l = C3(); | auto g = immutable V(C3()); |} | |@safe pure nothrow @nogc |version(mir_core_test) unittest |{ | static struct S4 { | uint* value; | this(return ref scope const typeof(this) rhs) pure immutable {} | } | static struct C4 { immutable(uint)* value; } | | | S4 s; | S4 r = s; | r = s; | r = S4.init; | | alias V = Variant!(S4, C4); | V v = S4(); | V w = v; | w = S4(); | w = V.init; | w = v; | | { | const V e = S4(); | const k = w; | auto t = const V(k); | auto j = immutable V(k); | } | | immutable V e = S4(); | immutable k = w; | auto t = immutable V(S4()); | auto j = const V(t); | auto h = t; | | immutable V l = C4(); | import core.lifetime; | auto g = immutable V(C4()); | immutable b = immutable V(s); |} | |@safe pure nothrow @nogc |version(mir_core_test) unittest |{ | import core.lifetime: move; | | static struct S5 { | immutable(uint)* value; | this(return ref scope typeof(this) rhs) {} | this(return ref scope const typeof(this) rhs) immutable {} | } | static struct C5 { immutable(uint)* value; } | | S5 s; | S5 r = s; | r = s; | r = S5.init; | | alias V = Variant!(S5, C5); | V v = S5(); | V w = v; | w = S5(); | w = V.init; | w = v; | | immutable V e = S5(); | immutable f = V(S5()); | immutable k = w; | auto t = immutable V(S5()); | auto j = const V(t); | auto h = t; | | immutable V l = C5(); | import core.lifetime; | immutable n = w.move; | auto g = immutable V(C5()); | immutable b = immutable V(s); |} | |@safe pure nothrow @nogc |version(mir_core_test) unittest |{ | static struct S { | uint* value; | this(this) @safe pure nothrow @nogc {} | // void opAssign(typeof(this) rhs) {} | } | static struct C { const(uint)* value; } | | S s; | S r = s; | r = s; | r = S.init; | | alias V = Variant!(S, C); | V v = S(); | V w = v; | w = S(); | w = V.init; | w = v; |} | |/++ |Applies a delegate or function to the given Variant depending on the held type, |ensuring that all types are handled by the visiting functions. |+/ |alias visit(visitors...) = visitImpl!(naryFun!visitors, Exhaustive.compileTime, false); | |/// |@safe pure @nogc nothrow |version(mir_core_test) unittest |{ | alias Number = Variant!(int, double); | | Number x = 23; | Number y = 1.0; | | assert(x.visit!((int v) => true, (float v) => false)); | assert(y.visit!((int v) => false, (float v) => true)); |} | |/// |@safe pure @nogc |version(mir_core_test) unittest |{ | alias Number = Nullable!(int, double); | | Number z = null; // default | Number x = 23; | Number y = 1.0; | | () nothrow { | assert(x.visit!((int v) => true, (float v) => false)); | assert(y.visit!((int v) => false, (v) => true)); | assert(z.visit!((typeof(null) v) => true, (v) => false)); | } (); | | auto xx = x.get; | static assert (is(typeof(xx) == Variant!(int, double))); | assert(xx.visit!((int v) => v, (float v) => 0) == 23); | assert(xx.visit!((ref v) => v) == 23); | | x = null; | y.nullify; | | assert(x.isNull); | assert(y.isNull); | assert(z.isNull); | assert(z == y); |} | |/++ |Checks $(LREF .Algebraic.toString) and `void` |$(LREF Algerbraic)`.toString` requries `mir-algorithm` package |+/ |@safe pure nothrow version(mir_core_test) unittest |{ | import mir.conv: to; | enum MIR_ALGORITHM = __traits(compiles, { import mir.format; }); | | alias visitorHandler = visit!( | (typeof(null)) => "NULL", | () => "VOID", | (ref r) {r += 1;}, // returns void | ); | | alias secondOrderVisitorHandler = visit!( | () => "SO VOID", // void => to "RV VOID" | (str) => str, // string to => it self | ); | | alias V = Nullable!(void, int); | static assert(is(V == Variant!(typeof(null), void, int))); | | V variant; | | assert(secondOrderVisitorHandler(visitorHandler(variant)) == "NULL"); | assert(variant.to!string == "null"); | | variant = V._void; | assert(variant._is!void); | assert(is(typeof(variant.get!void()) == void)); | | assert(secondOrderVisitorHandler(visitorHandler(variant)) == "VOID"); | assert(variant.to!string == "void"); | | variant = 5; | | assert(secondOrderVisitorHandler(visitorHandler(variant)) == "SO VOID"); | assert(variant == 6); | assert(variant.to!string == (MIR_ALGORITHM ? "6" : "int")); |} | |/++ |Behaves as $(LREF visit) but doesn't enforce at compile time that all types can be handled by the visiting functions. |Throws: Exception if `naryFun!visitors` can't be called with provided arguments |+/ |alias tryVisit(visitors...) = visitImpl!(naryFun!visitors, Exhaustive.exception, false); | |/// |@safe pure @nogc |version(mir_core_test) unittest |{ | alias Number = Variant!(int, double); | | Number x = 23; | | assert(x.tryVisit!((int v) => true)); |} | |/++ |Behaves as $(LREF visit) but doesn't enforce at compile time that all types can be handled by the visiting functions. |Returns: nullable variant, null value is used if `naryFun!visitors` can't be called with provided arguments. |+/ |alias optionalVisit(visitors...) = visitImpl!(naryFun!visitors, Exhaustive.nullable, false); | |/// |@safe pure @nogc nothrow |version(mir_core_test) unittest |{ | static struct S { int a; } | | Variant!(S, double) variant; | | alias optionalVisitInst = optionalVisit!((ref value) => value + 0); | | // do nothing because of variant isn't initialized | Nullable!double result = optionalVisitInst(variant); | assert(result.isNull); | | variant = S(2); | // do nothing because of lambda can't compile | result = optionalVisitInst(variant); | assert(result == null); | | variant = 3.0; | result = optionalVisitInst(variant); | assert (result == 3.0); |} | |/++ |Behaves as $(LREF visit) but doesn't enforce at compile time that all types can be handled by the visiting functions. |Returns: optionally nullable type, null value is used if `naryFun!visitors` can't be called with provided arguments. |+/ |alias autoVisit(visitors...) = visitImpl!(naryFun!visitors, Exhaustive.auto_, false); | | |/++ |Applies a delegate or function to the given arguments depending on the held type, |ensuring that all types are handled by the visiting functions. | |The handler supports multiple dispatch or multimethods: a feature of handler in which |a function or method can be dynamically dispatched based on the run time (dynamic) type or, |in the more general case, some other attribute of more than one of its arguments. | |Fuses algebraic types on return. | |See_also: $(HTTPS en.wikipedia.org/wiki/Multiple_dispatch, Multiple dispatch) |+/ |alias match(visitors...) = visitImpl!(naryFun!visitors, Exhaustive.compileTime, true); | |/// |version(mir_core_test) |unittest |{ | struct Asteroid { uint size; } | struct Spaceship { uint size; } | alias SpaceObject = Variant!(Asteroid, Spaceship); | | alias collideWith = match!( | (Asteroid x, Asteroid y) => "a/a", | (Asteroid x, Spaceship y) => "a/s", | (Spaceship x, Asteroid y) => "s/a", | (Spaceship x, Spaceship y) => "s/s", | ); | | import mir.utility: min; | | // Direct access of a member in case of all algebraic types has this member | alias oops = (a, b) => (a.size + b.size) > 3 && min(a.size, b.size) > 1; | | alias collide = (x, y) => oops(x, y) ? "big-boom" : collideWith(x, y); | | auto ea = Asteroid(1); | auto es = Spaceship(2); | auto oa = SpaceObject(ea); | auto os = SpaceObject(es); | | // Asteroid-Asteroid | assert(collide(ea, ea) == "a/a"); | assert(collide(ea, oa) == "a/a"); | assert(collide(oa, ea) == "a/a"); | assert(collide(oa, oa) == "a/a"); | | // Asteroid-Spaceship | assert(collide(ea, es) == "a/s"); | assert(collide(ea, os) == "a/s"); | assert(collide(oa, es) == "a/s"); | assert(collide(oa, os) == "a/s"); | | // Spaceship-Asteroid | assert(collide(es, ea) == "s/a"); | assert(collide(es, oa) == "s/a"); | assert(collide(os, ea) == "s/a"); | assert(collide(os, oa) == "s/a"); | | // Spaceship-Spaceship | assert(collide(es, es) == "big-boom"); | assert(collide(es, os) == "big-boom"); | assert(collide(os, es) == "big-boom"); | assert(collide(os, os) == "big-boom"); |} | |/++ |Behaves as $(LREF match) but doesn't enforce at compile time that all types can be handled by the visiting functions. |Throws: Exception if `naryFun!visitors` can't be called with provided arguments | |Fuses algebraic types on return. |+/ |alias tryMatch(visitors...) = visitImpl!(naryFun!visitors, Exhaustive.exception, true); | |/// |version(mir_core_test) |unittest |{ | import std.exception: assertThrown; | struct Asteroid { uint size; } | struct Spaceship { uint size; } | alias SpaceObject = Variant!(Asteroid, Spaceship); | | alias collideWith = tryMatch!( | (Asteroid x, Asteroid y) => "a/a", | // No visitor for A/S pair | // (Asteroid x, Spaceship y) => "a/s", | (Spaceship x, Asteroid y) => "s/a", | (Spaceship x, Spaceship y) => "s/s", | ); | | import mir.utility: min; | // Direct access of a member in case of all algebraic types has this member | alias oops = (a, b) => (a.size + b.size) > 3 && min(a.size, b.size) > 1; | | alias collide = (x, y) => oops(x, y) ? "big-boom" : collideWith(x, y); | | auto ea = Asteroid(1); | auto es = Spaceship(2); | auto oa = SpaceObject(ea); | auto os = SpaceObject(es); | | // Asteroid-Asteroid | assert(collide(ea, ea) == "a/a"); | assert(collide(ea, oa) == "a/a"); | assert(collide(oa, ea) == "a/a"); | assert(collide(oa, oa) == "a/a"); | | // Asteroid-Spaceship | assertThrown!Exception(collide(ea, es)); | assertThrown!Exception(collide(ea, os)); | assertThrown!Exception(collide(oa, es)); | assertThrown!Exception(collide(oa, os)); | | // not enough information to deduce the type from (ea, es) pair | static assert(is(typeof(collide(ea, es)) == void)); | // can deduce the type based on other return values | static assert(is(typeof(collide(ea, os)) == string)); | static assert(is(typeof(collide(oa, es)) == string)); | static assert(is(typeof(collide(oa, os)) == string)); | | // Spaceship-Asteroid | assert(collide(es, ea) == "s/a"); | assert(collide(es, oa) == "s/a"); | assert(collide(os, ea) == "s/a"); | assert(collide(os, oa) == "s/a"); | | // Spaceship-Spaceship | assert(collide(es, es) == "big-boom"); | assert(collide(es, os) == "big-boom"); | assert(collide(os, es) == "big-boom"); | assert(collide(os, os) == "big-boom"); |} | |/++ |Behaves as $(LREF match) but doesn't enforce at compile time that all types can be handled by the visiting functions. |Returns: nullable variant, null value is used if `naryFun!visitors` can't be called with provided arguments. | |Fuses algebraic types on return. |+/ |alias optionalMatch(visitors...) = visitImpl!(naryFun!visitors, Exhaustive.nullable, true); | |/// |version(mir_core_test) |unittest |{ | struct Asteroid { uint size; } | struct Spaceship { uint size; } | alias SpaceObject = Variant!(Asteroid, Spaceship); | | alias collideWith = optionalMatch!( | (Asteroid x, Asteroid y) => "a/a", | // No visitor for A/S pair | // (Asteroid x, Spaceship y) => "a/s", | (Spaceship x, Asteroid y) => "s/a", | (Spaceship x, Spaceship y) => "s/s", | ); | | import mir.utility: min; | // Direct access of a member in case of all algebraic types has this member | alias oops = (a, b) => (a.size + b.size) > 3 && min(a.size, b.size) > 1; | | alias collide = (x, y) => oops(x, y) ? "big-boom".nullable : collideWith(x, y); | | auto ea = Asteroid(1); | auto es = Spaceship(2); | auto oa = SpaceObject(ea); | auto os = SpaceObject(es); | | // Asteroid-Asteroid | assert(collide(ea, ea) == "a/a"); | assert(collide(ea, oa) == "a/a"); | assert(collide(oa, ea) == "a/a"); | assert(collide(oa, oa) == "a/a"); | | // Asteroid-Spaceship | // assert(collide(ea, es).isNull); // Compiler error: incompatible types | assert(collideWith(ea, es).isNull); // OK | assert(collide(ea, os).isNull); | assert(collide(oa, es).isNull); | assert(collide(oa, os).isNull); | | | // Spaceship-Asteroid | assert(collide(es, ea) == "s/a"); | assert(collide(es, oa) == "s/a"); | assert(collide(os, ea) == "s/a"); | assert(collide(os, oa) == "s/a"); | | // Spaceship-Spaceship | assert(collide(es, es) == "big-boom"); | assert(collide(es, os) == "big-boom"); | assert(collide(os, es) == "big-boom"); | assert(collide(os, os) == "big-boom"); | | // check types | | static assert(!__traits(compiles, collide(Asteroid.init, Spaceship.init))); | static assert(is(typeof(collideWith(Asteroid.init, Spaceship.init)) == Nullable!())); | | static assert(is(typeof(collide(Asteroid.init, Asteroid.init)) == Nullable!string)); | static assert(is(typeof(collide(Asteroid.init, SpaceObject.init)) == Nullable!string)); | static assert(is(typeof(collide(SpaceObject.init, Asteroid.init)) == Nullable!string)); | static assert(is(typeof(collide(SpaceObject.init, SpaceObject.init)) == Nullable!string)); | static assert(is(typeof(collide(SpaceObject.init, Spaceship.init)) == Nullable!string)); | static assert(is(typeof(collide(Spaceship.init, Asteroid.init)) == Nullable!string)); | static assert(is(typeof(collide(Spaceship.init, SpaceObject.init)) == Nullable!string)); | static assert(is(typeof(collide(Spaceship.init, Spaceship.init)) == Nullable!string)); |} | |/++ |Behaves as $(LREF match) but doesn't enforce at compile time that all types can be handled by the visiting functions. |Returns: optionally nullable type, null value is used if `naryFun!visitors` can't be called with provided arguments. | |Fuses algebraic types on return. |+/ |alias autoMatch(visitors...) = visitImpl!(naryFun!visitors, Exhaustive.auto_, true); | |/// |version(mir_core_test) |unittest |{ | struct Asteroid { uint size; } | struct Spaceship { uint size; } | alias SpaceObject = Variant!(Asteroid, Spaceship); | | alias collideWith = autoMatch!( | (Asteroid x, Asteroid y) => "a/a", | // No visitor for A/S pair | // (Asteroid x, Spaceship y) => "a/s", | (Spaceship x, Asteroid y) => "s/a", | (Spaceship x, Spaceship y) => "s/s", | ); | | import mir.utility: min; | // Direct access of a member in case of all algebraic types has this member | alias oops = (a, b) => (a.size + b.size) > 3 && min(a.size, b.size) > 1; | | import mir.conv: to; | alias collide = (x, y) => oops(x, y) ? "big-boom".to!(typeof(collideWith(x, y))) : collideWith(x, y); | | auto ea = Asteroid(1); | auto es = Spaceship(2); | auto oa = SpaceObject(ea); | auto os = SpaceObject(es); | | // Asteroid-Asteroid | assert(collide(ea, ea) == "a/a"); | assert(collide(ea, oa) == "a/a"); | assert(collide(oa, ea) == "a/a"); | assert(collide(oa, oa) == "a/a"); | | // Asteroid-Spaceship | // assert(collide(ea, es).isNull); // Compiler error: incompatible types | assert(collideWith(ea, es).isNull); // OK | assert(collide(ea, os).isNull); | assert(collide(oa, es).isNull); | assert(collide(oa, os).isNull); | | // Spaceship-Asteroid | assert(collide(es, ea) == "s/a"); | assert(collide(es, oa) == "s/a"); | assert(collide(os, ea) == "s/a"); | assert(collide(os, oa) == "s/a"); | | // Spaceship-Spaceship | assert(collide(es, es) == "big-boom"); | assert(collide(es, os) == "big-boom"); | assert(collide(os, es) == "big-boom"); | assert(collide(os, os) == "big-boom"); | | // check types | | static assert(!__traits(compiles, collide(Asteroid.init, Spaceship.init))); | static assert(is(typeof(collideWith(Asteroid.init, Spaceship.init)) == Nullable!())); | | static assert(is(typeof(collide(Asteroid.init, Asteroid.init)) == string)); | static assert(is(typeof(collide(SpaceObject.init, Asteroid.init)) == string)); | static assert(is(typeof(collide(Spaceship.init, Asteroid.init)) == string)); | static assert(is(typeof(collide(Spaceship.init, SpaceObject.init)) == string)); | static assert(is(typeof(collide(Spaceship.init, Spaceship.init)) == string)); | | static assert(is(typeof(collide(Asteroid.init, SpaceObject.init)) == Nullable!string)); | static assert(is(typeof(collide(SpaceObject.init, SpaceObject.init)) == Nullable!string)); | static assert(is(typeof(collide(SpaceObject.init, Spaceship.init)) == Nullable!string)); |} | |/++ |Applies a member handler to the given Variant depending on the held type, |ensuring that all types are handled by the visiting handler. |+/ |alias getMember(string member) = visitImpl!(getMemberHandler!member, Exhaustive.compileTime, false); | |/// |@safe pure @nogc nothrow |version(mir_core_test) unittest |{ | static struct S { auto bar(int a) { return a; } enum boolean = true; } | static struct C { alias bar = (double a) => a * 2; enum boolean = false; } | | alias V = Variant!(S, C); | | V x = S(); | V y = C(); | | static assert(is(typeof(x.getMember!"bar"(2)) == Variant!(int, double))); | assert(x.getMember!"bar"(2) == 2); | assert(y.getMember!"bar"(2) != 4); | assert(y.getMember!"bar"(2) == 4.0); | | // direct implementation | assert(x.bar(2) == 2); | assert(y.bar(2) != 4); | assert(y.bar(2) == 4.0); | assert(x.boolean); | assert(!y.boolean); |} | |/++ |Applies a member handler to the given Variant depending on the held type, |ensuring that all types are handled by the visiting handler. | |Fuses algebraic types on return. |+/ |alias matchMember(string member) = visitImpl!(getMemberHandler!member, Exhaustive.compileTime, true); | |/// |@safe pure @nogc nothrow |version(mir_core_test) unittest |{ | static struct S | { | Nullable!int m; | } | | static struct C | { | Variant!(float, double) m; | } | | alias V = Variant!(S, C); | | V x = S(2.nullable); | V y = C(Variant!(float, double)(4.0)); | | // getMember returns an algebraic of algebaics | static assert(is(typeof(x.getMember!"m") == Variant!(Variant!(float, double), Nullable!int))); | // matchMember returns a fused algebraic | static assert(is(typeof(x.matchMember!"m") == Nullable!(int, float, double))); | assert(x.matchMember!"m" == 2); | assert(y.matchMember!"m" != 4); | assert(y.matchMember!"m" == 4.0); |} | |/++ |Behaves as $(LREF getMember) but doesn't enforce at compile time that all types can be handled by the member visitor. |Throws: Exception if member can't be accessed with provided arguments |+/ |alias tryGetMember(string member) = visitImpl!(getMemberHandler!member, Exhaustive.exception, false); | |/// |@safe pure @nogc |version(mir_core_test) unittest |{ | static struct S { int bar(int a) { return a; }} | static struct C { alias Bar = (double a) => a * 2; } | | alias V = Variant!(S, C); | | V x = S(); | V y = C(); | | static assert(is(typeof(x.tryGetMember!"bar"(2)) == int)); | static assert(is(typeof(y.tryGetMember!"Bar"(2)) == double)); | assert(x.tryGetMember!"bar"(2) == 2); | assert(y.tryGetMember!"Bar"(2) == 4.0); |} | |/// |@safe pure @nogc nothrow |version(mir_core_test) unittest |{ | alias Number = Variant!(int, double); | | Number x = Number(23); | Number y = Number(1.0); | | assert(x.visit!((int v) => true, (float v) => false)); | assert(y.visit!((int v) => false, (float v) => true)); |} | |/++ |Behaves as $(LREF matchMember) but doesn't enforce at compile time that all types can be handled by the member visitor. |Throws: Exception if member can't be accessed with provided arguments | |Fuses algebraic types on return. |+/ |alias tryMatchMember(string member) = visitImpl!(getMemberHandler!member, Exhaustive.exception, true); | |/++ |Behaves as $(LREF getMember) but doesn't enforce at compile time that all types can be handled by the member visitor. |Returns: nullable variant, null value is used if the member can't be called with provided arguments. |+/ |alias optionalGetMember(string member) = visitImpl!(getMemberHandler!member, Exhaustive.nullable, false); | |/++ |Behaves as $(LREF matchMember) but doesn't enforce at compile time that all types can be handled by the member visitor. |Returns: nullable variant, null value is used if the member can't be called with provided arguments. | |Fuses algebraic types on return. |+/ |alias optionalMatchMember(string member) = visitImpl!(getMemberHandler!member, Exhaustive.nullable, true); | |/++ |Behaves as $(LREF getMember) but doesn't enforce at compile time that all types can be handled by the member visitor. |Returns: optionally nullable type, null value is used if the member can't be called with provided arguments. |+/ |alias autoGetMember(string member) = visitImpl!(getMemberHandler!member, Exhaustive.auto_, false); | |/++ |Behaves as $(LREF matchMember) but doesn't enforce at compile time that all types can be handled by the member visitor. |Returns: optionally nullable type, null value is used if the member can't be called with provided arguments. | |Fuses algebraic types on return. |+/ |alias autoMatchMember(string member) = visitImpl!(getMemberHandler!member, Exhaustive.auto_, true); | |private template getMemberHandler(string member) |{ | /// | auto ref getMemberHandler(V, Args...)(ref V value, auto ref Args args) | { | static if (Args.length == 0) | { | return __traits(getMember, value, member); | } | else | { | import core.lifetime: forward; | import mir.reflection: hasField; | static if (hasField!(V, member) && Args.length == 1) | return __traits(getMember, value, member) = forward!args; | else | return __traits(getMember, value, member)(forward!args); | } | } |} | |private template VariantReturnTypes(T...) |{ | import std.meta: staticMap; | | alias VariantReturnTypes = NoDuplicates!(staticMap!(TryRemoveConst, T)); |} | |private enum Exhaustive |{ | compileTime, | exception, | nullable, | auto_, |} | |private template nextVisitor(T, alias visitor, alias arg) |{ | static if (is(T == void)) | { | alias nextVisitor = visitor; | } | else | auto ref nextVisitor(NextArgs...)(auto ref NextArgs nextArgs) | { | import core.lifetime: forward; | return visitor(arg.trustedGet!T, forward!nextArgs); | } |} | |private template nextVisitor(alias visitor, alias arg) |{ | auto ref nextVisitor(NextArgs...)(auto ref NextArgs nextArgs) | { | import core.lifetime: forward; | return visitor(forward!arg, forward!nextArgs); | } |} | |private template visitThis(alias visitor, Exhaustive nextExhaustive, args...) |{ | auto ref visitThis(T, Args...)(auto ref Args args) | { | import core.lifetime: forward; | return .visitImpl!(nextVisitor!(T, visitor, forward!(args[0])), nextExhaustive, true)(forward!(args[1 .. $])); | } |} | |private template visitLast(alias visitor, args...) |{ | auto ref visitLast(T, Args...)(auto ref Args args) | { | import core.lifetime: forward; | static if (is(T == void)) | return visitor(forward!(args[1 .. $])); | else | return visitor(args[0].trustedGet!T, forward!(args[1 .. $])); | } |} | |private template visitImpl(alias visitor, Exhaustive exhaustive, bool fused) |{ | import std.meta: anySatisfy, staticMap, AliasSeq; | | /// | auto ref visitImpl(Args...)(auto ref Args args) | { | import core.lifetime: forward; | | static if (!anySatisfy!(isVariant, Args)) | { | static if (exhaustive == Exhaustive.compileTime) | { | return visitor(forward!args); | } | else | static if (exhaustive == Exhaustive.exception) | { | static if (__traits(compiles, visitor(forward!args))) | return visitor(forward!args); | else | throw variantMemberException; | } | else | static if (exhaustive == Exhaustive.nullable) | { | static if (__traits(compiles, visitor(forward!args))) | return Nullable!(typeof(visitor(forward!args)))(visitor(forward!args)); | else | return Nullable!().init; | } | else | static if (exhaustive == Exhaustive.auto_) | { | static if (__traits(compiles, visitor(forward!args))) | return visitor(forward!args); | else | return Nullable!().init; | } | else | static assert(0, "not implemented"); | } | else | static if (!isVariant!(Args[0])) | { | return .visitImpl!(nextVisitor!(visitor, args[0]), exhaustive, fused)(forward!(args[1 .. $])); | } | else | { | static if (fused && anySatisfy!(isVariant, Args[1 .. $])) | { | alias fun = visitThis!(visitor, exhaustive); | } | else | { | static assert (isVariant!(Args[0]), "First argument should be a Mir Algebraic type"); | alias fun = visitLast!visitor; | } | | template VariantReturnTypesImpl(T) | { | static if (__traits(compiles, fun!T(forward!args))) | static if (fused && is(typeof(fun!T(forward!args)) : Algebraic!Types, Types...)) | alias VariantReturnTypesImpl = TryRemoveConst!(typeof(fun!T(forward!args))).AllowedTypes; | else | alias VariantReturnTypesImpl = AliasSeq!(TryRemoveConst!(typeof(fun!T(forward!args)))); | else | static if (exhaustive == Exhaustive.auto_) | alias VariantReturnTypesImpl = AliasSeq!(typeof(null)); | else | alias VariantReturnTypesImpl = AliasSeq!(); | } | | static if (exhaustive == Exhaustive.nullable) | alias AllReturnTypes = NoDuplicates!(typeof(null), staticMap!(VariantReturnTypesImpl, Args[0].AllowedTypes)); | else | alias AllReturnTypes = NoDuplicates!(staticMap!(VariantReturnTypesImpl, Args[0].AllowedTypes)); | | switch (args[0]._identifier_) | { | static foreach (i, T; Args[0].AllowedTypes) | { | case i: | static if (__traits(compiles, fun!T(forward!args)) || exhaustive == Exhaustive.compileTime && !is(T == typeof(null))) | { | static if (AllReturnTypes.length == 1) | { | return fun!T(forward!args); | } | else | static if (is(VariantReturnTypesImpl!T == AliasSeq!void)) | { | fun!T(forward!args); | return Variant!AllReturnTypes._void; | } | else | static if (is(typeof(fun!T(forward!args)) == Variant!AllReturnTypes)) | { | return fun!T(forward!args); | } | else | { | return Variant!AllReturnTypes(fun!T(forward!args)); | } | } | else | static if (exhaustive == Exhaustive.compileTime && is(T == typeof(null))) | { | assert(0, "Null " ~ Args[0].stringof); | } | else | static if (exhaustive == Exhaustive.nullable || exhaustive == Exhaustive.auto_) | { | return Variant!AllReturnTypes(null); | } | else | { | throw variantMemberException; | } | } | default: assert(0); | } | } | } |} | |private string enumKindText()(string[] strs) |{ | auto r = "enum Kind {"; | foreach (s; strs) | { | r ~= s; | r ~= ", "; | } | r ~= "}"; | return r; |} | |@safe pure @nogc |version(mir_core_test) unittest |{ | static struct S { int a; } | | Variant!(S, double) variant; | variant = 1.0; | variant.tryVisit!((ref value, b) => value += b)(2); | assert (variant.get!double == 3); | | alias fun = (ref value) { | static if (is(typeof(value) == S)) | value.a += 2; | else | value += 2; | }; | | variant.tryVisit!fun; | assert (variant.get!double == 5); | | variant = S(4); | variant.tryVisit!fun; | assert (variant.get!S.a == 6); | | alias D = Variant!(Variant!(S, double)); |} | |@safe pure @nogc |version(mir_core_test) unittest |{ | import std.meta: AliasSeq; | | static struct PODWithLongPointer { | long* x; | this(long l) pure | { | x = new long(l); | } | | @property: | long a() const { | return x ? *x : 0; | } | | void a(long l) { | if (x) { | *x = l; | } else { | x = new long(l); | } | } | } | static assert(is(TypeSet!(byte, immutable PODWithLongPointer) == AliasSeq!(byte, immutable PODWithLongPointer))); |} | |private enum isSimpleAggregateType(T) = is(T == class) || is(T == struct) || is(T == union) || is(T == interface); | |unittest |{ | static struct Test | { | alias Value = void; | } | | alias B = Nullable!Test; |} ../../../.dub/packages/mir-core-1.1.83/mir-core/source/mir/algebraic.d is 0% covered <<<<<< EOF # path=./source-mir-interpolate-polynomial.lst |/++ |$(H2 Lagrange Barycentric Interpolation) | |See_also: $(REF_ALTTEXT $(TT interp1), interp1, mir, interpolate) | |License: $(HTTP www.apache.org/licenses/LICENSE-2.0, Apache-2.0) |Copyright: 2020 Ilya Yaroshenko, Kaleidic Associates Advisory Limited, Symmetry Investments |Authors: Ilya Yaroshenko | |Macros: |SUBREF = $(REF_ALTTEXT $(TT $2), $2, mir, interpolate, $1)$(NBSP) |T2=$(TR $(TDNW $(LREF $1)) $(TD $+)) |+/ |module mir.interpolate.polynomial; | |public import mir.interpolate: atInterval; |import core.lifetime: move; |import mir.functional: RefTuple; |import mir.internal.utility : isFloatingPoint, Iota; |import mir.interpolate: findInterval; |import mir.math.common; |import mir.math.numeric; |import mir.math.sum; |import mir.ndslice.slice; |import mir.ndslice.topology: diff, map, member, iota, triplets; |import mir.rc.array; |import mir.utility: min, swap; |import std.traits: Unqual; | |@optmath: | |/// |version(mir_test) |@safe pure unittest |{ | import mir.algorithm.iteration: all; | import mir.math; | import mir.ndslice; | 724| auto f(double x) { return (x-2) * (x-5) * (x-9); } 480| auto fd(double x) { return (x-5) * (x-9) + (x-2) * (x-5) + (x-2) * (x-9); } 240| auto fd2(double x) { return (x-5) + (x-9) + (x-2) + (x-5) + (x-2) + (x-9); } 240| double[2] fWithDerivative(double x) { return [f(x), fd(x)]; } 240| double[3] fWithTwoDerivatives(double x) { return [f(x), fd(x), fd2(x)]; } | | // 1| auto x = [0.0, 3, 5, 10]; 1| auto y = x.map!f.slice.field; | // `!2` adds first two derivatives: f, f', and f'' | // default parameter is 0 2| auto l = x.lagrange!2(y); | 39| foreach(test; x ~ [2.0, 5, 9] ~ [double(PI), double(E), 10, 100, 1e3]) 180| foreach(scale; [-1, +1, 1 + double.epsilon, 1 + double.epsilon.sqrt]) 864| foreach(shift; [0, double.min_normal/2, double.min_normal*2, double.epsilon, double.epsilon.sqrt]) | { 240| auto testX = test * scale + shift; | 240| auto lx = l(testX); 240| auto l_ldx = l.withDerivative(testX); 240| auto l_ld_ld2x = l.withTwoDerivatives(testX); | 240| assert(lx.approxEqual(f(testX))); 240| assert(l_ldx[].all!approxEqual(fWithDerivative(testX)[])); 240| assert(l_ld_ld2x[].all!approxEqual(fWithTwoDerivatives(testX)[])); | } |} | |/++ |Constructs barycentric lagrange interpolant. |+/ |Lagrange!(T, maxDerivative) lagrange(uint maxDerivative = 0, T, X)(scope const X[] x, scope const T[] y) | if (maxDerivative < 16) |{ | import mir.ndslice.allocation: rcslice; 1| return x.rcslice!(immutable X).lagrange!maxDerivative(y.sliced); |} | |/// ditto |Lagrange!(Unqual!(Slice!(Iterator, 1, kind).DeepElement), maxDerivative, X) | lagrange(uint maxDerivative = 0, X, Iterator, SliceKind kind)(Slice!(RCI!(immutable X)) x, Slice!(Iterator, 1, kind) y) @trusted | if (maxDerivative < 16) |{ | alias T = Unqual!(Slice!(Iterator, 1, kind).DeepElement); 1| return Lagrange!(T, maxDerivative)(x.move, rcarray!T(y)); |} | |/++ |+/ 0000000|struct Lagrange(T, uint maxAdditionalFunctions = 0, X = T) | if (isFloatingPoint!T && maxAdditionalFunctions < 16) |{ | /// $(RED for internal use only.) | Slice!(RCI!(immutable X)) _grid; | /// $(RED for internal use only.) | RCArray!(immutable T) _inversedBarycentricWeights; | /// $(RED for internal use only.) | RCArray!T[maxAdditionalFunctions + 1] _normalizedValues; | /// $(RED for internal use only.) | T[maxAdditionalFunctions + 1] _asums; | |@optmath @safe pure @nogc extern(D): | | /// | enum uint derivativeOrder = maxAdditionalFunctions; | | /++ | Complexity: `O(N)` | +/ | pragma(inline, false) | this(Slice!(RCI!(immutable X)) grid, RCArray!T values, RCArray!(immutable T) inversedBarycentricWeights) | { | import mir.algorithm.iteration: all; | assert(grid.length > 1); | assert(grid.length == values.length); | assert(grid.length == inversedBarycentricWeights.length); | enum msg = "Lagrange: grid points are too close or have not finite values."; | version(D_Exceptions) | { | enum T md = T.min_normal / T.epsilon; | static immutable exc = new Exception(msg); | if (!grid.lightScope.diff.all!(a => md <= a && a <= T.max)) | throw exc; | } | swap(_grid, grid); | swap(_inversedBarycentricWeights, inversedBarycentricWeights); | swap(_normalizedValues[0], values); | auto w = _inversedBarycentricWeights[].sliced; | foreach (_; Iota!maxAdditionalFunctions) | { | auto y = _normalizedValues[_][].sliced; | static if (_ == 0) | _asums[_] = y.asumNorm; | _normalizedValues[_ + 1] = RCArray!T(_grid.length, true, false); | auto d = _normalizedValues[_ + 1][].sliced; | polynomialDerivativeValues(d, _grid.lightScope, y, w); | _asums[_ + 1] = d.asumNorm * _asums[_]; | } | } | | /++ | Complexity: `O(N^^2)` | +/ 1| this(Slice!(RCI!(immutable X)) grid, RCArray!T values) | { | import core.lifetime: move; 2| auto weights = grid.lightScope.inversedBarycentricWeights; 1| this(grid.move, values.move, weights.move); | } | |scope const: | | /// | Lagrange lightConst()() const @property @trusted { return *cast(Lagrange*)&this; } | /// | Lagrange lightImmutable()() immutable @property @trusted { return *cast(Lagrange*)&this; } | | @property | { | /// 0000000| ref const(Slice!(RCI!(immutable X))) grid() { return _grid; } | /// 720| immutable(X)[] gridScopeView() scope return const @property @trusted { return _grid.lightScope.field; } | /// 0000000| ref const(RCArray!(immutable T)) inversedBarycentricWeights() { return _inversedBarycentricWeights; } | /// 0000000| ref const(RCArray!T)[maxAdditionalFunctions + 1] normalizedValues() { return _normalizedValues; } | /// 1272| ref const(T)[maxAdditionalFunctions + 1] asums() { return _asums; } | /// | size_t intervalCount(size_t dimension = 0)() | { 720| assert(_grid.length > 1); 720| return _grid.length - 1; | } | } | | template opCall(uint derivative = 0) | if (derivative <= maxAdditionalFunctions) | { | /// | pragma(inline, false) | auto opCall(T x) | { 720| return opCall!derivative(RefTuple!(size_t, T)(this.findInterval(x), x)); | } | | /// | pragma(inline, false) | auto opCall(RefTuple!(size_t, T) tuple) | { | 720| const x = tuple[1]; 720| const idx = tuple[0]; 720| const inversedBarycentricWeights = _inversedBarycentricWeights[].sliced; 720| Slice!(const(T)*)[derivative + 1] values; | foreach (i; Iota!(derivative + 1)) 1440| values[i] = _normalizedValues[i][].sliced; 720| const T[2] pd = [ | T(x - _grid[idx + 0]).fabs, | T(x - _grid[idx + 1]).fabs, | ]; 720| ptrdiff_t fastIndex = 60| pd[0] < T.min_normal ? idx + 0 : 684| pd[1] < T.min_normal ? idx + 1 : 636| -1; 720| if (fastIndex >= 0) | { | static if (derivative == 0) | { 28| return values[0][fastIndex] * _asums[0]; | } | else | { 56| T[derivative + 1] ret = 0; | foreach (_; Iota!(derivative + 1)) 140| ret[_] = values[_][fastIndex] * _asums[_]; 56| return ret; | } | } 636| T d = 0; 636| T[derivative + 1] n = 0; 9540| foreach (i; 0 .. _grid.length) | { 2544| T w = 1 / (inversedBarycentricWeights[i] * (x - _grid[i])); 2544| d += w; | foreach (_; Iota!(derivative + 1)) 5088| n[_] += w * values[_][i]; | } | foreach (_; Iota!(derivative + 1)) | { 1272| n[_] /= d; 1272| n[_] *= asums[_]; | } | static if (derivative == 0) 212| return n[0]; | else 424| return n; | } | } | | static if (maxAdditionalFunctions) | { | /// | alias withDerivative = opCall!1; | | static if (maxAdditionalFunctions > 1) | { | /// | alias withTwoDerivatives = opCall!2; | } | } |} | | |/++ |+/ |pragma(inline, false) |@nogc |RCArray!(immutable T) inversedBarycentricWeights(T)(Slice!(const(T)*) x) | if (isFloatingPoint!T) |{ | 1| const n = x.length; 2| scope prodsa = RCArray!(ProdAccumulator!T)(n); 1| scope p = prodsa[].sliced; 18| foreach (triplet; n.iota.triplets) with(triplet) | { 26| foreach (l; left) | { 6| auto e = prod!T(x[center] - x[l]); 6| p[l] *= -e; 6| p[center] *= e; | } | } | import mir.algorithm.iteration: reduce; 1| const minExp = long.max.reduce!min(p.member!"exp"); 14| foreach (ref e; p) 4| e = e.ldexp(1 - minExp); 1| auto ret = rcarray!(immutable T)(p.member!"prod"); 1| return ret; |} | |/++ |Computes derivative values in the same points |Params: | d = derivative values (output) | y = values | x = grid | w = inversed barycentric weights |Returns: | Derivative values in the same points |+/ |@nogc |Slice!(T*) polynomialDerivativeValues(T)(return Slice!(T*) d, Slice!(const(T)*) x, Slice!(const(T)*) y, Slice!(const(T)*) w) | if (isFloatingPoint!T) |{ | pragma(inline, false); | import mir.math.sum; | 2| const n = x.length; 2| assert(n == w.length); 2| assert(n == y.length); | 2| d.fillCollapseSums!(Summation.fast, 8| (c, s1, s2) => w[c] * s1 + y[c] * s2, 16| (c, _) => y[_] / (w[_] * (x[c] - x[_])), 16| (c, _) => map!"1 / a"(x[c] - x[_])); 2| return d; |} | |/// |@nogc |Slice!(T*) polynomialDerivativeValues(T)(return Slice!(T*) d, Slice!(const(T)*) x, Slice!(const(T)*) y) | if (isFloatingPoint!T) |{ | return .polynomialDerivativeValues(d, x, y, x.inversedBarycentricWeights[].sliced); |} | |private T asumNorm(T)(Slice!(T*) v) |{ | pragma(inline, false); 3| auto n = v.map!fabs.sum!"fast"; 3| v[] /= n; 3| return n; |} source/mir/interpolate/polynomial.d is 95% covered <<<<<< EOF # path=./source-mir-interpolate-utility.lst |module mir.interpolate.utility; | |import mir.ndslice.slice; |import std.traits; |import mir.math.common: optmath; | |/++ |Quadratic function structure |+/ |struct ParabolaKernel(T) |{ | /// | T a = 0; | /// | T b = 0; | /// | T c = 0; | |@optmath: | | /// 1| this(T a, T b, T c) | { 1| this.a = a; 1| this.b = b; 1| this.c = c; | } | | /// Builds parabola given three points 5| this(T x0, T x1, T x2, T y0, T y1, T y2) | { 5| auto b1 = x1 - x0; 5| auto b2 = x2 - x1; 5| auto d1 = y1 - y0; 5| auto d2 = y2 - y1; 5| auto a1 = b1 * (x1 + x0); 5| auto a2 = b2 * (x2 + x1); 5| auto bm = - (b2 / b1); 5| auto a3 = bm * a1 + a2; 5| auto d3 = bm * d1 + d2; 5| a = d3 / a3; 5| b = (d1 - a1 * a) / b1; 5| c = y1 - x1 * (a * x1 + b); | } | | /++ | Params: | x0 = `x0` | x1 = `x1` | y0 = `f(x0)` | y1 = `f(x1)` | d1 = `f'(x1)` | +/ | static ParabolaKernel fromFirstDerivative(T x0, T x1, T y0, T y1, T d1) | { 1| auto dy = y1 - y0; 1| auto dx = x1 - x0; 1| auto dd = dy / dx; 1| auto a = (d1 - dd) / dx; 1| auto b = dd - a * (x0 + x1); 1| auto c = y1 - (a * x1 + b) * x1; 1| return ParabolaKernel(a, b, c); | } | | /// | auto opCall(uint derivative = 0)(T x) const | if (derivative <= 2) | { 5| auto y = (a * x + b) * x + c; | static if (derivative == 0) 5| return y; | else | { 0000000| auto y1 = 2 * a * x + b; | static if (derivative == 1) 0000000| return cast(T[2])[y, y1]; | else 0000000| return cast(T[3])[y, y1, 2 * a]; | } | } | | /// | alias withDerivative = opCall!1; | /// | alias withTwoDerivatives = opCall!2; |} | |/// ditto |ParabolaKernel!(Unqual!(typeof(X.init - Y.init))) parabolaKernel(X, Y)(in X x0, in X x1, in X x2, in Y y0, in Y y1, in Y y2) |{ 1| return typeof(return)(x0, x1, x2, y0, y1, y2); |} | |/++ |Returns: `[f'(x0), f'(x1), f'(x2)]` |+/ |Unqual!(typeof(X.init - Y.init))[3] parabolaDerivatives(X, Y)(in X x0, in X x1, in X x2, in Y y0, in Y y1, in Y y2) |{ 21| auto d0 = (y2 - y1) / (x2 - x1); 21| auto d1 = (y0 - y2) / (x0 - x2); 21| auto d2 = (y1 - y0) / (x1 - x0); 21| return [d1 + d2 - d0, d0 + d2 - d1, d0 + d1 - d2]; |} | |/// |unittest |{ | import mir.math.common: approxEqual; | 4| alias f = (double x) => 3 * x ^^ 2 + 7 * x + 5; 1| auto x0 = 4; 1| auto x1 = 9; 1| auto x2 = 20; 1| auto p = parabolaKernel(x0, x1, x2, f(x0), f(x1), f(x2)); | 1| assert(p.a.approxEqual(3)); 1| assert(p.b.approxEqual(7)); 1| assert(p.c.approxEqual(5)); 1| assert(p(10).approxEqual(f(10))); |} | | |/// |unittest |{ | import mir.math.common: approxEqual; | 2| alias f = (double x) => 3 * x ^^ 2 + 7 * x + 5; 1| alias d = (double x) => 2 * 3 * x + 7; 1| auto x0 = 4; 1| auto x1 = 9; 1| auto p = ParabolaKernel!double.fromFirstDerivative(x0, x1, f(x0), f(x1), d(x1)); | 1| assert(p.a.approxEqual(3)); 1| assert(p.b.approxEqual(7)); 1| assert(p.c.approxEqual(5)); |} | |/++ |Cubic function structure |+/ |struct CubicKernel(T) |{ | /// | T a = 0; | /// | T b = 0; | /// | T c = 0; | /// | T d = 0; | |@optmath: | | /// 1| this(T a, T b, T c, T d) | { 1| this.a = a; 1| this.b = b; 1| this.c = c; 1| this.d = d; | } | | /++ | Params: | x0 = `x0` | x1 = `x1` | y0 = `f(x0)` | y1 = `f(x1)` | dd0 = `f''(x0)` | d1 = `f'(x1)` | +/ | static CubicKernel fromSecondAndFirstDerivative(T x0, T x1, T y0, T y1, T dd0, T d1) | { 1| auto hdd0 = 0.5f * dd0; 1| auto dy = y1 - y0; 1| auto dx = x1 - x0; 1| auto dd = dy / dx; 1| auto a = 0.5f * ((d1 - dd) / dx - hdd0) / dx; 1| auto b = hdd0 - 3 * a * x0; 1| auto c = d1 - (3 * a * x1 + 2 * b) * x1; 1| auto d = y1 - ((a * x1 + b) * x1 + c) * x1; 1| return CubicKernel!T(a, b, c, d); | } | | /// | auto opCall(uint derivative = 0)(T x) const | if (derivative <= 3) | { 4| auto y = ((a * x + b) * x + c) * x + d; | static if (derivative == 0) 1| return y; | else | { 3| T[derivative + 1] ret; 3| ret[0] = y; 3| ret[1] = (3 * a * x + 2 * b) * x + c; | static if (derivative > 1) | { 2| ret[2] = 6 * a * x + 2 * b; | static if (derivative > 2) 1| ret[3] = 6 * a; | } 3| return ret; | } | } | | /// | alias withDerivative = opCall!1; | /// | alias withTwoDerivatives = opCall!2; |} | |/// |unittest |{ | import mir.math.common: approxEqual; | 3| alias f = (double x) => 3 * x ^^ 3 + 7 * x ^^ 2 + 5 * x + 10; 2| alias d = (double x) => 3 * 3 * x ^^ 2 + 2 * 7 * x + 5; 2| alias s = (double x) => 6 * 3 * x + 2 * 7; 1| auto x0 = 4; 1| auto x1 = 9; 1| auto p = CubicKernel!double.fromSecondAndFirstDerivative(x0, x1, f(x0), f(x1), s(x0), d(x1)); | 1| assert(p.a.approxEqual(3)); 1| assert(p.b.approxEqual(7)); 1| assert(p.c.approxEqual(5)); 1| assert(p.d.approxEqual(10)); 1| assert(p(13).approxEqual(f(13))); 1| assert(p.opCall!1(13)[1].approxEqual(d(13))); 1| assert(p.opCall!2(13)[2].approxEqual(s(13))); 1| assert(p.opCall!3(13)[3].approxEqual(18)); |} | | |package auto pchipTail(T)(in T step0, in T step1, in T diff0, in T diff1) |{ | import mir.math.common: copysign, fabs; | if (!diff0) | { | return 0; | } | auto slope = ((step0 * 2 + step1) * diff0 - step0 * diff1) / (step0 + step1); | if (copysign(1f, slope) != copysign(1f, diff0)) | { | return 0; | } | if ((copysign(1f, diff0) != copysign(1f, diff1)) && (fabs(slope) > fabs(diff0 * 3))) | { | return diff0 * 3; | } | return slope; |} source/mir/interpolate/utility.d is 96% covered <<<<<< EOF # path=./source-mir-algorithm-setops.lst |// Written in the D programming language. |/** |This is a submodule of $(MREF mir, algorithm). It contains `nothrow` `@nogc` BetterC alternative to `MultiwayMerge` from `std.algorithm.setops`. | |License: $(HTTP www.apache.org/licenses/LICENSE-2.0, Apache-2.0) |Copyright: 2020 Ilya Yaroshenko, Kaleidic Associates Advisory Limited, Symmetry Investments | |Authors: $(HTTP erdani.com, Andrei Alexandrescu) (original Phobos code), Ilya Yaroshenko (Mir & BetterC rework, optimization). | */ |module mir.algorithm.setops; | |import core.lifetime: move; |import mir.functional: naryFun; |import mir.primitives; |import mir.qualifier; |import std.range.primitives: isRandomAccessRange; | |/** |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) | if (isRandomAccessRange!RangeOfRanges) |{ | import mir.primitives; | import mir.container.binaryheap; | | /// | @disable this(); | /// | @disable this(this); | | /// | static bool compFront(ElementType!RangeOfRanges a, ElementType!RangeOfRanges b) | { | // revert comparison order so we get the smallest elements first 322| return less(b.front, a.front); | } | | /// Heap | BinaryHeap!(compFront, RangeOfRanges) _heap; | | /// 23| this(RangeOfRanges ror) | { | // Preemptively get rid of all empty ranges in the input | // No need for stability either 23| auto temp = ror.lightScope; 87| for (;!temp.empty;) | { 64| if (!temp.front.empty) | { 64| temp.popFront; 64| continue; | } | import mir.utility: swap; 0000000| swap(temp.back, temp.front); 0000000| temp.popBack; 0000000| ror.popBack; | } | //Build the heap across the range 23| _heap = typeof(_heap)(ror.move); | } | | /// 716| @property bool empty() scope const { return _heap.empty; } | | /// | @property auto ref front() | { 292| assert(!empty); 292| return _heap.front.front; | } | | /// | void popFront() scope @safe | { 173| _heap._store.front.popFront; 173| if (!_heap._store.front.empty) 109| _heap.siftDown(_heap._store[], 0, _heap._length); | else 64| _heap.removeFront; | } |} | |/// Ditto |MultiwayMerge!(naryFun!less, RangeOfRanges) multiwayMerge |(alias less = "a < b", RangeOfRanges) |(RangeOfRanges ror) |{ 23| return typeof(return)(move(ror)); |} | |/// |@safe nothrow @nogc version(mir_test) unittest |{ | import mir.algorithm.iteration: equal; | | static a = | [ | [ 1, 4, 7, 8 ], | [ 1, 7 ], | [ 1, 7, 8], | [ 4 ], | [ 7 ], | ]; | static witness = [ | 1, 1, 1, 4, 4, 7, 7, 7, 7, 8, 8 | ]; 1| assert(a.multiwayMerge.equal(witness)); | | static b = | [ | // range with duplicates | [ 1, 1, 4, 7, 8 ], | [ 7 ], | [ 1, 7, 8], | [ 4 ], | [ 7 ], | ]; | // duplicates are propagated to the resulting range 1| assert(b.multiwayMerge.equal(witness)); |} | |/** |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 mir.functional: not; | import mir.algorithm.iteration : Uniq; | 21| return Uniq!(not!less, typeof(multiwayMerge!less(ror)))(multiwayMerge!less(move(ror))); |} | |/// |@safe version(mir_test) unittest |{ | import mir.algorithm.iteration: equal; | | // sets 1| double[][] a = | [ | [ 1, 4, 7, 8 ], | [ 1, 7 ], | [ 1, 7, 8], | [ 4 ], | [ 7 ], | ]; | 1| auto witness = [1, 4, 7, 8]; 1| assert(a.multiwayUnion.equal(witness)); | | // multisets 1| double[][] b = | [ | [ 1, 1, 1, 4, 7, 8 ], | [ 1, 7 ], | [ 1, 7, 7, 8], | [ 4 ], | [ 7 ], | ]; 1| assert(b.multiwayUnion.equal(witness)); |} | |/++ |Computes the length of union of multiple ranges. The input ranges are passed |as a range of ranges and each is assumed to be sorted by `less`. | |Params: | less = Predicate the given ranges are sorted by. | ror = A range of ranges sorted by `less` to compute the intersection for. |Returns: | A length of the union of the ranges in `ror`. |+/ |pragma(inline, false) |size_t unionLength(alias less = "a < b", RangeOfRanges)(RangeOfRanges ror) |{ 15| size_t length; 15| auto u = move(ror).multiwayUnion!less; 15| if (!u.empty) do { 78| length++; 78| u.popFront; 78| } while(!u.empty); 15| return length; |} source/mir/algorithm/setops.d is 91% covered <<<<<< EOF # path=./source-mir-ndslice-fuse.lst |/++ |This is a submodule of $(MREF mir,ndslice). | |Allocation routines that construct ndslices from ndranges. | |License: $(HTTP www.apache.org/licenses/LICENSE-2.0, Apache-2.0) |Copyright: 2020 Ilya Yaroshenko, Kaleidic Associates Advisory Limited, Symmetry Investments |Authors: Ilya Yaroshenko | |See_also: $(SUBMODULE concatenation) submodule. | |Macros: |SUBMODULE = $(MREF_ALTTEXT $1, mir, ndslice, $1) |SUBREF = $(REF_ALTTEXT $(TT $2), $2, mir, ndslice, $1)$(NBSP) |T2=$(TR $(TDNW $(LREF $1)) $(TD $+)) |+/ |module mir.ndslice.fuse; | |import mir.internal.utility; |import mir.ndslice.slice; |import mir.primitives; |import mir.qualifier; |import std.meta; |import std.traits; | |/++ |Fuses ndrange `r` into GC-allocated ($(LREF fuse)) or RC-allocated ($(LREF rcfuse)) ndslice. |Can be used to join rows or columns into a matrix. | |Params: | Dimensions = (optional) indices of dimensions to be brought to the first position |Returns: | ndslice |+/ |alias fuse(Dimensions...) = fuseImpl!(false, void, Dimensions); |/// ditto |alias rcfuse(Dimensions...) = fuseImpl!(true, void, Dimensions); | |/// |@safe pure version(mir_test) unittest |{ | import mir.ndslice.fuse; | import mir.ndslice.slice : Contiguous, Slice; | import mir.ndslice.topology: iota; | import mir.rc.array: RCI; | | enum ror = [ | [0, 1, 2, 3], | [4, 5, 6, 7], | [8, 9,10,11]]; | | // 0 1 2 3 | // 4 5 6 7 | // 8 9 10 11 1| auto matrix = ror.fuse; | 2| auto rcmatrix = ror.rcfuse; // nogc version | 1| assert(matrix == [3, 4].iota); 1| assert(rcmatrix == [3, 4].iota); | static assert(ror.fuse == [3, 4].iota); // CTFE-able | | // matrix is contiguos | static assert(is(typeof(matrix) == Slice!(int*, 2))); | static assert(is(typeof(rcmatrix) == Slice!(RCI!int, 2))); |} | |/// Transposed |@safe pure version(mir_test) unittest |{ | import mir.ndslice.fuse; | import mir.ndslice.topology: iota; | import mir.ndslice.dynamic: transposed; | import mir.ndslice.slice : Contiguous, Slice; | | enum ror = [ | [0, 1, 2, 3], | [4, 5, 6, 7], | [8, 9,10,11]]; | | // 0 4 8 | // 1 5 9 | // 2 6 10 | // 3 7 11 | | // `!1` brings dimensions under index 1 to the front (0 index). 1| auto matrix = ror.fuse!1; | 1| assert(matrix == [3, 4].iota.transposed!1); | // TODO: CTFE | // static assert(ror.fuse!1 == [3, 4].iota.transposed!1); // CTFE-able | // matrix is contiguos | static assert(is(typeof(matrix) == Slice!(int*, 2))); |} | |/// 3D |@safe pure version(mir_test) unittest |{ | import mir.ndslice.fuse; | import mir.ndslice.topology: iota; | import mir.ndslice.dynamic: transposed; | 1| auto ror = | [[[ 0, 1, 2, 3], | [ 4, 5, 6, 7]], | [[ 8, 9,10,11], | [12,13,14,15]]]; | 1| auto nd = [2, 2, 4].iota; | 1| assert(ror.fuse == nd); 1| assert(ror.fuse!2 == nd.transposed!2); 1| assert(ror.fuse!(1, 2) == nd.transposed!(1, 2)); 1| assert(ror.fuse!(2, 1) == nd.transposed!(2, 1)); |} | |/// Work with RC Arrays of RC Arrays |@safe pure version(mir_test) unittest |{ | import mir.ndslice.fuse; | import mir.ndslice.slice; | import mir.ndslice.topology: map; | import mir.rc.array; | | Slice!(const(double)*, 2) conv(RCArray!(const RCArray!(const double)) a) | { 0000000| return a[].map!"a[]".fuse; | } |} | |/++ |Fuses ndrange `r` into GC-allocated ($(LREF fuseAs)) or RC-allocated ($(LREF rcfuseAs)) ndslice. |Can be used to join rows or columns into a matrix. | |Params: | T = output type of ndslice elements | Dimensions = (optional) indices of dimensions to be brought to the first position |Returns: | ndslice |+/ |alias fuseAs(T, Dimensions...) = fuseImpl!(false, T, Dimensions); |/// ditto |alias rcfuseAs(T, Dimensions...) = fuseImpl!(true, T, Dimensions); | |/// |@safe pure version(mir_test) unittest |{ | import mir.ndslice.fuse; | import mir.ndslice.slice : Contiguous, Slice; | import mir.ndslice.topology: iota; | import mir.rc.array: RCI; | | enum ror = [ | [0, 1, 2, 3], | [4, 5, 6, 7], | [8, 9,10,11]]; | | // 0 1 2 3 | // 4 5 6 7 | // 8 9 10 11 1| auto matrix = ror.fuseAs!double; | 2| auto rcmatrix = ror.rcfuseAs!double; // nogc version | 1| assert(matrix == [3, 4].iota); 1| assert(rcmatrix == [3, 4].iota); | static assert(ror.fuseAs!double == [3, 4].iota); // CTFE-able | | // matrix is contiguos | static assert(is(typeof(matrix) == Slice!(double*, 2))); | static assert(is(typeof(rcmatrix) == Slice!(RCI!double, 2))); |} | |/// |template fuseImpl(bool RC, T_, Dimensions...) |{ | import mir.ndslice.internal: isSize_t, toSize_t; | static if (allSatisfy!(isSize_t, Dimensions)) | /++ | Params: | r = parallelotope (ndrange) with length/shape and input range primitives. | +/ | auto fuseImpl(NDRange)(NDRange r) | if (hasShape!NDRange) | { | import mir.conv: emplaceRef; | import mir.algorithm.iteration: each; | import mir.ndslice.allocation; 108| auto shape = fuseShape(r); | static if (is(T_ == void)) | alias T = FuseElementType!NDRange; | else | alias T = T_; | alias UT = Unqual!T; | static if (RC) | { | import mir.rc.array: RCI; | alias R = Slice!(RCI!T, fuseDimensionCount!NDRange); 4| Slice!(RCI!UT, fuseDimensionCount!NDRange) ret; | } | else | { | alias R = Slice!(T*, fuseDimensionCount!NDRange); 106| Slice!(UT*, fuseDimensionCount!NDRange) ret; | } | static if (Dimensions.length) | { | import mir.ndslice.topology: iota; | import mir.ndslice.dynamic: transposed, completeTranspose; | enum perm = completeTranspose!(shape.length)([Dimensions]); 10| size_t[shape.length] shapep; | foreach(i; Iota!(shape.length)) 23| shapep[i] = shape[perm[i]]; | // enum iperm = perm.length.iota[completeTranspose!(shape.length)([Dimensions])[].sliced].slice; | alias InverseDimensions = aliasSeqOf!( | (size_t[] perm){ | auto ar = new size_t[perm.length]; | ar.sliced[perm.sliced] = perm.length.iota; | return ar; | }(perm) | ); | static if (RC) | { | ret = shapep.uninitRcslice!UT; | ret.lightScope.transposed!InverseDimensions.each!(emplaceRef!T)(r); | } | else | { 10| if (__ctfe) | { 0000000| ret = shapep.slice!UT; 0000000| ret.transposed!InverseDimensions.each!"a = b"(r); | } | else | { 10| ret = shapep.uninitSlice!UT; 10| ret.transposed!InverseDimensions.each!(emplaceRef!T)(r); | } | | } | } | else | { | static if (RC) | { 2| ret = shape.uninitRCslice!UT; 2| ret.lightScope.each!(emplaceRef!T)(r); | } | else | { 96| if (__ctfe) | { 0000000| ret = shape.slice!UT; 0000000| ret.each!"a = b"(r); | } | else | { 96| ret = shape.uninitSlice!UT; 96| ret.each!(emplaceRef!T)(r); | } | } | } | static if (RC) | { | import core.lifetime: move; 4| return move(*(() @trusted => cast(R*)&ret)()); | } | else | { 212| return *(() @trusted => cast(R*)&ret)(); | } | } | else | alias fuseImpl = .fuseImpl!(RC, T_, staticMap!(toSize_t, Dimensions)); |} | |private template fuseDimensionCount(R) |{ | static if (is(typeof(R.init.shape) : size_t[N], size_t N) && (isDynamicArray!R || __traits(hasMember, R, "front"))) | { | import mir.ndslice.topology: repeat; | enum size_t fuseDimensionCount = N + fuseDimensionCount!(DeepElementType!R); | } | else | enum size_t fuseDimensionCount = 0; |} | |private static immutable shapeExceptionMsg = "fuseShape Exception: elements have different shapes/lengths"; | |version(D_Exceptions) | static immutable shapeException = new Exception(shapeExceptionMsg); | |/+ |TODO docs |+/ |size_t[fuseDimensionCount!Range] fuseShape(Range)(Range r) | if (hasShape!Range) |{ | // auto outerShape = r.shape; | enum N = r.shape.length; | enum RN = typeof(return).length; | enum M = RN - N; | static if (M == 0) | { 466| return r.shape; | } | else | { | import mir.ndslice.topology: repeat; | typeof(return) ret; | ret[0 .. N] = r.shape; | if (!ret[0 .. N].anyEmptyShape) | { | ret[N .. $] = fuseShape(mixin("r" ~ ".front".repeat(N).fuseCells.field)); | import mir.algorithm.iteration: all; | if (!all!((a) => cast(size_t[M]) ret[N .. $] == .fuseShape(a))(r)) | { | version (D_Exceptions) | throw shapeException; | else | assert(0, shapeExceptionMsg); | } | } | return ret; | } |} | |private template FuseElementType(NDRange) |{ | import mir.ndslice.topology: repeat; | alias FuseElementType = typeof(mixin("NDRange.init" ~ ".front".repeat(fuseDimensionCount!NDRange).fuseCells.field)); |} | |/++ |Fuses `cells` into GC-allocated ndslice. | |Params: | cells = ndrange of ndcells, ndrange and ndcell should have `shape` and multidimensional input range primivies (`front!d`, `empty!d`, `popFront!d`). |Returns: ndslice composed of fused cells. |See_also: $(SUBREF chunks, chunks) |+/ |auto fuseCells(S)(S cells) |{ | alias T = DeepElementType!(DeepElementType!S); | alias UT = Unqual!T; 2| if (__ctfe) | { | import mir.ndslice.allocation: slice; 0000000| auto ret = cells.fuseCellsShape.slice!UT; 0000000| ret.fuseCellsAssign!"a = b" = cells; | static if (is(T == immutable)) 0000000| return (() @trusted => cast(immutable) ret)()[]; | else | static if (is(T == const)) | return (() @trusted => cast(const) ret)()[]; | else 0000000| return ret; | } | else | { | import mir.ndslice.allocation: uninitSlice; | import mir.conv; 2| auto ret = cells.fuseCellsShape.uninitSlice!UT; 2| ret.fuseCellsAssign!(emplaceRef!T) = cells; | alias R = Slice!(T*, ret.N); 4| return R(ret._structure, (() @trusted => cast(T*)ret._iterator)()); | } |} | |/// 1D |@safe pure version(mir_test) unittest |{ | import mir.ndslice.topology: iota; | enum ar = [[0, 1], [], [2, 3, 4, 5], [6], [7, 8, 9]]; | static assert ([[0, 1], [], [2, 3, 4, 5], [6], [7, 8, 9]].fuseCells == 10.iota); 1| assert (ar.fuseCells == 10.iota); |} | |/// 2D |@safe pure version(mir_test) unittest |{ | import mir.ndslice.topology: iota; | import mir.ndslice.chunks; | 1| auto sl = iota(11, 17); 1| assert(sl.chunks!(0, 1)(3, 4).fuseCells == sl); |} | |/+ |TODO docs |+/ |auto fuseCellsAssign(alias fun = "a = b", Iterator, size_t N, SliceKind kind, S)(Slice!(Iterator, N, kind) to, S cells) |{ 2| assert(to.shape == cells.fuseCellsShape, "'cells.fuseCellsShape' should be equal to 'to.shape'"); | 2| if (cells.anyEmpty) 0000000| goto R; | | import mir.functional: naryFun; | import mir.ndslice.topology: canonical; | static if (kind == Contiguous) 2| fuseCellsEmplaceImpl!(naryFun!fun, 0, N)(to.canonical, cells); | else | fuseCellsEmplaceImpl!(naryFun!fun, 0, N)(to, cells); 2| R: return to; |} | |/+ |TODO docs |+/ |size_t[S.init.shape.length] fuseCellsShape(S)(S cells) @property |{ 4| typeof(return) ret; | enum N = ret.length; | static if (N == 1) | { 36| foreach (ref e; cells) 10| ret[0] += e.length; | } | else | { | import mir.ndslice.topology: repeat; | enum expr = "e" ~ ".front".repeat(N).fuseCells.field; | foreach (i; Iota!N) 44| for (auto e = cells.save; !e.empty!i; e.popFront!i) 18| ret[i] += mixin(expr).length!i; | } 4| return ret; |} | |private auto fuseCellsEmplaceImpl(alias fun, size_t i, size_t M, Iterator, size_t N, SliceKind kind, S)(Slice!(Iterator, N, kind) to, S cells) |{ | do | { 29| auto from = cells.front; | static if (M == 1) | { 5| auto n = from.length!i; | } | else | { | import mir.ndslice.topology: repeat; | enum expr = "from" ~ ".front".repeat(N - 1 - i).fuseCells.field; 24| auto n = mixin(expr).length!i; | } 29| assert (to.length!i >= n); | static if (i + 1 == M) | { | import mir.algorithm.iteration: each; 25| each!fun(to.selectFront!i(n), from); | } | else | { 4| .fuseCellsEmplaceImpl!(fun, i + 1, N)(to.selectFront!i(n), from); | } 29| to.popFrontExactly!i(n); 29| cells.popFront; | } 29| while(!cells.empty); 6| return to; |} source/mir/ndslice/fuse.d is 85% covered <<<<<< EOF # path=./source-mir-container-binaryheap.lst |/** |This module provides a $(D BinaryHeap) (aka priority queue) |adaptor that makes a binary heap out of any user-provided random-access range. | |Current implementation is suitable for Mir/BetterC concepts. | |This module is a submodule of $(MREF mir, container). | |Copyright: 2020 Ilya Yaroshenko, Kaleidic Associates Advisory Limited, Symmetry Investments | |License: $(HTTP www.apache.org/licenses/LICENSE-2.0, Apache-2.0) | |Authors: $(HTTP erdani.com, Andrei Alexandrescu) (original Phobos code), Ilya Yaroshenko (Mir & BetterC rework). |*/ |module mir.container.binaryheap; | |import mir.primitives; |import std.range.primitives: isRandomAccessRange, hasSwappableElements; |import std.traits; | |/// |@system nothrow @nogc version(mir_test) unittest |{ | import mir.algorithm.iteration : equal; | import std.range : takeExactly; | static a = [4, 7, 3, 1, 5], b = [7, 5, 4]; 1| auto maxHeap = a.heapify; 1| assert((&maxHeap).takeExactly(3).equal(b)); | | static c = [4, 7, 3, 1, 5], d = [1, 3, 4]; 1| auto minHeap = c.heapify!"a > b"; 1| assert((&minHeap).takeExactly(3).equal(d)); |} | |/++ |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 not a container, 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(alias less = "a < b", Store) |if (isRandomAccessRange!Store || isRandomAccessRange!(typeof(Store.init[]))) |{ | import mir.utility : min; | import mir.functional : naryFun; | import core.lifetime: move; | import std.algorithm.mutation : swapAt; | | static if (isRandomAccessRange!Store) | alias Range = Store; | else | alias Range = typeof(Store.init[]); | | alias percolate = HeapOps!(less, Range).percolate; | alias siftDown = HeapOps!(less, Range).siftDown; | alias buildHeap = HeapOps!(less, Range).buildHeap; | | @disable this(); | @disable this(this); | | // Comparison predicate | private alias comp = naryFun!(less); | // Convenience accessors | | // Asserts that the heap property is respected. | private void assertValid() scope | { | debug | { 32| if (_length < 2) return; 276| for (size_t n = _length - 1; n >= 1; --n) | { 106| auto parentIdx = (n - 1) / 2; 106| assert(!comp(_store[parentIdx], _store[n])); | } | } | } | |public: | | /// The payload includes the support store and the effective length | size_t _length; | /// ditto | Store _store; | | | /++ | 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). | +/ 33| this(Store s, size_t initialSize = size_t.max) | { 33| 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) | { 33| _store = move(s); 33| _length = min(_store.length, initialSize); 34| if (_length < 2) return; 32| buildHeap(_store[]); 32| assertValid(); | } | | /++ | Takes ownership of a store assuming it already was organized as a | heap. | +/ | void assume(Store s, size_t initialSize = size_t.max) | { 0000000| _store = move(s); 0000000| _length = min(_store.length, initialSize); 0000000| assertValid(); | } | | /++ | Returns the _length of the heap. | +/ | @property size_t length() scope const | { 1204| return _length; | } | | /++ | Returns $(D true) if the heap is _empty, $(D false) otherwise. | +/ | @property bool empty() scope const | { 1190| return !length; | } | | /++ | 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() scope const | { | static if (is(typeof(_store.capacity) : size_t)) | { 0000000| return _store.capacity; | } | else | { 0000000| return _store.length; | } | } | | /++ | Returns a _front of the heap, which is the largest element | according to `less`. | +/ | @property auto ref ElementType!Store front() scope return | { 336| assert(!empty, "Cannot call front on an empty heap."); 336| return _store.front; | } | | | | /++ | Inserts `value` into the store. If the underlying store is a range | and `length == capacity`, throws an AssertException. | +/ | size_t insert(ElementType!Store value) scope | { | static if (is(typeof(_store.insertBack(value)))) | { 0000000| if (length == _store.length) | { | // reallocate 0000000| _store.insertBack(value); | } | else | { | // no reallocation 0000000| _store[_length] = value; | } | } | else | { | // can't grow 10| assert(length < _store.length, "Cannot grow a heap created over a range"); 10| _store[_length] = value; | } | | // sink down the element 29| for (size_t n = _length; n; ) | { 17| auto parentIdx = (n - 1) / 2; 25| if (!comp(_store[parentIdx], _store[n])) break; // done! | // must swap and continue 9| _store.swapAt(parentIdx, n); 9| n = parentIdx; | } 10| ++_length; | debug(BinaryHeap) assertValid(); 10| return 1; | } | | /++ | Removes the largest element from the heap. | +/ | void removeFront() scope | { 105| assert(!empty, "Cannot call removeFront on an empty heap."); 105| if (--_length) 79| _store.swapAt(0, _length); 105| percolate(_store[], 0, _length); | } | | /// ditto | alias popFront = removeFront; | | /++ | Removes the largest element from the heap and returns | 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. | +/ | auto ref removeAny() scope | { 0000000| removeFront(); 0000000| return _store[_length]; | } | | /++ | Replaces the largest element in the store with `value`. | +/ | void replaceFront(ElementType!Store value) scope | { | // must replace the top 0000000| assert(!empty, "Cannot call replaceFront on an empty heap."); 0000000| _store.front = value; 0000000| percolate(_store[], 0, _length); | debug(BinaryHeap) assertValid(); | } | | /++ | If the heap has room to grow, inserts `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) scope | { 0000000| if (_length < _store.length) | { 0000000| insert(value); 0000000| return true; | } | 0000000| assert(!_store.empty, "Cannot replace front of an empty heap."); 0000000| if (!comp(value, _store.front)) return false; // value >= largest 0000000| _store.front = value; | 0000000| percolate(_store[], 0, _length); | debug(BinaryHeap) assertValid(); 0000000| 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) scope | { 10| assert(_length == _store.length); 10| assert(!_store.empty, "Cannot swap front of an empty heap."); | 15| if (!comp(value, _store.front)) return false; // value >= largest | | import std.algorithm.mutation : swap; 5| swap(_store.front, value); | 5| percolate(_store[], 0, _length); | debug(BinaryHeap) assertValid(); | 5| return true; | } |} | |/// Example from "Introduction to Algorithms" Cormen et al, p 146 |@system nothrow version(mir_test) unittest |{ | import mir.algorithm.iteration : equal; 1| int[] a = [ 4, 1, 3, 2, 16, 9, 10, 14, 8, 7 ]; 1| auto h = heapify(a); | // largest element 1| assert(h.front == 16); | // a has the heap property 1| 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 nothrow version(mir_test) unittest |{ | import mir.algorithm.iteration : equal; | import std.range : takeExactly; 1| int[] a = [4, 1, 3, 2, 16, 9, 10, 14, 8, 7]; 1| auto heap = heapify(a); 1| auto top5 = (&heap).takeExactly(5); 1| 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!(less, Store) heapify(alias less = "a < b", Store)(Store s, | size_t initialSize = size_t.max) |{ | 9| return BinaryHeap!(less, Store)(s, initialSize); |} | |/// |@system nothrow version(mir_test) unittest |{ | import std.range.primitives; | { | // example from "Introduction to Algorithms" Cormen et al., p 146 1| int[] a = [ 4, 1, 3, 2, 16, 9, 10, 14, 8, 7 ]; 1| auto h = heapify(a); 1| h = heapify!"a < b"(a); 1| assert(h.front == 16); 1| assert(a == [ 16, 14, 10, 8, 7, 9, 3, 2, 4, 1 ]); 1| auto witness = [ 16, 14, 10, 9, 8, 7, 4, 3, 2, 1 ]; 21| for (; !h.empty; h.removeFront(), witness.popFront()) | { 10| assert(!witness.empty); 10| assert(witness.front == h.front); | } 1| assert(witness.empty); | } | { 1| int[] a = [ 4, 1, 3, 2, 16, 9, 10, 14, 8, 7 ]; 1| int[] b = new int[a.length]; 1| BinaryHeap!("a < b", int[]) h = BinaryHeap!("a < b", int[])(b, 0); 33| foreach (e; a) | { 10| h.insert(e); | } 1| assert(b == [ 16, 14, 10, 8, 7, 3, 9, 1, 4, 2 ]); | } |} | |@system nothrow version(mir_test) unittest |{ | // Test range interface. | import mir.primitives: isInputRange; | import mir.algorithm.iteration : equal; 1| int[] a = [4, 1, 3, 2, 16, 9, 10, 14, 8, 7]; 1| auto h = heapify(a); | static assert(isInputRange!(typeof(h))); 1| assert((&h).equal([16, 14, 10, 9, 8, 7, 4, 3, 2, 1])); |} | |@system nothrow version(mir_test) unittest // 15675 |{ | import std.container.array : Array; | 2| Array!int elements = [1, 2, 10, 12]; 2| auto heap = heapify(elements); 1| assert(heap.front == 12); |} | |@system nothrow version(mir_test) unittest |{ | import mir.algorithm.iteration : equal; | import std.internal.test.dummyrange; | | alias RefRange = DummyRange!(ReturnBy.Reference, Length.Yes, RangeType.Random); | 1| RefRange a; 1| RefRange b; 1| a.reinit(); 1| b.reinit(); | 1| auto heap = heapify(a); 32| foreach (ref elem; b) | { 10| heap.conditionalSwap(elem); | } | 1| assert(equal(&heap, [ 5, 5, 4, 4, 3, 3, 2, 2, 1, 1])); 1| assert(equal(b, [10, 9, 8, 7, 6, 6, 7, 8, 9, 10])); |} | |/// Heap operations for random-access ranges |template HeapOps(alias less, Range) |{ | import std.range.primitives : hasSwappableElements, hasAssignableElements; | import mir.functional; | import std.algorithm.mutation : swapAt; | | static assert(isRandomAccessRange!Range); | static assert(hasLength!Range); | static assert(hasSwappableElements!Range || hasAssignableElements!Range); | | alias lessFun = naryFun!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) | { 32| immutable n = r.length; 127| for (size_t i = n / 2; i-- > 0; ) | { 63| siftDown(r, i, n); | } 32| assert(isHeap(r)); | } | | /// | bool isHeap()(Range r) | { 32| size_t parent = 0; 414| foreach (child; 1 .. r.length) | { 106| if (lessFun(r[parent], r[child])) return false; | // Increment parent every other pass 106| parent += !(child & 1); | } 32| 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 (;;) | { 250| auto child = (parent + 1) * 2; 250| if (child >= end) | { | // Leftover left child? 223| if (child == end && lessFun(r[parent], r[--child])) 43| r.swapAt(parent, child); 138| break; | } 112| auto leftChild = child - 1; 161| if (lessFun(r[child], r[leftChild])) child = leftChild; 146| if (!lessFun(r[parent], r[child])) break; 78| r.swapAt(parent, child); 78| 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) | { 110| immutable root = parent; | | // Sift down | for (;;) | { 182| auto child = (parent + 1) * 2; | 182| if (child >= end) | { 110| if (child == end) | { | // Leftover left node. 21| --child; 21| r.swapAt(parent, child); 21| parent = child; | } 110| break; | } | 72| auto leftChild = child - 1; 107| if (lessFun(r[child], r[leftChild])) child = leftChild; 72| r.swapAt(parent, child); 72| parent = child; | } | | // Sift up 246| for (auto child = parent; child > root; child = parent) | { 65| parent = (child - 1) / 2; 117| if (!lessFun(r[parent], r[child])) break; 13| r.swapAt(parent, child); | } | } |} source/mir/container/binaryheap.d is 84% covered <<<<<< EOF # path=./source-mir-ndslice-allocation.lst |/++ |This is a submodule of $(MREF mir,ndslice). | |It contains allocation utilities. | | |$(BOOKTABLE $(H2 Common utilities), |$(T2 shape, Returns a shape of a common n-dimensional array. ) |) | |$(BOOKTABLE $(H2 GC Allocation utilities), |$(TR $(TH Function Name) $(TH Description)) |$(T2 slice, Allocates a slice using GC.) |$(T2 bitSlice, GC-Allocates a bitwise packed n-dimensional boolean slice.) |$(T2 ndarray, Allocates a common n-dimensional array from a slice. ) |$(T2 uninitSlice, Allocates an uninitialized slice using GC. ) |) | |$(BOOKTABLE $(H2 Ref counted allocation utilities), |$(T2 rcslice, Allocates an n-dimensional reference-counted (thread-safe) slice.) |$(T2 bitRcslice, Allocates a bitwise packed n-dimensional reference-counted (thread-safe) boolean slice.) |$(T2 mininitRcslice, Allocates a minimally initialized n-dimensional reference-counted (thread-safe) slice.) |) | |$(BOOKTABLE $(H2 Custom allocation utilities), |$(TR $(TH Function Name) $(TH Description)) |$(T2 makeNdarray, Allocates a common n-dimensional array from a slice using an allocator. ) |$(T2 makeSlice, Allocates a slice using an allocator. ) |$(T2 makeUninitSlice, Allocates an uninitialized slice using an allocator. ) |) | |$(BOOKTABLE $(H2 CRuntime allocation utilities), |$(TR $(TH Function Name) $(TH Description)) |$(T2 stdcSlice, Allocates a slice copy using `core.stdc.stdlib.malloc`) |$(T2 stdcUninitSlice, Allocates an uninitialized slice using `core.stdc.stdlib.malloc`.) |$(T2 stdcFreeSlice, Frees memory using `core.stdc.stdlib.free`) |) | |$(BOOKTABLE $(H2 Aligned allocation utilities), |$(TR $(TH Function Name) $(TH Description)) |$(T2 uninitAlignedSlice, Allocates an uninitialized aligned slice using GC. ) |$(T2 stdcUninitAlignedSlice, Allocates an uninitialized aligned slice using CRuntime.) |$(T2 stdcFreeAlignedSlice, Frees memory using CRuntime) |) | |License: $(HTTP www.apache.org/licenses/LICENSE-2.0, Apache-2.0) |Copyright: 2020 Ilya Yaroshenko, Kaleidic Associates Advisory Limited, Symmetry Investments |Authors: Ilya Yaroshenko | |Macros: |SUBREF = $(REF_ALTTEXT $(TT $2), $2, mir, ndslice, $1)$(NBSP) |T2=$(TR $(TDNW $(LREF $1)) $(TD $+)) |+/ |module mir.ndslice.allocation; | |import mir.math.common: optmath; |import mir.ndslice.concatenation; |import mir.ndslice.field: BitField; |import mir.ndslice.internal; |import mir.ndslice.iterator: FieldIterator; |import mir.ndslice.slice; |import mir.rc.array; |import std.traits; |import std.meta: staticMap; | |@optmath: | |/++ |Allocates an n-dimensional reference-counted (thread-safe) slice. |Params: | lengths = List of lengths for each dimension. | init = Value to initialize with (optional). | slice = Slice to copy shape and data from (optional). |Returns: | n-dimensional slice |+/ |Slice!(RCI!T, N) | rcslice(T, size_t N)(size_t[N] lengths...) |{ 23| immutable len = lengths.lengthsProduct; 23| auto _lengths = lengths; 23| return typeof(return)(_lengths, RCI!T(RCArray!T(len))); |} | |/// ditto |Slice!(RCI!T, N) | rcslice(T, size_t N)(size_t[N] lengths, T init) |{ 6| auto ret = (()@trusted => mininitRcslice!T(lengths))(); 3| ret.lightScope.field[] = init; 3| return ret; |} | |/// ditto |auto rcslice(Iterator, size_t N, SliceKind kind)(Slice!(Iterator, N, kind) slice) |{ | import mir.conv: emplaceRef; | alias E = slice.DeepElement; | 219| auto result = (() @trusted => slice.shape.mininitRcslice!(Unqual!E))(); | | import mir.algorithm.iteration: each; 73| each!(emplaceRef!E)(result.lightScope, slice.lightScope); | 146| return *(() @trusted => cast(Slice!(RCI!E, N)*) &result)(); |} | |/// ditto |auto rcslice(T)(T[] array) |{ 24| return rcslice(array.sliced); |} | |/// ditto |auto rcslice(T, I)(I[] array) | if (!isImplicitlyConvertible!(I[], T[])) |{ | import mir.ndslice.topology: as; 1| return rcslice(array.sliced.as!T); |} | |/// |version(mir_test) |@safe pure nothrow @nogc unittest |{ | import mir.ndslice.slice: Slice; | import mir.rc.array: RCI; 2| auto tensor = rcslice!int(5, 6, 7); 1| assert(tensor.length == 5); 1| assert(tensor.elementCount == 5 * 6 * 7); | static assert(is(typeof(tensor) == Slice!(RCI!int, 3))); | | // creates duplicate using `rcslice` 2| auto dup = tensor.rcslice; 1| assert(dup == tensor); |} | |/// |version(mir_test) |@safe pure nothrow @nogc unittest |{ | import mir.ndslice.slice: Slice; | import mir.rc.array: RCI; 2| auto tensor = rcslice([2, 3], 5); 1| assert(tensor.elementCount == 2 * 3); 1| assert(tensor[1, 1] == 5); | | import mir.rc.array; | static assert(is(typeof(tensor) == Slice!(RCI!int, 2))); |} | |/// ditto |auto rcslice(size_t dim, Slices...)(Concatenation!(dim, Slices) concatenation) |{ | alias T = Unqual!(concatenation.DeepElement); 2| auto ret = (()@trusted => mininitRcslice!T(concatenation.shape))(); 1| ret.lightScope.opIndexAssign(concatenation); 1| return ret; |} | |/// |version(mir_test) |@safe pure nothrow @nogc unittest |{ | import mir.rc.array: RCI; | import mir.ndslice.slice: Slice; | import mir.ndslice.topology : iota; | import mir.ndslice.concatenation; 2| auto tensor = concatenation([2, 3].iota, [3].iota(6)).rcslice; 1| assert(tensor == [3, 3].iota); | | static assert(is(typeof(tensor) == Slice!(RCI!ptrdiff_t, 2))); |} | |/++ |Allocates an n-dimensional reference-counted (thread-safe) slice without memory initialisation. |Params: | lengths = List of lengths for each dimension. |Returns: | n-dimensional slice |+/ |Slice!(RCI!T, N) | uninitRCslice(T, size_t N)(size_t[N] lengths...) |{ 3| immutable len = lengths.lengthsProduct; 3| auto _lengths = lengths; 3| return typeof(return)(_lengths, RCI!T(RCArray!T(len, false))); |} | |/// |version(mir_test) |@safe pure nothrow @nogc unittest |{ | import mir.ndslice.slice: Slice; | import mir.rc.array: RCI; 2| auto tensor = uninitRCslice!int(5, 6, 7); 1| tensor[] = 1; 1| assert(tensor.length == 5); 1| assert(tensor.elementCount == 5 * 6 * 7); | static assert(is(typeof(tensor) == Slice!(RCI!int, 3))); |} | |/++ |Allocates a bitwise packed n-dimensional reference-counted (thread-safe) boolean slice. |Params: | lengths = List of lengths for each dimension. |Returns: | n-dimensional bitwise rcslice |See_also: $(SUBREF topology, bitwise). |+/ |Slice!(FieldIterator!(BitField!(RCI!size_t)), N) bitRcslice(size_t N)(size_t[N] lengths...) |{ | import mir.ndslice.topology: bitwise; | enum elen = size_t.sizeof * 8; 2| immutable len = lengths.lengthsProduct; 2| immutable dlen = (len / elen + (len % elen != 0)); 2| return RCArray!size_t(dlen).asSlice.bitwise[0 .. len].sliced(lengths); |} | |/// 1D |@safe pure nothrow @nogc |version(mir_test) unittest |{ 2| auto bitarray = 100.bitRcslice; // allocates 16 bytes total (plus RC context) 1| assert(bitarray.shape == cast(size_t[1])[100]); 1| assert(bitarray[72] == false); 1| bitarray[72] = true; 1| assert(bitarray[72] == true); |} | |/// 2D |@safe pure nothrow @nogc |version(mir_test) unittest |{ 2| auto bitmatrix = bitRcslice(20, 6); // allocates 16 bytes total (plus RC context) 1| assert(bitmatrix.shape == cast(size_t[2])[20, 6]); 1| assert(bitmatrix[3, 4] == false); 1| bitmatrix[3, 4] = true; 1| assert(bitmatrix[3, 4] == true); |} | |/++ |Allocates a minimally initialized n-dimensional reference-counted (thread-safe) slice. |Params: | lengths = list of lengths for each dimension |Returns: | contiguous minimally initialized n-dimensional reference-counted (thread-safe) slice |+/ |Slice!(RCI!T, N) mininitRcslice(T, size_t N)(size_t[N] lengths...) |{ 78| immutable len = lengths.lengthsProduct; 78| auto _lengths = lengths; 78| return Slice!(RCI!T, N)(_lengths, RCI!T(mininitRcarray!T(len))); |} | |/// |version(mir_test) |pure nothrow @nogc unittest |{ | import mir.ndslice.slice: Slice; | import mir.rc.array: RCI; 2| auto tensor = mininitRcslice!int(5, 6, 7); 1| assert(tensor.length == 5); 1| assert(tensor.elementCount == 5 * 6 * 7); | static assert(is(typeof(tensor) == Slice!(RCI!int, 3))); |} | |private alias Pointer(T) = T*; |private alias Pointers(Args...) = staticMap!(Pointer, Args); | |/++ |GC-Allocates an n-dimensional slice. |+/ |template slice(Args...) | if (Args.length) |{ | /// | alias LabelTypes = Args[1 .. $]; | /// | alias T = Args[0]; | | /++ | Params: | lengths = List of lengths for each dimension. | init = Value to initialize with (optional). | Returns: | initialzed n-dimensional slice | +/ | Slice!(T*, N, Contiguous, Pointers!LabelTypes) | slice(size_t N)(size_t[N] lengths...) | if (N >= LabelTypes.length) | { 80| auto shape = lengths; // DMD variadic bug workaround 80| immutable len = shape.lengthsProduct; 239| auto ret = typeof(return)(shape, len == 0 ? null : (()@trusted=>new T[len].ptr)()); | foreach (i, L; LabelTypes) // static 28| ret._labels[i] = (()@trusted=>new L[shape[i]].ptr)(); 80| return ret; | } | | /// ditto | Slice!(T*, N, Contiguous, Pointers!LabelTypes) | slice(size_t N)(size_t[N] lengths, T init) | if (N >= LabelTypes.length) | { | import mir.conv: emplaceRef; | import std.array : uninitializedArray; 5| immutable len = lengths.lengthsProduct; 5| auto arr = uninitializedArray!(Unqual!T[])(len); 135| foreach (ref e; arr) 40| emplaceRef(e, init); 15| auto ret = typeof(return)(lengths, len == 0 ? null : (()@trusted=>cast(T*)arr.ptr)()); | foreach (i, L; LabelTypes) // static | ret._labels[i] = (()@trusted=>new L[shape[i]].ptr)(); 5| return ret; | } |} | |/// |version(mir_test) |@safe pure nothrow unittest |{ | import mir.ndslice.slice: Slice; 1| auto tensor = slice!int(5, 6, 7); 1| assert(tensor.length == 5); 1| assert(tensor.length!1 == 6); 1| assert(tensor.elementCount == 5 * 6 * 7); | static assert(is(typeof(tensor) == Slice!(int*, 3))); |} | |/// 2D DataFrame example |version(mir_test) |@safe pure unittest |{ | import mir.ndslice.slice; | import mir.ndslice.allocation: slice; | import mir.date: Date; | 1| auto dataframe = slice!(double, Date, string)(4, 3); 1| assert(dataframe.length == 4); 1| assert(dataframe.length!1 == 3); 1| assert(dataframe.elementCount == 4 * 3); | | static assert(is(typeof(dataframe) == | Slice!(double*, 2, Contiguous, Date*, string*))); | | // Dataframe labels are contiguous 1-dimensional slices. | | // Fill row labels 1| dataframe.label[] = [ | Date(2019, 1, 24), | Date(2019, 2, 2), | Date(2019, 2, 4), | Date(2019, 2, 5), | ]; | 1| assert(dataframe.label!0[2] == Date(2019, 2, 4)); | | // Fill column labels 1| dataframe.label!1[] = ["income", "outcome", "balance"]; | 1| assert(dataframe.label!1[2] == "balance"); | | // Change label element 1| dataframe.label!1[2] = "total"; 1| assert(dataframe.label!1[2] == "total"); | | // Attach a newly allocated label 1| dataframe.label!1 = ["Income", "Outcome", "Balance"].sliced; | 1| assert(dataframe.label!1[2] == "Balance"); |} | |/++ |GC-Allocates an n-dimensional slice. |Params: | lengths = List of lengths for each dimension. | init = Value to initialize with (optional). |Returns: | initialzed n-dimensional slice |+/ |Slice!(T*, N) | slice(size_t N, T)(size_t[N] lengths, T init) |{ 1| return .slice!T(lengths, init); |} | |// TODO: make it a dataframe compatible. This function performs copy. |/// ditto |auto slice(Iterator, size_t N, SliceKind kind)(Slice!(Iterator, N, kind) slice) |{ 108| if (__ctfe) | { | import mir.ndslice.topology: flattened; | import mir.array.allocation: array; 0000000| return slice.flattened.array.sliced(slice.shape); | } | else | { | import mir.conv: emplaceRef; | alias E = slice.DeepElement; | 216| auto result = (() @trusted => slice.shape.uninitSlice!(Unqual!E))(); | | import mir.algorithm.iteration: each; 108| each!(emplaceRef!E)(result, slice); | 216| return (() @trusted => cast(Slice!(E*, N)) result)(); | } |} | |/// |version(mir_test) |@safe pure nothrow unittest |{ 1| auto tensor = slice([2, 3], 5); 1| assert(tensor.elementCount == 2 * 3); 1| assert(tensor[1, 1] == 5); | | // creates duplicate using `slice` 1| auto dup = tensor.slice; 1| assert(dup == tensor); |} | |/// ditto |auto slice(size_t dim, Slices...)(Concatenation!(dim, Slices) concatenation) |{ | alias T = Unqual!(concatenation.DeepElement); | static if (hasElaborateAssign!T) | alias fun = .slice; | else | alias fun = .uninitSlice; 42| auto ret = (()@trusted => fun!T(concatenation.shape))(); 21| ret.opIndexAssign(concatenation); 21| return ret; |} | |/// |version(mir_test) |@safe pure nothrow unittest |{ | import mir.ndslice.slice: Slice; | import mir.ndslice.topology : iota; | import mir.ndslice.concatenation; 1| auto tensor = concatenation([2, 3].iota, [3].iota(6)).slice; 1| assert(tensor == [3, 3].iota); | | static assert(is(typeof(tensor) == Slice!(ptrdiff_t*, 2))); |} | |/++ |GC-Allocates a bitwise packed n-dimensional boolean slice. |Params: | lengths = List of lengths for each dimension. |Returns: | n-dimensional bitwise slice |See_also: $(SUBREF topology, bitwise). |+/ |Slice!(FieldIterator!(BitField!(size_t*)), N) bitSlice(size_t N)(size_t[N] lengths...) |{ | import mir.ndslice.topology: bitwise; | enum elen = size_t.sizeof * 8; 10| immutable len = lengths.lengthsProduct; 10| immutable dlen = (len / elen + (len % elen != 0)); 10| return new size_t[dlen].sliced.bitwise[0 .. len].sliced(lengths); |} | |/// 1D |@safe pure version(mir_test) unittest |{ 1| auto bitarray = bitSlice(100); // allocates 16 bytes total 1| assert(bitarray.shape == [100]); 1| assert(bitarray[72] == false); 1| bitarray[72] = true; 1| assert(bitarray[72] == true); |} | |/// 2D |@safe pure version(mir_test) unittest |{ 1| auto bitmatrix = bitSlice(20, 6); // allocates 16 bytes total 1| assert(bitmatrix.shape == [20, 6]); 1| assert(bitmatrix[3, 4] == false); 1| bitmatrix[3, 4] = true; 1| assert(bitmatrix[3, 4] == true); |} | |/++ |GC-Allocates an uninitialized n-dimensional slice. |Params: | lengths = list of lengths for each dimension |Returns: | contiguous uninitialized n-dimensional slice |+/ |auto uninitSlice(T, size_t N)(size_t[N] lengths...) |{ 485| immutable len = lengths.lengthsProduct; | import std.array : uninitializedArray; 485| auto arr = uninitializedArray!(T[])(len); | version (mir_secure_memory) | {()@trusted{ | (cast(ubyte[])arr)[] = 0; | }();} 485| return arr.sliced(lengths); |} | |/// |version(mir_test) |@safe pure nothrow unittest |{ | import mir.ndslice.slice: Slice; 1| auto tensor = uninitSlice!int(5, 6, 7); 1| assert(tensor.length == 5); 1| assert(tensor.elementCount == 5 * 6 * 7); | static assert(is(typeof(tensor) == Slice!(int*, 3))); |} | |/++ |GC-Allocates an uninitialized aligned an n-dimensional slice. |Params: | lengths = list of lengths for each dimension | alignment = memory alignment (bytes) |Returns: | contiguous uninitialized n-dimensional slice |+/ |auto uninitAlignedSlice(T, size_t N)(size_t[N] lengths, uint alignment) @system |{ 1| immutable len = lengths.lengthsProduct; | import std.array : uninitializedArray; 2| assert((alignment != 0) && ((alignment & (alignment - 1)) == 0), "'alignment' must be a power of two"); 2| size_t offset = alignment <= 16 ? 0 : alignment - 1; 1| void* basePtr = uninitializedArray!(byte[])(len * T.sizeof + offset).ptr; 1| T* alignedPtr = cast(T*)((cast(size_t)(basePtr) + offset) & ~offset); 1| return alignedPtr.sliced(lengths); |} | |/// |version(mir_test) |@system pure nothrow unittest |{ | import mir.ndslice.slice: Slice; 1| auto tensor = uninitAlignedSlice!double([5, 6, 7], 64); 1| tensor[] = 0; 1| assert(tensor.length == 5); 1| assert(tensor.elementCount == 5 * 6 * 7); 1| assert(cast(size_t)(tensor.ptr) % 64 == 0); | static assert(is(typeof(tensor) == Slice!(double*, 3))); |} | |/++ |Allocates an array through a specified allocator and creates an n-dimensional slice over it. |See also $(MREF std, experimental, allocator). |Params: | alloc = allocator | lengths = list of lengths for each dimension | init = default value for array initialization | slice = slice to copy shape and data from |Returns: | a structure with fields `array` and `slice` |Note: | `makeSlice` always returns slice with mutable elements |+/ |auto makeSlice(Allocator, size_t N, Iterator)(auto ref Allocator alloc, Slice!(N, Iterator) slice) |{ | alias T = Unqual!(slice.DeepElement); | return makeSlice!(T)(alloc, slice); |} | |/// ditto |Slice!(T*, N) |makeSlice(T, Allocator, size_t N)(auto ref Allocator alloc, size_t[N] lengths...) |{ | import std.experimental.allocator : makeArray; 1| return alloc.makeArray!T(lengths.lengthsProduct).sliced(lengths); |} | |/// ditto |Slice!(T*, N) |makeSlice(T, Allocator, size_t N)(auto ref Allocator alloc, size_t[N] lengths, T init) |{ | import std.experimental.allocator : makeArray; 2| immutable len = lengths.lengthsProduct; 2| auto array = alloc.makeArray!T(len, init); 2| return array.sliced(lengths); |} | |/// ditto |auto makeSlice(Allocator, Iterator, size_t N, SliceKind kind) | (auto ref Allocator allocator, Slice!(Iterator, N, kind) slice) |{ | import mir.conv: emplaceRef; | alias E = slice.DeepElement; | 1| auto result = allocator.makeUninitSlice!(Unqual!E)(slice.shape); | | import mir.algorithm.iteration: each; 1| each!(emplaceRef!E)(result, slice); | 1| return cast(Slice!(E*, N)) result; |} | |/// Initialization with default value |version(mir_test) |@nogc unittest |{ | import std.experimental.allocator; | import std.experimental.allocator.mallocator; | import mir.algorithm.iteration: all; | import mir.ndslice.topology: map; | 1| auto sl = Mallocator.instance.makeSlice([2, 3, 4], 10); 1| auto ar = sl.field; 1| assert(sl.all!"a == 10"); | 1| auto sl2 = Mallocator.instance.makeSlice(sl.map!"a * 2"); 1| auto ar2 = sl2.field; 1| assert(sl2.all!"a == 20"); | 1| Mallocator.instance.dispose(ar); 1| Mallocator.instance.dispose(ar2); |} | |version(mir_test) |@nogc unittest |{ | import std.experimental.allocator; | import std.experimental.allocator.mallocator; | | // cast to your own type 1| auto sl = makeSlice!double(Mallocator.instance, [2, 3, 4], 10); 1| auto ar = sl.field; 1| assert(sl[1, 1, 1] == 10.0); 1| Mallocator.instance.dispose(ar); |} | |/++ |Allocates an uninitialized array through a specified allocator and creates an n-dimensional slice over it. |See also $(MREF std, experimental, allocator). |Params: | alloc = allocator | lengths = list of lengths for each dimension |Returns: | a structure with fields `array` and `slice` |+/ |Slice!(T*, N) |makeUninitSlice(T, Allocator, size_t N)(auto ref Allocator alloc, size_t[N] lengths...) | if (N) |{ 8| if (immutable len = lengths.lengthsProduct) | { 8| auto mem = alloc.allocate(len * T.sizeof); 8| if (mem.length == 0) assert(0); 16| auto array = () @trusted { return cast(T[]) mem; }(); | version (mir_secure_memory) | {() @trusted { | (cast(ubyte[])array)[] = 0; | }();} 8| return array.sliced(lengths); | } | else | { 0000000| return T[].init.sliced(lengths); | } |} | |/// |version(mir_test) |@system @nogc unittest |{ | import std.experimental.allocator; | import std.experimental.allocator.mallocator; | 1| auto sl = makeUninitSlice!int(Mallocator.instance, 2, 3, 4); 1| auto ar = sl.field; 1| assert(ar.ptr is sl.iterator); 1| assert(ar.length == 24); 1| assert(sl.elementCount == 24); | 1| Mallocator.instance.dispose(ar); |} | |/++ |Allocates a common n-dimensional array from a slice. |Params: | slice = slice |Returns: | multidimensional D array |+/ |auto ndarray(Iterator, size_t N, SliceKind kind)(Slice!(Iterator, N, kind) slice) |{ | import mir.array.allocation : array; | static if (slice.N == 1) | { 3| return slice.array; | } | else | { | import mir.ndslice.topology: ipack, map; 1| return slice.ipack!1.map!(.ndarray).array; | } |} | |/// |version(mir_test) |@safe pure nothrow unittest |{ | import mir.ndslice.topology : iota; 1| auto slice = iota(3, 4); 1| auto m = slice.ndarray; | static assert(is(typeof(m) == sizediff_t[][])); // sizediff_t is long for 64 bit platforms 1| assert(m == [[0, 1, 2, 3], [4, 5, 6, 7], [8, 9, 10, 11]]); |} | |/++ |Allocates a common n-dimensional array using data from a slice. |Params: | alloc = allocator (optional) | slice = slice |Returns: | multidimensional D array |+/ |auto makeNdarray(T, Allocator, Iterator, size_t N, SliceKind kind)(auto ref Allocator alloc, Slice!(Iterator, N, kind) slice) |{ | import std.experimental.allocator : makeArray; | static if (slice.N == 1) | { 3| return makeArray!T(alloc, slice); | } | else | { | alias E = typeof(makeNdarray!T(alloc, slice[0])); 1| auto ret = makeArray!E(alloc, slice.length); 15| foreach (i, ref e; ret) 3| e = .makeNdarray!T(alloc, slice[i]); 1| return ret; | } |} | |/// |version(mir_test) |@nogc unittest |{ | import std.experimental.allocator; | import std.experimental.allocator.mallocator; | import mir.ndslice.topology : iota; | 1| auto slice = iota(3, 4); 1| auto m = Mallocator.instance.makeNdarray!long(slice); | | static assert(is(typeof(m) == long[][])); | | static immutable ar = [[0L, 1, 2, 3], [4L, 5, 6, 7], [8L, 9, 10, 11]]; 1| assert(m == ar); | 12| foreach (ref row; m) 3| Mallocator.instance.dispose(row); 1| Mallocator.instance.dispose(m); |} | |/++ |Shape of a common n-dimensional array. |Params: | array = common n-dimensional array | err = error flag passed by reference |Returns: | static array of dimensions type of `size_t[n]` |+/ |auto shape(T)(T[] array, ref int err) |{ | static if (isDynamicArray!T) | { 4| size_t[1 + typeof(shape(T.init, err)).length] ret; | 4| if (array.length) | { 3| ret[0] = array.length; 3| ret[1..$] = shape(array[0], err); 3| if (err) 0000000| goto L; 25| foreach (ar; array) | { 6| if (shape(ar, err) != ret[1..$]) 1| err = 1; 6| if (err) 1| goto L; | } | } | } | else | { 9| size_t[1] ret; 9| ret[0] = array.length; | } 12| err = 0; |L: 13| return ret; |} | |/// |version(mir_test) |@safe pure unittest |{ 1| int err; 1| size_t[2] shape = [[1, 2, 3], [4, 5, 6]].shape(err); 1| assert(err == 0); 1| assert(shape == [2, 3]); | 1| [[1, 2], [4, 5, 6]].shape(err); 1| assert(err == 1); |} | |/// Slice from ndarray |version(mir_test) |unittest |{ | import mir.ndslice.allocation: slice, shape; 1| int err; 1| auto array = [[1, 2, 3], [4, 5, 6]]; 1| auto s = array.shape(err).slice!int; 1| s[] = [[1, 2, 3], [4, 5, 6]]; 1| assert(s == array); |} | |version(mir_test) |@safe pure unittest |{ 1| int err; 1| size_t[2] shape = (int[][]).init.shape(err); 1| assert(shape[0] == 0); 1| assert(shape[1] == 0); |} | |version(mir_test) |nothrow unittest |{ | import mir.ndslice.allocation; | import mir.ndslice.topology : iota; | 1| auto sl = iota([0, 0], 1); | 1| assert(sl.empty!0); 1| assert(sl.empty!1); | 1| auto gcsl1 = sl.slice; 1| auto gcsl2 = slice!double(0, 0); | | import std.experimental.allocator; | import std.experimental.allocator.mallocator; | 1| auto sl2 = makeSlice!double(Mallocator.instance, 0, 0); | 1| Mallocator.instance.dispose(sl2.field); |} | |/++ |Allocates an uninitialized array using `core.stdc.stdlib.malloc` and creates an n-dimensional slice over it. |Params: | lengths = list of lengths for each dimension |Returns: | contiguous uninitialized n-dimensional slice |See_also: | $(LREF stdcSlice), $(LREF stdcFreeSlice) |+/ |Slice!(T*, N) stdcUninitSlice(T, size_t N)(size_t[N] lengths...) |{ | import core.stdc.stdlib: malloc; 2| immutable len = lengths.lengthsProduct; 2| auto p = malloc(len * T.sizeof); 2| if (p is null) assert(0); | version (mir_secure_memory) | { | (cast(ubyte*)p)[0 .. len * T.sizeof] = 0; | } 4| auto ptr = len ? cast(T*) p : null; 2| return ptr.sliced(lengths); |} | |/++ |Allocates a copy of a slice using `core.stdc.stdlib.malloc`. |Params: | slice = n-dimensional slice |Returns: | contiguous n-dimensional slice |See_also: | $(LREF stdcUninitSlice), $(LREF stdcFreeSlice) |+/ |auto stdcSlice(Iterator, size_t N, SliceKind kind)(Slice!(Iterator, N, kind) slice) |{ | alias E = slice.DeepElement; | alias T = Unqual!E; | static assert (!hasElaborateAssign!T, "stdcSlice is not miplemented for slices that have elaborate assign"); 1| auto ret = stdcUninitSlice!T(slice.shape); | | import mir.conv: emplaceRef; | import mir.algorithm.iteration: each; 1| each!(emplaceRef!E)(ret, slice); 1| return ret; |} | |/++ |Frees memory using `core.stdc.stdlib.free`. |Params: | slice = n-dimensional slice |See_also: | $(LREF stdcSlice), $(LREF stdcUninitSlice) |+/ |void stdcFreeSlice(T, size_t N)(Slice!(T*, N) slice) |{ | import core.stdc.stdlib: free; | version (mir_secure_memory) | { | (cast(ubyte[])slice.field)[] = 0; | } 2| slice._iterator.free; |} | |/// |version(mir_test) |unittest |{ | import mir.ndslice.topology: iota; | 1| auto i = iota(3, 4); 1| auto s = i.stdcSlice; 1| auto t = s.shape.stdcUninitSlice!size_t; | 1| t[] = s; | 1| assert(t == i); | 1| s.stdcFreeSlice; 1| t.stdcFreeSlice; |} | |/++ |Allocates an uninitialized aligned array using `core.stdc.stdlib.malloc` and creates an n-dimensional slice over it. |Params: | lengths = list of lengths for each dimension | alignment = memory alignment (bytes) |Returns: | contiguous uninitialized n-dimensional slice |+/ |auto stdcUninitAlignedSlice(T, size_t N)(size_t[N] lengths, uint alignment) @system |{ 1| immutable len = lengths.lengthsProduct; | import mir.internal.memory: alignedAllocate; 1| auto arr = (cast(T*)alignedAllocate(len * T.sizeof, alignment))[0 .. len]; | version (mir_secure_memory) | { | (cast(ubyte[])arr)[] = 0; | } 1| return arr.sliced(lengths); |} | |/// |version(mir_test) |@system pure nothrow unittest |{ | import mir.ndslice.slice: Slice; 1| auto tensor = stdcUninitAlignedSlice!double([5, 6, 7], 64); 1| assert(tensor.length == 5); 1| assert(tensor.elementCount == 5 * 6 * 7); 1| assert(cast(size_t)(tensor.ptr) % 64 == 0); | static assert(is(typeof(tensor) == Slice!(double*, 3))); 1| stdcFreeAlignedSlice(tensor); |} | |/++ |Frees aligned memory allocaged by CRuntime. |Params: | slice = n-dimensional slice |See_also: | $(LREF stdcSlice), $(LREF stdcUninitSlice) |+/ |void stdcFreeAlignedSlice(T, size_t N)(Slice!(T*, N) slice) |{ | import mir.internal.memory: alignedFree; | version (mir_secure_memory) | { | (cast(ubyte[])slice.field)[] = 0; | } 1| slice._iterator.alignedFree; |} source/mir/ndslice/allocation.d is 98% covered <<<<<< EOF # path=./source-mir-series.lst |/++ |$(H1 Index-series) | |The module contains $(LREF Series) data structure with special iteration and indexing methods. |It is aimed to construct index or time-series using Mir and Phobos algorithms. | |Public_imports: $(MREF mir,ndslice,slice). | |Copyright: 2020 Ilya Yaroshenko, Kaleidic Associates Advisory Limited, Symmetry Investments |Authors: Ilya Yaroshenko | |Macros: |NDSLICE = $(REF_ALTTEXT $(TT $2), $2, mir, ndslice, $1)$(NBSP) |T2=$(TR $(TDNW $(LREF $1)) $(TD $+)) |+/ |module mir.series; | |public import mir.ndslice.slice; |public import mir.ndslice.sorting: sort; |import mir.ndslice.iterator: IotaIterator; |import mir.ndslice.sorting: transitionIndex; |import mir.qualifier; |import std.traits; | |/++ |See_also: $(LREF unionSeries), $(LREF troykaSeries), $(LREF troykaGalop). |+/ |@safe version(mir_test) unittest |{ | import mir.ndslice; | import mir.series; | | import mir.array.allocation: array; | import mir.algorithm.setops: multiwayUnion; | | import mir.date: Date; | import core.lifetime: move; | import std.exception: collectExceptionMsg; | | ////////////////////////////////////// | // Constructs two time-series. | ////////////////////////////////////// 1| auto index0 = [ | Date(2017, 01, 01), | Date(2017, 03, 01), | Date(2017, 04, 01)]; | 1| auto data0 = [1.0, 3, 4]; 1| auto series0 = index0.series(data0); | 1| auto index1 = [ | Date(2017, 01, 01), | Date(2017, 02, 01), | Date(2017, 05, 01)]; | 1| auto data1 = [10.0, 20, 50]; 1| auto series1 = index1.series(data1); | | ////////////////////////////////////// | // asSlice method | ////////////////////////////////////// 1| assert(series0 | .asSlice | // ref qualifier is optional 3| .map!((ref key, ref value) => key.yearMonthDay.month == value) | .all); | | ////////////////////////////////////// | // get* methods | ////////////////////////////////////// | 1| auto refDate = Date(2017, 03, 01); 1| auto missingDate = Date(2016, 03, 01); | | // default value 1| double defaultValue = 100; 1| assert(series0.get(refDate, defaultValue) == 3); 1| assert(series0.get(missingDate, defaultValue) == defaultValue); | | // Exceptions handlers 1| assert(series0.get(refDate) == 3); 1| assert(series0.get(refDate, new Exception("My exception msg")) == 3); 1| assert(series0.getVerbose(refDate) == 3); 1| assert(series0.getExtraVerbose(refDate, "My exception msg") == 3); | 1| assert(collectExceptionMsg!Exception( 1| series0.get(missingDate) | ) == "Series double[date]: Missing required key"); | 1| assert(collectExceptionMsg!Exception( 2| series0.get(missingDate, new Exception("My exception msg")) | ) == "My exception msg"); | 1| assert(collectExceptionMsg!Exception( 1| series0.getVerbose(missingDate) | ) == "Series double[date]: Missing 2016-03-01 key"); | 1| assert(collectExceptionMsg!Exception( 1| series0.getExtraVerbose(missingDate, "My exception msg") | ) == "My exception msg. Series double[date]: Missing 2016-03-01 key"); | | // assign with get* 1| series0.get(refDate) = 100; 1| assert(series0.get(refDate) == 100); 1| series0.get(refDate) = 3; | | // tryGet 1| double val; 1| assert(series0.tryGet(refDate, val)); 1| assert(val == 3); 1| assert(!series0.tryGet(missingDate, val)); 1| assert(val == 3); // val was not changed | | ////////////////////////////////////// | // Merges multiple series into one. | // Allocates using GC. M | // Makes exactly two allocations per merge: | // one for index/time and one for data. | ////////////////////////////////////// 1| auto m0 = unionSeries(series0, series1); 1| auto m1 = unionSeries(series1, series0); // order is matter | 1| assert(m0.index == [ | Date(2017, 01, 01), | Date(2017, 02, 01), | Date(2017, 03, 01), | Date(2017, 04, 01), | Date(2017, 05, 01)]); | 1| assert(m0.index == m1.index); 1| assert(m0.data == [ 1, 20, 3, 4, 50]); 1| assert(m1.data == [10, 20, 3, 4, 50]); | | ////////////////////////////////////// | // Joins two time-series into a one with two columns. | ////////////////////////////////////// 1| auto u = [index0, index1].multiwayUnion; 1| auto index = u.move.array; 1| auto data = slice!double([index.length, 2], 0); // initialized to 0 value 1| auto series = index.series(data); | 1| series[0 .. $, 0][] = series0; // fill first column 1| series[0 .. $, 1][] = series1; // fill second column | 1| assert(data == [ | [1, 10], | [0, 20], | [3, 0], | [4, 0], | [0, 50]]); |} | |/// |unittest{ | | import mir.series; | 1| double[int] map; 1| map[1] = 4.0; 1| map[2] = 5.0; 1| map[4] = 6.0; 1| map[5] = 10.0; 1| map[10] = 11.0; | 1| const s = series(map); | 1| double value; 1| int key; 2| assert(s.tryGet(2, value) && value == 5.0); 1| assert(!s.tryGet(8, value)); | 2| assert(s.tryGetNext(2, value) && value == 5.0); 2| assert(s.tryGetPrev(2, value) && value == 5.0); 2| assert(s.tryGetNext(8, value) && value == 11.0); 2| assert(s.tryGetPrev(8, value) && value == 10.0); 1| assert(!s.tryGetFirst(8, 9, value)); 2| assert(s.tryGetFirst(2, 10, value) && value == 5.0); 2| assert(s.tryGetLast(2, 10, value) && value == 11.0); 2| assert(s.tryGetLast(2, 8, value) && value == 10.0); | 4| key = 2; assert(s.tryGetNextUpdateKey(key, value) && key == 2 && value == 5.0); 4| key = 2; assert(s.tryGetPrevUpdateKey(key, value) && key == 2 && value == 5.0); 4| key = 8; assert(s.tryGetNextUpdateKey(key, value) && key == 10 && value == 11.0); 4| key = 8; assert(s.tryGetPrevUpdateKey(key, value) && key == 5 && value == 10.0); 4| key = 2; assert(s.tryGetFirstUpdateLower(key, 10, value) && key == 2 && value == 5.0); 4| key = 10; assert(s.tryGetLastUpdateKey(2, key, value) && key == 10 && value == 11.0); 4| key = 8; assert(s.tryGetLastUpdateKey(2, key, value) && key == 5 && value == 10.0); |} | |import mir.ndslice.slice; |import mir.ndslice.internal: is_Slice, isIndex; |import mir.math.common: optmath; | |import std.meta; | |@optmath: | |/++ |Plain index/time observation data structure. |Observation are used as return tuple for for indexing $(LREF Series). |+/ |struct mir_observation(Index, Data) |{ | /// Date, date-time, time, or index. | Index index; | /// An alias for time-series index. | alias time = index; | /// An alias for key-value representation. | alias key = index; | /// Value or ndslice. | Data data; | /// An alias for key-value representation. | alias value = data; |} | |/// ditto |alias Observation = mir_observation; | |/// Convenient function for $(LREF Observation) construction. |auto observation(Index, Data)(Index index, Data data) |{ | alias R = mir_observation!(Index, Data); 8| return R(index, data); |} | |/++ |Convinient alias for 1D Contiguous $(LREF Series). |+/ |alias SeriesMap(K, V) = mir_series!(K*, V*); | |/// |version(mir_test) unittest |{ | import std.traits; | import mir.series; | | static assert (is(SeriesMap!(string, double) == Series!(string*, double*))); | | /// LHS, RHS | static assert (isAssignable!(SeriesMap!(string, double), SeriesMap!(string, double))); | static assert (isAssignable!(SeriesMap!(string, double), typeof(null))); | | static assert (isAssignable!(SeriesMap!(const string, double), SeriesMap!(string, double))); | static assert (isAssignable!(SeriesMap!(string, const double), SeriesMap!(string, double))); | static assert (isAssignable!(SeriesMap!(const string, const double), SeriesMap!(string, double))); | | static assert (isAssignable!(SeriesMap!(immutable string, double), SeriesMap!(immutable string, double))); | static assert (isAssignable!(SeriesMap!(immutable string, const double), SeriesMap!(immutable string, double))); | static assert (isAssignable!(SeriesMap!(const string, const double), SeriesMap!(immutable string, double))); | static assert (isAssignable!(SeriesMap!(string, immutable double), SeriesMap!(string, immutable double))); | static assert (isAssignable!(SeriesMap!(const string, immutable double), SeriesMap!(string, immutable double))); | static assert (isAssignable!(SeriesMap!(const string, const double), SeriesMap!(string, immutable double))); | // etc |} | |/++ |Plain index series data structure. | |`*.index[i]`/`*.key[i]`/`*.time` corresponds to `*.data[i]`/`*.value`. | |Index is assumed to be sorted. |$(LREF sort) can be used to normalise a series. |+/ 15|struct mir_series(IndexIterator_, Iterator_, size_t N_ = 1, SliceKind kind_ = Contiguous) |{ | private enum doUnittest = is(typeof(this) == Series!(int*, double*)); | | /// | alias IndexIterator = IndexIterator_; | | /// | alias Iterator = Iterator_; | | /// | enum size_t N = N_; | | /// | enum SliceKind kind = kind_; | | /// | Slice!(Iterator, N, kind) _data; | | /// | IndexIterator _index; | | /// Index / Key / Time type aliases | alias Index = typeof(typeof(this).init.index.front); | /// ditto | alias Key = Index; | /// ditto | alias Time = Index; | /// Data / Value type aliases | alias Data = typeof(typeof(this).init.data.front); | /// ditto | alias Value = Data; | | /// An alias for time-series index. | alias time = index; | /// An alias for key-value representation. | alias key = index; | /// An alias for key-value representation. | alias value = data; | | private enum defaultMsg() = "Series " ~ Unqual!(this.Data).stringof ~ "[" ~ Unqual!(this.Index).stringof ~ "]: Missing"; | private static immutable defaultExc() = new Exception(defaultMsg!() ~ " required key"); | |@optmath: | | /// 171| this()(Slice!IndexIterator index, Slice!(Iterator, N, kind) data) | { 171| assert(index.length == data.length, "Series constructor: index and data lengths must be equal."); 171| _data = data; 171| _index = index._iterator; | } | | | /// Construct from null 2| this(typeof(null)) | { 2| _data = _data.init; 2| _index = _index.init; | } | | /// | bool opEquals(RIndexIterator, RIterator, size_t RN, SliceKind rkind, )(Series!(RIndexIterator, RIterator, RN, rkind) rhs) const | { 12| return this.lightScopeIndex == rhs.lightScopeIndex && this._data.lightScope == rhs._data.lightScope; | } | | /++ | Index series is assumed to be sorted. | | `IndexIterator` is an iterator on top of date, date-time, time, or numbers or user defined types with defined `opCmp`. | For example, `Date*`, `DateTime*`, `immutable(long)*`, `mir.ndslice.iterator.IotaIterator`. | +/ | auto index()() @property @trusted | { 691| return _index.sliced(_data._lengths[0]); | } | | /// ditto | auto index()() @property @trusted const | { 204| return _index.lightConst.sliced(_data._lengths[0]); | } | | /// ditto | auto index()() @property @trusted immutable | { | return _index.lightImmutable.sliced(_data._lengths[0]); | } | | private auto lightScopeIndex()() @property @trusted | { 97| return .lightScope(_index).sliced(_data._lengths[0]); | } | | private auto lightScopeIndex()() @property @trusted const | { 31| return .lightScope(_index).sliced(_data._lengths[0]); | } | | private auto lightScopeIndex()() @property @trusted immutable | { | return .lightScope(_index).sliced(_data._lengths[0]); | } | | /++ | Data is any ndslice with only one constraints, | `data` and `index` lengths should be equal. | +/ | auto data()() @property @trusted | { 458| return _data; | } | | /// ditto | auto data()() @property @trusted const | { 204| return _data[]; | } | | /// ditto | auto data()() @property @trusted immutable | { | return _data[]; | } | | /// | typeof(this) opBinary(string op : "~")(typeof(this) rhs) | { 2| return unionSeries(this.lightScope, rhs.lightScope); | } | | /// ditto | auto opBinary(string op : "~")(const typeof(this) rhs) const | { 2| return unionSeries(this.lightScope, rhs.lightScope); | } | | static if (doUnittest) | /// | @safe pure nothrow version(mir_test) unittest | { | import mir.date: Date; | | ////////////////////////////////////// | // Constructs two time-series. | ////////////////////////////////////// 1| auto index0 = [1,3,4]; 1| auto data0 = [1.0, 3, 4]; 1| auto series0 = index0.series(data0); | 1| auto index1 = [1,2,5]; 1| auto data1 = [10.0, 20, 50]; 1| auto series1 = index1.series(data1); | | ////////////////////////////////////// | // Merges multiple series into one. | ////////////////////////////////////// | // Order is matter. | // The first slice has higher priority. 1| auto m0 = series0 ~ series1; 1| auto m1 = series1 ~ series0; | 1| assert(m0.index == m1.index); 1| assert(m0.data == [ 1, 20, 3, 4, 50]); 1| assert(m1.data == [10, 20, 3, 4, 50]); | } | | static if (doUnittest) | @safe pure nothrow version(mir_test) unittest | { | import mir.date: Date; | | ////////////////////////////////////// | // Constructs two time-series. | ////////////////////////////////////// 1| auto index0 = [1,3,4]; 1| auto data0 = [1.0, 3, 4]; 1| auto series0 = index0.series(data0); | 1| auto index1 = [1,2,5]; 1| auto data1 = [10.0, 20, 50]; 1| const series1 = index1.series(data1); | | ////////////////////////////////////// | // Merges multiple series into one. | ////////////////////////////////////// | // Order is matter. | // The first slice has higher priority. 1| auto m0 = series0 ~ series1; 1| auto m1 = series1 ~ series0; | 1| assert(m0.index == m1.index); 1| assert(m0.data == [ 1, 20, 3, 4, 50]); 1| assert(m1.data == [10, 20, 3, 4, 50]); | } | | /++ | Special `[] =` index-assign operator for index-series. | Assigns data from `r` with index intersection. | If a index index in `r` is not in the index index for this series, then no op-assign will take place. | This and r series are assumed to be sorted. | | Params: | r = rvalue index-series | +/ | void opIndexAssign(IndexIterator_, Iterator_, size_t N_, SliceKind kind_) | (Series!(IndexIterator_, Iterator_, N_, kind_) r) | { 3| opIndexOpAssign!("", IndexIterator_, Iterator_, N_, kind_)(r); | } | | static if (doUnittest) | /// | version(mir_test) unittest | { 1| auto index = [1, 2, 3, 4]; 1| auto data = [10.0, 10, 10, 10]; 1| auto series = index.series(data); | 1| auto rindex = [0, 2, 4, 5]; 1| auto rdata = [1.0, 2, 3, 4]; 1| auto rseries = rindex.series(rdata); | | // series[] = rseries; 1| series[] = rseries; 1| assert(series.data == [10, 2, 10, 3]); | } | | /++ | Special `[] op=` index-op-assign operator for index-series. | Op-assigns data from `r` with index intersection. | If a index index in `r` is not in the index index for this series, then no op-assign will take place. | This and r series are assumed to be sorted. | | Params: | rSeries = rvalue index-series | +/ | void opIndexOpAssign(string op, IndexIterator_, Iterator_, size_t N_, SliceKind kind_) | (auto ref Series!(IndexIterator_, Iterator_, N_, kind_) rSeries) | { 4| auto l = this.lightScope; 4| auto r = rSeries.lightScope; 4| if (r.empty) 0000000| return; 4| if (l.empty) 0000000| return; 4| Unqual!(typeof(*r._index)) rf = *r._index; 4| Unqual!(typeof(*l._index)) lf = *l._index; 4| goto Begin; | R: 2| r.popFront; 2| if (r.empty) 0000000| goto End; 2| rf = *r._index; | Begin: 6| if (lf > rf) 2| goto R; 4| if (lf < rf) 2| goto L; | E: | static if (N != 1) | mixin("l.data.front[] " ~ op ~ "= r.data.front;"); | else | mixin("l.data.front " ~ op ~ "= r.data.front;"); | 10| r.popFront; 10| if (r.empty) 2| goto End; 8| rf = *r._index; | L: 15| l.popFront; 15| if (l.empty) 2| goto End; 13| lf = *l._index; | 13| if (lf < rf) 5| goto L; 8| if (lf == rf) 8| goto E; 0000000| goto R; | End: | } | | static if (doUnittest) | /// | version(mir_test) unittest | { 1| auto index = [1, 2, 3, 4]; 1| auto data = [10.0, 10, 10, 10]; 1| auto series = index.series(data); | 1| auto rindex = [0, 2, 4, 5]; 1| auto rdata = [1.0, 2, 3, 4]; 1| auto rseries = rindex.series(rdata); | 1| series[] += rseries; 1| assert(series.data == [10, 12, 10, 13]); | } | | /++ | This function uses a search with policy sp to find the largest left subrange on which | `t < key` is true for all `t`. | The search schedule and its complexity are documented in `std.range.SearchPolicy`. | +/ | auto lowerBound(Index)(auto ref scope const Index key) | { 1| return opIndex(opSlice(0, lightScopeIndex.transitionIndex(key))); | } | | /// ditto | auto lowerBound(Index)(auto ref scope const Index key) const | { 1| return opIndex(opSlice(0, lightScopeIndex.transitionIndex(key))); | } | | | /++ | This function uses a search with policy sp to find the largest right subrange on which | `t > key` is true for all `t`. | The search schedule and its complexity are documented in `std.range.SearchPolicy`. | +/ | auto upperBound(Index)(auto ref scope const Index key) | { 1| return opIndex(opSlice(lightScopeIndex.transitionIndex!"a <= b"(key), length)); | } | | /// ditto | auto upperBound(Index)(auto ref scope const Index key) const | { 1| return opIndex(opSlice(lightScopeIndex.transitionIndex!"a <= b"(key), length)); | } | | /** | Gets data for the index. | Params: | key = index | _default = default value is returned if the series does not contains the index. | Returns: | data that corresponds to the index or default value. | */ | ref get(Index, Value)(auto ref scope const Index key, return ref Value _default) @trusted | if (!is(Value : const(Exception))) | { 2| size_t idx = lightScopeIndex.transitionIndex(key); 6| return idx < _data._lengths[0] && _index[idx] == key ? _data[idx] : _default; | } | | /// ditto | ref get(Index, Value)(auto ref scope const Index key, return ref Value _default) const | if (!is(Value : const(Exception))) | { | return this.lightScope.get(key, _default); | } | | /// ditto | ref get(Index, Value)(auto ref scope const Index key, return ref Value _default) immutable | if (!is(Value : const(Exception))) | { | return this.lightScope.get(key, _default); | } | | auto get(Index, Value)(auto ref scope const Index key, Value _default) @trusted | if (!is(Value : const(Exception))) | { | size_t idx = lightScopeIndex.transitionIndex(key); | return idx < _data._lengths[0] && _index[idx] == key ? _data[idx] : _default; | } | | /// ditto | auto get(Index, Value)(auto ref scope const Index key, Value _default) const | if (!is(Value : const(Exception))) | { | import core.lifetime: forward; | return this.lightScope.get(key, forward!_default); | } | | /// ditto | auto get(Index, Value)(auto ref scope const Index key, Value _default) immutable | if (!is(Value : const(Exception))) | { | import core.lifetime: forward; | return this.lightScope.get(key, forward!_default); | } | | /** | Gets data for the index. | Params: | key = index | exc = (lazy, optional) exception to throw if the series does not contains the index. | Returns: data that corresponds to the index. | Throws: | Exception if the series does not contains the index. | See_also: $(LREF Series.getVerbose), $(LREF Series.tryGet) | */ | auto ref get(Index)(auto ref scope const Index key) @trusted | { 5| size_t idx = lightScopeIndex.transitionIndex(key); 10| if (idx < _data._lengths[0] && _index[idx] == key) | { 4| return _data[idx]; | } 1| throw defaultExc!(); | } | | /// ditto | auto ref get(Index)(auto ref scope const Index key, lazy const Exception exc) @trusted | { 6| size_t idx = lightScopeIndex.transitionIndex(key); 12| if (idx < _data._lengths[0] && _index[idx] == key) | { 3| return _data[idx]; | } 3| throw exc; | } | | /// ditto | auto ref get(Index)(auto ref scope const Index key) const | { | return this.lightScope.get(key); | } | | /// ditto | auto ref get(Index)(auto ref scope const Index key, lazy const Exception exc) const | { | return this.lightScope.get(key, exc); | } | | | /// ditto | auto ref get(Index)(auto ref scope const Index key) immutable | { | return this.lightScope.get(key); | } | | /// ditto | auto ref get(Index)(auto ref scope const Index key, lazy const Exception exc) immutable | { | return this.lightScope.get(key, exc); | } | | /** | Gets data for the index (verbose exception). | Params: | key = index | Returns: data that corresponds to the index. | Throws: | Detailed exception if the series does not contains the index. | See_also: $(LREF Series.get), $(LREF Series.tryGet) | */ | auto ref getVerbose(Index)(auto ref scope const Index key, string file = __FILE__, int line = __LINE__) | { | import std.format: format; 3| return this.get(key, new Exception(format("%s %s key", defaultMsg!(), key), file, line)); | } | | /// ditto | auto ref getVerbose(Index)(auto ref scope const Index key, string file = __FILE__, int line = __LINE__) const | { | return this.lightScope.getVerbose(key, file, line); | } | | /// ditto | auto ref getVerbose(Index)(auto ref scope const Index key, string file = __FILE__, int line = __LINE__) immutable | { | return this.lightScope.getVerbose(key, file, line); | } | | /** | Gets data for the index (extra verbose exception). | Params: | key = index | Returns: data that corresponds to the index. | Throws: | Detailed exception if the series does not contains the index. | See_also: $(LREF Series.get), $(LREF Series.tryGet) | */ | auto ref getExtraVerbose(Index)(auto ref scope const Index key, string exceptionInto, string file = __FILE__, int line = __LINE__) | { | import std.format: format; 3| return this.get(key, new Exception(format("%s. %s %s key", exceptionInto, defaultMsg!(), key), file, line)); | } | | /// ditto | auto ref getExtraVerbose(Index)(auto ref scope const Index key, string exceptionInto, string file = __FILE__, int line = __LINE__) const | { | return this.lightScope.getExtraVerbose(key, exceptionInto, file, line); | } | | /// ditto | auto ref getExtraVerbose(Index)(auto ref scope const Index key, string exceptionInto, string file = __FILE__, int line = __LINE__) immutable | { | return this.lightScope.getExtraVerbose(key, exceptionInto, file, line); | } | | /// | bool contains(Index)(auto ref scope const Index key) const @trusted | { 2| size_t idx = lightScopeIndex.transitionIndex(key); 3| return idx < _data._lengths[0] && _index[idx] == key; | } | | /// | auto opBinaryRight(string op : "in", Index)(auto ref scope const Index key) @trusted | { 2| size_t idx = lightScopeIndex.transitionIndex(key); 3| bool cond = idx < _data._lengths[0] && _index[idx] == key; | static if (__traits(compiles, &_data[size_t.init])) | { 2| if (cond) 1| return &_data[idx]; 1| return null; | } | else | { | return bool(cond); | } | } | | /// ditto | auto opBinaryRight(string op : "in", Index)(auto ref scope const Index key) const | { | auto val = key in this.lightScope; | return val; | } | | /// ditto | auto opBinaryRight(string op : "in", Index)(auto ref scope const Index key) immutable | { | auto val = key in this.lightScope; | return val; | } | | /++ | Tries to get the first value, such that `key_i == key`. | | Returns: `true` on success. | +/ | bool tryGet(Index, Value)(Index key, scope ref Value val) @trusted | { 4| size_t idx = lightScopeIndex.transitionIndex(key); 8| auto cond = idx < _data._lengths[0] && _index[idx] == key; 4| if (cond) 2| val = _data[idx]; 4| return cond; | } | | /// ditto | bool tryGet(Index, Value)(Index key, scope ref Value val) const | { 2| return this.lightScope.tryGet(key, val); | } | | /// ditto | bool tryGet(Index, Value)(Index key, scope ref Value val) immutable | { | return this.lightScope.tryGet(key, val); | } | | /++ | Tries to get the first value, such that `key_i >= key`. | | Returns: `true` on success. | +/ | bool tryGetNext(Index, Value)(auto ref scope const Index key, scope ref Value val) | { 2| size_t idx = lightScopeIndex.transitionIndex(key); 2| auto cond = idx < _data._lengths[0]; 2| if (cond) 2| val = _data[idx]; 2| return cond; | } | | /// ditto | bool tryGetNext(Index, Value)(auto ref scope const Index key, scope ref Value val) const | { 2| return this.lightScope.tryGetNext(key, val); | } | | /// ditto | bool tryGetNext(Index, Value)(auto ref scope const Index key, scope ref Value val) immutable | { | return this.lightScope.tryGetNext(key, val); | } | | /++ | Tries to get the first value, such that `key_i >= key`. | Updates `key` with `key_i`. | | Returns: `true` on success. | +/ | bool tryGetNextUpdateKey(Index, Value)(scope ref Index key, scope ref Value val) @trusted | { 2| size_t idx = lightScopeIndex.transitionIndex(key); 2| auto cond = idx < _data._lengths[0]; 2| if (cond) | { 2| key = _index[idx]; 2| val = _data[idx]; | } 2| return cond; | } | | /// ditto | bool tryGetNextUpdateKey(Index, Value)(scope ref Index key, scope ref Value val) const | { 2| return this.lightScope.tryGetNextUpdateKey(key, val); | } | | /// ditto | bool tryGetNextUpdateKey(Index, Value)(scope ref Index key, scope ref Value val) immutable | { | return this.lightScope.tryGetNextUpdateKey(key, val); | } | | /++ | Tries to get the last value, such that `key_i <= key`. | | Returns: `true` on success. | +/ | bool tryGetPrev(Index, Value)(auto ref scope const Index key, scope ref Value val) | { 2| size_t idx = lightScopeIndex.transitionIndex!"a <= b"(key) - 1; 2| auto cond = 0 <= sizediff_t(idx); 2| if (cond) 2| val = _data[idx]; 2| return cond; | } | | /// ditto | bool tryGetPrev(Index, Value)(auto ref scope const Index key, scope ref Value val) const | { 2| return this.lightScope.tryGetPrev(key, val); | } | | /// ditto | bool tryGetPrev(Index, Value)(auto ref scope const Index key, scope ref Value val) immutable | { | return this.lightScope.tryGetPrev(key, val); | } | | /++ | Tries to get the last value, such that `key_i <= key`. | Updates `key` with `key_i`. | | Returns: `true` on success. | +/ | bool tryGetPrevUpdateKey(Index, Value)(scope ref Index key, scope ref Value val) @trusted | { 2| size_t idx = lightScopeIndex.transitionIndex!"a <= b"(key) - 1; 2| auto cond = 0 <= sizediff_t(idx); 2| if (cond) | { 2| key = _index[idx]; 2| val = _data[idx]; | } 2| return cond; | } | | /// ditto | bool tryGetPrevUpdateKey(Index, Value)(scope ref Index key, scope ref Value val) const | { 2| return this.lightScope.tryGetPrevUpdateKey(key, val); | } | | /// ditto | bool tryGetPrevUpdateKey(Index, Value)(scope ref Index key, scope ref Value val) immutable | { | return this.lightScope.tryGetPrevUpdateKey(key, val); | } | | /++ | Tries to get the first value, such that `lowerBound <= key_i <= upperBound`. | | Returns: `true` on success. | +/ | bool tryGetFirst(Index, Value)(auto ref scope const Index lowerBound, auto ref scope const Index upperBound, scope ref Value val) @trusted | { 2| size_t idx = lightScopeIndex.transitionIndex(lowerBound); 4| auto cond = idx < _data._lengths[0] && _index[idx] <= upperBound; 2| if (cond) 1| val = _data[idx]; 2| return cond; | } | | /// ditto | bool tryGetFirst(Index, Value)(Index lowerBound, auto ref scope const Index upperBound, scope ref Value val) const | { 2| return this.lightScope.tryGetFirst(lowerBound, upperBound, val); | } | | /// ditto | bool tryGetFirst(Index, Value)(Index lowerBound, auto ref scope const Index upperBound, scope ref Value val) immutable | { | return this.lightScope.tryGetFirst(lowerBound, upperBound, val); | } | | /++ | Tries to get the first value, such that `lowerBound <= key_i <= upperBound`. | Updates `lowerBound` with `key_i`. | | Returns: `true` on success. | +/ | bool tryGetFirstUpdateLower(Index, Value)(ref Index lowerBound, auto ref scope const Index upperBound, scope ref Value val) @trusted | { 1| size_t idx = lightScopeIndex.transitionIndex(lowerBound); 2| auto cond = idx < _data._lengths[0] && _index[idx] <= upperBound; 1| if (cond) | { 1| lowerBound = _index[idx]; 1| val = _data[idx]; | } 1| return cond; | } | | /// ditto | bool tryGetFirstUpdateLower(Index, Value)(ref Index lowerBound, auto ref scope const Index upperBound, scope ref Value val) const | { 1| return this.lightScope.tryGetFirstUpdateLower(lowerBound, upperBound, val); | } | | /// ditto | bool tryGetFirstUpdateLower(Index, Value)(ref Index lowerBound, auto ref scope const Index upperBound, scope ref Value val) immutable | { | return this.lightScope.tryGetFirstUpdateLower(lowerBound, upperBound, val); | } | | /++ | Tries to get the last value, such that `lowerBound <= key_i <= upperBound`. | | Returns: `true` on success. | +/ | bool tryGetLast(Index, Value)(Index lowerBound, auto ref scope const Index upperBound, scope ref Value val) @trusted | { 2| size_t idx = lightScopeIndex.transitionIndex!"a <= b"(upperBound) - 1; 4| auto cond = 0 <= sizediff_t(idx) && _index[idx] >= lowerBound; 2| if (cond) 2| val = _data[idx]; 2| return cond; | } | | /// ditto | bool tryGetLast(Index, Value)(Index lowerBound, auto ref scope const Index upperBound, scope ref Value val) const | { 2| return this.lightScope.tryGetLast(lowerBound, upperBound, val); | } | | /// ditto | bool tryGetLast(Index, Value)(Index lowerBound, auto ref scope const Index upperBound, scope ref Value val) immutable | { | return this.lightScope.tryGetLast(lowerBound, upperBound, val); | } | | /++ | Tries to get the last value, such that `lowerBound <= key_i <= upperBound`. | Updates `upperBound` with `key_i`. | | Returns: `true` on success. | +/ | bool tryGetLastUpdateKey(Index, Value)(Index lowerBound, ref Index upperBound, scope ref Value val) @trusted | { 2| size_t idx = lightScopeIndex.transitionIndex!"a <= b"(upperBound) - 1; 4| auto cond = 0 <= sizediff_t(idx) && _index[idx] >= lowerBound; 2| if (cond) | { 2| upperBound = _index[idx]; 2| val = _data[idx]; | } 2| return cond; | } | | /// ditto | bool tryGetLastUpdateKey(Index, Value)(Index lowerBound, ref Index upperBound, scope ref Value val) const | { 2| return this.lightScope.tryGetLastUpdateKey(lowerBound, upperBound, val); | } | | /// ditto | bool tryGetLastUpdateKey(Index, Value)(Index lowerBound, ref Index upperBound, scope ref Value val) immutable | { | return this.lightScope.tryGetLastUpdateKey(lowerBound, upperBound, val); | } | | /++ | Returns: | 1D Slice with creared with $(NDSLICE topology, zip) ([0] - key, [1] - value). | See_also: | $(NDSLICE topology, map) uses multiargument lambdas to handle zipped slices. | +/ | auto asSlice()() @property | { | import mir.ndslice.topology: zip, map, ipack; | static if (N == 1) 1| return index.zip(data); | else | return index.zip(data.ipack!1.map!"a"); | } | | /// ditto | auto asSlice()() const @property | { | return opIndex.asSlice; | } | | /// ditto | auto asSlice()() immutable @property | { | return opIndex.asSlice; | } | | /// ndslice-like primitives | bool empty(size_t dimension = 0)() const @property | if (dimension < N) | { 623| return !length!dimension; | } | | /// ditto | size_t length(size_t dimension = 0)() const @property | if (dimension < N) | { 973| return _data.length!dimension; | } | | /// ditto | auto front(size_t dimension = 0)() @property | if (dimension < N) | { 166| assert(!empty!dimension); | static if (dimension) | { | return index.series(data.front!dimension); | } | else | { 166| return Observation!(Index, Data)(index.front, data.front); | } | } | | /// ditto | auto back(size_t dimension = 0)() @property | if (dimension < N) | { | assert(!empty!dimension); | static if (dimension) | { | return index.series(_data.back!dimension); | } | else | { | return index.back.observation(_data.back); | } | } | | /// ditto | void popFront(size_t dimension = 0)() @trusted | if (dimension < N) | { 264| assert(!empty!dimension); | static if (dimension == 0) 263| _index++; 264| _data.popFront!dimension; | } | | /// ditto | void popBack(size_t dimension = 0)() | if (dimension < N) | { | assert(!empty!dimension); | _data.popBack!dimension; | } | | /// ditto | void popFrontExactly(size_t dimension = 0)(size_t n) @trusted | if (dimension < N) | { | assert(length!dimension >= n); | static if (dimension == 0) | _index += n; | _data.popFrontExactly!dimension(n); | } | | /// ditto | void popBackExactly(size_t dimension = 0)(size_t n) | if (dimension < N) | { 1| assert(length!dimension >= n); 1| _data.popBackExactly!dimension(n); | } | | /// ditto | void popFrontN(size_t dimension = 0)(size_t n) | if (dimension < N) | { | auto len = length!dimension; | n = n <= len ? n : len; | popFrontExactly!dimension(n); | } | | /// ditto | void popBackN(size_t dimension = 0)(size_t n) | if (dimension < N) | { 1| auto len = length!dimension; 2| n = n <= len ? n : len; 1| popBackExactly!dimension(n); | } | | /// ditto | Slice!(IotaIterator!size_t) opSlice(size_t dimension = 0)(size_t i, size_t j) const | if (dimension < N) | in | { 13| assert(i <= j, | "Series.opSlice!" ~ dimension.stringof ~ ": the left opSlice boundary must be less than or equal to the right bound."); | enum errorMsg = ": difference between the right and the left bounds" | ~ " must be less than or equal to the length of the given dimension."; 13| assert(j - i <= _data._lengths[dimension], | "Series.opSlice!" ~ dimension.stringof ~ errorMsg); | } | do | { 13| return typeof(return)(j - i, typeof(return).Iterator(i)); | } | | /// ditto | size_t opDollar(size_t dimension = 0)() const | { 6| return _data.opDollar!dimension; | } | | /// ditto | auto opIndex(Slices...)(Slices slices) | if (allSatisfy!(templateOr!(is_Slice, isIndex), Slices)) | { | static if (Slices.length == 0) | { 1| return this; | } | else | static if (is_Slice!(Slices[0])) | { 12| return index[slices[0]].series(data[slices]); | } | else | { 3| return index[slices[0]].observation(data[slices]); | } | } | | /// ditto | auto opIndex(Slices...)(Slices slices) const | if (allSatisfy!(templateOr!(is_Slice, isIndex), Slices)) | { 4| return lightConst.opIndex(slices); | } | | /// ditto | auto opIndex(Slices...)(Slices slices) immutable | if (allSatisfy!(templateOr!(is_Slice, isIndex), Slices)) | { | return lightImmutable.opIndex(slices); | } | | /// | ref opAssign(typeof(this) rvalue) return @trusted | { | import mir.utility: swap; 8| this._data._structure = rvalue._data._structure; 8| swap(this._data._iterator, rvalue._data._iterator); 8| swap(this._index, rvalue._index); 8| return this; | } | | /// ditto | ref opAssign(RIndexIterator, RIterator)(Series!(RIndexIterator, RIterator, N, kind) rvalue) return | if (isAssignable!(IndexIterator, RIndexIterator) && isAssignable!(Iterator, RIterator)) | { | import core.lifetime: move; 1| this._data._structure = rvalue._data._structure; 1| this._data._iterator = rvalue._data._iterator.move; 1| this._index = rvalue._index.move; 1| return this; | } | | /// ditto | ref opAssign(RIndexIterator, RIterator)(auto ref const Series!(RIndexIterator, RIterator, N, kind) rvalue) return | if (isAssignable!(IndexIterator, LightConstOf!RIndexIterator) && isAssignable!(Iterator, LightConstOf!RIterator)) | { | return this = rvalue.opIndex; | } | | /// ditto | ref opAssign(RIndexIterator, RIterator)(auto ref immutable Series!(RIndexIterator, RIterator, N, kind) rvalue) return | if (isAssignable!(IndexIterator, LightImmutableOf!RIndexIterator) && isAssignable!(Iterator, LightImmutableOf!RIterator)) | { | return this = rvalue.opIndex; | } | | /// ditto | ref opAssign(typeof(null)) return | { 0000000| return this = this.init; | } | | /// ditto | auto save()() @property | { | return this; | } | | /// | Series!(LightScopeOf!IndexIterator, LightScopeOf!Iterator, N, kind) lightScope()() @trusted @property | { 40| return typeof(return)(lightScopeIndex, _data.lightScope); | } | | /// ditto | Series!(LightConstOf!(LightScopeOf!IndexIterator), LightConstOf!(LightScopeOf!Iterator), N, kind) lightScope()() @trusted const @property | { 21| return typeof(return)(lightScopeIndex, _data.lightScope); | } | | /// ditto | Series!(LightConstOf!(LightScopeOf!IndexIterator), LightConstOf!(LightScopeOf!Iterator), N, kind) lightScope()() @trusted immutable @property | { | return typeof(return)(lightScopeIndex, _data.lightScope); | } | | /// | Series!(LightConstOf!IndexIterator, LightConstOf!Iterator, N, kind) lightConst()() const @property @trusted | { 4| return index.series(data); | } | | /// | Series!(LightImmutableOf!IndexIterator, LightImmutableOf!Iterator, N, kind) lightImmutable()() immutable @property @trusted | { | return index.series(data); | } | | /// | auto toConst()() const @property | { | return index.toConst.series(data.toConst); | } | | /// | void toString(Writer, Spec)(auto ref Writer w, const ref Spec f) const | { | import std.format: formatValue, formatElement; | import std.range: put; | 150| if (f.spec != 's' && f.spec != '(') 0000000| throw new Exception("incompatible format character for Mir Series argument: %" ~ f.spec); | | enum defSpec = "%s" ~ f.keySeparator ~ "%s" ~ f.seqSeparator; 200| auto fmtSpec = f.spec == '(' ? f.nested : defSpec; | 100| if (f.spec == 's') 50| put(w, f.seqBefore); 200| if (length) for (size_t i = 0;;) | { 200| auto fmt = Spec(fmtSpec); 200| fmt.writeUpToNextSpec(w); 200| if (f.flDash) | { 50| formatValue(w, index[i], fmt); 50| fmt.writeUpToNextSpec(w); 50| formatValue(w, data[i], fmt); | } | else | { 150| formatElement(w, index[i], fmt); 150| fmt.writeUpToNextSpec(w); 150| formatElement(w, data[i], fmt); | } 200| if (f.sep !is null) | { 0000000| fmt.writeUpToNextSpec(w); 0000000| if (++i != length) 0000000| put(w, f.sep); | else 0000000| break; | } | else | { 200| if (++i != length) 100| fmt.writeUpToNextSpec(w); | else 100| break; | } | } 100| if (f.spec == 's') 50| put(w, f.seqAfter); | } | | version(mir_test) | /// | unittest | { | import mir.series: series, sort; 25| auto s = ["b", "a"].series([9, 8]).sort; | | import std.conv : to; 25| assert(s.to!string == `["a":8, "b":9]`); | | import std.format : format; 25| assert("%s".format(s) == `["a":8, "b":9]`); 25| assert("%(%s %s | %)".format(s) == `"a" 8 | "b" 9`); 25| assert("%-(%s,%s\n%)\n".format(s) == "a,8\nb,9\n"); | } |} | |/// ditto |alias Series = mir_series; | |/// 1-dimensional data |@safe pure version(mir_test) unittest |{ 1| auto index = [1, 2, 3, 4]; 1| auto data = [2.1, 3.4, 5.6, 7.8]; 1| auto series = index.series(data); 1| const cseries = series; | 1| assert(series.contains(2)); 2| assert( ()@trusted{ return (2 in series) is &data[1]; }() ); | 1| assert(!series.contains(5)); 2| assert( ()@trusted{ return (5 in series) is null; }() ); | 1| assert(series.lowerBound(2) == series[0 .. 1]); 1| assert(series.upperBound(2) == series[2 .. $]); | 1| assert(cseries.lowerBound(2) == cseries[0 .. 1]); 1| assert(cseries.upperBound(2) == cseries[2 .. $]); | | // slicing type deduction for const / immutable series | static assert(is(typeof(series[]) == | Series!(int*, double*))); | static assert(is(typeof(cseries[]) == | Series!(const(int)*, const(double)*))); | static assert(is(typeof((cast(immutable) series)[]) == | Series!(immutable(int)*, immutable(double)*))); | | /// slicing 1| auto seriesSlice = series[1 .. $ - 1]; 1| assert(seriesSlice.index == index[1 .. $ - 1]); 1| assert(seriesSlice.data == data[1 .. $ - 1]); | static assert(is(typeof(series) == typeof(seriesSlice))); | | /// indexing 2| assert(series[1] == observation(2, 3.4)); | | /// range primitives 1| assert(series.length == 4); 2| assert(series.front == observation(1, 2.1)); | 1| series.popFront; 2| assert(series.front == observation(2, 3.4)); | 1| series.popBackN(10); 1| assert(series.empty); |} | |/// 2-dimensional data |@safe pure version(mir_test) unittest |{ | import mir.date: Date; | import mir.ndslice.topology: canonical, iota; | 1| size_t row_length = 5; | 1| auto index = [ | Date(2017, 01, 01), | Date(2017, 02, 01), | Date(2017, 03, 01), | Date(2017, 04, 01)]; | | // 1, 2, 3, 4, 5 | // 6, 7, 8, 9, 10 | // 11, 12, 13, 14, 15 | // 16, 17, 18, 19, 20 1| auto data = iota!int([index.length, row_length], 1); | | // canonical and universal ndslices are more flexible then contiguous 1| auto series = index.series(data.canonical); | | /// slicing 1| auto seriesSlice = series[1 .. $ - 1, 2 .. 4]; 1| assert(seriesSlice.index == index[1 .. $ - 1]); 1| assert(seriesSlice.data == data[1 .. $ - 1, 2 .. 4]); | | static if (kindOf!(typeof(series.data)) != Contiguous) | static assert(is(typeof(series) == typeof(seriesSlice))); | | /// indexing 1| assert(series[1, 4] == observation(Date(2017, 02, 01), 10)); 2| assert(series[2] == observation(Date(2017, 03, 01), iota!int([row_length], 11))); | | /// range primitives 1| assert(series.length == 4); 1| assert(series.length!1 == 5); | 1| series.popFront!1; 1| assert(series.length!1 == 4); |} | |/// Construct from null |@safe pure nothrow @nogc version(mir_test) unittest |{ | import mir.series; | alias Map = Series!(string*, double*); 1| Map a = null; 1| auto b = Map(null); 1| assert(a.empty); 1| assert(b.empty); | | auto fun(Map a = null) | { | | } |} | |/++ |Convenient function for $(LREF Series) construction. |See_also: $(LREF assocArray) |Attention: | This overloads do not sort the data. | User should call $(LREF directly) if index was not sorted. |+/ |auto series(IndexIterator, Iterator, size_t N, SliceKind kind) | ( | Slice!IndexIterator index, | Slice!(Iterator, N, kind) data, | ) |{ 110| assert(index.length == data.length); 110| return Series!(IndexIterator, Iterator, N, kind)(index, data); |} | |/// ditto |auto series(Index, Data)(Index[] index, Data[] data) |{ 52| assert(index.length == data.length); 52| return .series(index.sliced, data.sliced); |} | |/// ditto |auto series(IndexIterator, Data)(Slice!IndexIterator index, Data[] data) |{ | assert(index.length == data.length); | return .series(index, data.sliced); |} | |/// ditto |auto series(Index, Iterator, size_t N, SliceKind kind)(Index[] index, Slice!(Iterator, N, kind) data) |{ 3| assert(index.length == data.length); 3| return .series(index.sliced, data); |} | |/** |Constructs a GC-allocated series from an associative array. |Performs exactly two allocations. | |Params: | aa = associative array or a pointer to associative array |Returns: | sorted GC-allocated series. |See_also: $(LREF assocArray) |*/ |Series!(K*, V*) series(RK, RV, K = RK, V = RV)(RV[RK] aa) | if (is(typeof(K.init < K.init)) && is(typeof(Unqual!K.init < Unqual!K.init))) |{ | import mir.conv: to; 5| const size_t length = aa.length; | alias R = typeof(return); 5| if (__ctfe) | { 0000000| K[] keys; 0000000| V[] values; 0000000| foreach(ref kv; aa.byKeyValue) | { 0000000| keys ~= kv.key.to!K; 0000000| values ~= kv.value.to!V; | } 0000000| auto ret = series(keys, values); 0000000| .sort((()@trusted=>cast(Series!(Unqual!K*, Unqual!V*))ret)()); | static if (is(typeof(ret) == typeof(return))) 0000000| return ret; | else | return ()@trusted{ return *cast(R*) &ret; }(); | } | import mir.ndslice.allocation: uninitSlice; 5| Series!(Unqual!K*, Unqual!V*) ret = series(length.uninitSlice!(Unqual!K), length.uninitSlice!(Unqual!V)); 5| auto it = ret; 67| foreach(ref kv; aa.byKeyValue) | { | import mir.conv: emplaceRef; 19| emplaceRef!K(it.index.front, kv.key.to!K); 19| emplaceRef!V(it._data.front, kv.value.to!V); 19| it.popFront; | } 5| .sort(ret); | static if (is(typeof(ret) == typeof(return))) 4| return ret; | else 2| return ()@trusted{ return *cast(R*) &ret; }(); |} | |/// ditto |Series!(RK*, RV*) series(K, V, RK = const K, RV = const V)(const V[K] aa) | if (is(typeof(K.init < K.init)) && is(typeof(Unqual!K.init < Unqual!K.init))) |{ | return .series!(K, V, RK, RV)((()@trusted => cast(V[K]) aa)()); |} | |/// ditto |Series!(RK*, RV*) series( K, V, RK = immutable K, RV = immutable V)(immutable V[K] aa) | if (is(typeof(K.init < K.init)) && is(typeof(Unqual!K.init < Unqual!K.init))) |{ 2| return .series!(K, V, RK, RV)((()@trusted => cast(V[K]) aa)()); |} | |/// ditto |auto series(K, V)(V[K]* aa) | if (is(typeof(K.init < K.init)) && is(typeof(Unqual!K.init < Unqual!K.init))) |{ | return series(*a); |} | |/// |@safe pure nothrow version(mir_test) unittest |{ 1| auto s = [1: 1.5, 3: 3.3, 2: 20.9].series; 1| assert(s.index == [1, 2, 3]); 1| assert(s.data == [1.5, 20.9, 3.3]); 1| assert(s.data[s.findIndex(2)] == 20.9); |} | |pure nothrow version(mir_test) unittest |{ 1| immutable aa = [1: 1.5, 3: 3.3, 2: 2.9]; 1| auto s = aa.series; 1| s = cast() s; 1| s = cast(const) s; 1| s = cast(immutable) s; 1| s = s; 1| assert(s.index == [1, 2, 3]); 1| assert(s.data == [1.5, 2.9, 3.3]); 1| assert(s.data[s.findIndex(2)] == 2.9); |} | | |/** |Constructs a RC-allocated series from an associative array. |Performs exactly two allocations. | |Params: | aa = associative array or a pointer to associative array |Returns: | sorted RC-allocated series. |See_also: $(LREF assocArray) |*/ |auto rcseries(RK, RV, K = RK, V = RV)(RV[RK] aa) | if (is(typeof(K.init < K.init)) && is(typeof(Unqual!K.init < Unqual!K.init))) |{ | import mir.rc.array; | import mir.conv: to; | alias R = Series!(RCI!K, RCI!V); 2| const size_t length = aa.length; 3| auto ret = series(length.mininitRcarray!(Unqual!K).asSlice, length.mininitRcarray!(Unqual!V).asSlice); 2| auto it = ret.lightScope; 22| foreach(ref kv; aa.byKeyValue) | { | import mir.conv: emplaceRef; 6| emplaceRef!K(it.lightScopeIndex.front, kv.key.to!K); 6| emplaceRef!V(it._data.front, kv.value.to!V); 6| it.popFront; | } | import core.lifetime: move; 2| .sort(ret.lightScope); | static if (is(typeof(ret) == R)) 1| return ret; | else 2| return ()@trusted{ return (*cast(R*) &ret); }(); |} | |/// ditto |auto rcseries(K, V, RK = const K, RV = const V)(const V[K] aa) | if (is(typeof(K.init < K.init)) && is(typeof(Unqual!K.init < Unqual!K.init))) |{ | return .rcseries!(K, V, RK, RV)((()@trusted => cast(V[K]) aa)()); |} | |/// ditto |auto rcseries( K, V, RK = immutable K, RV = immutable V)(immutable V[K] aa) | if (is(typeof(K.init < K.init)) && is(typeof(Unqual!K.init < Unqual!K.init))) |{ 2| return .rcseries!(K, V, RK, RV)((()@trusted => cast(V[K]) aa)()); |} | |/// ditto |auto rcseries(K, V)(V[K]* aa) | if (is(typeof(K.init < K.init)) && is(typeof(Unqual!K.init < Unqual!K.init))) |{ | return rcseries(*a); |} | |/// |@safe pure nothrow version(mir_test) unittest |{ 2| auto s = [1: 1.5, 3: 3.3, 2: 20.9].rcseries; 1| assert(s.index == [1, 2, 3]); 1| assert(s.data == [1.5, 20.9, 3.3]); 1| assert(s.data[s.findIndex(2)] == 20.9); |} | |// pure nothrow |version(mir_test) unittest |{ | import mir.rc.array; 1| immutable aa = [1: 1.5, 3: 3.3, 2: 2.9]; 2| auto s = aa.rcseries; 2| Series!(RCI!(const int), RCI!(const double)) c; 1| s = cast() s; 1| c = s; 1| s = cast(const) s; 1| s = cast(immutable) s; 1| s = s; 1| assert(s.index == [1, 2, 3]); 1| assert(s.data == [1.5, 2.9, 3.3]); 1| assert(s.data[s.findIndex(2)] == 2.9); |} | |/++ |Constructs a manually allocated series from an associative array. |Performs exactly two allocations. | |Params: | aa == associative array or a pointer to associative array |Returns: | sorted manually allocated series. |+/ |Series!(K*, V*) makeSeries(Allocator, K, V)(auto ref Allocator allocator, V[K] aa) | if (is(typeof(K.init < K.init)) && is(typeof(Unqual!K.init < Unqual!K.init))) |{ | import mir.ndslice.allocation: makeUninitSlice; | import mir.conv: emplaceRef; | 1| immutable size_t length = aa.length; | 1| auto ret = series( | allocator.makeUninitSlice!(Unqual!K)(length), | allocator.makeUninitSlice!(Unqual!V)(length)); | 1| auto it = ret; 11| foreach(ref kv; aa.byKeyValue) | { 3| it.index.front.emplaceRef!K(kv.key); 3| it.data.front.emplaceRef!V(kv.value); 3| it.popFront; | } | 1| ret.sort; | static if (is(typeof(ret) == typeof(return))) 1| return ret; | else | return ()@trusted{ return cast(typeof(return)) ret; }(); |} | |/// ditto |Series!(K*, V*) makeSeries(Allocator, K, V)(auto ref Allocator allocator, V[K]* aa) | if (is(typeof(K.init < K.init)) && is(typeof(Unqual!K.init < Unqual!K.init))) |{ | return makeSeries(allocator, *a); |} | |/// |pure nothrow version(mir_test) unittest |{ | import std.experimental.allocator; | import std.experimental.allocator.building_blocks.region; | 1| InSituRegion!(1024) allocator; 1| auto aa = [1: 1.5, 3: 3.3, 2: 2.9]; | 1| auto s = (double[int] aa) @nogc @trusted pure nothrow { 1| return allocator.makeSeries(aa); | }(aa); | 1| auto indexArray = s.index.field; 1| auto dataArray = s.data.field; | 1| assert(s.index == [1, 2, 3]); 1| assert(s.data == [1.5, 2.9, 3.3]); 1| assert(s.data[s.findIndex(2)] == 2.9); | 1| allocator.dispose(indexArray); 1| allocator.dispose(dataArray); |} | |/++ |Returns a newly allocated associative array from a range of key/value tuples. | |Params: | series = index / time $(LREF Series), may not be sorted | |Returns: A newly allocated associative array out of elements of the input |_series. Returns a null associative |array reference when given an empty _series. | |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. |+/ |auto assocArray(IndexIterator, Iterator, size_t N, SliceKind kind) | (Series!(IndexIterator, Iterator, N, kind) series) |{ | alias SK = series.Key; | alias SV = series.Value; | alias UK = Unqual!SK; | alias UV = Unqual!SV; | static if (isImplicitlyConvertible!(SK, UK)) | alias K = UK; | else | alias K = SK; | static if (isImplicitlyConvertible!(SV, UV)) | alias V = UV; | else | alias V = SV; | static assert(isMutable!V, "mir.series.assocArray: value type ( " ~ V.stringof ~ " ) must be mutable"); | 1| V[K] aa; 1| aa.insertOrAssign = series; 1| return aa; |} | |/// |@safe pure version(mir_test) unittest |{ | import mir.ndslice; //iota and etc | import mir.series; | 1| auto s = ["c", "a", "b"].series(3.iota!int); 1| assert(s.assocArray == [ | "c": 0, | "a": 1, | "b": 2, | ]); |} | |/// Returns: true if `U` is a $(LREF Series); |enum isSeries(U) = is(U : Series!(IndexIterator, Iterator, N, kind), IndexIterator, Iterator, size_t N, SliceKind kind); | |/++ |Finds an index such that `series.index[index] == key`. | |Params: | series = series | key = index to find in the series |Returns: | `size_t.max` if the series does not contain the key and appropriate index otherwise. |+/ |size_t findIndex(IndexIterator, Iterator, size_t N, SliceKind kind, Index)(Series!(IndexIterator, Iterator, N, kind) series, auto ref scope const Index key) |{ 7| auto idx = series.lightScopeIndex.transitionIndex(key); 14| if (idx < series._data._lengths[0] && series.index[idx] == key) | { 6| return idx; | } 1| return size_t.max; |} | |/// |@safe pure nothrow version(mir_test) unittest |{ 1| auto index = [1, 2, 3, 4].sliced; 1| auto data = [2.1, 3.4, 5.6, 7.8].sliced; 1| auto series = index.series(data); | 1| assert(series.data[series.findIndex(3)] == 5.6); 1| assert(series.findIndex(0) == size_t.max); |} | |/++ |Finds a backward index such that `series.index[$ - backward_index] == key`. | |Params: | series = series | key = index key to find in the series |Returns: | `0` if the series does not contain the key and appropriate backward index otherwise. |+/ |size_t find(IndexIterator, Iterator, size_t N, SliceKind kind, Index)(Series!(IndexIterator, Iterator, N, kind) series, auto ref scope const Index key) |{ 2| auto idx = series.lightScopeIndex.transitionIndex(key); 2| auto bidx = series._data._lengths[0] - idx; 4| if (bidx && series.index[idx] == key) | { 1| return bidx; | } 1| return 0; |} | |/// |@safe pure nothrow version(mir_test) unittest |{ 1| auto index = [1, 2, 3, 4].sliced; 1| auto data = [2.1, 3.4, 5.6, 7.8].sliced; 1| auto series = index.series(data); | 1| if (auto bi = series.find(3)) | { 1| assert(series.data[$ - bi] == 5.6); | } | else | { | assert(0); | } | 1| assert(series.find(0) == 0); |} | |/++ |Iterates union using three functions to handle each intersection case separately. |Params: | lfun = binary function that accepts left side key (and left side value) | cfun = trinary function that accepts left side key, (left side value,) and right side value | rfun = binary function that accepts right side key (and right side value) |+/ |template troykaGalop(alias lfun, alias cfun, alias rfun) |{ | import mir.primitives: isInputRange; | | /++ | Params: | lhs = left hand series | rhs = right hand series | +/ | pragma(inline, false) | void troykaGalop( | IndexIterL, IterL, size_t LN, SliceKind lkind, | IndexIterR, IterR, size_t RN, SliceKind rkind, | )( | Series!(IndexIterL, IterL, LN, lkind) lhs, | Series!(IndexIterR, IterR, RN, rkind) rhs, | ) | { 14| if (lhs.empty) 0000000| goto R0; 14| if (rhs.empty) 0000000| goto L1; | for(;;) | { 60| if (lhs.index.front < rhs.index.front) | { 22| lfun(lhs.index.front, lhs.data.front); 22| lhs.popFront; 22| if (lhs.empty) 6| goto R1; 16| continue; | } | else 38| if (lhs.index.front > rhs.index.front) | { 22| rfun(rhs.index.front, rhs.data.front); 22| rhs.popFront; 22| if (rhs.empty) 6| goto L1; 16| continue; | } | else | { 16| cfun(lhs.index.front, lhs.data.front, rhs.data.front); 16| lhs.popFront; 16| rhs.popFront; 16| if (rhs.empty) 2| goto L0; 14| if (lhs.empty) 0000000| goto R1; 14| continue; | } | } | | L0: 2| if (lhs.empty) 2| return; | L1: | do | { 6| lfun(lhs.index.front, lhs.data.front); 6| lhs.popFront; 6| } while(!lhs.empty); 6| return; | | R0: 0000000| if (rhs.empty) 0000000| return; | R1: | do | { 6| rfun(rhs.index.front, rhs.data.front); 6| rhs.popFront; 6| } while(!rhs.empty); 6| return; | } | | /++ | Params: | lhs = left hand input range | rhs = right hand input range | +/ | pragma(inline, false) | void troykaGalop (LeftRange, RightRange)(LeftRange lhs, RightRange rhs) | if (isInputRange!LeftRange && isInputRange!RightRange && !isSeries!LeftRange && !isSeries!RightRange) | { 2| if (lhs.empty) 0000000| goto R0; 2| if (rhs.empty) 0000000| goto L1; | for(;;) | { 12| if (lhs.front < rhs.front) | { 4| lfun(lhs.front); 4| lhs.popFront; 4| if (lhs.empty) 0000000| goto R1; 4| continue; | } | else 8| if (lhs.front > rhs.front) | { 4| rfun(rhs.front); 4| rhs.popFront; 4| if (rhs.empty) 0000000| goto L1; 4| continue; | } | else | { 4| cfun(lhs.front, rhs.front); 4| lhs.popFront; 4| rhs.popFront; 4| if (rhs.empty) 2| goto L0; 2| if (lhs.empty) 0000000| goto R1; 2| continue; | } | } | | L0: 2| if (lhs.empty) 2| return; | L1: | do | { 0000000| lfun(lhs.front); 0000000| lhs.popFront; 0000000| } while(!lhs.empty); 0000000| return; | | R0: 0000000| if (rhs.empty) 0000000| return; | R1: | do | { 0000000| rfun(rhs.front); 0000000| rhs.popFront; 0000000| } while(!rhs.empty); 0000000| return; | } |} | |/++ |Constructs union using three functions to handle each intersection case separately. |Params: | lfun = binary function that accepts left side key and left side value | cfun = trinary function that accepts left side key, left side value, and right side value | rfun = binary function that accepts right side key and right side value |+/ |template troykaSeries(alias lfun, alias cfun, alias rfun) |{ | /++ | Params: | lhs = left hand series | rhs = right hand series | Returns: | GC-allocated union series with length equal to $(LREF troykaLength) | +/ | auto troykaSeries | ( | IndexIterL, IterL, size_t LN, SliceKind lkind, | IndexIterR, IterR, size_t RN, SliceKind rkind, | )( | Series!(IndexIterL, IterL, LN, lkind) lhs, | Series!(IndexIterR, IterR, RN, rkind) rhs, | ) | { | alias I = CommonType!(typeof(lhs.index.front), typeof(rhs.index.front)); | alias E = CommonType!( | typeof(lfun(lhs.index.front, lhs.data.front)), | typeof(cfun(lhs.index.front, lhs.data.front, rhs.data.front)), | typeof(rfun(rhs.index.front, rhs.data.front)), | ); | alias R = Series!(I*, E*); | alias UI = Unqual!I; | alias UE = Unqual!E; 1| const length = troykaLength(lhs.index, rhs.index); | import mir.ndslice.allocation: uninitSlice; 1| auto index = length.uninitSlice!UI; 1| auto data = length.uninitSlice!UE; 1| auto ret = index.series(data); | alias algo = troykaSeriesImpl!(lfun, cfun, rfun); 1| algo!(I, E)(lhs.lightScope, rhs.lightScope, ret); 2| return (()@trusted => cast(R) ret)(); | } |} | |/// |version(mir_test) unittest |{ | import mir.ndslice; 1| auto a = [1, 2, 3, 9].sliced.series(iota!int([4], 1)); 1| auto b = [0, 2, 4, 9].sliced.series(iota!int([4], 1) * 10.0); | alias unionAlgorithm = troykaSeries!( 2| (key, left) => left, 2| (key, left, right) => left + right, 2| (key, right) => -right, | ); 1| auto c = unionAlgorithm(a, b); 1| assert(c.index == [0, 1, 2, 3, 4, 9]); 1| assert(c.data == [-10, 1, 22, 3, -30, 44]); |} | |/++ |Constructs union using three functions to handle each intersection case separately. |Params: | lfun = binary function that accepts left side key and left side value | cfun = trinary function that accepts left side key, left side value, and right side value | rfun = binary function that accepts right side key and right side value |+/ |template rcTroykaSeries(alias lfun, alias cfun, alias rfun) |{ | /++ | Params: | lhs = left hand series | rhs = right hand series | Returns: | RC-allocated union series with length equal to $(LREF troykaLength) | +/ | auto rcTroykaSeries | ( | IndexIterL, IterL, size_t LN, SliceKind lkind, | IndexIterR, IterR, size_t RN, SliceKind rkind, | )( | auto ref Series!(IndexIterL, IterL, LN, lkind) lhs, | auto ref Series!(IndexIterR, IterR, RN, rkind) rhs, | ) | { | import mir.rc.array; | alias I = CommonType!(typeof(lhs.index.front), typeof(rhs.index.front)); | alias E = CommonType!( | typeof(lfun(lhs.index.front, lhs.data.front)), | typeof(cfun(lhs.index.front, lhs.data.front, rhs.data.front)), | typeof(rfun(rhs.index.front, rhs.data.front)), | ); | alias R = Series!(RCI!I, RCI!E); | alias UI = Unqual!I; | alias UE = Unqual!E; 1| const length = troykaLength(lhs.index, rhs.index); | import mir.ndslice.allocation: uninitSlice; 2| auto ret = length.mininitRcarray!UI.asSlice.series(length.mininitRcarray!UE.asSlice); | alias algo = troykaSeriesImpl!(lfun, cfun, rfun); 1| algo!(I, E)(lhs.lightScope, rhs.lightScope, ret.lightScope); 2| return (()@trusted => *cast(R*) &ret)(); | } |} | |/// |version(mir_test) unittest |{ | import mir.ndslice; 1| auto a = [1, 2, 3, 9].sliced.series(iota!int([4], 1)); 1| auto b = [0, 2, 4, 9].sliced.series(iota!int([4], 1) * 10.0); | alias unionAlgorithm = rcTroykaSeries!( 2| (key, left) => left, 2| (key, left, right) => left + right, 2| (key, right) => -right, | ); 2| auto c = unionAlgorithm(a, b); 1| assert(c.index == [0, 1, 2, 3, 4, 9]); 1| assert(c.data == [-10, 1, 22, 3, -30, 44]); |} | | |/++ |Length for Troyka union handlers. |Params: | lhs = left hand side series/range | rhs = right hand side series/range |Returns: Total count of lambda function calls in $(LREF troykaGalop) union handler. |+/ |size_t troykaLength( | IndexIterL, IterL, size_t LN, SliceKind lkind, | IndexIterR, IterR, size_t RN, SliceKind rkind, |)( | Series!(IndexIterL, IterL, LN, lkind) lhs, | Series!(IndexIterR, IterR, RN, rkind) rhs, |) |{ | return troykaLength(lhs.index, rhs.index); |} | |/// ditto |size_t troykaLength(LeftRange, RightRange)(LeftRange lhs, RightRange rhs) | if (!isSeries!LeftRange && !isSeries!RightRange) |{ 2| size_t length; 8| alias counter = (scope auto ref _) => ++length; 4| alias ccounter = (scope auto ref _l, scope auto ref _r) => ++length; 2| troykaGalop!(counter, ccounter, counter)(lhs, rhs); 2| return length; |} | |/// |template troykaSeriesImpl(alias lfun, alias cfun, alias rfun) |{ | /// | void troykaSeriesImpl | ( | I, E, | IndexIterL, IterL, size_t LN, SliceKind lkind, | IndexIterR, IterR, size_t RN, SliceKind rkind, | UI, UE, | )( | Series!(IndexIterL, IterL, LN, lkind) lhs, | Series!(IndexIterR, IterR, RN, rkind) rhs, | Series!(UI*, UE*) uninitSlice, | ) | { | import mir.conv: emplaceRef; 14| troykaGalop!( | (auto ref key, auto ref value) { 28| uninitSlice.index.front.emplaceRef!I(key); 28| uninitSlice.data.front.emplaceRef!E(lfun(key, value)); 28| uninitSlice.popFront; | }, | (auto ref key, auto ref lvalue, auto ref rvalue) { 16| uninitSlice.index.front.emplaceRef!I(key); 16| uninitSlice.data.front.emplaceRef!E(cfun(key, lvalue, rvalue)); 16| uninitSlice.popFront; | }, | (auto ref key, auto ref value) { 28| uninitSlice.index.front.emplaceRef!I(key); 28| uninitSlice.data.front.emplaceRef!E(rfun(key, value)); 28| uninitSlice.popFront; | }, | )(lhs, rhs); 14| assert(uninitSlice.length == 0); | } |} | |/** |Merges multiple (time) series into one. |Makes exactly one memory allocation for two series union |and two memory allocation for three and more series union. | |Params: | seriesTuple = variadic static array of composed of series, each series must be sorted. |Returns: sorted GC-allocated series. |See_also $(LREF Series.opBinary) $(LREF makeUnionSeries) |*/ |auto unionSeries(IndexIterator, Iterator, size_t N, SliceKind kind, size_t C)(Series!(IndexIterator, Iterator, N, kind)[C] seriesTuple...) | if (C > 1) |{ 11| return unionSeriesImplPrivate!false(seriesTuple); |} | |/// |@safe pure nothrow version(mir_test) unittest |{ | import mir.date: Date; | | ////////////////////////////////////// | // Constructs two time-series. | ////////////////////////////////////// 1| auto index0 = [1,3,4]; 1| auto data0 = [1.0, 3, 4]; 1| auto series0 = index0.series(data0); | 1| auto index1 = [1,2,5]; 1| auto data1 = [10.0, 20, 50]; 1| auto series1 = index1.series(data1); | | ////////////////////////////////////// | // Merges multiple series into one. | ////////////////////////////////////// | // Order is matter. | // The first slice has higher priority. 1| auto m0 = unionSeries(series0, series1); 1| auto m1 = unionSeries(series1, series0); | 1| assert(m0.index == m1.index); 1| assert(m0.data == [ 1, 20, 3, 4, 50]); 1| assert(m1.data == [10, 20, 3, 4, 50]); |} | |/// |@safe pure nothrow version(mir_test) unittest |{ | import mir.date: Date; | | ////////////////////////////////////// | // Constructs three time-series. | ////////////////////////////////////// 1| auto index0 = [1,3,4]; 1| auto data0 = [1.0, 3, 4]; 1| auto series0 = index0.series(data0); | 1| auto index1 = [1,2,5]; 1| auto data1 = [10.0, 20, 50]; 1| auto series1 = index1.series(data1); | 1| auto index2 = [1, 6]; 1| auto data2 = [100.0, 600]; 1| auto series2 = index2.series(data2); | | ////////////////////////////////////// | // Merges multiple series into one. | ////////////////////////////////////// | // Order is matter. | // The first slice has higher priority. 1| auto m0 = unionSeries(series0, series1, series2); 1| auto m1 = unionSeries(series1, series0, series2); 1| auto m2 = unionSeries(series2, series0, series1); | 1| assert(m0.index == m1.index); 1| assert(m0.index == m2.index); 1| assert(m0.data == [ 1, 20, 3, 4, 50, 600]); 1| assert(m1.data == [ 10, 20, 3, 4, 50, 600]); 1| assert(m2.data == [100, 20, 3, 4, 50, 600]); |} | |/** |Merges multiple (time) series into one. | |Params: | allocator = memory allocator | seriesTuple = variadic static array of composed of series. |Returns: sorted manually allocated series. |See_also $(LREF unionSeries) |*/ |auto makeUnionSeries(IndexIterator, Iterator, size_t N, SliceKind kind, size_t C, Allocator)(auto ref Allocator allocator, Series!(IndexIterator, Iterator, N, kind)[C] seriesTuple...) | if (C > 1) |{ 2| return unionSeriesImplPrivate!false(seriesTuple, allocator); |} | |/// |@system pure nothrow version(mir_test) unittest |{ | import std.experimental.allocator; | import std.experimental.allocator.building_blocks.region; | | ////////////////////////////////////// | // Constructs two time-series. | ////////////////////////////////////// 1| auto index0 = [1,3,4]; | 1| auto data0 = [1.0, 3, 4]; 1| auto series0 = index0.series(data0); | 1| auto index1 = [1,2,5]; | 1| auto data1 = [10.0, 20, 50]; 1| auto series1 = index1.series(data1); | | ////////////////////////////////////// | // Merges multiple series into one. | ////////////////////////////////////// | 1| InSituRegion!(1024) allocator; | 1| auto m0 = allocator.makeUnionSeries(series0, series1); 1| auto m1 = allocator.makeUnionSeries(series1, series0); // order is matter | 1| assert(m0.index == m1.index); 1| assert(m0.data == [ 1, 20, 3, 4, 50]); 1| assert(m1.data == [10, 20, 3, 4, 50]); | | /// series should have the same sizes as after allocation 1| allocator.dispose(m0.index.field); 1| allocator.dispose(m0.data.field); 1| allocator.dispose(m1.index.field); 1| allocator.dispose(m1.data.field); |} | |/** |Merges multiple (time) series into one. | |Params: | seriesTuple = variadic static array of composed of series. |Returns: sorted manually allocated series. |See_also $(LREF unionSeries) |*/ |auto rcUnionSeries(IndexIterator, Iterator, size_t N, SliceKind kind, size_t C)(Series!(IndexIterator, Iterator, N, kind)[C] seriesTuple...) | if (C > 1) |{ 2| return unionSeriesImplPrivate!true(seriesTuple); |} | |/// |@safe pure nothrow version(mir_test) unittest |{ | import mir.rc.array; | | ////////////////////////////////////// | // Constructs two time-series. | ////////////////////////////////////// 1| auto index0 = [1,3,4]; | 1| auto data0 = [1.0, 3, 4]; 1| auto series0 = index0.series(data0); | 1| auto index1 = [1,2,5]; | 1| auto data1 = [10.0, 20, 50]; 1| auto series1 = index1.series(data1); | | ////////////////////////////////////// | // Merges multiple series into one. | ////////////////////////////////////// | 2| Series!(RCI!int, RCI!double) m0 = rcUnionSeries(series0, series1); 2| Series!(RCI!int, RCI!double) m1 = rcUnionSeries(series1, series0); // order is matter | 1| assert(m0.index == m1.index); 1| assert(m0.data == [ 1, 20, 3, 4, 50]); 1| assert(m1.data == [10, 20, 3, 4, 50]); |} | |/** |Initialize preallocated series using union of multiple (time) series. |Doesn't make any allocations. | |Params: | seriesTuple = dynamic array composed of series. | uninitSeries = uninitialized series with exactly required length. |*/ |pragma(inline, false) |auto unionSeriesImpl(I, E, | IndexIterator, Iterator, size_t N, SliceKind kind, UI, UE)( | Series!(IndexIterator, Iterator, N, kind)[] seriesTuple, | Series!(UI*, UE*, N) uninitSeries, | ) |{ | import mir.conv: emplaceRef; | import mir.algorithm.setops: multiwayUnion; | | enum N = N; | alias I = DeepElementType!(typeof(seriesTuple[0].index)); | alias E = DeepElementType!(typeof(seriesTuple[0]._data)); | 3| if(uninitSeries.length) | { 3| auto u = seriesTuple.multiwayUnion!"a.index < b.index"; | do | { 18| auto obs = u.front; 18| emplaceRef!I(uninitSeries.index.front, obs.index); | static if (N == 1) 18| emplaceRef!E(uninitSeries._data.front, obs.data); | else | each!(emplaceRef!E)(uninitSeries._data.front, obs.data); 18| u.popFront; 18| uninitSeries.popFront; | } 18| while(uninitSeries.length); | } |} | |private auto unionSeriesImplPrivate(bool rc, IndexIterator, Iterator, size_t N, SliceKind kind, size_t C, Allocator...)(ref Series!(IndexIterator, Iterator, N, kind)[C] seriesTuple, ref Allocator allocator) | if (C > 1 && Allocator.length <= 1) |{ | import mir.algorithm.setops: unionLength; | import mir.ndslice.topology: iota; | import mir.internal.utility: Iota; | import mir.ndslice.allocation: uninitSlice, makeUninitSlice; | static if (rc) | import mir.rc.array; | 15| Slice!IndexIterator[C] indeces; | foreach (i; Iota!C) 33| indeces[i] = seriesTuple[i].index; | 15| immutable len = indeces[].unionLength; | | alias I = typeof(seriesTuple[0].index.front); | alias E = typeof(seriesTuple[0].data.front); | static if (rc) | alias R = Series!(RCI!I, RCI!E, N); | else | alias R = Series!(I*, E*, N); | alias UI = Unqual!I; | alias UE = Unqual!E; | | static if (N > 1) | { | auto shape = seriesTuple[0]._data._lengths; | shape[0] = len; | | foreach (ref sl; seriesTuple[1 .. $]) | foreach (i; Iota!(1, N)) | if (seriesTuple._data[0]._lengths[i] != sl._data._lengths[i]) | assert(0, "shapes mismatch"); | } | else | { | alias shape = len; | } | | static if (rc == false) | { | static if (Allocator.length) 4| auto ret = (()@trusted => allocator[0].makeUninitSlice!UI(len).series(allocator[0].makeUninitSlice!UE(shape)))(); | else 22| auto ret = (()@trusted => len.uninitSlice!UI.series(shape.uninitSlice!UE))(); | } | else | { | static if (Allocator.length) | static assert(0, "rcUnionSeries with allocators is not implemented."); | else 4| auto ret = (()@trusted => 2| len | .mininitRcarray!UI | .asSlice | .series( | shape | .iota | .elementCount | .mininitRcarray!UE | .asSlice | .sliced(shape)))(); | } | | static if (C == 2) // fast path | { | alias algo = troykaSeriesImpl!( 24| ref (scope ref key, scope return ref left) => left, 12| ref (scope ref key, scope return ref left, scope return ref right) => left, 24| ref (scope ref key, scope return ref right) => right, | ); 12| algo!(I, E)(seriesTuple[0], seriesTuple[1], ret.lightScope); | } | else | { 3| unionSeriesImpl!(I, E)(seriesTuple, ret.lightScope); | } | 30| return () @trusted {return *cast(R*) &ret; }(); |} | |/** |Inserts or assigns a series to the associative array `aa`. |Params: | aa = associative array | series = series |Returns: | associative array |*/ |ref V[K] insertOrAssign(V, K, IndexIterator, Iterator, size_t N, SliceKind kind)(return ref V[K] aa, auto ref Series!(IndexIterator, Iterator, N, kind) series) @property |{ 2| auto s = series.lightScope; 24| foreach (i; 0 .. s.length) | { 6| aa[s.index[i]] = s.data[i]; | } 2| return aa; |} | |/// |@safe pure nothrow version(mir_test) unittest |{ 1| auto a = [1: 3.0, 4: 2.0]; 1| auto s = series([1, 2, 3], [10, 20, 30]); 1| a.insertOrAssign = s; 1| assert(a.series == series([1, 2, 3, 4], [10.0, 20, 30, 2])); |} | |/** |Inserts a series to the associative array `aa`. |Params: | aa = associative array | series = series |Returns: | associative array |*/ |ref V[K] insert(V, K, IndexIterator, Iterator, size_t N, SliceKind kind)(return ref V[K] aa, auto ref Series!(IndexIterator, Iterator, N, kind) series) @property |{ 1| auto s = series.lightScope; 12| foreach (i; 0 .. s.length) | { 3| if (s.index[i] in aa) 1| continue; 2| aa[s.index[i]] = s.data[i]; | } 1| return aa; |} | |/// |@safe pure nothrow version(mir_test) unittest |{ 1| auto a = [1: 3.0, 4: 2.0]; 1| auto s = series([1, 2, 3], [10, 20, 30]); 1| a.insert = s; 1| assert(a.series == series([1, 2, 3, 4], [3.0, 20, 30, 2])); |} | | |static if (__VERSION__ < 2078) |//////////////////// OBJECT.d |{ | |private: | |extern (C) |{ | // from druntime/src/rt/aaA.d | | // size_t _aaLen(in void* p) pure nothrow @nogc; | private void* _aaGetY(void** paa, const TypeInfo_AssociativeArray ti, in size_t valuesize, in void* pkey) pure nothrow; | // inout(void)* _aaGetRvalueX(inout void* p, in TypeInfo keyti, in size_t valuesize, in void* pkey); | inout(void)[] _aaValues(inout void* p, in size_t keysize, in size_t valuesize, const TypeInfo tiValArray) pure nothrow; | inout(void)[] _aaKeys(inout void* p, in size_t keysize, const TypeInfo tiKeyArray) pure nothrow; | void* _aaRehash(void** pp, in TypeInfo keyti) pure nothrow; | void _aaClear(void* p) pure nothrow; | | // alias _dg_t = extern(D) int delegate(void*); | // int _aaApply(void* aa, size_t keysize, _dg_t dg); | | // alias _dg2_t = extern(D) int delegate(void*, void*); | // int _aaApply2(void* aa, size_t keysize, _dg2_t dg); | | // private struct AARange { void* impl; size_t idx; } | alias AARange = ReturnType!(object._aaRange); | AARange _aaRange(void* aa) pure nothrow @nogc @safe; | bool _aaRangeEmpty(AARange r) pure nothrow @nogc @safe; | void* _aaRangeFrontKey(AARange r) pure nothrow @nogc @safe; | void* _aaRangeFrontValue(AARange r) pure nothrow @nogc @safe; | void _aaRangePopFront(ref AARange r) pure nothrow @nogc @safe; | |} | |auto byKeyValue(T : V[K], K, V)(T aa) pure nothrow @nogc @safe |{ | import core.internal.traits : substInout; | | static struct Result | { | AARange r; | | pure nothrow @nogc: | @property bool empty() @safe { return _aaRangeEmpty(r); } | @property auto front() | { | static struct Pair | { | // We save the pointers here so that the Pair we return | // won't mutate when Result.popFront is called afterwards. | private void* keyp; | private void* valp; | | @property ref key() inout | { | auto p = (() @trusted => cast(substInout!K*) keyp) (); | return *p; | }; | @property ref value() inout | { | auto p = (() @trusted => cast(substInout!V*) valp) (); | return *p; | }; | } | return Pair(_aaRangeFrontKey(r), | _aaRangeFrontValue(r)); | } | void popFront() @safe { return _aaRangePopFront(r); } | @property Result save() { return this; } | } | | return Result(_aaToRange(aa)); |} | |auto byKeyValue(T : V[K], K, V)(T* aa) pure nothrow @nogc |{ | return (*aa).byKeyValue(); |} | |// this should never be made public. |private AARange _aaToRange(T: V[K], K, V)(ref T aa) pure nothrow @nogc @safe |{ | // ensure we are dealing with a genuine AA. | static if (is(const(V[K]) == const(T))) | alias realAA = aa; | else | const(V[K]) realAA = aa; | return _aaRange(() @trusted { return cast(void*)realAA; } ()); |} | |} source/mir/series.d is 94% covered <<<<<< EOF # path=./..-..-..-.dub-packages-mir-core-1.1.83-mir-core-source-mir-reflection.lst |/++ |Base reflection utilities. | |License: $(HTTP www.apache.org/licenses/LICENSE-2.0, Apache-2.0) |Authors: Ilya Yaroshenko |Macros: |+/ |module mir.reflection; | |import std.meta; |import std.traits: hasUDA, getUDAs, Parameters, isSomeFunction, FunctionAttribute, functionAttributes, EnumMembers, isAggregateType; | |deprecated |package alias isSomeStruct = isAggregateType; | |/++ |Match types like `std.typeconst: Nullable`. |+/ |template isStdNullable(T) |{ | import std.traits : hasMember; | | T* aggregate; | | enum bool isStdNullable = | hasMember!(T, "isNull") && | hasMember!(T, "get") && | hasMember!(T, "nullify") && | is(typeof(__traits(getMember, aggregate, "isNull")()) == bool) && | !is(typeof(__traits(getMember, aggregate, "get")()) == void) && | is(typeof(__traits(getMember, aggregate, "nullify")()) == void); |} | |/// |version(mir_core_test) unittest |{ | import std.typecons; | static assert(isStdNullable!(Nullable!double)); |} | |/// |version(mir_core_test) unittest |{ | import mir.algebraic; | static assert(isStdNullable!(Nullable!double)); |} | |/// Attribute for deprecated API |struct reflectDeprecated(string target) |{ | string info; |} | |/// Attribute to rename methods, types and functions |template ReflectName(string target) |{ | /// | struct ReflectName(Args...) | { | /// | string name; | } |} | |/// ditto |template reflectName(string target = null, Args...) |{ | /// | auto reflectName(string name) | { | alias TargetName = ReflectName!target; | return TargetName!Args(name); | } |} | |/// |version(mir_core_test) unittest |{ | enum E { A, B, C } | | struct S | { | @reflectName("A") | int a; | | @reflectName!"c++"("B") | int b; | | @reflectName!("C", double)("cd") | @reflectName!("C", float)("cf") | F c(F)() | { | return b; | } | } | | import std.traits: hasUDA; | | alias UniName = ReflectName!null; | alias CppName = ReflectName!"c++"; | alias CName = ReflectName!"C"; | | static assert(hasUDA!(S.a, UniName!()("A"))); | static assert(hasUDA!(S.b, CppName!()("B"))); | | // static assert(hasUDA!(S.c, ReflectName)); // doesn't work for now | static assert(hasUDA!(S.c, CName)); | static assert(hasUDA!(S.c, CName!double)); | static assert(hasUDA!(S.c, CName!float)); | static assert(hasUDA!(S.c, CName!double("cd"))); | static assert(hasUDA!(S.c, CName!float("cf"))); |} | |/// Attribute to rename methods, types and functions |template ReflectMeta(string target, string[] fields) |{ | /// | struct ReflectMeta(Args...) | { | /// | Args args; | static foreach(i, field; fields) | mixin(`alias ` ~ field ~` = args[` ~ i.stringof ~`];`); | } |} | |/// ditto |template reflectMeta(string target, string[] fields) |{ | /// | auto reflectMeta(Args...)(Args args) | if (args.length <= fields.length) | { | alias TargetMeta = ReflectMeta!(target, fields); | return TargetMeta!Args(args); | } |} | |/// |version(mir_core_test) unittest |{ | enum E { A, B, C } | | struct S | { | int a; | @reflectMeta!("c++", ["type"])(E.C) | int b; | } | | import std.traits: hasUDA; | | alias CppMeta = ReflectMeta!("c++", ["type"]); | | static assert(CppMeta!E(E.C).type == E.C); | static assert(!hasUDA!(S.a, CppMeta!E(E.A))); | static assert(hasUDA!(S.b, CppMeta!E(E.C))); |} | |/++ |Attribute to ignore a reflection target |+/ |template reflectIgnore(string target) |{ | enum reflectIgnore; |} | |/// |version(mir_core_test) unittest |{ | struct S | { | @reflectIgnore!"c++" | int a; | } | | import std.traits: hasUDA; | static assert(hasUDA!(S.a, reflectIgnore!"c++")); |} | |/// Attribute for documentation and unittests |struct ReflectDoc(string target) |{ | /// | string text; | /// | reflectUnittest!target test; | | /// | @safe pure nothrow @nogc | this(string text) | { | this.text = text; | } | | /// | @safe pure nothrow @nogc | this(string text, reflectUnittest!target test) | { | this.text = text; | this.test = test; | } | | /// | void toString(W)(scope ref W w) scope const | { | w.put(cast()this.text); | | if (this.test.text.length) | { | w.put("\nExample usage:\n"); | w.put(cast()this.test.text); | } | } | | /// | @safe pure nothrow | string toString()() scope const | { | return this.text ~ "\nExample usage:\n" ~ this.test.text; | } |} | |/++ |Attribute for documentation. |+/ |template reflectDoc(string target = null) |{ | /// | ReflectDoc!target reflectDoc(string text) | { | return ReflectDoc!target(text); | } | | /// | ReflectDoc!target reflectDoc(string text, reflectUnittest!target test) | { | return ReflectDoc!target(text, test); | } |} | |/++ |+/ |template reflectGetDocs(string target, alias symbol) |{ | static if (hasUDA!(symbol, ReflectDoc!target)) | static immutable(ReflectDoc!target[]) reflectGetDocs = [getUDAs!(symbol, ReflectDoc!target)]; | else | static immutable(ReflectDoc!target[]) reflectGetDocs = null; |} | |/// ditto |template reflectGetDocs(string target) |{ | /// | alias reflectGetDocs(alias symbol) = .reflectGetDocs!(target, symbol); | | /// ditto | immutable(ReflectDoc!target)[] reflectGetDocs(T)(T value) | @safe pure nothrow @nogc | if (is(T == enum)) | { | foreach (i, member; EnumMembers!T) | {{ | alias all = __traits(getAttributes, EnumMembers!T[i]); | }} | static immutable ReflectDoc!target[][EnumMembers!T.length] docs = [staticMap!(reflectGetDocs, EnumMembers!T)]; | import mir.enums: getEnumIndex; | uint index = void; | if (getEnumIndex(value, index)) | return docs[index]; | assert(0); | } |} | |/// |version(mir_core_test) unittest |{ | enum E | { | @reflectDoc("alpha") | a, | @reflectDoc!"C#"("Beta", reflectUnittest!"C#"("some c# code")) | @reflectDoc("beta") | b, | c, | } | | alias Doc = ReflectDoc!null; | alias CSDoc = ReflectDoc!"C#"; | | static assert(reflectGetDocs!null(E.a) == [Doc("alpha")]); | static assert(reflectGetDocs!"C#"(E.b) == [CSDoc("Beta", reflectUnittest!"C#"("some c# code"))]); | static assert(reflectGetDocs!null(E.b) == [Doc("beta")]); | static assert(reflectGetDocs!null(E.c) is null); | | struct S | { | @reflectDoc("alpha") | @reflectDoc!"C#"("Alpha") | int a; | } | | static assert(reflectGetDocs!(null, S.a) == [Doc("alpha")]); | static assert(reflectGetDocs!("C#", S.a) == [CSDoc("Alpha")]); | | import std.conv: to; | static assert(CSDoc("Beta", reflectUnittest!"C#"("some c# code")).to!string == "Beta\nExample usage:\nsome c# code"); |} | |/++ |Attribute for extern unit-test. |+/ |struct reflectUnittest(string target) |{ | /// | string text; | |@safe pure nothrow @nogc: | | this(string text) | { | this.text = text; | } | | this(const typeof(this) other) | { | this.text = other.text; | } |} | |/++ |+/ |template reflectGetUnittest(string target, alias symbol) |{ | static if (hasUDA!(symbol, reflectUnittest!target)) | enum string reflectGetUnittest = getUDA!(symbol, reflectUnittest).text; | else | enum string reflectGetUnittest = null; |} | |/// ditto |template reflectGetUnittest(string target) |{ | /// | alias reflectGetUnittest(alias symbol) = .reflectGetUnittest!(target, symbol); | | /// | string reflectGetUnittest(T)(T value) | if (is(T == enum)) | { | foreach (i, member; EnumMembers!T) | {{ | alias all = __traits(getAttributes, EnumMembers!T[i]); | }} | static immutable string[EnumMembers!T.length] tests = [staticMap!(reflectGetUnittest, EnumMembers!T)]; | import mir.enums: getEnumIndex; | uint index = void; | if (getEnumIndex(value, index)) | return tests[index]; | assert(0); | } |} | |/// |version(mir_core_test) unittest |{ | enum E | { | @reflectUnittest!"c++"("assert(E::a == 0);") | a, | @reflectUnittest!"c++"("assert(E::b == 1);") | b, | c, | } | | static assert(reflectGetUnittest!"c++"(E.a) == "assert(E::a == 0);"); | static assert(reflectGetUnittest!"c++"(E.b) == "assert(E::b == 1);"); | static assert(reflectGetUnittest!"c++"(E.c) is null); | | struct S | { | @reflectUnittest!"c++"("alpha") | int a; | } | | static assert(reflectGetUnittest!("c++", S.a) == "alpha"); |} | |/++ |Returns: single UDA. |+/ |template getUDA(alias symbol, alias attribute) |{ | private alias all = getUDAs!(symbol, attribute); | static if (all.length != 1) | static assert(0, "Exactly one " ~ attribute.stringof ~ " attribute is required, " ~ "got " ~ all.length.stringof); | else | { | static if (is(typeof(all[0]))) | enum getUDA = all[0]; | else | alias getUDA = all[0]; | } |} | |/++ |Checks if T has a field member. |+/ |enum bool isOriginalMember(T, string member) = __traits(identifier, __traits(getMember, T, member)) == member; | |/// |version(mir_core_test) unittest |{ | struct D | { | int a; | alias b = a; | } | | static assert(isOriginalMember!(D, "a")); | static assert(!isOriginalMember!(D, "b")); |} | |/++ |Checks if T has a field member. |+/ |enum bool hasField(T, string member) = __traits(compiles, (ref T aggregate) { return __traits(getMember, aggregate, member).offsetof; }); | |deprecated("use 'hasField' instead") alias isField = hasField; | |/// |version(mir_core_test) unittest |{ | struct D | { | int gi; | } | | struct I | { | int f; | | D base; | alias base this; | | void gi(double ) @property {} | void gi(uint ) @property {} | } | | struct S | { | int d; | | I i; | alias i this; | | int gm() @property {return 0;} | int gc() const @property {return 0;} | void gs(int) @property {} | } | | static assert(!hasField!(S, "gi")); | static assert(!hasField!(S, "gs")); | static assert(!hasField!(S, "gc")); | static assert(!hasField!(S, "gm")); | static assert(!hasField!(S, "gi")); | static assert(hasField!(S, "d")); | static assert(hasField!(S, "f")); | static assert(hasField!(S, "i")); |} | |/// with classes |version(mir_core_test) unittest |{ | class I | { | int f; | | void gi(double ) @property {} | void gi(uint ) @property {} | } | | class S | { | int d; | | I i; | alias i this; | | int gm() @property {return 0;} | int gc() const @property {return 0;} | void gs(int) @property {} | } | | static assert(!hasField!(S, "gi")); | static assert(!hasField!(S, "gs")); | static assert(!hasField!(S, "gc")); | static assert(!hasField!(S, "gm")); | static assert(hasField!(S, "d")); | static assert(hasField!(S, "f")); | static assert(hasField!(S, "i")); |} | |/++ |Checks if member is property. |+/ |template isProperty(T, string member) |{ | T* aggregate; | | static if (__traits(compiles, isSomeFunction!(__traits(getMember, *aggregate, member)))) | { | static if (isSomeFunction!(__traits(getMember, *aggregate, member))) | { | enum bool isProperty = isPropertyImpl!(__traits(getMember, *aggregate, member)); | } | else | { | enum bool isProperty = false; | } | } | else | enum bool isProperty = false; |} | |/// |version(mir_core_test) unittest |{ | struct D | { | int y; | | void gf(double ) @property {} | void gf(uint ) @property {} | } | | struct I | { | int f; | | D base; | alias base this; | | void gi(double ) @property {} | void gi(uint ) @property {} | } | | struct S | { | int d; | | I i; | alias i this; | | int gm() @property {return 0;} | int gc() const @property {return 0;} | void gs(int) @property {} | } | | static assert(isProperty!(S, "gf")); | static assert(isProperty!(S, "gi")); | static assert(isProperty!(S, "gs")); | static assert(isProperty!(S, "gc")); | static assert(isProperty!(S, "gm")); | static assert(!isProperty!(S, "d")); | static assert(!isProperty!(S, "f")); | static assert(!isProperty!(S, "y")); |} | |/++ |Returns: list of the setter properties. | |Note: The implementation ignores templates. |+/ |template getSetters(T, string member) |{ | static if (__traits(hasMember, T, member)) | alias getSetters = Filter!(hasSingleArgument, Filter!(isPropertyImpl, __traits(getOverloads, T, member))); | else | alias getSetters = AliasSeq!(); |} | |/// |version(mir_core_test) unittest |{ | struct I | { | int f; | | void gi(double ) @property {} | void gi(uint ) @property {} | } | | struct S | { | int d; | | I i; | alias i this; | | int gm() @property {return 0;} | int gc() const @property {return 0;} | void gs(int) @property {} | } | | static assert(getSetters!(S, "gi").length == 2); | static assert(getSetters!(S, "gs").length == 1); | static assert(getSetters!(S, "gc").length == 0); | static assert(getSetters!(S, "gm").length == 0); | static assert(getSetters!(S, "d").length == 0); | static assert(getSetters!(S, "f").length == 0); |} | |/++ |Returns: list of the serializable (public getters) members. |+/ |enum string[] SerializableMembers(T) = [Filter!(ApplyLeft!(Serializable, T), FieldsAndProperties!T)]; | |/// |version(mir_core_test) unittest |{ | struct D | { | int y; | | int gf() @property {return 0;} | } | | struct I | { | int f; | | D base; | alias base this; | | int gi() @property {return 0;} | } | | struct S | { | int d; | | package int p; | | int gm() @property {return 0;} | | private int q; | | I i; | alias i this; | | int gc() const @property {return 0;} | void gs(int) @property {} | } | | static assert(SerializableMembers!S == ["y", "gf", "f", "gi", "d", "gm", "gc"]); | static assert(SerializableMembers!(const S) == ["y", "f", "d", "gc"]); |} | |/++ |Returns: list of the deserializable (public setters) members. |+/ |enum string[] DeserializableMembers(T) = [Filter!(ApplyLeft!(Deserializable, T), FieldsAndProperties!T)]; | |/// |version(mir_core_test) unittest |{ | struct I | { | int f; | void ga(int) @property {} | } | | struct S | { | int d; | package int p; | | int gm() @property {return 0;} | void gm(int) @property {} | | private int q; | | I i; | alias i this; | | | void gc(int, int) @property {} | void gc(int) @property {} | } | | S s; | // s.gc(0); | | static assert (DeserializableMembers!S == ["f", "ga", "d", "gm", "gc"]); | static assert (DeserializableMembers!(const S) == []); |} | |// This trait defines what members should be serialized - |// public members that are either readable and writable or getter properties |private template Serializable(T, string member) |{ | static if (!isPublic!(T, member)) | enum Serializable = false; | else | enum Serializable = isReadable!(T, member); // any readable is good |} | |private enum bool hasSingleArgument(alias fun) = Parameters!fun.length == 1; |private enum bool hasZeroArguments(alias fun) = Parameters!fun.length == 0; | |// This trait defines what members should be serialized - |// public members that are either readable and writable or setter properties |private template Deserializable(T, string member) |{ | static if (!isPublic!(T, member)) | enum Deserializable = false; | else | static if (isReadableAndWritable!(T, member)) | enum Deserializable = true; | else | static if (getSetters!(T, member).length == 1) | enum Deserializable = is(typeof((ref T val){ __traits(getMember, val, member) = Parameters!(getSetters!(T, member)[0])[0].init; })); | else | enum Deserializable = false; |} | |private enum FieldsAndProperties(T) = Reverse!(NoDuplicates!(Reverse!(FieldsAndPropertiesImpl!T))); | |private template allMembers(T) |{ | static if (isAggregateType!T) | alias allMembers = __traits(allMembers, T); | else | alias allMembers = AliasSeq!(); |} | |private template FieldsAndPropertiesImpl(T) |{ | alias isProperty = ApplyLeft!(.isProperty, T); | alias hasField = ApplyLeft!(.hasField, T); | alias isOriginalMember = ApplyLeft!(.isOriginalMember, T); | alias isMember = templateAnd!(templateOr!(hasField, isProperty), isOriginalMember); | static if (__traits(getAliasThis, T).length) | { | T* aggregate; | alias A = typeof(__traits(getMember, aggregate, __traits(getAliasThis, T))); | static if (isAggregateType!T) | alias baseMembers = FieldsAndPropertiesImpl!A; | else | alias baseMembers = AliasSeq!(); | alias members = Erase!(__traits(getAliasThis, T)[0], __traits(allMembers, T)); | alias FieldsAndPropertiesImpl = AliasSeq!(baseMembers, Filter!(isMember, members)); | } | else | { | import mir.algebraic; | static if (isVariant!T) | alias members = staticMap!(allMembers, T.AllowedTypes); | else | alias members = allMembers!T; | alias FieldsAndPropertiesImpl = AliasSeq!(Filter!(isMember, members)); | } |} | |// check if the member is readable |private template isReadable(T, string member) |{ | T* aggregate; | enum bool isReadable = __traits(compiles, { static fun(T)(auto ref T t) {} fun(__traits(getMember, *aggregate, member)); }); |} | |// check if the member is readable/writeble? |private template isReadableAndWritable(T, string member) |{ | T* aggregate; | enum bool isReadableAndWritable = __traits(compiles, __traits(getMember, *aggregate, member) = __traits(getMember, *aggregate, member)); |} | |package template isPublic(T, string member) |{ | T* aggregate; | enum bool isPublic = !__traits(getProtection, __traits(getMember, *aggregate, member)).privateOrPackage; |} | |// check if the member is property |private template isSetter(T, string member) |{ | T* aggregate; | static if (__traits(compiles, isSomeFunction!(__traits(getMember, *aggregate, member)))) | { | static if (isSomeFunction!(__traits(getMember, *aggregate, member))) | { | enum bool isSetter = getSetters!(T, member).length > 0;; | } | else | { | enum bool isSetter = false; | } | } | else | enum bool isSetter = false; |} | |private template isGetter(T, string member) |{ | T* aggregate; | static if (__traits(compiles, isSomeFunction!(__traits(getMember, *aggregate, member)))) | { | static if (isSomeFunction!(__traits(getMember, *aggregate, member))) | { | enum bool isGetter = Filter!(hasZeroArguments, Filter!(isPropertyImpl, __traits(getOverloads, T, member))).length == 1; | } | else | { | enum bool isGetter = false; | } | } | else | enum bool isGetter = false; |} | |private enum bool isPropertyImpl(alias member) = (functionAttributes!member & FunctionAttribute.property) != 0; | |private bool privateOrPackage()(string protection) |{ | return protection == "private" || protection == "package"; |} ../../../.dub/packages/mir-core-1.1.83/mir-core/source/mir/reflection.d has no code <<<<<< EOF # path=./..-..-..-.dub-packages-mir-core-1.1.83-mir-core-source-mir-functional.lst |/++ |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 mir, ndslice). |$(BOOKTABLE $(H2 Functions), |$(TR $(TH Function Name) $(TH Description)) | $(TR $(TD $(LREF naryFun)) | $(TD Create a unary, binary or N-nary function from a string. Most often | used when defining algorithms on ranges and slices. | )) | $(TR $(TD $(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 not)) | $(TD Creates a function that negates another. | )) | $(TR $(TD $(LREF reverseArgs)) | $(TD Predicate that reverses the order of its arguments. | )) | $(TR $(TD $(LREF forward)) | $(TD Forwards function arguments with saving ref-ness. | )) | $(TR $(TD $(LREF refTuple)) | $(TD Removes $(LREF Ref) shell. | )) | $(TR $(TD $(LREF unref)) | $(TD Creates a $(LREF RefTuple) structure. | )) | $(TR $(TD $(LREF __ref)) | $(TD Creates a $(LREF Ref) structure. | )) |) |License: $(HTTP www.apache.org/licenses/LICENSE-2.0, Apache-2.0) |Authors: Ilya Yaroshenko, $(HTTP erdani.org, Andrei Alexandrescu (some original code from std.functional)) | |Macros: |NDSLICE = $(REF_ALTTEXT $(TT $2), $2, mir, ndslice, $1)$(NBSP) |+/ |module mir.functional; | |private enum isRef(T) = is(T : Ref!T0, T0); | |import mir.math.common: optmath; | |public import core.lifetime : forward; | |@optmath: | |/++ |Constructs static array. |+/ |T[N] staticArray(T, size_t N)(T[N] a...) |{ | return a; |} | |/++ |Simple wrapper that holds a pointer. |It is used for as workaround to return multiple auto ref values. |+/ |struct Ref(T) | if (!isRef!T) |{ | @optmath: | | @disable this(); | /// | this(ref T value) @trusted | { | __ptr = &value; | } | /// | T* __ptr; | /// | ref inout(T) __value() inout @property { return *__ptr; } | /// | alias __value this; | /// | bool opEquals(scope Ref!T rhs) const scope | { | return __value == rhs.__value; | } | | static if (__traits(hasMember, T, "toHash") || __traits(isScalar, T)) | /// | size_t toHash() const | { | return hashOf(__value); | } |} | |/// Creates $(LREF Ref) wrapper. |Ref!T _ref(T)(ref T value) |{ | return Ref!T(value); |} | |private mixin template _RefTupleMixin(T...) | if (T.length <= 26) |{ | static if (T.length) | { | enum i = T.length - 1; | static if (isRef!(T[i])) | mixin(`@optmath @property ref ` ~ cast(char)('a' + i) ~ `() { return *expand[` ~ i.stringof ~ `].__ptr; }` ); | else | mixin(`alias ` ~ cast(char)('a' + i) ~ ` = expand[` ~ i.stringof ~ `];`); | mixin ._RefTupleMixin!(T[0 .. $-1]); | } |} | |/++ |Simplified tuple structure. Some fields may be type of $(LREF Ref). |Ref stores a pointer to a values. |+/ |struct RefTuple(T...) |{ | @optmath: | T expand; | alias expand this; | mixin _RefTupleMixin!T; |} | |/// Removes $(LREF Ref) shell. |alias Unref(V : Ref!T, T) = T; |/// ditto |template Unref(V : RefTuple!T, T...) |{ | import std.meta: staticMap; | alias Unref = RefTuple!(staticMap!(.Unref, T)); |} | |/// ditto |alias Unref(V) = V; | |/++ |Returns: a $(LREF RefTuple) structure. |+/ |RefTuple!Args refTuple(Args...)(auto ref Args args) |{ | return RefTuple!Args(args); |} | |/// Removes $(LREF Ref) shell. |ref T unref(V : Ref!T, T)(scope return V value) |{ | return *value.__ptr; |} | |/// ditto |Unref!(RefTuple!T) unref(V : RefTuple!T, T...)(V value) |{ | typeof(return) ret; | foreach(i, ref elem; ret.expand) | elem = unref(value.expand[i]); | return ret; |} | |/// ditto |ref V unref(V)(scope return ref V value) |{ | return value; |} | |/// ditto |V unref(V)(V value) |{ | import std.traits: hasElaborateAssign; | static if (hasElaborateAssign!V) | { | import core.lifetime: move; | return move(value); | } | else | return value; |} | |private string joinStrings()(string[] strs) |{ | if (strs.length) | { | auto ret = strs[0]; | foreach(s; strs[1 .. $]) | ret ~= s; | return ret; | } | return null; |} | |/++ |Takes multiple functions and adjoins them together. The result is a |$(LREF RefTuple) 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 |(`F.length == 1`), adjoin simply aliases to the single passed function |(`F[0]`). |+/ |template adjoin(fun...) if (fun.length && fun.length <= 26) |{ | static if (fun.length != 1) | { | import std.meta: staticMap, Filter; | static if (Filter!(_needNary, fun).length == 0) | { | /// | @optmath auto adjoin(Args...)(auto ref Args args) | { | template _adjoin(size_t i) | { | static if (__traits(compiles, &fun[i](forward!args))) | enum _adjoin = "Ref!(typeof(fun[" ~ i.stringof ~ "](forward!args)))(fun[" ~ i.stringof ~ "](forward!args)), "; | else | enum _adjoin = "fun[" ~ i.stringof ~ "](forward!args), "; | } | | import mir.internal.utility; | mixin("return refTuple(" ~ [staticMap!(_adjoin, Iota!(fun.length))].joinStrings ~ ");"); | } | } | else alias adjoin = .adjoin!(staticMap!(naryFun, fun)); | } | else alias adjoin = naryFun!(fun[0]); |} | |/// |@safe version(mir_core_test) unittest |{ | 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) == RefTuple!(bool, int))); | assert(x.a == true && x.b == 2); |} | |@safe version(mir_core_test) unittest |{ | 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) == RefTuple!(bool, int))); | assert(x2.a && x2.b == 2); | auto x3 = adjoin!(F1, F2, F2)(5); | assert(is(typeof(x3) == RefTuple!(bool, int, int))); | assert(x3.a && x3.b == 2 && x3.c == 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 |version(mir_core_test) unittest |{ | import std.meta: staticMap; | alias funs = staticMap!(naryFun, "a", "a * 2", "a * 3", "a * a", "-a"); | alias afun = adjoin!funs; | int a = 5, b = 5; | assert(afun(a) == refTuple(Ref!int(a), 10, 15, 25, -5)); | assert(afun(a) == refTuple(Ref!int(b), 10, 15, 25, -5)); | | static class C{} | alias IC = immutable(C); | IC foo(){return typeof(return).init;} | RefTuple!(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 RefTuple!(IS, IS, IS, IS) ret2 = adjoin!(bar, bar, bar, bar)(); |} | |private template needOpCallAlias(alias fun) |{ | /* Determine whether or not naryFun need to alias to fun or | * fun.opCall. Basically, fun is a function object if fun(...) compiles. We | * want is(naryFun!fun) (resp., is(naryFun!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(naryFun!fun) should compile, but does not if naryFun | * 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)) | { | import std.traits: Parameters; | enum needOpCallAlias = !is(typeof(fun)) && __traits(compiles, () { | return fun(Parameters!fun.init); | }); | } | else | enum needOpCallAlias = false; |} | |private template _naryAliases(size_t n) | if (n <= 26) |{ | static if (n == 0) | enum _naryAliases = ""; | else | { | enum i = n - 1; | enum _naryAliases = _naryAliases!i ~ "alias " ~ cast(char)('a' + i) ~ " = args[" ~ i.stringof ~ "];\n"; | } |} | |/++ |Aliases itself to a set of functions. | |Transforms strings representing an expression into a binary function. The |strings must use symbol names `a`, `b`, ..., `z` as the parameters. |If `functions[i]` is not a string, `naryFun` aliases itself away to `functions[i]`. |+/ |template naryFun(functions...) | if (functions.length >= 1) |{ | static foreach (fun; functions) | { | static if (is(typeof(fun) : string)) | { | import mir.math.common; | /// Specialization for string lambdas | @optmath auto ref naryFun(Args...)(auto ref Args args) | if (args.length <= 26) | { | mixin(_naryAliases!(Args.length)); | return mixin(fun); | } | } | else static if (needOpCallAlias!fun) | alias naryFun = fun.opCall; | else | alias naryFun = fun; | } |} | |/// |@safe version(mir_core_test) unittest |{ | // Strings are compiled into functions: | alias isEven = naryFun!("(a & 1) == 0"); | assert(isEven(2) && !isEven(1)); |} | |/// |@safe version(mir_core_test) unittest |{ | alias less = naryFun!("a < b"); | assert(less(1, 2) && !less(2, 1)); | alias greater = naryFun!("a > b"); | assert(!greater("1", "2") && greater("2", "1")); |} | |/// `naryFun` accepts up to 26 arguments. |@safe version(mir_core_test) unittest |{ | assert(naryFun!("a * b + c")(2, 3, 4) == 10); |} | |/// `naryFun` can return by reference. |version(mir_core_test) unittest |{ | int a; | assert(&naryFun!("a")(a) == &a); |} | |/// `args` parameter tuple |version(mir_core_test) unittest |{ | assert(naryFun!("args[0] + args[1]")(2, 3) == 5); |} | |/// Multiple functions |@safe pure nothrow @nogc |version(mir_core_test) unittest |{ | alias fun = naryFun!( | (uint a) => a, | (ulong a) => a * 2, | a => a * 3, | ); | | int a = 10; | long b = 10; | float c = 10; | | assert(fun(a) == 10); | assert(fun(b) == 20); | assert(fun(c) == 30); |} | |@safe version(mir_core_test) unittest |{ | static int f1(int a) { return a + 1; } | static assert(is(typeof(naryFun!(f1)(1)) == int)); | assert(naryFun!(f1)(41) == 42); | int f2(int a) { return a + 1; } | static assert(is(typeof(naryFun!(f2)(1)) == int)); | assert(naryFun!(f2)(41) == 42); | assert(naryFun!("a + 1")(41) == 42); | | int num = 41; | assert(naryFun!"a + 1"(num) == 42); | | // Issue 9906 | struct Seen | { | static bool opCall(int n) { return true; } | } | static assert(needOpCallAlias!Seen); | static assert(is(typeof(naryFun!Seen(1)))); | assert(naryFun!Seen(1)); | | Seen s; | static assert(!needOpCallAlias!s); | static assert(is(typeof(naryFun!s(1)))); | assert(naryFun!s(1)); | | struct FuncObj | { | bool opCall(int n) { return true; } | } | FuncObj fo; | static assert(!needOpCallAlias!fo); | static assert(is(typeof(naryFun!fo))); | assert(naryFun!fo(1)); | | // Function object with non-static opCall can only be called with an | // instance, not with merely the type. | static assert(!is(typeof(naryFun!FuncObj))); |} | |@safe version(mir_core_test) unittest |{ | static int f1(int a, string b) { return a + 1; } | static assert(is(typeof(naryFun!(f1)(1, "2")) == int)); | assert(naryFun!(f1)(41, "a") == 42); | string f2(int a, string b) { return b ~ "2"; } | static assert(is(typeof(naryFun!(f2)(1, "1")) == string)); | assert(naryFun!(f2)(1, "4") == "42"); | assert(naryFun!("a + b")(41, 1) == 42); | //@@BUG | //assert(naryFun!("return a + b;")(41, 1) == 42); | | // Issue 9906 | struct Seen | { | static bool opCall(int x, int y) { return true; } | } | static assert(is(typeof(naryFun!Seen))); | assert(naryFun!Seen(1,1)); | | struct FuncObj | { | bool opCall(int x, int y) { return true; } | } | FuncObj fo; | static assert(!needOpCallAlias!fo); | static assert(is(typeof(naryFun!fo))); | assert(naryFun!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(naryFun!FuncObj))); |} | | |/++ |N-ary predicate that reverses the order of arguments, e.g., given |`pred(a, b, c)`, returns `pred(c, b, a)`. |+/ |template reverseArgs(alias fun) |{ | import std.meta: Reverse; | /// | @optmath auto ref reverseArgs(Args...)(auto ref Args args) | if (is(typeof(fun(Reverse!args)))) | { | return fun(Reverse!args); | } | |} | |/// |@safe version(mir_core_test) 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 version(mir_core_test) unittest |{ | int a(int a) { return a * 2; } | alias _a = reverseArgs!a; | assert(a(2) == _a(2)); |} | |@safe version(mir_core_test) unittest |{ | int b() { return 4; } | alias _b = reverseArgs!b; | assert(b() == _b()); |} | |@safe version(mir_core_test) unittest |{ | alias gt = reverseArgs!(naryFun!("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)); |} | |/++ |Negates predicate `pred`. |+/ |template not(alias pred) |{ | static if (!is(typeof(pred) : string) && !needOpCallAlias!pred) | /// | @optmath bool not(T...)(auto ref T args) | { | return !pred(args); | } | else | alias not = .not!(naryFun!pred); |} | |/// |@safe version(mir_core_test) unittest |{ | import std.algorithm.searching : find; | import std.uni : isWhite; | string a = " Hello, world!"; | assert(find!(not!isWhite)(a) == "Hello, world!"); |} | |@safe version(mir_core_test) 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)); |} | |private template _pipe(size_t n) |{ | static if (n) | { | enum i = n - 1; | enum _pipe = "f[" ~ i.stringof ~ "](" ~ ._pipe!i ~ ")"; | } | else | enum _pipe = "args"; |} | |private template _unpipe(alias fun) |{ | import std.traits: TemplateArgsOf, TemplateOf; | static if (__traits(compiles, TemplateOf!fun)) | static if (__traits(isSame, TemplateOf!fun, .pipe)) | alias _unpipe = TemplateArgsOf!fun; | else | alias _unpipe = fun; | else | alias _unpipe = fun; | |} | |private enum _needNary(alias fun) = is(typeof(fun) : string) || needOpCallAlias!fun; | |/++ |Composes passed-in functions `fun[0], fun[1], ...` returning a |function `f(x)` that in turn returns |`...(fun[1](fun[0](x)))...`. Each function can be a regular |functions, a delegate, a lambda, or a string. |+/ |template pipe(fun...) |{ | static if (fun.length != 1) | { | import std.meta: staticMap, Filter; | alias f = staticMap!(_unpipe, fun); | static if (f.length == fun.length && Filter!(_needNary, f).length == 0) | { | /// | @optmath auto ref pipe(Args...)(auto ref Args args) | { | return mixin (_pipe!(fun.length)); | } | } | else alias pipe = .pipe!(staticMap!(naryFun, f)); | } | else alias pipe = naryFun!(fun[0]); |} | |/// |@safe version(mir_core_test) unittest |{ | assert(pipe!("a + b", a => a * 10)(2, 3) == 50); |} | |/// `pipe` can return by reference. |version(mir_core_test) unittest |{ | int a; | assert(&pipe!("a", "a")(a) == &a); |} | |/// Template bloat reduction |version(mir_core_test) unittest |{ | enum a = "a * 2"; | alias b = e => e + 2; | | alias p0 = pipe!(pipe!(a, b), pipe!(b, a)); | alias p1 = pipe!(a, b, b, a); | | static assert(__traits(isSame, p0, p1)); |} | |@safe version(mir_core_test) 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(pipe!(split, map!(to!(int)))("1 2 3").equal([1, 2, 3])); |} | | |struct AliasCall(T, string methodName, TemplateArgs...) |{ | T __this; | alias __this this; | | /// | auto lightConst()() const @property | { | import mir.qualifier; | return AliasCall!(LightConstOf!T, methodName, TemplateArgs)(__this.lightConst); | } | | /// | auto lightImmutable()() immutable @property | { | import mir.qualifier; | return AliasCall!(LightImmutableOf!T, methodName, TemplateArgs)(__this.lightImmutable); | } | | this()(auto ref T value) | { | __this = value; | } | auto ref opCall(Args...)(auto ref Args args) | { | import std.traits: TemplateArgsOf; | mixin("return __this." ~ methodName ~ (TemplateArgs.length ? "!TemplateArgs" : "") ~ "(forward!args);"); | } |} | |/++ |Replaces call operator (`opCall`) for the value using its method. |The funciton is designed to use with $(NDSLICE, topology, vmap) or $(NDSLICE, topology, map). |Params: | methodName = name of the methods to use for opCall and opIndex | TemplateArgs = template arguments |+/ |template aliasCall(string methodName, TemplateArgs...) |{ | /++ | Params: | value = the value to wrap | Returns: | wrapped value with implemented opCall and opIndex methods | +/ | AliasCall!(T, methodName, TemplateArgs) aliasCall(T)(T value) @property | { | return typeof(return)(value); | } | | /// ditto | ref AliasCall!(T, methodName, TemplateArgs) aliasCall(T)(return ref T value) @property @trusted | { | return *cast(typeof(return)*) &value; | } |} | |/// |@safe pure nothrow version(mir_core_test) unittest |{ | static struct S | { | auto lightConst()() const @property { return S(); } | | auto fun(size_t ct_param = 1)(size_t rt_param) const | { | return rt_param + ct_param; | } | } | | S s; | | auto sfun = aliasCall!"fun"(s); | assert(sfun(3) == 4); | | auto sfun10 = aliasCall!("fun", 10)(s); // uses fun!10 | assert(sfun10(3) == 13); |} | |/++ |+/ |template recurseTemplatePipe(alias Template, size_t N, Args...) |{ | static if (N == 0) | alias recurseTemplatePipe = Args; | else | { | alias recurseTemplatePipe = Template!(.recurseTemplatePipe!(Template, N - 1, Args)); | } |} | |/// |@safe version(mir_core_test) unittest |{ | // import mir.ndslice.topology: map; | alias map(alias fun) = a => a; // some template | static assert (__traits(isSame, recurseTemplatePipe!(map, 2, "a * 2"), map!(map!"a * 2"))); |} | |/++ |+/ |template selfAndRecurseTemplatePipe(alias Template, size_t N, Args...) |{ | static if (N == 0) | alias selfAndRecurseTemplatePipe = Args; | else | { | alias selfAndRecurseTemplatePipe = Template!(.selfAndRecurseTemplatePipe!(Template, N - 1, Args)); | } |} | |/// |@safe version(mir_core_test) unittest |{ | // import mir.ndslice.topology: map; | alias map(alias fun) = a => a; // some template | static assert (__traits(isSame, selfAndRecurseTemplatePipe!(map, 2, "a * 2"), map!(pipe!("a * 2", map!"a * 2")))); |} | |/++ |+/ |template selfTemplatePipe(alias Template, size_t N, Args...) |{ | static if (N == 0) | alias selfTemplatePipe = Args; | else | { | alias selfTemplatePipe = Template!(.selfTemplatePipe!(Template, N - 1, Args)); | } |} | |/// |@safe version(mir_core_test) unittest |{ | // import mir.ndslice.topology: map; | alias map(alias fun) = a => a; // some template | static assert (__traits(isSame, selfTemplatePipe!(map, 2, "a * 2"), map!(pipe!("a * 2", map!"a * 2")))); |} ../../../.dub/packages/mir-core-1.1.83/mir-core/source/mir/functional.d has no code <<<<<< EOF # path=./source-mir-lob.lst |/++ |+/ |module mir.lob; | |/++ |Values of type clob are encoded as a sequence of octets that should be interpreted as text |with an unknown encoding (and thus opaque to the application). |+/ |struct Clob |{ | /// | const(char)[] data; |} | |/++ |This is a sequence of octets with no interpretation (and thus opaque to the application). |+/ |struct Blob |{ | /// | const(ubyte)[] data; |} source/mir/lob.d has no code <<<<<< EOF # path=./source-mir-format_impl.lst |/// |module mir.format_impl; | |import mir.format; | |@safe pure @nogc nothrow: | | |size_t printFloatingPointExtend(T, C)(T c, scope ref const FormatSpec spec, scope ref C[512] buf) @trusted |{ 0000000| char[512] cbuf = void; 0000000| return extendASCII(cbuf[].ptr, buf[].ptr, printFloatingPoint(cast(double)c, spec, cbuf)); |} | |size_t printFloatingPointGen(T)(T c, scope ref const FormatSpec spec, scope ref char[512] buf) @trusted | if(is(T == float) || is(T == double) || is(T == real)) |{ | import mir.math.common: copysign, fabs; 0000000| bool neg = copysign(1, c) < 0; 0000000| c = fabs(c); 0000000| char specFormat = spec.format; | version (CRuntime_Microsoft) | { | if (c != c || c.fabs == c.infinity) | { | size_t i; | char s = void; | if (copysign(1, c) < 0) | s = '-'; | else | if (spec.plus) | s = '+'; | else | if (spec.space) | s = ' '; | else | goto S; | buf[0] = s; | i = 1; | S: | static immutable char[3][2][2] special = [["inf", "INF"], ["nan", "NAN"]]; | auto p = &special[c != c][(specFormat & 0xDF) == specFormat][0]; | buf[i + 0] = p[0]; | buf[i + 1] = p[1]; | buf[i + 2] = p[2]; | return i + 3; | } | } | alias T = double; | static if (is(T == real)) | align(4) char[12] fmt = "%%%%%%*.*gL\0"; | else | align(4) char[12] fmt = "%%%%%%*.*g\0\0"; | 0000000| if (specFormat && specFormat != 's' && specFormat != 'g' && specFormat != 'G') | { 0000000| assert ( | specFormat == 'e' 0000000| || specFormat == 'E' 0000000| || specFormat == 'f' 0000000| || specFormat == 'F' 0000000| || specFormat == 'a' 0000000| || specFormat == 'A', "Wrong floating point format specifier."); 0000000| fmt[9] = specFormat; | } 0000000| uint fmtRevLen = 5; 0000000| if (spec.hash) fmt[fmtRevLen--] = '#'; 0000000| if (spec.space) fmt[fmtRevLen--] = ' '; 0000000| if (spec.zero) fmt[fmtRevLen--] = '0'; 0000000| if (spec.plus) fmt[fmtRevLen--] = '+'; 0000000| if (spec.dash) fmt[fmtRevLen--] = '-'; | | import core.stdc.stdio : snprintf; 0000000| ptrdiff_t res = assumePureSafe(&snprintf)((()@trusted =>buf.ptr)(), buf.length - 1, &fmt[fmtRevLen], spec.width, spec.precision, c); 0000000| assert (res >= 0, "snprintf failed to print a floating point number"); | import mir.utility: min; 0000000| return res < 0 ? 0 : min(cast(size_t)res, buf.length - 1); |} | |auto assumePureSafe(T)(T t) @trusted | // if (isFunctionPointer!T || isDelegate!T) |{ | import std.traits; | enum attrs = (functionAttributes!T | FunctionAttribute.pure_ | FunctionAttribute.safe) & ~FunctionAttribute.system; 0000000| return cast(SetFunctionAttributes!(T, functionLinkage!T, attrs)) t; |} | |////////// FLOATING POINT ////////// | |size_t printFloatingPoint(float c, scope ref const FormatSpec spec, scope ref char[512] buf) |{ 0000000| return printFloatingPoint(cast(double)c, spec, buf); |} | |size_t printFloatingPoint(double c, scope ref const FormatSpec spec, scope ref char[512] buf) |{ 0000000| return printFloatingPointGen(c, spec, buf); |} | |size_t printFloatingPoint(real c, scope ref const FormatSpec spec, scope ref char[512] buf) |{ | version (CRuntime_Microsoft) | { | return printFloatingPoint(cast(double) c, spec, buf); | } | else | { 0000000| return printFloatingPointGen(c, spec, buf); | } |} | |size_t printFloatingPoint(float c, scope ref const FormatSpec spec, scope ref wchar[512] buf) |{ 0000000| return printFloatingPoint(cast(double)c, spec, buf); |} | |size_t printFloatingPoint(double c, scope ref const FormatSpec spec, scope ref wchar[512] buf) |{ 0000000| return printFloatingPointExtend(c, spec, buf); |} | |size_t printFloatingPoint(real c, scope ref const FormatSpec spec, scope ref wchar[512] buf) |{ | version (CRuntime_Microsoft) | { | return printFloatingPoint(cast(double) c, spec, buf); | } | else | { 0000000| return printFloatingPointExtend(c, spec, buf); | } |} | |size_t printFloatingPoint(float c, scope ref const FormatSpec spec, scope ref dchar[512] buf) |{ 0000000| return printFloatingPoint(cast(double)c, spec, buf); |} | |size_t printFloatingPoint(double c, scope ref const FormatSpec spec, scope ref dchar[512] buf) |{ 0000000| return printFloatingPointExtend(c, spec, buf); |} | |size_t printFloatingPoint(real c, scope ref const FormatSpec spec, scope ref dchar[512] buf) |{ | version (CRuntime_Microsoft) | { | return printFloatingPoint(cast(double) c, spec, buf); | } | else | { 0000000| return printFloatingPointExtend(c, spec, buf); | } |} | |nothrow: | 0000000|size_t printHexadecimal(uint c, ref char[8] buf, bool upper) { return printHexadecimalGen!(uint, char)(c, buf, upper); } 0000000|size_t printHexadecimal(ulong c, ref char[16] buf, bool upper) { return printHexadecimalGen!(ulong, char)(c, buf, upper); } |static if (is(ucent)) |size_t printHexadecimal(ucent c, ref char[32] buf, bool upper) { return printHexadecimalGen!(ucent, char)(c, buf, upper); } | 0000000|size_t printHexadecimal(uint c, ref wchar[8] buf, bool upper) { return printHexadecimalGen!(uint, wchar)(c, buf, upper); } 0000000|size_t printHexadecimal(ulong c, ref wchar[16] buf, bool upper) { return printHexadecimalGen!(ulong, wchar)(c, buf, upper); } |static if (is(ucent)) |size_t printHexadecimal(ucent c, ref wchar[32] buf, bool upper) { return printHexadecimalGen!(ucent, wchar)(c, buf, upper); } | 0000000|size_t printHexadecimal(uint c, ref dchar[8] buf, bool upper) { return printHexadecimalGen!(uint, dchar)(c, buf, upper); } 0000000|size_t printHexadecimal(ulong c, ref dchar[16] buf, bool upper) { return printHexadecimalGen!(ulong, dchar)(c, buf, upper); } |static if (is(ucent)) |size_t printHexadecimal(ucent c, ref dchar[32] buf, bool upper) { return printHexadecimalGen!(ucent, dchar)(c, buf, upper); } | |size_t printHexadecimalGen(T, C)(T c, ref C[T.sizeof * 2] buf, bool upper) @trusted |{ 0000000| if (c < 10) | { 0000000| buf[0] = cast(char)('0' + c); 0000000| return 1; | } | import mir.bitop: ctlz; 0000000| immutable hexString = upper ? hexStringUpper : hexStringLower; 0000000| size_t ret = cast(size_t) ctlz(c); 0000000| ret = (ret >> 2) + ((ret & 3) != 0); 0000000| size_t i = ret; | do | { 0000000| buf.ptr[--i] = hexStringUpper[c & 0xF]; 0000000| c >>= 4; | } 0000000| while(i); 0000000| return ret; |} | 1| size_t printHexAddress(ubyte c, ref char[2] buf, bool upper) { return printHexAddressGen!(ubyte, char)(c, buf, upper); } 0000000| size_t printHexAddress(ushort c, ref char[4] buf, bool upper) { return printHexAddressGen!(ushort, char)(c, buf, upper); } 0000000|size_t printHexAddress(uint c, ref char[8] buf, bool upper) { return printHexAddressGen!(uint, char)(c, buf, upper); } 0000000|size_t printHexAddress(ulong c, ref char[16] buf, bool upper) { return printHexAddressGen!(ulong, char)(c, buf, upper); } |static if (is(ucent)) |size_t printHexAddress(ucent c, ref char[32] buf, bool upper) { return printHexAddressGen!(ucent, char)(c, buf, upper); } | 0000000| size_t printHexAddress(ubyte c, ref wchar[2] buf, bool upper) { return printHexAddressGen!(ubyte, wchar)(c, buf, upper); } 0000000| size_t printHexAddress(ushort c, ref wchar[4] buf, bool upper) { return printHexAddressGen!(ushort, wchar)(c, buf, upper); } 0000000|size_t printHexAddress(uint c, ref wchar[8] buf, bool upper) { return printHexAddressGen!(uint, wchar)(c, buf, upper); } 0000000|size_t printHexAddress(ulong c, ref wchar[16] buf, bool upper) { return printHexAddressGen!(ulong, wchar)(c, buf, upper); } |static if (is(ucent)) |size_t printHexAddress(ucent c, ref wchar[32] buf, bool upper) { return printHexAddressGen!(ucent, wchar)(c, buf, upper); } | 0000000| size_t printHexAddress(ubyte c, ref dchar[2] buf, bool upper) { return printHexAddressGen!(ubyte, dchar)(c, buf, upper); } 0000000| size_t printHexAddress(ushort c, ref dchar[4] buf, bool upper) { return printHexAddressGen!(ushort, dchar)(c, buf, upper); } 0000000|size_t printHexAddress(uint c, ref dchar[8] buf, bool upper) { return printHexAddressGen!(uint, dchar)(c, buf, upper); } 0000000|size_t printHexAddress(ulong c, ref dchar[16] buf, bool upper) { return printHexAddressGen!(ulong, dchar)(c, buf, upper); } |static if (is(ucent)) |size_t printHexAddress(ucent c, ref dchar[32] buf, bool upper) { return printHexAddressGen!(ucent, dchar)(c, buf, upper); } | |size_t printHexAddressGen(T, C)(T c, ref C[T.sizeof * 2] buf, bool upper) |{ | static if (T.sizeof == 16) | { | printHexAddress(cast(ulong)(c >> 64), buf[0 .. 16], upper); | printHexAddress(cast(ulong) c, buf[16 .. 32], upper); | } | else | { 2| immutable hexString = upper ? hexStringUpper : hexStringLower; 7| foreach_reverse(ref e; buf) | { 2| e = hexStringUpper[c & 0xF]; 2| c >>= 4; | } | } 1| return buf.length; |} | |static immutable hexStringUpper = "0123456789ABCDEF"; |static immutable hexStringLower = "0123456789abcdef"; | 0000000|size_t printBufferShift(size_t length, size_t shift, scope char* ptr) { return printBufferShiftGen!char(length, shift, ptr); } 0000000|size_t printBufferShift(size_t length, size_t shift, scope wchar* ptr) { return printBufferShiftGen!wchar(length, shift, ptr); } 0000000|size_t printBufferShift(size_t length, size_t shift, scope dchar* ptr) { return printBufferShiftGen!dchar(length, shift, ptr); } | |size_t printBufferShiftGen(C)(size_t length, size_t shift, scope C* ptr) @trusted |{ 0000000| size_t i; 0000000| do ptr[i] = ptr[shift + i]; 0000000| while(++i < length); 0000000| return length; |} | 0000000|size_t printSigned(int c, scope ref char[11] buf, char sign = '\0') { return printSignedGen(c, buf, sign); } 0000000|size_t printSigned(long c, scope ref char[21] buf, char sign = '\0') { return printSignedGen(c, buf, sign); } |static if (is(cent)) |size_t printSigned(cent c, scope ref char[40] buf, char sign = '\0') { return printSignedGen(c, buf, sign); } | 0000000|size_t printSigned(int c, scope ref wchar[11] buf, wchar sign = '\0') { return printSignedGen(c, buf, sign); } 0000000|size_t printSigned(long c, scope ref wchar[21] buf, wchar sign = '\0') { return printSignedGen(c, buf, sign); } |static if (is(cent)) |size_t printSigned(cent c, scope ref wchar[40] buf, wchar sign = '\0') { return printSignedGen(c, buf, sign); } | 0000000|size_t printSigned(int c, scope ref dchar[11] buf, dchar sign = '\0') { return printSignedGen(c, buf, sign); } 0000000|size_t printSigned(long c, scope ref dchar[21] buf, dchar sign = '\0') { return printSignedGen(c, buf, sign); } |static if (is(cent)) |size_t printSigned(cent c, scope ref dchar[40] buf, dchar sign = '\0') { return printSignedGen(c, buf, sign); } | | 112|size_t printSignedToTail(int c, scope ref char[11] buf, char sign = '\0') { return printSignedToTailGen(c, buf, sign); } 16|size_t printSignedToTail(long c, scope ref char[21] buf, char sign = '\0') { return printSignedToTailGen(c, buf, sign); } |static if (is(cent)) |size_t printSignedToTail(cent c, scope ref char[40] buf, char sign = '\0') { return printSignedToTailGen(c, buf, sign); } | 1|size_t printSignedToTail(int c, scope ref wchar[11] buf, wchar sign = '\0') { return printSignedToTailGen(c, buf, sign); } 0000000|size_t printSignedToTail(long c, scope ref wchar[21] buf, wchar sign = '\0') { return printSignedToTailGen(c, buf, sign); } |static if (is(cent)) |size_t printSignedToTail(cent c, scope ref wchar[40] buf, wchar sign = '\0') { return printSignedToTailGen(c, buf, sign); } | 0000000|size_t printSignedToTail(int c, scope ref dchar[11] buf, dchar sign = '\0') { return printSignedToTailGen(c, buf, sign); } 0000000|size_t printSignedToTail(long c, scope ref dchar[21] buf, dchar sign = '\0') { return printSignedToTailGen(c, buf, sign); } |static if (is(cent)) |size_t printSignedToTail(cent c, scope ref dchar[40] buf, dchar sign = '\0') { return printSignedToTailGen(c, buf, sign); } | |size_t printSignedGen(T, C, size_t N)(T c, scope ref C[N] buf, C sign) @trusted |{ 0000000| auto ret = printSignedToTail(c, buf, sign); 0000000| if (auto shift = buf.length - ret) | { 0000000| return printBufferShift(ret, shift, buf[].ptr); | } 0000000| return ret; |} | |size_t printSignedToTailGen(T, C, size_t N)(T c, scope ref C[N] buf, C sign) |{ 129| if (c < 0) | { 36| sign = '-'; 36| c = -c; | } | 129| auto ret = printUnsignedToTail(c, buf[1 .. N]); | 129| if (sign != '\0') | { 40| buf[$ - ++ret] = sign; | } 129| return ret; |} | 0000000|size_t printUnsigned(uint c, scope ref char[10] buf) { return printUnsignedGen(c, buf); } 0000000|size_t printUnsigned(ulong c, scope ref char[20] buf) { return printUnsignedGen(c, buf); } |static if (is(ucent)) |size_t printUnsigned(ucent c, scope ref char[39] buf) { return printUnsignedGen(c, buf); } | 0000000|size_t printUnsigned(uint c, scope ref wchar[10] buf) { return printUnsignedGen(c, buf); } 0000000|size_t printUnsigned(ulong c, scope ref wchar[20] buf) { return printUnsignedGen(c, buf); } |static if (is(ucent)) |size_t printUnsigned(ucent c, scope ref wchar[39] buf) { return printUnsignedGen(c, buf); } | 0000000|size_t printUnsigned(uint c, scope ref dchar[10] buf) { return printUnsignedGen(c, buf); } 0000000|size_t printUnsigned(ulong c, scope ref dchar[20] buf) { return printUnsignedGen(c, buf); } |static if (is(ucent)) |size_t printUnsigned(ucent c, scope ref dchar[39] buf) { return printUnsignedGen(c, buf); } | 360|size_t printUnsignedToTail(uint c, scope ref char[10] buf) { return printUnsignedToTailGen(c, buf); } 16|size_t printUnsignedToTail(ulong c, scope ref char[20] buf) { return printUnsignedToTailGen(c, buf); } |static if (is(ucent)) |size_t printUnsignedToTail(ucent c, scope ref char[39] buf) { return printUnsignedToTailGen(c, buf); } | 1|size_t printUnsignedToTail(uint c, scope ref wchar[10] buf) { return printUnsignedToTailGen(c, buf); } 0000000|size_t printUnsignedToTail(ulong c, scope ref wchar[20] buf) { return printUnsignedToTailGen(c, buf); } |static if (is(ucent)) |size_t printUnsignedToTail(ucent c, scope ref wchar[39] buf) { return printUnsignedToTailGen(c, buf); } | 1|size_t printUnsignedToTail(uint c, scope ref dchar[10] buf) { return printUnsignedToTailGen(c, buf); } 1|size_t printUnsignedToTail(ulong c, scope ref dchar[20] buf) { return printUnsignedToTailGen(c, buf); } |static if (is(ucent)) |size_t printUnsignedToTail(ucent c, scope ref dchar[39] buf) { return printUnsignedToTailGen(c, buf); } | |size_t printUnsignedToTailGen(T, C, size_t N)(T c, scope ref C[N] buf) @trusted |{ | static if (T.sizeof == 4) | { 362| if (c < 10) | { 172| buf[$ - 1] = cast(char)('0' + c); 172| return 1; | } | static assert(N == 10); | } | else | static if (T.sizeof == 8) | { 17| if (c <= uint.max) | { 17| return printUnsignedToTail(cast(uint)c, buf[$ - 10 .. $]); | } | static assert(N == 20); | } | else | static if (T.sizeof == 16) | { | if (c <= ulong.max) | { | return printUnsignedToTail(cast(ulong)c, buf[$ - 20 .. $]); | } | static assert(N == 39); | } | else | static assert(0); 190| size_t refLen = buf.length; | do { 547| T nc = c / 10; 547| buf[].ptr[--refLen] = cast(C)('0' + c - nc * 10); 547| c = nc; | } 547| while(c); 190| return buf.length - refLen; |} | |size_t printUnsignedGen(T, C, size_t N)(T c, scope ref C[N] buf) @trusted |{ 0000000| auto ret = printUnsignedToTail(c, buf); 0000000| if (auto shift = buf.length - ret) | { 0000000| return printBufferShift(ret, shift, buf[].ptr); | } 0000000| return ret; |} | |nothrow @trusted |size_t extendASCII(char* from, wchar* to, size_t n) |{ 0000000| foreach (i; 0 .. n) 0000000| to[i] = from[i]; 0000000| return n; |} | |nothrow @trusted |size_t extendASCII(char* from, dchar* to, size_t n) |{ 0000000| foreach (i; 0 .. n) 0000000| to[i] = from[i]; 0000000| return n; |} | |version (mir_test) unittest |{ | import mir.appender; | import mir.format; | 1| assert (stringBuf() << 123L << getData == "123"); | static assert (stringBuf() << 123 << getData == "123"); |} | |ref W printIntegralZeroImpl(C, size_t N, W, I)(scope return ref W w, I c, size_t zeroLen) |{ | static if (__traits(isUnsigned, I)) | alias impl = printUnsignedToTail; | else | alias impl = printSignedToTail; | C[N] buf = void; | size_t n = impl(c, buf); | static if (!__traits(isUnsigned, I)) | { | if (c < 0) | { | n--; | w.put(C('-')); | } | } | sizediff_t zeros = zeroLen - n; | if (zeros > 0) | { | do w.put(C('0')); | while(--zeros); | } | w.put(buf[$ - n .. $]); | return w; |} source/mir/format_impl.d is 25% covered <<<<<< EOF # path=./source-mir-timestamp.lst |/++ |Timestamp |+/ |module mir.timestamp; | 360|private alias isDigit = (dchar c) => uint(c - '0') < 10; |import mir.serde: serdeIgnore; | |version(D_Exceptions) |/// |class DateTimeException : Exception |{ | /// 0000000| @nogc @safe pure nothrow this(string msg, string file = __FILE__, size_t line = __LINE__, Throwable nextInChain = null) | { 0000000| super(msg, file, line, nextInChain); | } | | /// ditto 0000000| @nogc @safe pure nothrow this(string msg, Throwable nextInChain, string file = __FILE__, size_t line = __LINE__) | { 0000000| super(msg, file, line, nextInChain); | } |} | |version(D_Exceptions) |{ | private static immutable InvalidMonth = new DateTimeException("Invalid Month"); | private static immutable InvalidDay = new DateTimeException("Invalid Day"); | private static immutable InvalidISOString = new DateTimeException("Invalid ISO String"); | private static immutable InvalidISOExtendedString = new DateTimeException("Invalid ISO Extended String"); | private static immutable InvalidString = new DateTimeException("Invalid String"); |} | |/++ |Timestamp | |Note: The component values in the binary encoding are always in UTC, while components in the text encoding are in the local time! |This means that transcoding requires a conversion between UTC and local time. | |`Timestamp` precision is up to picosecond (second/10^12). |+/ |struct Timestamp |{ | import std.traits: isSomeChar; | | /// | enum Precision : ubyte | { | /// | year, | /// | month, | /// | day, | /// | minute, | /// | second, | /// | fraction, | } | | /// 8| this(scope const(char)[] str) @safe pure @nogc | { 8| this = fromString(str); | } | | /// | version (mir_test) | @safe pure @nogc unittest | { 1| assert(Timestamp("2010-07-04") == Timestamp(2010, 7, 4)); 1| assert(Timestamp("20100704") == Timestamp(2010, 7, 4)); 1| assert(Timestamp(2021, 01, 29, 12, 42, 44).withOffset(7 * 60 + 30) == Timestamp.fromISOString("20210129T201244+0730")); | static assert(Timestamp(2021, 01, 29, 4, 42, 44).withOffset(- (7 * 60 + 30)) == Timestamp.fromISOExtString("2021-01-28T21:12:44-07:30")); | 1| assert(Timestamp("T0740Z") == Timestamp.onlyTime(7, 40)); 1| assert(Timestamp("T074030Z") == Timestamp.onlyTime(7, 40, 30)); 1| assert(Timestamp("T074030.056Z") == Timestamp.onlyTime(7, 40, 30, -3, 56)); | 1| assert(Timestamp("07:40Z") == Timestamp.onlyTime(7, 40)); 1| assert(Timestamp("07:40:30Z") == Timestamp.onlyTime(7, 40, 30)); 1| assert(Timestamp("T07:40:30.056Z") == Timestamp.onlyTime(7, 40, 30, -3, 56)); | } | | version(all) | { | short offset; | } | else | /+ | If the time in UTC is known, but the offset to local time is unknown, this can be represented with an offset of “-00:00”. | This differs semantically from an offset of “Z” or “+00:00”, which imply that UTC is the preferred reference point for the specified time. | RFC2822 describes a similar convention for email. | private short _offset; | +/ | { | | /++ | Timezone offset in minutes | +/ | short offset() const @safe pure nothrow @nogc @property | { | return _offset >> 1; | } | | /++ | Returns: true if timezone has offset | +/ | bool hasOffset() const @safe pure nothrow @nogc @property | { | return _offset & 1; | } | } | |@serdeIgnore: | | /++ | Year | +/ | short year; | /++ | +/ | Precision precision; | | /++ | Month | | If the value equals to thero then this and all the following members are undefined. | +/ | ubyte month; | /++ | Day | | If the value equals to thero then this and all the following members are undefined. | +/ | ubyte day; | /++ | Hour | +/ | ubyte hour; | | version(D_Ddoc) | { | | /++ | Minute | | Note: the field is implemented as property. | +/ | ubyte minute; | /++ | Second | | Note: the field is implemented as property. | +/ | ubyte second; | /++ | Fraction | | The `fraction_exponent` and `fraction_coefficient` denote the fractional seconds of the timestamp as a decimal value | The fractional seconds’ value is `coefficient * 10 ^ exponent`. | It must be greater than or equal to zero and less than 1. | A missing coefficient defaults to zero. | Fractions whose coefficient is zero and exponent is greater than -1 are ignored. | | 'fractionCoefficient' allowed values are [0 ... 10^12-1]. | 'fractionExponent' allowed values are [-12 ... 0]. | | Note: the fields are implemented as property. | +/ | byte fractionExponent; | /// ditto | long fractionCoefficient; | } | else | { | import mir.bitmanip: bitfields; | version (LittleEndian) | { | | mixin(bitfields!( | ubyte, "minute", 8, | ubyte, "second", 8, | byte, "fractionExponent", 8, | long, "fractionCoefficient", 40, | )); | } | else | { | mixin(bitfields!( | long, "fractionCoefficient", 40, | byte, "fractionExponent", 8, | ubyte, "second", 8, | ubyte, "minute", 8, | )); | } | } | | /// | @safe pure nothrow @nogc 6| this(short year) | { 6| this.year = year; 6| this.precision = Precision.year; | } | | /// | @safe pure nothrow @nogc 4| this(short year, ubyte month) | { 4| this.year = year; 4| this.month = month; 4| this.precision = Precision.month; | } | | /// | @safe pure nothrow @nogc 62| this(short year, ubyte month, ubyte day) | { 62| this.year = year; 62| this.month = month; 62| this.day = day; 62| this.precision = Precision.day; | } | | /// | @safe pure nothrow @nogc 11| this(short year, ubyte month, ubyte day, ubyte hour, ubyte minute) | { 11| this.year = year; 11| this.month = month; 11| this.day = day; 11| this.hour = hour; 11| this.minute = minute; 11| this.precision = Precision.minute; | } | | /// | @safe pure nothrow @nogc 18| this(short year, ubyte month, ubyte day, ubyte hour, ubyte minute, ubyte second) | { 18| this.year = year; 18| this.month = month; 18| this.day = day; 18| this.hour = hour; 18| this.day = day; 18| this.minute = minute; 18| this.second = second; 18| this.precision = Precision.second; | } | | /// | @safe pure nothrow @nogc 8| this(short year, ubyte month, ubyte day, ubyte hour, ubyte minute, ubyte second, byte fractionExponent, ulong fractionCoefficient) | { 8| this.year = year; 8| this.month = month; 8| this.day = day; 8| this.hour = hour; 8| this.day = day; 8| this.minute = minute; 8| this.second = second; 8| assert(fractionExponent < 0); 8| this.fractionExponent = fractionExponent; 8| this.fractionCoefficient = fractionCoefficient; 8| this.precision = Precision.fraction; | } | | /// | @safe pure nothrow @nogc | static Timestamp onlyTime(ubyte hour, ubyte minute) | { 5| return Timestamp(0, 0, 0, hour, minute); | } | | /// | @safe pure nothrow @nogc | static Timestamp onlyTime(ubyte hour, ubyte minute, ubyte second) | { 6| return Timestamp(0, 0, 0, hour, minute, second); | } | | /// | @safe pure nothrow @nogc | static Timestamp onlyTime(ubyte hour, ubyte minute, ubyte second, byte fractionExponent, ulong fractionCoefficient) | { 5| return Timestamp(0, 0, 0, hour, minute, second, fractionExponent, fractionCoefficient); | } | | /// 2| this(Date)(const Date datetime) | if (Date.stringof == "Date" || Date.stringof == "date") | { | static if (__traits(hasMember, Date, "yearMonthDay")) 3| with(datetime.yearMonthDay) this(year, cast(ubyte)month, day); | else 2| with(datetime) this(year, month, day); | } | | /// | version (mir_test) | @safe unittest { | import mir.date : Date; 1| auto dt = Date(1982, 4, 1); 1| Timestamp ts = dt; 1| assert(ts.opCmp(ts) == 0); 1| assert(dt.toISOExtString == ts.toString); 1| assert(dt == cast(Date) ts); | } | | /// | version (mir_test) | @safe unittest { | import std.datetime.date : Date; 1| auto dt = Date(1982, 4, 1); 1| Timestamp ts = dt; 1| assert(dt.toISOExtString == ts.toString); 1| assert(dt == cast(Date) ts); | } | | /// 1| this(TimeOfDay)(const TimeOfDay timeOfDay) | if (TimeOfDay.stringof == "TimeOfDay") | { 2| with(timeOfDay) this = onlyTime(hour, minute, second); | } | | /// | version (mir_test) | @safe unittest { | import std.datetime.date : TimeOfDay; 1| auto dt = TimeOfDay(7, 14, 30); 1| Timestamp ts = dt; 1| assert(dt.toISOExtString ~ "Z" == ts.toString); 1| assert(dt == cast(TimeOfDay) ts); | } | | /// 1| this(DateTime)(const DateTime datetime) | if (DateTime.stringof == "DateTime") | { 2| with(datetime) this(year, cast(ubyte)month, day, hour, minute, second); | } | | /// | version (mir_test) | @safe unittest { | import std.datetime.date : DateTime; 1| auto dt = DateTime(1982, 4, 1, 20, 59, 22); 1| Timestamp ts = dt; 1| assert(dt.toISOExtString ~ "Z" == ts.toString); 1| assert(dt == cast(DateTime) ts); | } | | /// 1| this(SysTime)(const SysTime systime) | if (SysTime.stringof == "SysTime") | { 3| with(systime.toUTC) this(year, month, day, hour, minute, second, -7, fracSecs.total!"hnsecs"); 1| offset = cast(short) systime.utcOffset.total!"minutes"; | } | | /// | version (mir_test) | @safe unittest { | import core.time : hnsecs, minutes; | import std.datetime.date : DateTime; | import std.datetime.timezone : SimpleTimeZone; | import std.datetime.systime : SysTime; | 1| auto dt = DateTime(1982, 4, 1, 20, 59, 22); 1| auto tz = new immutable SimpleTimeZone(-330.minutes); 1| auto st = SysTime(dt, 1234567.hnsecs, tz); 1| Timestamp ts = st; | 1| assert(st.toISOExtString == ts.toString); 1| assert(st == cast(SysTime) ts); | } | | /// | T opCast(T)() const | if (T.stringof == "YearMonth" | || T.stringof == "YearMonthDay" | || T.stringof == "Date" | || T.stringof == "TimeOfDay" | || T.stringof == "date" | || T.stringof == "DateTime" | || T.stringof == "SysTime") | { | static if (T.stringof == "YearMonth") | { | return T(year, month, day); | } | else | static if (T.stringof == "Date" || T.stringof == "date" || T.stringof == "YearMonthDay") | { 2| return T(year, month, day); | } | else | static if (T.stringof == "DateTime") | { 1| return T(year, month, day, hour, minute, second); | } | else | static if (T.stringof == "TimeOfDay") | { 1| return T(hour, minute, second); | } | else | static if (T.stringof == "SysTime") | { | import core.time : hnsecs, minutes; | import std.datetime.date: DateTime; | import std.datetime.systime: SysTime; | import std.datetime.timezone: UTC, SimpleTimeZone; 1| auto ret = SysTime(DateTime(year, month, day, hour, minute, second), UTC()); 1| if (fractionCoefficient) | { 1| long coeff = fractionCoefficient; 1| int exp = fractionExponent; 1| while (exp > -7) | { 0000000| exp--; 0000000| coeff *= 10; | } 1| while (exp < -7) | { 0000000| exp++; 0000000| coeff /= 10; | } 1| ret.fracSecs = coeff.hnsecs; | } 1| if (offset) | { 1| ret = ret.toOtherTZ(new immutable SimpleTimeZone(offset.minutes)); | } 1| return ret; | } | } | | /++ | Returns: true if timestamp represent a time only value. | +/ | bool isOnlyTime() @property const @safe pure nothrow @nogc | { 91| return precision > Precision.day && day == 0; | } | | /// | int opCmp(Timestamp rhs) const @safe pure nothrow @nogc | { | import std.meta: AliasSeq; | static foreach (member; [ | "year", | "month", | "day", | "hour", | "minute", | "second", | ]) 6| if (auto d = int(__traits(getMember, this, member)) - int(__traits(getMember, rhs, member))) 0000000| return d; 1| int frel = this.fractionExponent; 1| int frer = rhs.fractionExponent; 1| ulong frcl = this.fractionCoefficient; 1| ulong frcr = rhs.fractionCoefficient; 1| while(frel > frer) | { 0000000| frel--; 0000000| frcl *= 10; | } 1| while(frer > frel) | { 0000000| frer--; 0000000| frcr *= 10; | } 1| if (frcl < frcr) return -1; 1| if (frcl > frcr) return +1; 1| if (auto d = int(this.fractionExponent) - int(rhs.fractionExponent)) 0000000| return d; 1| return int(this.offset) - int(rhs.offset); | } | | /++ | Attaches local offset, doesn't adjust other fields. | Local-time offsets may be represented as either `hour*60+minute` offsets from UTC, | or as the zero to denote a local time of UTC. They are required on timestamps with time and are not allowed on date values. | +/ | @safe pure nothrow @nogc const | Timestamp withOffset(short minutes) | { 18| assert(-24 * 60 <= minutes && minutes <= 24 * 60, "Offset absolute value should be less or equal to 24 * 60"); 9| assert(precision >= Precision.minute, "Offsets are not allowed on date values."); 9| Timestamp ret = this; 9| ret.offset = minutes; 9| return ret; | } | | version(D_BetterC){} else | private string toStringImpl(alias fun)() const @safe pure nothrow | { | import mir.appender: UnsafeArrayBuffer; 49| char[64] buffer = void; 49| auto w = UnsafeArrayBuffer!char(buffer); 49| fun(w); 49| return w.data.idup; | } | | /++ | Converts this $(LREF Timestamp) to a string with the format `YYYY-MM-DDThh:mm:ss±hh:mm`. | | If `w` writer is set, the resulting string will be written directly | to it. | | Returns: | A `string` when not using an output range; `void` otherwise. | +/ | alias toString = toISOExtString; | | /// | version (mir_test) | @safe pure nothrow unittest | { 1| assert(Timestamp.init.toString == "0000T"); 1| assert(Timestamp(2010, 7, 4).toString == "2010-07-04"); 1| assert(Timestamp(1998, 12, 25).toString == "1998-12-25"); 1| assert(Timestamp(0, 1, 5).toString == "0000-01-05"); 1| assert(Timestamp(-4, 1, 5).toString == "-0004-01-05"); | | // YYYY-MM-DDThh:mm:ss±hh:mm 1| assert(Timestamp(2021).toString == "2021T"); 1| assert(Timestamp(2021, 01).toString == "2021-01T", Timestamp(2021, 01).toString); 1| assert(Timestamp(2021, 01, 29).toString == "2021-01-29"); 1| assert(Timestamp(2021, 01, 29, 19, 42).toString == "2021-01-29T19:42Z"); 1| assert(Timestamp(2021, 01, 29, 12, 42, 44).withOffset(7 * 60).toString == "2021-01-29T19:42:44+07", Timestamp(2021, 01, 29, 12, 42, 44).withOffset(7 * 60).toString); 1| assert(Timestamp(2021, 01, 29, 12, 42, 44).withOffset(7 * 60 + 30).toString == "2021-01-29T20:12:44+07:30"); | 1| assert(Timestamp.onlyTime(7, 40).toString == "07:40Z"); 1| assert(Timestamp.onlyTime(7, 40, 30).toString == "07:40:30Z"); 1| assert(Timestamp.onlyTime(7, 40, 30, -3, 56).toString == "07:40:30.056Z"); | } | | /// | version (mir_test) | @safe unittest | { | // Test A.D. 1| assert(Timestamp(9, 12, 4).toISOExtString == "0009-12-04"); 1| assert(Timestamp(99, 12, 4).toISOExtString == "0099-12-04"); 1| assert(Timestamp(999, 12, 4).toISOExtString == "0999-12-04"); 1| assert(Timestamp(9999, 7, 4).toISOExtString == "9999-07-04"); 1| assert(Timestamp(10000, 10, 20).toISOExtString == "+10000-10-20"); | | // Test B.C. 1| assert(Timestamp(0, 12, 4).toISOExtString == "0000-12-04"); 1| assert(Timestamp(-9, 12, 4).toISOExtString == "-0009-12-04"); 1| assert(Timestamp(-99, 12, 4).toISOExtString == "-0099-12-04"); 1| assert(Timestamp(-999, 12, 4).toISOExtString == "-0999-12-04"); 1| assert(Timestamp(-9999, 7, 4).toISOExtString == "-9999-07-04"); 1| assert(Timestamp(-10000, 10, 20).toISOExtString == "-10000-10-20"); | 1| assert(Timestamp.onlyTime(7, 40).toISOExtString == "07:40Z"); 1| assert(Timestamp.onlyTime(7, 40, 30).toISOExtString == "07:40:30Z"); 1| assert(Timestamp.onlyTime(7, 40, 30, -3, 56).toISOExtString == "07:40:30.056Z"); | 1| const cdate = Timestamp(1999, 7, 6); 1| immutable idate = Timestamp(1999, 7, 6); 1| assert(cdate.toISOExtString == "1999-07-06"); 1| assert(idate.toISOExtString == "1999-07-06"); | } | | /// ditto | alias toISOExtString = toISOStringImp!true; | | /++ | Converts this $(LREF Timestamp) to a string with the format `YYYYMMDDThhmmss±hhmm`. | | If `w` writer is set, the resulting string will be written directly | to it. | | Returns: | A `string` when not using an output range; `void` otherwise. | +/ | alias toISOString = toISOStringImp!false; | | /// | version (mir_test) | @safe pure nothrow unittest | { 1| assert(Timestamp.init.toISOString == "0000T"); 1| assert(Timestamp(2010, 7, 4).toISOString == "20100704"); 1| assert(Timestamp(1998, 12, 25).toISOString == "19981225"); 1| assert(Timestamp(0, 1, 5).toISOString == "00000105"); 1| assert(Timestamp(-4, 1, 5).toISOString == "-00040105"); | | // YYYYMMDDThhmmss±hhmm 1| assert(Timestamp(2021).toISOString == "2021T"); 1| assert(Timestamp(2021, 01).toISOString == "2021-01T"); // always extended 1| assert(Timestamp(2021, 01, 29).toISOString == "20210129"); 1| assert(Timestamp(2021, 01, 29, 19, 42).toISOString == "20210129T1942Z"); 1| assert(Timestamp(2021, 01, 29, 12, 42, 44).withOffset(7 * 60).toISOString == "20210129T194244+07"); 1| assert(Timestamp(2021, 01, 29, 12, 42, 44).withOffset(7 * 60 + 30).toISOString == "20210129T201244+0730"); | static assert(Timestamp(2021, 01, 29, 12, 42, 44).withOffset(7 * 60 + 30).toISOString == "20210129T201244+0730"); | 1| assert(Timestamp.onlyTime(7, 40).toISOString == "T0740Z"); 1| assert(Timestamp.onlyTime(7, 40, 30).toISOString == "T074030Z"); 1| assert(Timestamp.onlyTime(7, 40, 30, -3, 56).toISOString == "T074030.056Z"); | } | | /// Helpfer for time zone offsets | void addMinutes(short minutes) @safe pure nothrow @nogc | { 10| int totalMinutes = minutes + (this.minute + this.hour * 60u); 10| auto h = totalMinutes / 60; | 10| int dayShift; | 11| while (totalMinutes < 0) | { 1| totalMinutes += 24 * 60; 1| dayShift--; | } | 10| while (totalMinutes >= 24 * 60) | { 0000000| totalMinutes -= 24 * 60; 0000000| dayShift++; | } | 10| if (dayShift) | { | import mir.date: Date; 1| auto ymd = (Date.trustedCreate(year, month, day) + dayShift).yearMonthDay; 1| year = ymd.year; 1| month = cast(ubyte)ymd.month; 1| day = ymd.day; | } | 10| hour = cast(ubyte) (totalMinutes / 60); 10| minute = cast(ubyte) (totalMinutes % 60); | } | | template toISOStringImp(bool ext) | { | version(D_BetterC){} else | string toISOStringImp() const @safe pure nothrow | { 49| return toStringImpl!toISOStringImp; | } | | /// ditto | void toISOStringImp(W)(scope ref W w) const scope | // if (isOutputRange!(W, char)) | { | import mir.format: printZeroPad; | // YYYY-MM-DDThh:mm:ss±hh:mm 49| Timestamp t = this; | 49| if (t.offset) | { 10| assert(-24 * 60 <= t.offset && t.offset <= 24 * 60, "Offset absolute value should be less or equal to 24 * 60"); 5| assert(precision >= Precision.minute, "Offsets are not allowed on date values."); 5| t.addMinutes(t.offset); | } | 49| if (!t.isOnlyTime) | { 39| if (t.year >= 10_000) 1| w.put('+'); 117| printZeroPad(w, t.year, t.year >= 0 ? t.year < 10_000 ? 4 : 5 : t.year > -10_000 ? 5 : 6); 39| if (precision == Precision.year) | { 4| w.put('T'); 4| return; | } 62| if (ext || precision == Precision.month) w.put('-'); | 35| printZeroPad(w, cast(uint)t.month, 2); 35| if (precision == Precision.month) | { 2| w.put('T'); 2| return; | } 25| static if (ext) w.put('-'); | 33| printZeroPad(w, t.day, 2); 33| if (precision == Precision.day) 25| return; | } | 18| if (!ext || !t.isOnlyTime) 11| w.put('T'); | 18| printZeroPad(w, t.hour, 2); 12| static if (ext) w.put(':'); 18| printZeroPad(w, t.minute, 2); | 18| if (precision >= Precision.second) | { 9| static if (ext) w.put(':'); 13| printZeroPad(w, t.second, 2); | 17| if (precision > Precision.second && (t.fractionExponent < 0 || t.fractionCoefficient)) | { 4| w.put('.'); 4| printZeroPad(w, t.fractionCoefficient, -int(t.fractionExponent)); | } | } | 18| if (t.offset == 0) | { 13| w.put('Z'); 13| return; | } | 5| bool sign = t.offset < 0; 10| uint absoluteOffset = !sign ? t.offset : -int(t.offset); 5| uint offsetHour = absoluteOffset / 60u; 5| uint offsetMinute = absoluteOffset % 60u; | 6| w.put(sign ? '-' : '+'); 5| printZeroPad(w, offsetHour, 2); 5| if (offsetMinute) | { 2| static if (ext) w.put(':'); 3| printZeroPad(w, offsetMinute, 2); | } | } | } | | /++ | Creates a $(LREF Timestamp) from a string with the format `YYYYMMDDThhmmss±hhmm | or its leading part allowed by the standard. | | or its leading part allowed by the standard. | | Params: | str = A string formatted in the way that $(LREF .Timestamp.toISOExtString) formats dates. | value = (optional) result value. | | Throws: | $(LREF DateTimeException) if the given string is | not in the correct format. Two arguments overload is `nothrow`. | Returns: | `bool` on success for two arguments overload, and the resulting timestamp for single argument overdload. | +/ | alias fromISOString = fromISOStringImpl!false; | | /// | version (mir_test) | @safe unittest | { 1| assert(Timestamp.fromISOString("20100704") == Timestamp(2010, 7, 4)); 1| assert(Timestamp.fromISOString("19981225") == Timestamp(1998, 12, 25)); 1| assert(Timestamp.fromISOString("00000105") == Timestamp(0, 1, 5)); | // assert(Timestamp.fromISOString("-00040105") == Timestamp(-4, 1, 5)); | 1| assert(Timestamp(2021) == Timestamp.fromISOString("2021")); 1| assert(Timestamp(2021) == Timestamp.fromISOString("2021T")); | // assert(Timestamp(2021, 01) == Timestamp.fromISOString("2021-01")); | // assert(Timestamp(2021, 01) == Timestamp.fromISOString("2021-01T")); 1| assert(Timestamp(2021, 01, 29) == Timestamp.fromISOString("20210129")); 1| assert(Timestamp(2021, 01, 29, 19, 42) == Timestamp.fromISOString("20210129T1942")); 1| assert(Timestamp(2021, 01, 29, 19, 42) == Timestamp.fromISOString("20210129T1942Z")); 1| assert(Timestamp(2021, 01, 29, 19, 42, 12) == Timestamp.fromISOString("20210129T194212")); 1| assert(Timestamp(2021, 01, 29, 19, 42, 12, -3, 67) == Timestamp.fromISOString("20210129T194212.067Z")); 1| assert(Timestamp(2021, 01, 29, 12, 42, 44).withOffset(7 * 60) == Timestamp.fromISOString("20210129T194244+07")); 1| assert(Timestamp(2021, 01, 29, 12, 42, 44).withOffset(7 * 60 + 30) == Timestamp.fromISOString("20210129T201244+0730")); | static assert(Timestamp(2021, 01, 29, 12, 42, 44).withOffset(7 * 60 + 30) == Timestamp.fromISOString("20210129T201244+0730")); | static assert(Timestamp(2021, 01, 29, 4, 42, 44).withOffset(- (7 * 60 + 30)) == Timestamp.fromISOString("20210128T211244-0730")); | } | | version (mir_test) | @safe unittest | { | import std.exception: assertThrown; 2| assertThrown!DateTimeException(Timestamp.fromISOString("")); 2| assertThrown!DateTimeException(Timestamp.fromISOString("990704")); 2| assertThrown!DateTimeException(Timestamp.fromISOString("0100704")); 2| assertThrown!DateTimeException(Timestamp.fromISOString("2010070")); 2| assertThrown!DateTimeException(Timestamp.fromISOString("120100704")); 2| assertThrown!DateTimeException(Timestamp.fromISOString("-0100704")); 2| assertThrown!DateTimeException(Timestamp.fromISOString("+0100704")); 2| assertThrown!DateTimeException(Timestamp.fromISOString("2010070a")); 2| assertThrown!DateTimeException(Timestamp.fromISOString("20100a04")); 2| assertThrown!DateTimeException(Timestamp.fromISOString("2010a704")); | 2| assertThrown!DateTimeException(Timestamp.fromISOString("99-07-04")); 2| assertThrown!DateTimeException(Timestamp.fromISOString("010-07-04")); 2| assertThrown!DateTimeException(Timestamp.fromISOString("2010-07-0")); 2| assertThrown!DateTimeException(Timestamp.fromISOString("12010-07-04")); 2| assertThrown!DateTimeException(Timestamp.fromISOString("-010-07-04")); 2| assertThrown!DateTimeException(Timestamp.fromISOString("+010-07-04")); 2| assertThrown!DateTimeException(Timestamp.fromISOString("2010-07-0a")); 2| assertThrown!DateTimeException(Timestamp.fromISOString("2010-0a-04")); 2| assertThrown!DateTimeException(Timestamp.fromISOString("2010-a7-04")); 2| assertThrown!DateTimeException(Timestamp.fromISOString("2010/07/04")); 2| assertThrown!DateTimeException(Timestamp.fromISOString("2010/7/04")); 2| assertThrown!DateTimeException(Timestamp.fromISOString("2010/7/4")); 2| assertThrown!DateTimeException(Timestamp.fromISOString("2010/07/4")); 2| assertThrown!DateTimeException(Timestamp.fromISOString("2010-7-04")); 2| assertThrown!DateTimeException(Timestamp.fromISOString("2010-7-4")); 2| assertThrown!DateTimeException(Timestamp.fromISOString("2010-07-4")); | 2| assertThrown!DateTimeException(Timestamp.fromISOString("99Jul04")); 2| assertThrown!DateTimeException(Timestamp.fromISOString("010Jul04")); 2| assertThrown!DateTimeException(Timestamp.fromISOString("2010Jul0")); 2| assertThrown!DateTimeException(Timestamp.fromISOString("12010Jul04")); 2| assertThrown!DateTimeException(Timestamp.fromISOString("-010Jul04")); 2| assertThrown!DateTimeException(Timestamp.fromISOString("+010Jul04")); 2| assertThrown!DateTimeException(Timestamp.fromISOString("2010Jul0a")); 2| assertThrown!DateTimeException(Timestamp.fromISOString("2010Jua04")); 2| assertThrown!DateTimeException(Timestamp.fromISOString("2010aul04")); | 2| assertThrown!DateTimeException(Timestamp.fromISOString("99-Jul-04")); 2| assertThrown!DateTimeException(Timestamp.fromISOString("010-Jul-04")); 2| assertThrown!DateTimeException(Timestamp.fromISOString("2010-Jul-0")); 2| assertThrown!DateTimeException(Timestamp.fromISOString("12010-Jul-04")); 2| assertThrown!DateTimeException(Timestamp.fromISOString("-010-Jul-04")); 2| assertThrown!DateTimeException(Timestamp.fromISOString("+010-Jul-04")); 2| assertThrown!DateTimeException(Timestamp.fromISOString("2010-Jul-0a")); 2| assertThrown!DateTimeException(Timestamp.fromISOString("2010-Jua-04")); 2| assertThrown!DateTimeException(Timestamp.fromISOString("2010-Jal-04")); 2| assertThrown!DateTimeException(Timestamp.fromISOString("2010-aul-04")); | | // assertThrown!DateTimeException(Timestamp.fromISOString("2010-07-04")); 2| assertThrown!DateTimeException(Timestamp.fromISOString("2010-Jul-04")); | 1| assert(Timestamp.fromISOString("19990706") == Timestamp(1999, 7, 6)); | // assert(Timestamp.fromISOString("-19990706") == Timestamp(-1999, 7, 6)); | // assert(Timestamp.fromISOString("+019990706") == Timestamp(1999, 7, 6)); 1| assert(Timestamp.fromISOString("19990706") == Timestamp(1999, 7, 6)); | } | | // bug# 17801 | version (mir_test) | @safe unittest | { | import std.conv : to; | import std.meta : AliasSeq; | static foreach (C; AliasSeq!(char, wchar, dchar)) | { | static foreach (S; AliasSeq!(C[], const(C)[], immutable(C)[])) 9| assert(Timestamp.fromISOString(to!S("20121221")) == Timestamp(2012, 12, 21)); | } | } | | /++ | Creates a $(LREF Timestamp) from a string with the format `YYYY-MM-DDThh:mm:ss±hh:mm` | or its leading part allowed by the standard. | | | Params: | str = A string formatted in the way that $(LREF .Timestamp.toISOExtString) formats dates. | value = (optional) result value. | | Throws: | $(LREF DateTimeException) if the given string is | not in the correct format. Two arguments overload is `nothrow`. | Returns: | `bool` on success for two arguments overload, and the resulting timestamp for single argument overdload. | +/ | alias fromISOExtString = fromISOStringImpl!true; | | | /// | version (mir_test) | @safe unittest | { 1| assert(Timestamp.fromISOExtString("2010-07-04") == Timestamp(2010, 7, 4)); 1| assert(Timestamp.fromISOExtString("1998-12-25") == Timestamp(1998, 12, 25)); 1| assert(Timestamp.fromISOExtString("0000-01-05") == Timestamp(0, 1, 5)); 1| assert(Timestamp.fromISOExtString("-0004-01-05") == Timestamp(-4, 1, 5)); | 1| assert(Timestamp(2021) == Timestamp.fromISOExtString("2021")); 1| assert(Timestamp(2021) == Timestamp.fromISOExtString("2021T")); 1| assert(Timestamp(2021, 01) == Timestamp.fromISOExtString("2021-01")); 1| assert(Timestamp(2021, 01) == Timestamp.fromISOExtString("2021-01T")); 1| assert(Timestamp(2021, 01, 29) == Timestamp.fromISOExtString("2021-01-29")); 1| assert(Timestamp(2021, 01, 29, 19, 42) == Timestamp.fromISOExtString("2021-01-29T19:42")); 1| assert(Timestamp(2021, 01, 29, 19, 42) == Timestamp.fromISOExtString("2021-01-29T19:42Z")); 1| assert(Timestamp(2021, 01, 29, 19, 42, 12) == Timestamp.fromISOExtString("2021-01-29T19:42:12")); 1| assert(Timestamp(2021, 01, 29, 19, 42, 12, -3, 67) == Timestamp.fromISOExtString("2021-01-29T19:42:12.067Z")); 1| assert(Timestamp(2021, 01, 29, 12, 42, 44).withOffset(7 * 60) == Timestamp.fromISOExtString("2021-01-29T19:42:44+07")); 1| assert(Timestamp(2021, 01, 29, 12, 42, 44).withOffset(7 * 60 + 30) == Timestamp.fromISOExtString("2021-01-29T20:12:44+07:30")); | static assert(Timestamp(2021, 01, 29, 12, 42, 44).withOffset(7 * 60 + 30) == Timestamp.fromISOExtString("2021-01-29T20:12:44+07:30")); | static assert(Timestamp(2021, 01, 29, 4, 42, 44).withOffset(- (7 * 60 + 30)) == Timestamp.fromISOExtString("2021-01-28T21:12:44-07:30")); | } | | version (mir_test) | @safe unittest | { | import std.exception: assertThrown; | 2| assertThrown!DateTimeException(Timestamp.fromISOExtString("")); 2| assertThrown!DateTimeException(Timestamp.fromISOExtString("990704")); 2| assertThrown!DateTimeException(Timestamp.fromISOExtString("0100704")); 2| assertThrown!DateTimeException(Timestamp.fromISOExtString("120100704")); 2| assertThrown!DateTimeException(Timestamp.fromISOExtString("-0100704")); 2| assertThrown!DateTimeException(Timestamp.fromISOExtString("+0100704")); 2| assertThrown!DateTimeException(Timestamp.fromISOExtString("2010070a")); 2| assertThrown!DateTimeException(Timestamp.fromISOExtString("20100a04")); 2| assertThrown!DateTimeException(Timestamp.fromISOExtString("2010a704")); | 2| assertThrown!DateTimeException(Timestamp.fromISOExtString("99-07-04")); 2| assertThrown!DateTimeException(Timestamp.fromISOExtString("010-07-04")); 2| assertThrown!DateTimeException(Timestamp.fromISOExtString("2010-07-0")); 2| assertThrown!DateTimeException(Timestamp.fromISOExtString("12010-07-04")); 2| assertThrown!DateTimeException(Timestamp.fromISOExtString("-010-07-04")); 2| assertThrown!DateTimeException(Timestamp.fromISOExtString("+010-07-04")); 2| assertThrown!DateTimeException(Timestamp.fromISOExtString("2010-07-0a")); 2| assertThrown!DateTimeException(Timestamp.fromISOExtString("2010-0a-04")); 2| assertThrown!DateTimeException(Timestamp.fromISOExtString("2010-a7-04")); 2| assertThrown!DateTimeException(Timestamp.fromISOExtString("2010/07/04")); 2| assertThrown!DateTimeException(Timestamp.fromISOExtString("2010/7/04")); 2| assertThrown!DateTimeException(Timestamp.fromISOExtString("2010/7/4")); 2| assertThrown!DateTimeException(Timestamp.fromISOExtString("2010/07/4")); 2| assertThrown!DateTimeException(Timestamp.fromISOExtString("2010-7-04")); 2| assertThrown!DateTimeException(Timestamp.fromISOExtString("2010-7-4")); 2| assertThrown!DateTimeException(Timestamp.fromISOExtString("2010-07-4")); | 2| assertThrown!DateTimeException(Timestamp.fromISOExtString("99Jul04")); 2| assertThrown!DateTimeException(Timestamp.fromISOExtString("010Jul04")); 2| assertThrown!DateTimeException(Timestamp.fromISOExtString("2010Jul0")); 2| assertThrown!DateTimeException(Timestamp.fromISOExtString("12010Jul04")); 2| assertThrown!DateTimeException(Timestamp.fromISOExtString("-010Jul04")); 2| assertThrown!DateTimeException(Timestamp.fromISOExtString("+010Jul04")); 2| assertThrown!DateTimeException(Timestamp.fromISOExtString("2010Jul0a")); 2| assertThrown!DateTimeException(Timestamp.fromISOExtString("2010Jua04")); 2| assertThrown!DateTimeException(Timestamp.fromISOExtString("2010aul04")); | 2| assertThrown!DateTimeException(Timestamp.fromISOExtString("99-Jul-04")); 2| assertThrown!DateTimeException(Timestamp.fromISOExtString("010-Jul-04")); 2| assertThrown!DateTimeException(Timestamp.fromISOExtString("2010-Jul-0")); 2| assertThrown!DateTimeException(Timestamp.fromISOExtString("12010-Jul-04")); 2| assertThrown!DateTimeException(Timestamp.fromISOExtString("-010-Jul-04")); 2| assertThrown!DateTimeException(Timestamp.fromISOExtString("+010-Jul-04")); 2| assertThrown!DateTimeException(Timestamp.fromISOExtString("2010-Jul-0a")); 2| assertThrown!DateTimeException(Timestamp.fromISOExtString("2010-Jua-04")); 2| assertThrown!DateTimeException(Timestamp.fromISOExtString("2010-Jal-04")); 2| assertThrown!DateTimeException(Timestamp.fromISOExtString("2010-aul-04")); | 2| assertThrown!DateTimeException(Timestamp.fromISOExtString("20100704")); 2| assertThrown!DateTimeException(Timestamp.fromISOExtString("2010-Jul-04")); | 1| assert(Timestamp.fromISOExtString("1999-07-06") == Timestamp(1999, 7, 6)); 1| assert(Timestamp.fromISOExtString("-1999-07-06") == Timestamp(-1999, 7, 6)); 1| assert(Timestamp.fromISOExtString("+01999-07-06") == Timestamp(1999, 7, 6)); | } | | // bug# 17801 | version (mir_test) | @safe unittest | { | import std.conv : to; | import std.meta : AliasSeq; | static foreach (C; AliasSeq!(char, wchar, dchar)) | { | static foreach (S; AliasSeq!(C[], const(C)[], immutable(C)[])) 9| assert(Timestamp.fromISOExtString(to!S("2012-12-21")) == Timestamp(2012, 12, 21)); | } | } | | /++ | Creates a $(LREF Timestamp) from a string with the format YYYY-MM-DD, YYYYMMDD, or YYYY-Mon-DD. | | Params: | str = A string formatted in the way that $(LREF .Timestamp.toISOExtString) and $(LREF .Timestamp.toISOString) format dates. The function is case sensetive. | value = (optional) result value. | | Throws: | $(LREF DateTimeException) if the given string is | not in the correct format. Two arguments overload is `nothrow`. | Returns: | `bool` on success for two arguments overload, and the resulting timestamp for single argument overdload. | +/ | static bool fromString(C)(scope const(C)[] str, out Timestamp value) @safe pure nothrow @nogc | { 10| return fromISOExtString(str, value) 5| || fromISOString(str, value); | } | | /// | version (mir_test) | @safe pure @nogc unittest | { 1| assert(Timestamp.fromString("2010-07-04") == Timestamp(2010, 7, 4)); 1| assert(Timestamp.fromString("20100704") == Timestamp(2010, 7, 4)); | } | | /// ditto | static Timestamp fromString(C)(scope const(C)[] str) @safe pure | if (isSomeChar!C) | { 10| Timestamp ret; 10| if (fromString(str, ret)) 10| return ret; 0000000| throw InvalidString; | } | | template fromISOStringImpl(bool ext) | { | static Timestamp fromISOStringImpl(C)(scope const(C)[] str) @safe pure | if (isSomeChar!C) | { 143| Timestamp ret; 143| if (fromISOStringImpl(str, ret)) 51| return ret; 92| throw InvalidISOExtendedString; | } | | static bool fromISOStringImpl(C)(scope const(C)[] str, out Timestamp value) @safe pure nothrow @nogc | if (isSomeChar!C) | { | import mir.parse: fromString, parse; | | static if (ext) 243| auto isOnlyTime = str.length >= 3 && (str[0] == 'T' || str[2] == ':'); | else 149| auto isOnlyTime = str.length >= 3 && str[0] == 'T'; | 158| if (!isOnlyTime) | { | // YYYY | static if (ext) | {{ 153| auto startIsDigit = str.length && str[0].isDigit; 77| auto strOldLength = str.length; 77| if (!parse(str, value.year)) 10| return false; 67| auto l = strOldLength - str.length; 67| if ((l == 4) != startIsDigit) 16| return false; | }} | else | { 206| if (str.length < 4 || !str[0].isDigit || !fromString(str[0 .. 4], value.year)) 15| return false; 57| str = str[4 .. $]; | } | 108| value.precision = Precision.year; 214| if (str.length == 0 || str == "T") 4| return true; | | static if (ext) | { 49| if (str[0] != '-') 9| return false; 40| str = str[1 .. $]; | } | | // MM 256| if (str.length < 2 || !str[0].isDigit || !fromString(str[0 .. 2], value.month)) 36| return false; 59| str = str[2 .. $]; 59| value.precision = Precision.month; 116| if (str.length == 0 || str == "T") 3| return ext; | | static if (ext) | { 28| if (str[0] != '-') 0000000| return false; 28| str = str[1 .. $]; | } | | // DD 160| if (str.length < 2 || !str[0].isDigit || !fromString(str[0 .. 2], value.day)) 6| return false; 50| str = str[2 .. $]; 50| value.precision = Precision.day; 50| if (str.length == 0) 36| return true; | } | | // str isn't empty here | // T 23| if (str[0] == 'T') | { 20| str = str[1 .. $]; | // OK, onlyTime requires length >= 3 20| if (str.length == 0) 0000000| return true; | } | else | { 3| if (!(ext && isOnlyTime)) 1| return false; | } | 22| value.precision = Precision.minute; // we don't have hour precision | | // hh 66| if (str.length < 2 || !str[0].isDigit || !fromString(str[0 .. 2], value.hour)) 0000000| return false; 22| str = str[2 .. $]; 22| if (str.length == 0) 0000000| return true; | | static if (ext) | { 12| if (str[0] != ':') 3| return false; 9| str = str[1 .. $]; | } | | // mm | { 19| uint minute; 57| if (str.length < 2 || !str[0].isDigit || !fromString(str[0 .. 2], minute)) 0000000| return false; 19| value.minute = cast(ubyte) minute; 19| str = str[2 .. $]; 19| if (str.length == 0) 2| return true; | } | | static if (ext) | { 8| if (str[0] != ':') 2| goto TZ; 6| str = str[1 .. $]; | } | | // ss | { 15| uint second; 28| if (str.length < 2 || !str[0].isDigit) 2| goto TZ; 13| if (!fromString(str[0 .. 2], second)) 0000000| return false; 13| value.second = cast(ubyte) second; 13| str = str[2 .. $]; 13| value.precision = Precision.second; 13| if (str.length == 0) 2| return true; | } | | // . 11| if (str[0] != '.') 7| goto TZ; 4| str = str[1 .. $]; 4| value.precision = Precision.fraction; | | // fraction | { 4| const strOldLength = str.length; 4| ulong fractionCoefficient; 12| if (str.length < 1 || !str[0].isDigit || !parse!ulong(str, fractionCoefficient)) 0000000| return false; 4| sizediff_t fractionExponent = str.length - strOldLength; 4| if (fractionExponent < -12) 0000000| return false; 4| value.fractionExponent = cast(byte)fractionExponent; 4| value.fractionCoefficient = fractionCoefficient; 4| if (str.length == 0) 0000000| return true; | } | | TZ: | 15| if (str == "Z") 10| return true; | 5| int hour; 5| int minute; 15| if (str.length < 3 || str[0].isDigit || !fromString(str[0 .. 3], hour)) 0000000| return false; 5| str = str[3 .. $]; | 5| if (str.length) | { | static if (ext) | { 1| if (str[0] != ':') 0000000| return false; 1| str = str[1 .. $]; | } 9| if (str.length != 2 || !str[0].isDigit || !fromString(str[0 .. 2], minute)) 0000000| return false; | } | 10| value.offset = cast(short)(hour * 60 + (hour < 0 ? -minute : minute)); 5| value.addMinutes(cast(short)-int(value.offset)); 5| return true; | } | } |} source/mir/timestamp.d is 93% covered <<<<<< EOF # path=./source-mir-ndslice-package.lst |/+ |## Guide for Slice/BLAS contributors | |1. Make sure functions are | a. inlined(!), | b. `@nogc`, | c. `nothrow`, | d. `pure`. | For this reason, it is preferable to use _simple_ `assert`s with messages | that can be computed at compile time. | The goals are: | 1. to reduce executable size for _any_ compilation mode | 2. to reduce template bloat in object files | 3. to reduce compilation time | 4. to allow users to write extern C bindings for code libraries on `Slice` type. | |2. `std.format`, `std.string`, and `std.conv` should not be used in error | message formatting.`"Use" ~ Concatenation.stringof`. | |3. `mixin template`s may be used for pretty error message formatting. | |4. `Exception`s/`enforce`s should no be used to check indices and lengths. | Exceptions are only allowed for algorithms where validation of input data is | too complicated for the user. `reshape` function is a good example of a case | where Exceptions are required. | If a function might throw an exception, an example with exception handing should be added. | |5. For simple checks like matrix transposition, compile time flags should not be used. | It is much better to opt for runtime matrix transposition. | Furthermore, Slice type provides runtime matrix transposition out of the box. | |6. _Fortran_VS_C_ flags should not be used. They are about notation, | but not about the algorithm itself. For math world users, | a corresponding code example might be included in the documentation. | `transposed` / `everted` can be used in cache-friendly codes. | |7. Compile time evaluation should not be used to produce dummy types like `IdentityMatrix`. | |8. Memory allocation and algorithm logic should be separated whenever possible. | |9. CTFE version(mir_test) unittests should be added to new functions. |+/ | |/** |$(H1 Multidimensional Random Access Ranges) | |The package provides a multidimensional array implementation. |It would be well suited to creating machine learning and image |processing algorithms, but should also be general enough for use anywhere with |homogeneously-typed multidimensional data. |In addition, it includes various functions for iteration, accessing, and manipulation. | |Quick_Start: |$(SUBREF slice, sliced) is a function designed to create |a multidimensional view over a range. |Multidimensional view is presented by $(SUBREF slice, Slice) type. | |------ |import mir.ndslice; | |auto matrix = slice!double(3, 4); |matrix[] = 0; |matrix.diagonal[] = 1; | |auto row = matrix[2]; |row[3] = 6; |assert(matrix[2, 3] == 6); // D & C index order |------ | |Note: |In many examples $(REF iota, mir,_ndslice,topology) is used |instead of a regular array, which makes it |possible to carry out tests without memory allocation. | |$(SCRIPT inhibitQuickIndex = 1;) | |$(DIVC quickindex, |$(BOOKTABLE, | |$(TR $(TH Submodule) $(TH Declarations)) | |$(TR $(TDNW $(SUBMODULE slice) $(BR) | $(SMALL $(SUBREF slice, Slice) structure | $(BR) Basic constructors)) | $(TD | $(SUBREF slice, Canonical) | $(SUBREF slice, Contiguous) | $(SUBREF slice, DeepElementType) | $(SUBREF slice, isSlice) | $(SUBREF slice, kindOf) | $(SUBREF slice, Slice) | $(SUBREF slice, sliced) | $(SUBREF slice, slicedField) | $(SUBREF slice, slicedNdField) | $(SUBREF slice, SliceKind) | $(SUBREF slice, Structure) | $(SUBREF slice, Universal) | ) |) | |$(TR $(TDNW $(SUBMODULE allocation) $(BR) | $(SMALL Allocation utilities)) | $(TD | $(SUBREF allocation, bitRcslice) | $(SUBREF allocation, bitSlice) | $(SUBREF allocation, makeNdarray) | $(SUBREF allocation, makeSlice) | $(SUBREF allocation, makeUninitSlice) | $(SUBREF allocation, mininitRcslice) | $(SUBREF allocation, ndarray) | $(SUBREF allocation, rcslice) | $(SUBREF allocation, shape) | $(SUBREF allocation, slice) | $(SUBREF allocation, stdcFreeAlignedSlice) | $(SUBREF allocation, stdcFreeSlice) | $(SUBREF allocation, stdcSlice) | $(SUBREF allocation, stdcUninitAlignedSlice) | $(SUBREF allocation, stdcUninitSlice) | $(SUBREF allocation, uninitAlignedSlice) | $(SUBREF allocation, uninitSlice) | ) |) | |$(TR $(TDNW $(SUBMODULE topology) $(BR) | $(SMALL Subspace manipulations | $(BR) Advanced constructors | $(BR) SliceKind conversion utilities)) | $(TD | $(SUBREF topology, alongDim) | $(SUBREF topology, as) | $(SUBREF topology, asKindOf) | $(SUBREF topology, assumeCanonical) | $(SUBREF topology, assumeContiguous) | $(SUBREF topology, assumeHypercube) | $(SUBREF topology, assumeSameShape) | $(SUBREF topology, bitpack) | $(SUBREF topology, bitwise) | $(SUBREF topology, blocks) | $(SUBREF topology, byDim) | $(SUBREF topology, bytegroup) | $(SUBREF topology, cached) | $(SUBREF topology, cachedGC) | $(SUBREF topology, canonical) | $(SUBREF topology, cartesian) | $(SUBREF topology, chopped) | $(SUBREF topology, cycle) | $(SUBREF topology, diagonal) | $(SUBREF topology, diff) | $(SUBREF topology, dropBorders) | $(SUBREF topology, evertPack) | $(SUBREF topology, flattened) | $(SUBREF topology, indexed) | $(SUBREF topology, iota) | $(SUBREF topology, ipack) | $(SUBREF topology, kronecker) | $(SUBREF topology, linspace) | $(SUBREF topology, magic) | $(SUBREF topology, map) | $(SUBREF topology, member) | $(SUBREF topology, ndiota) | $(SUBREF topology, orthogonalReduceField) | $(SUBREF topology, pack) | $(SUBREF topology, pairwise) | $(SUBREF topology, repeat) | $(SUBREF topology, reshape) | $(SUBREF topology, ReshapeError) | $(SUBREF topology, retro) | $(SUBREF topology, slide) | $(SUBREF topology, slideAlong) | $(SUBREF topology, squeeze) | $(SUBREF topology, stairs) | $(SUBREF topology, stride) | $(SUBREF topology, subSlices) | $(SUBREF topology, triplets) | $(SUBREF topology, universal) | $(SUBREF topology, unsqueeze) | $(SUBREF topology, unzip) | $(SUBREF topology, vmap) | $(SUBREF topology, windows) | $(SUBREF topology, zip) | ) |) | |$(TR $(TDNW $(SUBMODULE filling) $(BR) | $(SMALL Specialized initialisation routines)) | $(TD | $(SUBREF filling, fillVandermonde) | ) |) | |$(TR $(TDNW $(SUBMODULE fuse) $(BR) | $(SMALL Data fusing (stacking) | $(BR) See also $(SUBMODULE concatenation) submodule. | )) | $(TD | $(SUBREF fuse, fuse) | $(SUBREF fuse, fuseAs) | $(SUBREF fuse, rcfuse) | $(SUBREF fuse, rcfuseAs) | $(SUBREF fuse, fuseCells) | ) |) | |$(TR $(TDNW $(SUBMODULE concatenation) $(BR) | $(SMALL Concatenation, padding, and algorithms | $(BR) See also $(SUBMODULE fuse) submodule. | )) | $(TD | $(SUBREF concatenation, forEachFragment) | $(SUBREF concatenation, isConcatenation) | $(SUBREF concatenation, pad) | $(SUBREF concatenation, padEdge) | $(SUBREF concatenation, padWrap) | $(SUBREF concatenation, padSymmetric) | $(SUBREF concatenation, concatenation) | $(SUBREF concatenation, Concatenation) | $(SUBREF concatenation, concatenationDimension) | $(SUBREF concatenation, until) | ) |) | |$(TR $(TDNW $(SUBMODULE dynamic) | $(BR) $(SMALL Dynamic dimension manipulators)) | $(TD | $(SUBREF dynamic, allReversed) | $(SUBREF dynamic, dropToHypercube) | $(SUBREF dynamic, everted) | $(SUBREF dynamic, normalizeStructure) | $(SUBREF dynamic, reversed) | $(SUBREF dynamic, rotated) | $(SUBREF dynamic, strided) | $(SUBREF dynamic, swapped) | $(SUBREF dynamic, transposed) | ) |) | |$(TR $(TDNW $(SUBMODULE sorting) | $(BR) $(SMALL Sorting utilities)) | $(TD | $(SUBREF sorting, sort) | Examples for `isSorted`, `isStrictlyMonotonic`, `makeIndex`, and `schwartzSort`. | ) |) | |$(TR $(TDNW $(SUBMODULE mutation) | $(BR) $(SMALL Mutation utilities)) | $(TD | $(SUBREF mutation, copyMinor) | $(SUBREF mutation, reverseInPlace) | ) |) | |$(TR $(TDNW $(SUBMODULE iterator) | $(BR) $(SMALL Declarations)) | $(TD | $(SUBREF iterator, BytegroupIterator) | $(SUBREF iterator, CachedIterator) | $(SUBREF iterator, ChopIterator) | $(SUBREF iterator, FieldIterator) | $(SUBREF iterator, FlattenedIterator) | $(SUBREF iterator, IndexIterator) | $(SUBREF iterator, IotaIterator) | $(SUBREF iterator, MapIterator) | $(SUBREF iterator, MemberIterator) | $(SUBREF iterator, RetroIterator) | $(SUBREF iterator, SliceIterator) | $(SUBREF iterator, SlideIterator) | $(SUBREF iterator, StairsIterator) | $(SUBREF iterator, StrideIterator) | $(SUBREF iterator, SubSliceIterator) | $(SUBREF iterator, Triplet) | $(SUBREF iterator, TripletIterator) | $(SUBREF iterator, ZipIterator) | ) |) | |$(TR $(TDNW $(SUBMODULE field) | $(BR) $(SMALL Declarations)) | $(TD | $(SUBREF field, BitField) | $(SUBREF field, BitpackField) | $(SUBREF field, CycleField) | $(SUBREF field, LinspaceField) | $(SUBREF field, MagicField) | $(SUBREF field, MapField) | $(SUBREF field, ndIotaField) | $(SUBREF field, OrthogonalReduceField) | $(SUBREF field, RepeatField) | ) |) | |$(TR $(TDNW $(SUBMODULE ndfield) | $(BR) $(SMALL Declarations)) | $(TD | $(SUBREF ndfield, Cartesian) | $(SUBREF ndfield, Kronecker) | ) |) | |$(TR $(TDNW $(SUBMODULE chunks) | $(BR) $(SMALL Declarations)) | $(TD | $(SUBREF field, chunks) | $(SUBREF field, Chunks) | $(SUBREF field, isChunks) | $(SUBREF field, popFrontTuple) | ) |) | |$(TR $(TDNW $(SUBMODULE traits) | $(BR) $(SMALL Declarations)) | $(TD | $(SUBREF traits, isIterator) | $(SUBREF traits, isVector) | $(SUBREF traits, isMatrix) | $(SUBREF traits, isContiguousSlice) | $(SUBREF traits, isCanonicalSlice) | $(SUBREF traits, isUniversalSlice) | $(SUBREF traits, isContiguousVector) | $(SUBREF traits, isUniversalVector) | $(SUBREF traits, isContiguousMatrix) | $(SUBREF traits, isCanonicalMatrix) | $(SUBREF traits, isUniversalMatrix) | ) |) | |)) | |$(H2 Example: Image Processing) | |A median filter is implemented as an example. The function |`movingWindowByChannel` can also be used with other filters that use a sliding |window as the argument, in particular with convolution matrices such as the |$(LINK2 https://en.wikipedia.org/wiki/Sobel_operator, Sobel operator). | |`movingWindowByChannel` iterates over an image in sliding window mode. |Each window is transferred to a `filter`, which calculates the value of the |pixel that corresponds to the given window. | |This function does not calculate border cases in which a window overlaps |the image partially. However, the function can still be used to carry out such |calculations. That can be done by creating an amplified image, with the edges |reflected from the original image, and then applying the given function to the |new file. | |Note: You can find the example at |$(LINK2 https://github.com/libmir/mir/blob/master/examples/median_filter.d, GitHub). | |------- |/++ |Params: | filter = unary function. Dimension window 2D is the argument. | image = image dimensions `(h, w, c)`, | where с is the number of channels in the image | nr = number of rows in the window | nс = number of columns in the window | |Returns: | image dimensions `(h - nr + 1, w - nc + 1, c)`, | where с is the number of channels in the image. | Dense data layout is guaranteed. |+/ |Slice!(ubyte*, 3) movingWindowByChannel |(Slice!(Universal, [3], ubyte*) image, size_t nr, size_t nc, ubyte delegate(Slice!(Universal, [2], ubyte*)) filter) |{ | // 0. 3D | // The last dimension represents the color channel. | return image | // 1. 2D composed of 1D | // Packs the last dimension. | .pack!1 | // 2. 2D composed of 2D composed of 1D | // Splits image into overlapping windows. | .windows(nr, nc) | // 3. 5D | // Unpacks the windows. | .unpack | .transposed!(0, 1, 4) | // 4. 5D | // Brings the color channel dimension to the third position. | .pack!2 | // 2D to pixel lazy conversion. | .map!filter | // Creates the new image. The only memory allocation in this function. | .slice; |} |------- | |A function that calculates the value of iterator median is also necessary. | |------- |/++ | |Params: | r = input range | buf = buffer with length no less than the number of elements in `r` |Returns: | median value over the range `r` |+/ |T median(Range, T)(Slice!(Universal, [2], Range) sl, T[] buf) |{ | import std.algorithm.sorting : topN; | // copy sl to the buffer | auto retPtr = reduce!( | (ptr, elem) { *ptr = elem; return ptr + 1;} )(buf.ptr, sl); | auto n = retPtr - buf.ptr; | buf[0 .. n].topN(n / 2); | return buf[n / 2]; |} |------- | |The `main` function: | |------- |void main(string[] args) |{ | import std.conv : to; | import std.getopt : getopt, defaultGetoptPrinter; | import std.path : stripExtension; | | uint nr, nc, def = 3; | auto helpInformation = args.getopt( | "nr", "number of rows in window, default value is " ~ def.to!string, &nr, | "nc", "number of columns in window, default value is equal to nr", &nc); | if (helpInformation.helpWanted) | { | defaultGetoptPrinter( | "Usage: median-filter [] []\noptions:", | helpInformation.options); | return; | } | if (!nr) nr = def; | if (!nc) nc = nr; | | auto buf = new ubyte[nr * nc]; | | foreach (name; args[1 .. $]) | { | import imageformats; // can be found at code.dlang.org | | IFImage image = read_image(name); | | auto ret = image.pixels | .sliced(cast(size_t)image.h, cast(size_t)image.w, cast(size_t)image.c) | .movingWindowByChannel | !(window => median(window, buf)) | (nr, nc); | | write_image( | name.stripExtension ~ "_filtered.png", | ret.length!1, | ret.length!0, | (&ret[0, 0, 0])[0 .. ret.elementCount]); | } |} |------- | |This program works both with color and grayscale images. | |------- |$ median-filter --help |Usage: median-filter [] [] |options: | --nr number of rows in window, default value is 3 | --nc number of columns in window default value equals to nr |-h --help This help information. |------- | |$(H2 Compared with `numpy.ndarray`) | |numpy is undoubtedly one of the most effective software packages that has |facilitated the work of many engineers and scientists. However, due to the |specifics of implementation of Python, a programmer who wishes to use the |functions not represented in numpy may find that the built-in functions |implemented specifically for numpy are not enough, and their Python |implementations work at a very low speed. Extending numpy can be done, but |is somewhat laborious as even the most basic numpy functions that refer |directly to `ndarray` data must be implemented in C for reasonable performance. | |At the same time, while working with `ndslice`, an engineer has access to the |whole set of standard D library, so the functions he creates will be as |efficient as if they were written in C. | | |License: $(HTTP www.apache.org/licenses/LICENSE-2.0, Apache-2.0) |Copyright: 2020 Ilya Yaroshenko, Kaleidic Associates Advisory Limited, Symmetry Investments |Authors: Ilya Yaroshenko |Acknowledgements: John Loughran Colvin | |Macros: |SUBMODULE = $(MREF_ALTTEXT $1, mir, ndslice, $1) |SUBREF = $(REF_ALTTEXT $(TT $2), $2, mir, ndslice, $1)$(NBSP) |T2=$(TR $(TDNW $(LREF $1)) $(TD $+)) |TDNW2 = $0 |*/ |module mir.ndslice; | |public import mir.algorithm.iteration; |public import mir.ndslice.allocation; |public import mir.ndslice.chunks; |public import mir.ndslice.concatenation; |public import mir.ndslice.dynamic; |public import mir.ndslice.field; |public import mir.ndslice.filling; |public import mir.ndslice.fuse; |public import mir.ndslice.iterator; |public import mir.ndslice.mutation; |public import mir.ndslice.ndfield; |public import mir.ndslice.slice; |public import mir.ndslice.topology; |public import mir.ndslice.traits; | | |version(mir_test) unittest |{ 1| auto matrix = new double[12].sliced(3, 4); 1| matrix[] = 0; 1| matrix.diagonal[] = 1; | 1| auto row = matrix[2]; 1| row[3] = 6; 1| assert(matrix[2, 3] == 6); // D & C index order | //assert(matrix(3, 2) == 6); // Math & Fortran index order |} | |// relaxed example |version(mir_test) unittest |{ | import mir.qualifier; | | static Slice!(ubyte*, 3) movingWindowByChannel | (Slice!(ubyte*, 3, Universal) image, size_t nr, size_t nc, ubyte delegate(LightConstOf!(Slice!(ubyte*, 2, Universal))) filter) | { 0000000| return image | .pack!1 | .windows(nr, nc) | .unpack | .unpack | .transposed!(0, 1, 4) | .pack!2 | .map!filter | .slice; | } | | static T median(Iterator, T)(Slice!(Iterator, 2, Universal) sl, T[] buf) | { | import std.algorithm.sorting : topN; | // copy sl to the buffer | auto retPtr = reduce!( | (ptr, elem) { 0000000| *ptr = elem; 0000000| return ptr + 1; | } )(buf.ptr, sl); | auto n = retPtr - buf.ptr; | buf[0 .. n].topN(n / 2); | return buf[n / 2]; | } | | import std.conv : to; | import std.getopt : getopt, defaultGetoptPrinter; | import std.path : stripExtension; | 1| auto args = ["std"]; 3| uint nr, nc, def = 3; 1| auto helpInformation = args.getopt( | "nr", "number of rows in window, default value is " ~ def.to!string, &nr, | "nc", "number of columns in window default value equals to nr", &nc); 1| if (helpInformation.helpWanted) | { 0000000| defaultGetoptPrinter( | "Usage: median-filter [] []\noptions:", | helpInformation.options); 0000000| return; | } 2| if (!nr) nr = def; 2| if (!nc) nc = nr; | 1| auto buf = new ubyte[nr * nc]; | 3| foreach (name; args[1 .. $]) | { 0000000| auto ret = | movingWindowByChannel 0000000| (new ubyte[300].sliced(10, 10, 3).universal, nr, nc, window => median(window, buf)); | } |} | |@safe @nogc pure nothrow version(mir_test) unittest |{ 1| immutable r = 1000.iota; | 1| auto t0 = r.sliced(1000); 1| assert(t0.front == 0); 1| assert(t0.back == 999); 1| assert(t0[9] == 9); | 1| auto t1 = t0[10 .. 20]; 1| assert(t1.front == 10); 1| assert(t1.back == 19); 1| assert(t1[9] == 19); | 1| t1.popFront(); 1| assert(t1.front == 11); 1| t1.popFront(); 1| assert(t1.front == 12); | 1| t1.popBack(); 1| assert(t1.back == 18); 1| t1.popBack(); 1| assert(t1.back == 17); | 1| assert(t1 == iota([6], 12)); |} | |pure nothrow version(mir_test) unittest |{ | import mir.ndslice.topology : iota; | import mir.array.allocation : array; 1| auto r = 1000.iota.array; | 1| auto t0 = r.sliced(1000); 1| assert(t0.length == 1000); 1| assert(t0.front == 0); 1| assert(t0.back == 999); 1| assert(t0[9] == 9); | 1| auto t1 = t0[10 .. 20]; 1| assert(t1.front == 10); 1| assert(t1.back == 19); 1| assert(t1[9] == 19); | 1| t1.popFront(); 1| assert(t1.front == 11); 1| t1.popFront(); 1| assert(t1.front == 12); | 1| t1.popBack(); 1| assert(t1.back == 18); 1| t1.popBack(); 1| assert(t1.back == 17); | 1| assert(t1 == iota([6], 12)); | 1| t1.front = 13; 1| assert(t1.front == 13); 1| t1.front++; 1| assert(t1.front == 14); 1| t1.front += 2; 1| assert(t1.front == 16); 1| t1.front = 12; 1| assert((t1.front = 12) == 12); | 1| t1.back = 13; 1| assert(t1.back == 13); 1| t1.back++; 1| assert(t1.back == 14); 1| t1.back += 2; 1| assert(t1.back == 16); 1| t1.back = 12; 1| assert((t1.back = 12) == 12); | 1| t1[3] = 13; 1| assert(t1[3] == 13); 1| t1[3]++; 1| assert(t1[3] == 14); 1| t1[3] += 2; 1| assert(t1[3] == 16); 1| t1[3] = 12; 1| assert((t1[3] = 12) == 12); | 1| t1[3 .. 5] = 100; 1| assert(t1[2] != 100); 1| assert(t1[3] == 100); 1| assert(t1[4] == 100); 1| assert(t1[5] != 100); | 1| t1[3 .. 5] += 100; 1| assert(t1[2] < 100); 1| assert(t1[3] == 200); 1| assert(t1[4] == 200); 1| assert(t1[5] < 100); | 1| --t1[3 .. 5]; | 1| assert(t1[2] < 100); 1| assert(t1[3] == 199); 1| assert(t1[4] == 199); 1| assert(t1[5] < 100); | 1| --t1[]; 1| assert(t1[3] == 198); 1| assert(t1[4] == 198); | 1| t1[] += 2; 1| assert(t1[3] == 200); 1| assert(t1[4] == 200); | 1| t1[].opIndexOpAssign!"*"(t1); 1| assert(t1[3] == 40000); 1| assert(t1[4] == 40000); | | 1| assert(&t1[$ - 1] is &(t1.back())); |} | |@safe @nogc pure nothrow version(mir_test) unittest |{ | import std.range : iota; 1| auto r = (10_000L * 2 * 3 * 4).iota; | 1| auto t0 = r.slicedField(10, 20, 30, 40); 1| assert(t0.length == 10); 1| assert(t0.length!0 == 10); 1| assert(t0.length!1 == 20); 1| assert(t0.length!2 == 30); 1| assert(t0.length!3 == 40); |} | |pure nothrow version(mir_test) unittest |{ 1| auto tensor = new int[3 * 4 * 8].sliced(3, 4, 8); 1| assert(&(tensor.back.back.back()) is &tensor[2, 3, 7]); 1| assert(&(tensor.front.front.front()) is &tensor[0, 0, 0]); |} | |pure nothrow version(mir_test) unittest |{ 1| auto slice = new int[24].sliced(2, 3, 4); 1| auto r0 = slice.pack!1[1, 2]; 1| slice.pack!1[1, 2][] = 4; 1| auto r1 = slice[1, 2]; 1| assert(slice[1, 2, 3] == 4); |} | |pure nothrow version(mir_test) unittest |{ 1| auto ar = new int[3 * 8 * 9]; | 1| auto tensor = ar.sliced(3, 8, 9); 1| tensor[0, 1, 2] = 4; 1| tensor[0, 1, 2]++; 1| assert(tensor[0, 1, 2] == 5); 1| tensor[0, 1, 2]--; 1| assert(tensor[0, 1, 2] == 4); 1| tensor[0, 1, 2] += 2; 1| assert(tensor[0, 1, 2] == 6); | 1| auto matrix = tensor[0 .. $, 1, 0 .. $]; 1| matrix[] = 10; 1| assert(tensor[0, 1, 2] == 10); 1| assert(matrix[0, 2] == tensor[0, 1, 2]); 1| assert(&matrix[0, 2] is &tensor[0, 1, 2]); |} source/mir/ndslice/package.d is 94% covered <<<<<< EOF # path=./source-mir-rc-array.lst |/++ |$(H1 Thread-safe reference-counted arrays and iterators). |+/ |module mir.rc.array; | |import mir.primitives: hasLength; |import mir.qualifier; |import mir.rc.context; |import mir.type_info; |import std.traits; | |package static immutable allocationExcMsg = "mir_rcarray: out of memory error."; | |version (D_Exceptions) |{ | import core.exception: OutOfMemoryError; | package static immutable allocationError = new OutOfMemoryError(allocationExcMsg); |} | |/++ |Thread safe reference counting array. | |The implementation never adds roots into the GC. |+/ |struct mir_rcarray(T) |{ | import mir.internal.utility: isComplex, realType; | /// | package T* _payload; | package ref mir_rc_context context() inout scope return pure nothrow @nogc @trusted @property | { 17686| assert(_payload); 17686| return (cast(mir_rc_context*)_payload)[-1]; | } 0000000| package void _reset() { _payload = null; } | | package alias ThisTemplate = .mir_rcarray; | package alias _thisPtr = _payload; | | /// | alias serdeKeysProxy = T; | | /// | void proxySwap(ref typeof(this) rhs) pure nothrow @nogc @safe | { 192| auto t = this._payload; 192| this._payload = rhs._payload; 192| rhs._payload = t; | } | | /// 1| this(typeof(null)) | { | } | | /// | mixin CommonRCImpl; | | /// | pragma(inline, true) | bool opEquals(typeof(null)) @safe scope const pure nothrow @nogc | { 0000000| return !this; | } | | /// ditto | bool opEquals(Y)(auto ref scope const ThisTemplate!Y rhs) @safe scope const pure nothrow @nogc | { | static if (isComplex!T) 0000000| return cast(const realType!T[]) opIndex() == cast(const realType!Y[]) rhs.opIndex(); | else 0000000| return opIndex() == rhs.opIndex(); | } | | /// | int opCmp(Y)(auto ref scope const ThisTemplate!Y rhs) @trusted scope const pure nothrow @nogc | { | static if (isComplex!T) 0000000| return __cmp(cast(const realType!T[])opIndex(), cast(const realType!Y[])rhs.opIndex()); | else 0000000| return __cmp(opIndex(), rhs.opIndex()); | } | | /// | size_t toHash() @trusted scope const pure nothrow @nogc | { | static if (isComplex!T) 0000000| return hashOf(cast(const realType!T[])opIndex()); | else 0000000| return hashOf(opIndex()); | } | | /// | ~this() nothrow | { | static if (hasElaborateDestructor!T || hasDestructor!T) | { 19| if (false) // break @safe and pure attributes | { 0000000| Unqual!T* object; 0000000| (*object).__xdtor; | } | } 3224| if (this) | { 3698| (() @trusted { mir_rc_decrease_counter(context); })(); | } | } | | /// | size_t length() @trusted scope pure nothrow @nogc const @property | { 21402| return _payload !is null ? context.length : 0; | } | | /// | inout(T)* ptr() @system scope inout | { 150| return _payload; | } | | /// | ref opIndex(size_t i) @trusted scope inout | { 1252| assert(_payload); 1252| assert(i < context.length); 1252| return _payload[i]; | } | | /// | inout(T)[] opIndex() @trusted scope inout | { 4410| return _payload !is null ? _payload[0 .. context.length] : null; | } | | /// | size_t opDollar(size_t pos : 0)() @trusted scope pure nothrow @nogc const | { 5| return length; | } | | /// | auto asSlice()() @property | { | import mir.ndslice.slice: mir_slice; | alias It = mir_rci!T; 22| return mir_slice!It([length], It(this)); | } | | /// | auto asSlice()() const @property | { | import mir.ndslice.slice: mir_slice; | alias It = mir_rci!(const T); | return mir_slice!It([length], It(this.lightConst)); | } | | /// | auto asSlice()() immutable @property | { | import mir.ndslice.slice: mir_slice; | alias It = mir_rci!(immutable T); | return mir_slice!It([length], It(this.lightImmutable)); | } | | /// | auto moveToSlice()() @property | { | import core.lifetime: move; | import mir.ndslice.slice: mir_slice; | alias It = mir_rci!T; 10| return mir_slice!It([length], It(move(this))); | } | | /++ | Params: | length = array length | initialize = Flag, don't initialize memory with default value if `false`. | deallocate = Flag, never deallocates memory if `false`. | +/ 175| this(size_t length, bool initialize = true, bool deallocate = true) @trusted @nogc | { 175| if (length == 0) 0000000| return; 175| Unqual!T[] ar; 175| () @trusted { | static if (is(T == class) || is(T == interface)) | auto ctx = mir_rc_create(mir_get_type_info!T, length, mir_get_payload_ptr!T, initialize, deallocate); | else 175| auto ctx = mir_rc_create(mir_get_type_info!T, length, mir_get_payload_ptr!T, initialize, deallocate); 175| if (!ctx) | { | version(D_Exceptions) 0000000| throw allocationError; | else | assert(0, allocationExcMsg); | } 175| _payload = cast(T*)(ctx + 1); 175| ar = cast(Unqual!T[])_payload[0 .. length]; | } (); 284| if (initialize || hasElaborateAssign!(Unqual!T)) | { | import mir.conv: uninitializedFillDefault; 69| uninitializedFillDefault(ar); | } | } | | static if (isImplicitlyConvertible!(const T, T)) | static if (isImplicitlyConvertible!(const Unqual!T, T)) | package alias V = const Unqual!T; | else | package alias V = const T; | else | package alias V = T; | | static if (is(T == const) || is(T == immutable)) 0000000| this(return ref scope const typeof(this) rhs) @trusted pure nothrow @nogc | { 0000000| if (rhs) | { 0000000| this._payload = cast(typeof(this._payload))rhs._payload; 0000000| mir_rc_increase_counter(context); | } | } | | static if (is(T == immutable)) 0000000| this(return ref scope const typeof(this) rhs) immutable @trusted pure nothrow @nogc | { 0000000| if (rhs) | { 0000000| this._payload = cast(typeof(this._payload))rhs._payload; 0000000| mir_rc_increase_counter(context); | } | } | | static if (is(T == immutable)) 0000000| this(return ref scope const typeof(this) rhs) const @trusted pure nothrow @nogc | { 0000000| if (rhs) | { 0000000| this._payload = cast(typeof(this._payload))rhs._payload; 0000000| mir_rc_increase_counter(context); | } | } | 1686| this(return ref scope inout typeof(this) rhs) inout @trusted pure nothrow @nogc | { 1686| if (rhs) | { 1675| this._payload = rhs._payload; 1675| mir_rc_increase_counter(context); | } | } | | /// | ref opAssign(typeof(null)) return @trusted // pure nothrow @nogc | { 1| this = typeof(this).init; | } | | /// | ref opAssign(return typeof(this) rhs) return @trusted // pure nothrow @nogc | { 2| this.proxySwap(rhs); 2| return this; | } | | /// | ref opAssign(Q)(return ThisTemplate!Q rhs) return @trusted // pure nothrow @nogc | if (isImplicitlyConvertible!(Q*, T*)) | { 4| this.proxySwap(*()@trusted{return cast(typeof(this)*)&rhs;}()); 2| return this; | } |} | |/// ditto |alias RCArray = mir_rcarray; | |/// |version(mir_test) |@safe pure @nogc nothrow |unittest |{ 2| auto a = RCArray!double(10); 43| foreach(i, ref e; a) 10| e = i; 2| auto b = a; 1| assert(b[$ - 1] == 9); 43| foreach(i, ref e; b) 10| assert(e == i); 1| b[4] = 100; 1| assert(a[4] == 100); | | import mir.ndslice.slice; | 2| auto s = a.asSlice; // as RC random access range (ndslice) | static assert(is(typeof(s) == Slice!(RCI!double))); | static assert(is(typeof(s) == mir_slice!(mir_rci!double))); | 1| auto r = a[]; // scope array | static assert(is(typeof(r) == double[])); | 1| auto fs = r.sliced; // scope fast random access range (ndslice) | static assert(is(typeof(fs) == Slice!(double*))); |} | |version(mir_test) |@safe pure @nogc nothrow |unittest |{ | import mir.complex; 2| auto a = rcarray(complex(2.0, 3), complex(4.9, 2)); |} | |package template LikeArray(Range) |{ | static if (__traits(identifier, Range) == "mir_slice") | { | import mir.ndslice.slice; | enum LikeArray = is(Range : Slice!(T*, N, kind), T, size_t N, SliceKind kind); | } | else | { | enum LikeArray = false; | } |} | |/// |auto rcarray(T = void, Range)(ref Range range) | if (is(T == void) && !is(Range == LightScopeOf!Range)) |{ | return .rcarray(range.lightScope); |} | |/// ditto |auto rcarray(T = void, Range)(Range range) | if (is(T == void) && isIterable!Range && is(Range == LightScopeOf!Range) && !isArray!Range) |{ | static if (LikeArray!Range) | { | return .rcarray(range.field); | } | else | { 1| return .rcarray!(ForeachType!Range)(range); | } |} | |/// ditto |RCArray!V rcarray(T = void, V)(V[] values...) | if (is(T == void) && hasIndirections!V) |{ 1| return .rcarray(values, true); |} | |/// ditto |RCArray!V rcarray(T = void, V)(scope V[] values...) | if (is(T == void) && !hasIndirections!V) |{ 2| return .rcarray(values, true); |} | |/// ditto |RCArray!V rcarray(T = void, V)(V[] values, bool deallocate) | if (is(T == void) && hasIndirections!V) |{ 1| return .rcarray!V(values, deallocate); |} | |/// ditto |RCArray!V rcarray(T = void, V)(scope V[] values, bool deallocate) | if (is(T == void) && !hasIndirections!V) |{ 2| return .rcarray!V(values, deallocate); |} | |/// ditto |template rcarray(T) | if(!is(T == E[], E) && !is(T == void)) |{ | import mir.primitives: isInputRange, isInfinite; | | /// | auto rcarray(Range)(ref Range range) | if (!is(Range == LightScopeOf!Range)) | { | return .rcarray!T(range.lightScope); | } | | /// ditto | auto rcarray(Range)(Range range) | if ((isInputRange!Range || isIterable!Range) && !isInfinite!Range && !isArray!Range || isPointer!Range && (isInputRange!(PointerTarget!Range) || isIterable!(PointerTarget!Range))) | { | static if (LikeArray!Range) | { 1| return .rcarray!T(range.field); | } | else static if (hasLength!Range) | { | import mir.conv: emplaceRef; 2| auto ret = RCArray!T(range.length, false); 2| size_t i; | static if (isInputRange!Range) 2010| for (; !range.empty; range.popFront) 1004| ret[i++].emplaceRef!T(range.front); | else | static if (isPointer!Range) | foreach (e; *range) | ret[i++].emplaceRef!T(e); | else | foreach (e; range) | ret[i++].emplaceRef!T(e); 2| return ret; | } | else | { | import mir.appender: scopedBuffer; | import mir.conv: emplaceRef; 2| auto a = scopedBuffer!T; | static if (isInputRange!Range) 5| for (; !range.empty; range.popFront) 2| a.put(range.front); | else | static if (isPointer!Range) | foreach (e; *range) | a.put(e); | else | foreach (e; range) | a.put(e); 1| scope values = a.data; 1| return ()@trusted { 1| auto ret = RCArray!T(values.length, false); 1| a.moveDataAndEmplaceTo(ret[]); 1| return ret; | } (); | } | } | | /// ditto | RCArray!T rcarray(V)(V[] values...) | if (hasIndirections!V) | { | return .rcarray!T(values, true); | } | | /// ditto | RCArray!T rcarray(V)(scope V[] values...) | if (!hasIndirections!V) | { 9| return .rcarray!T(values, true); | } | | /// ditto | RCArray!T rcarray(V)(V[] values, bool deallocate) | if (hasIndirections!V) | { 1| auto ret = mir_rcarray!T(values.length, false, deallocate); | static if (!hasElaborateAssign!(Unqual!T) && is(Unqual!V == Unqual!T)) | { | ()@trusted { | import core.stdc.string: memcpy; | memcpy(cast(void*)ret.ptr, cast(const void*)values.ptr, values.length * T.sizeof); | }(); | } | else | { | import mir.conv: emplaceRef; 1| auto lhs = ret[]; 7| foreach (i, ref e; values) 1| lhs[i].emplaceRef!T(e); | } 1| return ret; | } | | /// ditto | RCArray!T rcarray(V)(scope V[] values, bool deallocate) | if (!hasIndirections!V) | { 11| auto ret = mir_rcarray!T(values.length, false); | static if (!hasElaborateAssign!(Unqual!T) && is(Unqual!V == Unqual!T)) | { 9| ()@trusted { | import core.stdc.string: memcpy; 9| memcpy(cast(void*)ret.ptr, cast(const void*)values.ptr, values.length * T.sizeof); | }(); | } | else | { | import mir.conv: emplaceRef; 2| auto lhs = ret[]; 14| foreach (i, ref e; values) 2| lhs[i].emplaceRef!T(e); | } 11| return ret; | } |} | |/// |version(mir_test) |@safe pure @nogc nothrow |unittest |{ 2| RCArray!double a = rcarray!double(1.0, 2, 5, 3); 1| assert(a[0] == 1); 1| assert(a[$ - 1] == 3); | 2| auto s = rcarray!char("hello!"); 1| assert(s[0] == 'h'); 1| assert(s[$ - 1] == '!'); | | alias rcstring = rcarray!(immutable char); 2| auto r = rcstring("string"); 1| assert(r[0] == 's'); 1| assert(r[$ - 1] == 'g'); |} | |/// With Input Ranges |version(mir_test) |@safe pure @nogc nothrow |unittest |{ | import mir.algorithm.iteration: filter; | static immutable numbers = [3, 2, 5, 2, 3, 7, 3]; | static immutable filtered = [5.0, 7]; 2| auto result = numbers.filter!"a > 3".rcarray!(immutable double); | static assert(is(typeof(result) == RCArray!(immutable double))); 1| assert (result[] == filtered); |} | |/++ |Params: | length = array length | deallocate = Flag, never deallocates memory if `false`. |Returns: minimally initialized rcarray. |+/ |RCArray!T mininitRcarray(T)(size_t length, bool deallocate = true) |{ 89| return RCArray!T(length, false, deallocate); |} | |/// |@safe pure nothrow @nogc unittest |{ 2| auto a = mininitRcarray!double(5); 1| assert(a.length == 5); 1| assert(a._counter == 1); 1| a[][] = 0; // a.opIndex()[] = 0; |} | |/++ |Thread safe reference counting iterator. |+/ 1616|struct mir_rci(T) |{ | import mir.ndslice.slice: Slice; | import mir.ndslice.iterator: IotaIterator; | | /// | T* _iterator; | | /// | RCArray!T _array; | | /// 137| this(RCArray!T array) | { 274| this._iterator = (()@trusted => array.ptr)(); 137| this._array.proxySwap(array); | } | | /// 42| this(T* _iterator, RCArray!T array) | { 42| this._iterator = _iterator; 42| this._array.proxySwap(array); | } | | /// | inout(T)* lightScope()() scope return inout @property @trusted | { | debug | { 955| assert(_array._payload <= _iterator); 1910| assert(_iterator is null || _iterator <= _array._payload + _array.length); | } 955| return _iterator; | } | | /// | ref opAssign(typeof(null)) scope return nothrow | { | pragma(inline, true); 0000000| _iterator = null; 0000000| _array = null; 0000000| return this; | } | | /// | ref opAssign(return typeof(this) rhs) scope return @trusted | { 7| _iterator = rhs._iterator; 7| _array.proxySwap(rhs._array); 7| return this; | } | | /// | ref opAssign(Q)(return mir_rci!Q rhs) scope return nothrow | if (isImplicitlyConvertible!(Q*, T*)) | { | import core.lifetime: move; 2| _iterator = rhs._iterator; 2| _array = move(rhs._array); 2| return this; | } | | /// | mir_rci!(const T) lightConst()() scope return const nothrow @property 37| { return typeof(return)(_iterator, _array.lightConst); } | | /// | mir_rci!(immutable T) lightImmutable()() scope return immutable nothrow @property | { return typeof(return)(_iterator, _array.lightImmutable); } | | /// | ref inout(T) opUnary(string op : "*")() inout scope return | { | debug | { 221| assert(_iterator); 221| assert(_array._payload); 221| assert(_array._payload <= _iterator); 221| assert(_iterator <= _array._payload + _array.length); | } 221| return *_iterator; | } | | /// | ref inout(T) opIndex(ptrdiff_t index) inout scope return @trusted | { | debug | { 9454| assert(_iterator); 9454| assert(_array._payload); 9454| assert(_array._payload <= _iterator + index); 9454| assert(_iterator + index <= _array._payload + _array.length); | } 9454| return _iterator[index]; | } | | /// Returns: slice type of `Slice!(IotaIterator!size_t)` | Slice!(IotaIterator!size_t) opSlice(size_t dimension)(size_t i, size_t j) @safe scope const | if (dimension == 0) | in | { 8| assert(i <= j, "RCI!T.opSlice!0: the left opSlice boundary must be less than or equal to the right bound."); | } | do | { 8| return typeof(return)(j - i, typeof(return).Iterator(i)); | } | | /// Returns: ndslice on top of the refcounted iterator | auto opIndex(Slice!(IotaIterator!size_t) slice) | { | import core.lifetime: move; 16| auto it = this; 8| it += slice._iterator._index; 8| return Slice!(RCI!T)(slice.length, it.move); | } | | /// ditto | auto opIndex(Slice!(IotaIterator!size_t) slice) const | { | import core.lifetime: move; 0000000| auto it = lightConst; 0000000| it += slice._iterator._index; 0000000| return Slice!(RCI!(const T))(slice.length, it.move); | } | | /// | void opUnary(string op)() scope | if (op == "--" || op == "++") | { mixin(op ~ "_iterator;"); } | | /// | void opOpAssign(string op)(ptrdiff_t index) scope | if (op == "-" || op == "+") | { mixin("_iterator " ~ op ~ "= index;"); } | | /// | mir_rci!T opBinary(string op)(ptrdiff_t index) | if (op == "+" || op == "-") 5| { return mir_rci!T(_iterator + index, _array); } | | /// | mir_rci!(const T) opBinary(string op)(ptrdiff_t index) const | if (op == "+" || op == "-") | { return mir_rci!T(_iterator + index, _array); } | | /// | mir_rci!(immutable T) opBinary(string op)(ptrdiff_t index) immutable | if (op == "+" || op == "-") | { return mir_rci!T(_iterator + index, _array); } | | /// | ptrdiff_t opBinary(string op : "-")(scope ref const typeof(this) right) scope const 0000000| { return this._iterator - right._iterator; } | | /// | bool opEquals()(scope ref const typeof(this) right) scope const 0000000| { return this._iterator == right._iterator; } | | /// | int opCmp()(scope ref const typeof(this) right) scope const 0000000| { auto d = this - right; return d ? d < 0 ? -1 : 1 : 0; } |} | |/// ditto |alias RCI = mir_rci; | |/// |version(mir_test) |@safe @nogc unittest |{ | | import mir.ndslice.traits: isIterator; | import mir.ndslice.slice; | import mir.rc.array; 2| auto slice = mir_rcarray!double(10).asSlice; | static assert(isIterator!(RCI!double)); | static assert(is(typeof(slice) == Slice!(RCI!double))); 2| auto matrix = slice.sliced(2, 5); | static assert(is(typeof(matrix) == Slice!(RCI!double, 2))); 1| slice[7] = 44; 1| assert(matrix[1, 2] == 44); |} | |/// |version(mir_test) |@safe @nogc unittest |{ | import mir.ndslice.slice; | import mir.rc.array; | | alias rcvec = Slice!(RCI!double); | 4| RCI!double a, b; 1| a = b; | 4| RCI!(const double) ca, cb; 1| ca = cb; 1| ca = cast(const) cb; | | void foo(scope ref rcvec x, scope ref rcvec y) | { 0000000| x[] = y[]; 0000000| x[1] = y[1]; 0000000| x[1 .. $] += y[1 .. $]; 0000000| x = x.save; | } |} | |version(mir_test) |@safe @nogc unittest |{ | import mir.ndslice; | import mir.rc.array; | import mir.series; | | @safe void bar(ref const mir_rcarray!(const double) a, ref mir_rcarray!(const double) b) | { 0000000| b = a; | } | | @safe void bari(ref immutable mir_rcarray!(immutable double) a, ref mir_rcarray!(immutable double) b) | { 0000000| b = a; | } | | @safe void foo(ref const RCI!(const double) a, ref RCI!(const double) b) | { 0000000| b = a; | } | | @safe void fooi(ref immutable RCI!(immutable double) a, ref RCI!(immutable double) b) | { 0000000| b = a; | } | | struct S | { | uint i; | @safe pure: | ~this() {} | } | | @safe void goo(ref const Series!(RCI!(const double), RCI!(const S)) a, ref Series!(RCI!(const double), RCI!(const S)) b) | { 0000000| b = a; | } | | @safe void gooi(ref immutable Series!(RCI!(immutable double), RCI!(const S)) a, ref Series!(RCI!(immutable double), RCI!(const S)) b) | { 0000000| b = a; | } | 2| struct C | { | Series!(RCI!(const S), RCI!(const S)) a; | Series!(RCI!(const S), RCI!(const S)) b; | } | 4| C a, b; 1| a = b; 1| a = cast(const) b; |} | |version(mir_test) |unittest |{ | import mir.ndslice.slice: Slice; | static RCArray!int foo() @safe | { 0000000| auto ret = RCArray!int(10); 0000000| return ret; | } | | | static Slice!(RCI!int) bat() @safe | { 0000000| auto ret = RCArray!int(10); 0000000| return ret.asSlice; | } | | static Slice!(RCI!int) bar() @safe | { 0000000| auto ret = RCArray!int(10); 0000000| auto d = ret.asSlice; 0000000| return d; | } |} | |version(mir_test) |@safe unittest |{ | import core.stdc.stdio; | | struct S | { | uint s; | this(this) @nogc nothrow @safe | { | // () @trusted { | // puts("this(this)\n"); | // } (); | } | | ~this() nothrow @nogc @safe | { | // () @trusted { | // if (s) | // puts("~this()\n"); | // else | // puts("~this() - zero\n"); | // } (); | } | } | | struct C | { | S s; | } | 2| S[1] d = [S(1)]; 2| auto r = rcarray(d); |} | |version(mir_test) |unittest |{ | import mir.small_string; | alias S = SmallString!32u; 1| auto ars = [S("123"), S("422")]; | alias R = mir_rcarray!S; 2| auto rc = ars.rcarray!S; | 2| RCArray!int value = null; 1| value = null; |} source/mir/rc/array.d is 75% covered <<<<<< EOF # path=./source-mir-bignum-decimal.lst |/++ |Stack-allocated decimal type. | |Note: | The module doesn't provide full arithmetic API for now. |+/ |module mir.bignum.decimal; | |import mir.serde: serdeProxy, serdeScoped; |import std.traits: isSomeChar; |public import mir.bignum.low_level_view: DecimalExponentKey; |import mir.bignum.low_level_view: ceilLog10Exp2; | |private enum expBufferLength = 2 + ceilLog10Exp2(size_t.sizeof * 8); |private static immutable C[9] zerosImpl(C) = "0.00000.0"; | |/++ |Stack-allocated decimal type. |Params: | maxSize64 = count of 64bit words in coefficient |+/ |@serdeScoped @serdeProxy!(const(char)[]) |struct Decimal(size_t maxSize64) | if (maxSize64 && maxSize64 <= ushort.max) |{ | import mir.format: NumericSpec; | import mir.bignum.integer; | import mir.bignum.low_level_view; | import std.traits: isMutable, isFloatingPoint; | | /// | sizediff_t exponent; | /// | BigInt!maxSize64 coefficient; | | /// | DecimalView!size_t view() | { 1| return typeof(return)(coefficient.sign, exponent, coefficient.view.unsigned); | } | | /// ditto | DecimalView!(const size_t) view() const | { 43| return typeof(return)(coefficient.sign, exponent, coefficient.view.unsigned); | } | | /// | this(C)(scope const(C)[] str, int exponentShift = 0) @safe pure @nogc | if (isSomeChar!C) | { 12| DecimalExponentKey key; 12| if (fromStringImpl(str, key, exponentShift) || key == DecimalExponentKey.nan || key == DecimalExponentKey.infinity) 12| return; | static if (__traits(compiles, () @nogc { throw new Exception("Can't parse Decimal."); })) | { | import mir.exception: MirException; | throw new MirException("Can't parse Decimal!" ~ maxSize64.stringof ~ " from string `", str , "`"); | } | else | { | static immutable exception = new Exception("Can't parse Decimal!" ~ maxSize64.stringof ~ "."); 0000000| throw exception; | } | } | | static if (maxSize64 == 3) | /// | version(mir_test) @safe pure @nogc unittest | { | import mir.math.constant: PI; 1| Decimal!2 decimal = "3.141592653589793378e-40"; // constructor 1| assert(cast(double) decimal == double(PI) / 1e40); | } | | /++ | Constructs Decimal from the floating point number using the $(HTTPS github.com/ulfjack/ryu, Ryu algorithm). | | The number is the shortest decimal representation that being converted back would result the same floating-point number. | +/ 72| this(T)(const T x) | if (isFloatingPoint!T && maxSize64 >= 1 + (T.mant_dig >= 64)) | { | import mir.bignum.internal.ryu.generic_128: genericBinaryToDecimal; 72| this = genericBinaryToDecimal(x); | } | | static if (maxSize64 == 3) | /// | version(mir_bignum_test) | @safe pure nothrow @nogc | unittest | { | // float and double can be used to construct Decimal of any length 1| auto decimal64 = Decimal!1(-1.235e-7); 1| assert(decimal64.exponent == -10); 1| assert(decimal64.coefficient == -1235); | | // real number may need Decimal at least length of 2 1| auto decimal128 = Decimal!2(-1.235e-7L); 1| assert(decimal128.exponent == -10); 1| assert(decimal128.coefficient == -1235); | 1| decimal128 = Decimal!2(1234e3f); 1| assert(decimal128.exponent == 3); 1| assert(decimal128.coefficient == 1234); | } | | /// | ref opAssign(size_t rhsMaxSize64)(auto ref scope const Decimal!rhsMaxSize64 rhs) return | if (rhsMaxSize64 < maxSize64) | { 4| this.exponent = rhs.exponent; 4| this.coefficient = rhs.coefficient; 4| return this; | } | | /++ | Handle thousand separators for non exponential numbers. | | Returns: false in case of overflow or incorrect string. | +/ | bool fromStringWithThousandsSeparatorImpl(C, | bool allowSpecialValues = true, | bool allowStartingPlus = true, | bool allowLeadingZeros = true, | )( | scope const(C)[] str, | const C thousandsSeparator, | const C fractionSeparator, | out DecimalExponentKey key, | int exponentShift = 0, | ) | if (isSomeChar!C) | { | import mir.algorithm.iteration: find; | import mir.format: stringBuf; | import mir.ndslice.chunks: chunks; | import mir.ndslice.slice: sliced; | import mir.ndslice.topology: retro; | | stringBuf buffer; | assert(thousandsSeparator != fractionSeparator); | if (str.length && (str[0] == '+' || str[0] == '-')) | { | buffer.put(cast(char)str[0]); | str = str[1 .. $]; | } | auto integer = str[0 .. $ - str.find!(a => a == fractionSeparator)]; | if (integer.length % 4 == 0) | return false; | foreach_reverse (chunk; integer.sliced.retro.chunks(4)) | { | auto s = chunk.retro.field; | if (s.length == 4) | { | if (s[0] != thousandsSeparator) | return false; | s = s[1 .. $]; | } | do | { | if (s[0] < '0' || s[0] > '9') | return false; | buffer.put(cast(char)s[0]); | s = s[1 .. $]; | } | while(s.length); | } | if (str.length > integer.length) | { | buffer.put('.'); | str = str[integer.length + 1 .. $]; | if (str.length == 0) | return false; | do | { | buffer.put(cast(char)str[0]); | str = str[1 .. $]; | } | while(str.length); | } | return fromStringImpl!(char, | allowSpecialValues, | false, // allowDotOnBounds | false, // allowDExponent | allowStartingPlus, | false, // allowUnderscores | allowLeadingZeros, // allowLeadingZeros | false, // allowExponent | false, // checkEmpty | )(buffer.data, key, exponentShift); | } | | static if (maxSize64 == 3) | /// | version(mir_bignum_test) | @safe pure nothrow @nogc | unittest | { 1| Decimal!3 decimal; 1| DecimalExponentKey key; | 1| assert(decimal.fromStringWithThousandsSeparatorImpl("12,345.678", ',', '.', key)); 1| assert(cast(double) decimal == 12345.678); 1| assert(key == DecimalExponentKey.dot); | 1| assert(decimal.fromStringWithThousandsSeparatorImpl("12,345,678", ',', '.', key, -3)); 1| assert(cast(double) decimal == 12345.678); 1| assert(key == DecimalExponentKey.none); | 1| assert(decimal.fromStringWithThousandsSeparatorImpl("021 345,678", ' ', ',', key)); 1| assert(cast(double) decimal == 21345.678); 1| assert(key == DecimalExponentKey.dot); | } | | /++ | Returns: false in case of overflow or incorrect string. | +/ | bool fromStringImpl(C, | bool allowSpecialValues = true, | bool allowDotOnBounds = true, | bool allowDExponent = true, | bool allowStartingPlus = true, | bool allowUnderscores = true, | bool allowLeadingZeros = true, | bool allowExponent = true, | bool checkEmpty = true, | ) | (scope const(C)[] str, out DecimalExponentKey key, int exponentShift = 0) | scope @trusted pure @nogc nothrow | if (isSomeChar!C) | { | enum optimize = size_t.sizeof == 8 && maxSize64 == 1; | version(LDC) | { | static if (optimize || (allowSpecialValues && allowDExponent && allowStartingPlus && checkEmpty) == false) | pragma(inline, true); | } | static if (optimize) | { | import mir.utility: _expect; | static if (checkEmpty) | { 29| if (_expect(str.length == 0, false)) 0000000| return false; | } | 29| coefficient.sign = str[0] == '-'; 29| if (coefficient.sign) | { 7| str = str[1 .. $]; 7| if (_expect(str.length == 0, false)) 0000000| return false; | } | else | static if (allowStartingPlus) | { 22| if (_expect(str[0] == '+', false)) | { 1| str = str[1 .. $]; 1| if (_expect(str.length == 0, false)) 0000000| return false; | } | } | 29| uint d = str[0] - '0'; 29| str = str[1 .. $]; 29| exponent = 0; | 29| ulong v; 29| bool dot; | static if (allowUnderscores) | { 29| bool recentUnderscore; | } | static if (!allowLeadingZeros) | { | if (d == 0) | { | if (str.length == 0) | goto R; | if (str[0] >= '0' && str[0] <= '9') | return false; | goto S; | } | } | 29| if (d < 10) | { 20| goto S; | } | | static if (allowDotOnBounds) | { 9| if (d == '.' - '0') | { 2| if (str.length == 0) 1| return false; 1| key = DecimalExponentKey.dot; 1| dot = true; 1| goto F; | } | } | | static if (allowSpecialValues) | { 7| goto NI; | } | else | { | return false; | } | | F: for(;;) | { 82| d = str[0] - '0'; 82| str = str[1 .. $]; | 82| if (_expect(d <= 10, true)) | { | static if (allowUnderscores) | { 64| recentUnderscore = false; | } | { | import mir.checkedint: mulu; 64| bool overflow; 64| v = mulu(v, cast(uint)10, overflow); 64| if (overflow) 1| break; | } | S: 83| v += d; 83| exponentShift -= dot; 83| if (str.length) 72| continue; | E: 15| exponent += exponentShift; | R: 24| coefficient.data[0] = v; 24| coefficient.length = v != 0; | static if (allowUnderscores) | { 24| return !recentUnderscore; | } | else | { | return true; | } | } | static if (allowUnderscores) | { 18| if (recentUnderscore) 0000000| return false; | } 18| switch (d) | { 13| case DecimalExponentKey.dot: 13| key = DecimalExponentKey.dot; 13| if (_expect(dot, false)) 2| break; 11| dot = true; 11| if (str.length) | { | static if (allowUnderscores) | { 9| recentUnderscore = true; | } 9| continue; | } | static if (allowDotOnBounds) | { 2| goto R; | } | else | { | break; | } | static if (allowExponent) | { | static if (allowDExponent) | { 1| case DecimalExponentKey.d: 2| case DecimalExponentKey.D: 2| goto case DecimalExponentKey.e; | } 4| case DecimalExponentKey.e: 5| case DecimalExponentKey.E: | import mir.parse: parse; 5| key = cast(DecimalExponentKey)d; 9| if (parse(str, exponent) && str.length == 0) 4| goto E; 1| break; | } | static if (allowUnderscores) | { 0000000| case '_' - '0': 0000000| recentUnderscore = true; 0000000| if (str.length) 0000000| continue; 0000000| break; | } 0000000| default: | } 3| break; | } 4| return false; | static if (allowSpecialValues) | { | NI: 7| exponent = exponent.max; 7| if (str.length == 2) | { 7| auto stail = cast(C[2])str[0 .. 2]; 17| if (d == 'i' - '0' && stail == cast(C[2])"nf" || d == 'I' - '0' && (stail == cast(C[2])"nf" || stail == cast(C[2])"NF")) | { 4| key = DecimalExponentKey.infinity; 4| goto R; | } 9| if (d == 'n' - '0' && stail == cast(C[2])"an" || d == 'N' - '0' && (stail == cast(C[2])"aN" || stail == cast(C[2])"AN")) | { 3| v = 1; 3| key = DecimalExponentKey.nan; 3| goto R; | } | } 0000000| return false; | } | } | else | { | import mir.bignum.low_level_view: DecimalView, BigUIntView, MaxWordPow10; 57| auto work = DecimalView!size_t(false, 0, BigUIntView!size_t(coefficient.data)); 57| auto ret = work.fromStringImpl!(C, | allowSpecialValues, | allowDotOnBounds, | allowDExponent, | allowStartingPlus, | allowUnderscores, | allowLeadingZeros, | allowExponent, | checkEmpty, | )(str, key, exponentShift); 57| coefficient.length = cast(uint) work.coefficient.coefficients.length; 57| coefficient.sign = work.sign; 57| exponent = work.exponent; 57| return ret; | } | } | | static if (maxSize64 == 3) | /// | version(mir_bignum_test) | @safe pure nothrow @nogc | unittest | { | import mir.conv: to; 1| Decimal!3 decimal; 1| DecimalExponentKey key; | | // Check precise percentate parsing 1| assert(decimal.fromStringImpl("71.7", key, -2)); 1| assert(key == DecimalExponentKey.dot); | // The result is exact value instead of 0.7170000000000001 = 71.7 / 100 1| assert(cast(double) decimal == 0.717); | 1| assert(decimal.fromStringImpl("+0.334e-5"w, key)); 1| assert(key == DecimalExponentKey.e); 1| assert(cast(double) decimal == 0.334e-5); | 1| assert(decimal.fromStringImpl("100_000_000"w, key)); 1| assert(key == DecimalExponentKey.none); 1| assert(cast(double) decimal == 1e8); | 1| assert(decimal.fromStringImpl("-334D-5"d, key)); 1| assert(key == DecimalExponentKey.D); 1| assert(cast(double) decimal == -334e-5); | 1| assert(decimal.fromStringImpl("2482734692817364218734682973648217364981273648923423", key)); 1| assert(key == DecimalExponentKey.none); 1| assert(cast(double) decimal == 2482734692817364218734682973648217364981273648923423.0); | 1| assert(decimal.fromStringImpl(".023", key)); 1| assert(key == DecimalExponentKey.dot); 1| assert(cast(double) decimal == .023); | 1| assert(decimal.fromStringImpl("0E100", key)); 1| assert(key == DecimalExponentKey.E); 1| assert(cast(double) decimal == 0); | 12| foreach (str; ["-nan", "-NaN", "-NAN"]) | { 3| assert(decimal.fromStringImpl(str, key)); 3| assert(decimal.coefficient.length > 0); 3| assert(decimal.exponent == decimal.exponent.max); 3| assert(decimal.coefficient.sign); 3| assert(key == DecimalExponentKey.nan); 3| assert(cast(double) decimal != cast(double) decimal); | } | 12| foreach (str; ["inf", "Inf", "INF"]) | { 3| assert(decimal.fromStringImpl(str, key)); 3| assert(decimal.coefficient.length == 0); 3| assert(decimal.exponent == decimal.exponent.max); 3| assert(key == DecimalExponentKey.infinity); 3| assert(cast(double) decimal == double.infinity); | } | 1| assert(decimal.fromStringImpl("-inf", key)); 1| assert(decimal.coefficient.length == 0); 1| assert(decimal.exponent == decimal.exponent.max); 1| assert(key == DecimalExponentKey.infinity); 1| assert(cast(double) decimal == -double.infinity); | 1| assert(!decimal.fromStringImpl("3.3.4", key)); 1| assert(!decimal.fromStringImpl("3.4.", key)); 1| assert(decimal.fromStringImpl("4.", key)); 1| assert(!decimal.fromStringImpl(".", key)); 1| assert(decimal.fromStringImpl("0.", key)); 1| assert(decimal.fromStringImpl("00", key)); 1| assert(!decimal.fromStringImpl("0d", key)); | } | | static if (maxSize64 == 3) | version(mir_bignum_test) | @safe pure nothrow @nogc | unittest | { | import mir.conv: to; 1| Decimal!1 decimal; 1| DecimalExponentKey key; | 1| assert(decimal.fromStringImpl("1.334", key)); 1| assert(key == DecimalExponentKey.dot); 1| assert(cast(double) decimal == 1.334); | 1| assert(decimal.fromStringImpl("+0.334e-5"w, key)); 1| assert(key == DecimalExponentKey.e); 1| assert(cast(double) decimal == 0.334e-5); | 1| assert(decimal.fromStringImpl("-334D-5"d, key)); 1| assert(key == DecimalExponentKey.D); 1| assert(cast(double) decimal == -334e-5); | 1| assert(!decimal.fromStringImpl("2482734692817364218734682973648217364981273648923423", key)); | 1| assert(decimal.fromStringImpl(".023", key)); 1| assert(key == DecimalExponentKey.dot); 1| assert(cast(double) decimal == .023); | 1| assert(decimal.fromStringImpl("0E100", key)); 1| assert(key == DecimalExponentKey.E); 1| assert(cast(double) decimal == 0); | 12| foreach (str; ["-nan", "-NaN", "-NAN"]) | { 3| assert(decimal.fromStringImpl(str, key)); 3| assert(decimal.coefficient.length > 0); 3| assert(decimal.exponent == decimal.exponent.max); 3| assert(decimal.coefficient.sign); 3| assert(key == DecimalExponentKey.nan); 3| assert(cast(double) decimal != cast(double) decimal); | } | 12| foreach (str; ["inf", "Inf", "INF"]) | { 3| assert(decimal.fromStringImpl(str, key)); 3| assert(decimal.coefficient.length == 0); 3| assert(decimal.exponent == decimal.exponent.max); 3| assert(key == DecimalExponentKey.infinity); 3| assert(cast(double) decimal == double.infinity); | } | 1| assert(decimal.fromStringImpl("-inf", key)); 1| assert(decimal.coefficient.length == 0); 1| assert(decimal.exponent == decimal.exponent.max); 1| assert(key == DecimalExponentKey.infinity); 1| assert(cast(double) decimal == -double.infinity); | 1| assert(!decimal.fromStringImpl("3.3.4", key)); 1| assert(!decimal.fromStringImpl("3.4.", key)); 1| assert(decimal.fromStringImpl("4.", key)); 1| assert(!decimal.fromStringImpl(".", key)); 1| assert(decimal.fromStringImpl("0.", key)); 1| assert(decimal.fromStringImpl("00", key)); 1| assert(!decimal.fromStringImpl("0d", key)); | } | | private enum coefficientBufferLength = 2 + ceilLog10Exp2(coefficient.data.length * (size_t.sizeof * 8)); // including dot and sign | private enum eDecimalLength = coefficientBufferLength + expBufferLength; | | /// | immutable(C)[] toString(C = char)(NumericSpec spec = NumericSpec.init) const @safe pure nothrow | if(isSomeChar!C && isMutable!C) | { | import mir.appender: UnsafeArrayBuffer; 2| C[eDecimalLength] data = void; 2| auto buffer = UnsafeArrayBuffer!C(data); 2| toString(buffer, spec); 2| return buffer.data.idup; | } | | static if (maxSize64 == 3) | /// | version(mir_bignum_test) @safe pure unittest | { 1| auto str = "-3.4010447314490204552169750449563978034784726557588085989975288830070948234680e-13245"; 1| auto decimal = Decimal!4(str); 1| assert(decimal.toString == str, decimal.toString); | 1| decimal = Decimal!4.init; 1| assert(decimal.toString == "0.0"); | } | | /// | void toString(C = char, W)(scope ref W w, NumericSpec spec = NumericSpec.init) const | if(isSomeChar!C && isMutable!C) | { 146| assert(spec.format == NumericSpec.Format.exponent || spec.format == NumericSpec.Format.human); | import mir.utility: _expect; | // handle special values 73| if (_expect(exponent == exponent.max, false)) | { | static immutable C[3] nan = "nan"; | static immutable C[4] ninf = "-inf"; | static immutable C[4] pinf = "+inf"; 20| w.put(coefficient.length == 0 ? coefficient.sign ? ninf[] : pinf[] : nan[]); 8| return; | } | 65| C[coefficientBufferLength + 16] buffer0 = void; 65| auto buffer = buffer0[0 .. $ - 16]; | 65| size_t coefficientLength; | static if (size_t.sizeof == 8) | { 65| if (__ctfe) | { 0000000| uint[coefficient.data.length * 2] data; 0000000| foreach (i; 0 .. coefficient.length) | { 0000000| auto l = cast(uint)coefficient.data[i]; 0000000| auto h = cast(uint)(coefficient.data[i] >> 32); | version (LittleEndian) | { 0000000| data[i * 2 + 0] = l; 0000000| data[i * 2 + 1] = h; | } | else | { | data[$ - 1 - (i * 2 + 0)] = l; | data[$ - 1 - (i * 2 + 1)] = h; | } | } 0000000| auto work = BigUIntView!uint(data); 0000000| work = work.topLeastSignificantPart(coefficient.length * 2).normalized; 0000000| coefficientLength = work.toStringImpl(buffer); | } | else | { 65| BigInt!maxSize64 work = coefficient; 65| coefficientLength = work.view.unsigned.toStringImpl(buffer); | } | } | else | { | BigInt!maxSize64 work = coefficient; | coefficientLength = work.view.unsigned.toStringImpl(buffer); | } | 130| C[1] sign = coefficient.sign ? "-" : "+"; 116| bool addSign = coefficient.sign || spec.plus; 65| sizediff_t s = this.exponent + coefficientLength; | | alias zeros = zerosImpl!C; | 65| if (spec.format == NumericSpec.Format.human) | { 65| if (!spec.separatorCount) 0000000| spec.separatorCount = 3; | void putL(scope const(C)[] b) | { 11| assert(b.length); | 11| if (addSign) 2| w.put(sign[]); | 11| auto r = b.length % spec.separatorCount; 11| if (r == 0) 4| r = spec.separatorCount; 11| C[1] sep = spec.separatorChar; 11| goto LS; | do | { 18| w.put(sep[]); | LS: 29| w.put(b[0 .. r]); 29| b = b[r .. $]; 29| r = spec.separatorCount; | } 29| while(b.length); | } | | // try print decimal form without exponent | // up to 6 digits exluding leading 0. or final .0 65| if (s <= 0) | { | //0.001.... | //0.0001 | //0.00001 | //0.000001 | //If separatorChar is defined lets be less greed for space. 60| if (this.exponent >= -6 || s >= -2 - (spec.separatorChar != 0) * 3) | { 30| if (addSign) 8| w.put(sign[]); 30| w.put(zeros[0 .. -s + 2]); 30| w.put(buffer[$ - coefficientLength .. $]); 30| return; | } | } | else 28| if (this.exponent >= 0) | { | ///dddddd.0 18| if (!spec.separatorChar) | { 10| if (s <= 6) | { 8| buffer[$ - coefficientLength - 1] = sign[0]; 8| w.put(buffer[$ - coefficientLength - addSign .. $]); 8| w.put(zeros[$ - (this.exponent + 2) .. $]); 8| return; | } | } | else | { 8| if (s <= 12) | { 7| buffer0[$ - 16 .. $] = '0'; 7| putL(buffer0[$ - coefficientLength - 16 .. $ - 16 + this.exponent]); 7| w.put(zeros[$ - 2 .. $]); 7| return; | } | } | } | else | { | ///dddddd.0 10| if (!spec.separatorChar) | { | ///dddddd.d.... 7| if (s <= 6 || coefficientLength <= 6) | { 5| buffer[$ - coefficientLength - 1] = sign[0]; 5| w.put(buffer[$ - coefficientLength - addSign .. $ - coefficientLength + s]); | T2: 9| buffer[$ - coefficientLength + s - 1] = '.'; 9| w.put(buffer[$ - coefficientLength + s - 1 .. $]); 9| return; | } | } | else | { 4| if (s <= 12 || coefficientLength <= 12) | { 4| putL(buffer[$ - coefficientLength .. $ - coefficientLength + s]); 4| goto T2; | } | } | } | } | 11| assert(coefficientLength); | 11| sizediff_t exponent = s - 1; | 11| if (coefficientLength > 1) | { 9| auto c = buffer[$ - coefficientLength]; 9| buffer[$ - coefficientLength] = '.'; 9| buffer[$ - ++coefficientLength] = c; | } | 11| buffer[$ - coefficientLength - 1] = sign[0]; 11| w.put(buffer[$ - coefficientLength - addSign .. $]); | | import mir.format_impl: printSignedToTail; | | static if (sizediff_t.sizeof == 8) | enum N = 21; | else | enum N = 11; | | // prints e+/-exponent 11| auto expLength = printSignedToTail(exponent, buffer0[$ - N - 16 .. $ - 16], '+'); 11| buffer[$ - ++expLength] = spec.exponentChar; 11| w.put(buffer[$ - expLength .. $]); | } | | static if (maxSize64 == 3) | /// Check @nogc toString impl | version(mir_bignum_test) @safe pure @nogc unittest | { | import mir.format: stringBuf; 1| auto str = "5.28238923728e-876543210"; 1| auto decimal = Decimal!1(str); 2| stringBuf buffer; 1| buffer << decimal; 1| assert(buffer.data == str, buffer.data); | } | | /++ | Mir parsing supports up-to quadruple precision. The conversion error is 0 ULP for normal numbers. | Subnormal numbers with an exponent greater than or equal to -512 have upper error bound equal to 1 ULP. +/ | T opCast(T, bool wordNormalized = false, bool nonZero = false)() const | if (isFloatingPoint!T && isMutable!T) | { | | enum optimize = maxSize64 == 1 && size_t.sizeof == 8 && T.mant_dig < 64; | | version(LDC) | { | static if (optimize || wordNormalized) | pragma(inline, true); | } | | static if (optimize) | { | import mir.bignum.fixed: UInt; | import mir.bignum.fp: Fp, extendedMul; | import mir.bignum.internal.dec2flt_table; | import mir.bignum.low_level_view: MaxWordPow5, MaxFpPow5; | import mir.math.common: floor; | import mir.utility: _expect; | 17| T ret = 0; 17| size_t length = coefficient.length; | | | static if (!wordNormalized) | { 17| if (coefficient.data[0] == 0) 5| length = 0; | } | 17| if (_expect(exponent == exponent.max, false)) | { 20| ret = length ? T.nan : T.infinity; 10| goto R; | } | | static if (!nonZero) 7| if (length == 0) 1| goto R; | enum S = 9; | | Fp!64 load(typeof(exponent) e) | { 6| auto p10coeff = p10_coefficients[cast(sizediff_t)e - min_p10_e][0]; 6| auto p10exp = p10_exponents[cast(sizediff_t)e - min_p10_e]; 6| return Fp!64(false, p10exp, UInt!64(p10coeff)); | } | { 6| auto expSign = exponent < 0; 12| if (_expect((expSign ? -exponent : exponent) >>> S == 0, true)) | { | enum ulong mask = (1UL << (64 - T.mant_dig)) - 1; | enum ulong half = (1UL << (64 - T.mant_dig - 1)); | enum ulong bound = ulong(1) << T.mant_dig; | 6| auto c = Fp!64(UInt!64(coefficient.data[0])); 6| auto z = c.extendedMul(load(exponent)); 6| ret = cast(T) z; 6| long bitsDiff = (cast(ulong) z.opCast!(Fp!64).coefficient & mask) - half; 12| if (_expect((bitsDiff < 0 ? -bitsDiff : bitsDiff) > 3 * expSign, true)) 6| goto R; 0000000| if (!expSign && exponent <= MaxWordPow5!ulong || exponent == 0) 0000000| goto R; 0000000| if (expSign && MaxFpPow5!T >= -exponent && cast(ulong)c.coefficient < bound) | { 0000000| auto e = load(-exponent); 0000000| ret = c.opCast!(T, true) / cast(T) (cast(ulong)e.coefficient >> e.exponent); 0000000| goto R; | } 0000000| ret = algoR!T(ret, view.coefficient, cast(int) exponent); 0000000| goto R; | } 0000000| ret = expSign ? 0 : T.infinity; | } | R: 17| if (coefficient.sign) 8| ret = -ret; 17| return ret; | } | else | { 43| return view.opCast!(T, wordNormalized, nonZero); | } | } | | /// | bool isNaN() const @property | { 0000000| return exponent == exponent.max && coefficient.length; | } | | /// | bool isInfinity() const @property | { 0000000| return exponent == exponent.max && !coefficient.length; | } | | /// | ref opOpAssign(string op, size_t rhsMaxSize64)(ref const Decimal!rhsMaxSize64 rhs) @safe pure return | if (op == "+" || op == "-") | { 20| BigInt!rhsMaxSize64 rhsCopy; 20| BigIntView!(const size_t) rhsView; 20| auto expDiff = exponent - rhs.exponent; 20| if (expDiff >= 0) | { 11| exponent = rhs.exponent; 11| coefficient.mulPow5(expDiff); 11| coefficient.opOpAssign!"<<"(expDiff); 11| rhsView = rhs.coefficient.view; | } | else | { 9| rhsCopy.copyFrom(rhs.coefficient.view); 9| rhsCopy.mulPow5(-expDiff); 9| rhsCopy.opOpAssign!"<<"(-expDiff); 9| rhsView = rhsCopy.view; | } 20| coefficient.opOpAssign!op(rhsView); 20| return this; | } | | static if (maxSize64 == 3) | /// | version(mir_bignum_test) @safe pure @nogc unittest | { | import std.stdio; 1| auto a = Decimal!1("777.7"); 1| auto b = Decimal!1("777"); | import mir.format; 1| assert(stringBuf() << cast(double)a - cast(double)b << getData == "0.7000000000000455"); 1| a -= b; 1| assert(stringBuf() << a << getData == "0.7"); | 1| a = Decimal!1("-777.7"); 1| b = Decimal!1("777"); 1| a += b; 1| assert(stringBuf() << a << getData == "-0.7"); | 1| a = Decimal!1("777.7"); 1| b = Decimal!1("-777"); 1| a += b; 1| assert(stringBuf() << a << getData == "0.7"); | 1| a = Decimal!1("777"); 1| b = Decimal!1("777.7"); 1| a -= b; 1| assert(stringBuf() << a << getData == "-0.7"); | } |} | |/// |version(mir_bignum_test) |@safe pure nothrow @nogc |unittest |{ | import mir.conv: to; 1| Decimal!3 decimal; 1| DecimalExponentKey key; | | import mir.math.constant: PI; 1| assert(decimal.fromStringImpl("3.141592653589793378e-10", key)); 1| assert(cast(double) decimal == double(PI) / 1e10); 1| assert(key == DecimalExponentKey.e); |} | | |/// |version(mir_bignum_test) |@safe pure nothrow @nogc |unittest |{ | import mir.conv: to; 1| Decimal!3 decimal; 1| DecimalExponentKey key; | 1| assert(decimal.fromStringImpl("0", key)); 1| assert(key == DecimalExponentKey.none); 1| assert(decimal.exponent == 0); 1| assert(decimal.coefficient.length == 0); 1| assert(!decimal.coefficient.sign); 1| assert(cast(double) decimal.coefficient == 0); | 1| assert(decimal.fromStringImpl("-0.0", key)); 1| assert(key == DecimalExponentKey.dot); 1| assert(decimal.exponent == -1); 1| assert(decimal.coefficient.length == 0); 1| assert(decimal.coefficient.sign); 1| assert(cast(double) decimal.coefficient == 0); | 1| assert(decimal.fromStringImpl("0e0", key)); 1| assert(key == DecimalExponentKey.e); 1| assert(decimal.exponent == 0); 1| assert(decimal.coefficient.length == 0); 1| assert(!decimal.coefficient.sign); 1| assert(cast(double) decimal.coefficient == 0); |} | |deprecated("use decimal.fromStringImpl insteade") |@trusted @nogc pure nothrow |bool parseDecimal(size_t maxSize64, C)(scope const(C)[] str, ref Decimal!maxSize64 decimal, out DecimalExponentKey key) | if (isSomeChar!C) |{ | return decimal.fromStringImpl(str, key); |} | | |version(mir_bignum_test) |@safe pure @nogc unittest |{ 1| Decimal!4 i = "-0"; | 1| assert(i.view.coefficient.coefficients.length == 0); 1| assert(i.coefficient.view.unsigned.coefficients.length == 0); 1| assert(i.coefficient.view == 0L); 1| assert(cast(long) i.coefficient == 0); |} source/mir/bignum/decimal.d is 91% covered <<<<<< EOF # path=./.dub-code-mir-algorithm-test-default-unittest-cov-posix.osx.darwin-x86_64-dmd_v2.097.2-CBCE2EF5AB377C2231318462D391AFCD_dub_test_root.lst |module dub_test_root; |import std.typetuple; |static import mir.algebraic_alias.json; |static import mir.algorithm.iteration; |static import mir.algorithm.setops; |static import mir.appender; |static import mir.array.allocation; |static import mir.bignum.decimal; |static import mir.bignum.fixed; |static import mir.bignum.fp; |static import mir.bignum.integer; |static import mir.bignum.internal.dec2flt_table; |static import mir.bignum.internal.ryu.generic_128; |static import mir.bignum.low_level_view; |static import mir.container.binaryheap; |static import mir.cpp_export.numeric; |static import mir.date; |static import mir.format; |static import mir.format_impl; |static import mir.graph.tarjan; |static import mir.interpolate.constant; |static import mir.interpolate.generic; |static import mir.interpolate.linear; |static import mir.interpolate.polynomial; |static import mir.interpolate.spline; |static import mir.interpolate.utility; |static import mir.lob; |static import mir.math.func.expdigamma; |static import mir.math.numeric; |static import mir.math.stat; |static import mir.math.sum; |static import mir.ndslice.allocation; |static import mir.ndslice.chunks; |static import mir.ndslice.concatenation; |static import mir.ndslice.connect.cpython; |static import mir.ndslice.dynamic; |static import mir.ndslice.field; |static import mir.ndslice.filling; |static import mir.ndslice.fuse; |static import mir.ndslice.internal; |static import mir.ndslice.iterator; |static import mir.ndslice.mutation; |static import mir.ndslice.ndfield; |static import mir.ndslice.slice; |static import mir.ndslice.sorting; |static import mir.ndslice.topology; |static import mir.ndslice.traits; |static import mir.numeric; |static import mir.parse; |static import mir.polynomial; |static import mir.range; |static import mir.rc.array; |static import mir.rc.context; |static import mir.rc.ptr; |static import mir.rc.slim_ptr; |static import mir.serde; |static import mir.series; |static import mir.small_array; |static import mir.small_string; |static import mir.string_map; |static import mir.timestamp; |static import mir.type_info; |alias allModules = TypeTuple!(mir.algebraic_alias.json, mir.algorithm.iteration, mir.algorithm.setops, m