Implement a select

Implement a select

And run the select.

--- CodePlayground/Program.cs
+++ CodePlayground/Program.cs
@@ -16,6 +16,14 @@
                 if (predicate(item))
                     yield return item;
         }
+
+        public static IEnumerable<TResult> Select<TSource, TResult>(
+            this IEnumerable<TSource> inputSequence,
+            Func<TSource, TResult> transform)
+        {
+            foreach (TSource item in inputSequence)
+                yield return transform(item);
+        }
     }
     class Program
     {
@@ -24,6 +32,10 @@
             // Generate items using a factory:
             var items = GenerateSequence(i => i.ToString());
             foreach (var item in items.Where(item => item.Length < 2))
+                Console.WriteLine(item);
+
+            foreach (var item in items.Select(item =>
+                new string(item.PadRight(9).Reverse().ToArray())))
                 Console.WriteLine(item);

             return;


Prevent setting the hovertext from firing changed_color

--- OpenSim/Region/Framework/Scenes/SceneObjectPart.cs
+++ OpenSim/Region/Framework/Scenes/SceneObjectPart.cs
@@ -871,7 +871,6 @@
             set
             {
                 m_color = value;
-                TriggerScriptChangedEvent(Changed.COLOR);

                 /* ScheduleFullUpdate() need not be called b/c after
                  * setting the color, the text will be set, so then


Upgrade Program.cs to Asp.net Core 2.1 #127

--- src/Fan.Web/Program.cs
+++ src/Fan.Web/Program.cs
@@ -38,7 +38,7 @@
             {
                 Log.Information("Starting web host");

-                BuildWebHost(args).Run();
+                CreateWebHostBuilder(args).Build().Run();
             }
             catch (Exception ex)
             {
@@ -51,10 +51,9 @@
         }

         /// <summary>
-        /// Build WebHost.
+        /// Returns a <see cref="IWebHostBuilder"/>.
         /// </summary>
         /// <param name="args"></param>
-        /// <returns></returns>
         /// <remarks>
         /// It's built with <see href="https://github.com/aspnet/MetaPackages/blob/dev/src/Microsoft.AspNetCore/WebHost.cs#L148">CreateDefaultBuilder</see>
         /// it loads confiuration from these providers in this order:
@@ -67,11 +66,10 @@
         /// <see href="https://github.com/serilog/serilog-aspnetcore/issues/3">but I'm using Serilog in place of those</see>,
         /// the configuration of Serilog is at the beginning of the Main method.
         /// </remarks>
-        public static IWebHost BuildWebHost(string[] args) =>
+        public static IWebHostBuilder CreateWebHostBuilder(string[] args) =>
             WebHost.CreateDefaultBuilder(args)
                 .UseApplicationInsights()
                 .UseSerilog()
-                .UseStartup<Startup>()
-                .Build();
+                .UseStartup<Startup>();
     }
 }


Added FatDirectoryEntryMetadata api docs

--- source/Cosmos.System2/FileSystem/FAT/Listing/FatDirectoryEntryMetadata.cs
+++ source/Cosmos.System2/FileSystem/FAT/Listing/FatDirectoryEntryMetadata.cs
@@ -1,8 +1,17 @@
 namespace Cosmos.System.FileSystem.FAT.Listing
 {
+    /// <summary>
+    /// Fat directory entry metadata class.
+    /// </summary>
     public sealed class FatDirectoryEntryMetadata
     {
+        /// <summary>
+        /// Entry data offset.
+        /// </summary>
         public readonly uint DataOffset;
+        /// <summary>
+        /// Entry data length.
+        /// </summary>
         public readonly uint DataLength;

         private FatDirectoryEntryMetadata(uint aDataOffset, uint aDataLength)
@@ -11,30 +20,90 @@
             DataLength = aDataLength;
         }

+        /// <summary>
+        /// Directory entry first byte matadata.
+        /// </summary>
         public static readonly FatDirectoryEntryMetadata FirstByte = new FatDirectoryEntryMetadata(0x00, 1);
+        /// <summary>
+        /// Directory entry short name matadata.
+        /// </summary>
         public static readonly FatDirectoryEntryMetadata ShortName = new FatDirectoryEntryMetadata(0x00, 11);
+        /// <summary>
+        /// Directory entry attributes matadata.
+        /// </summary>
         public static readonly FatDirectoryEntryMetadata Attributes = new FatDirectoryEntryMetadata(0x0B, 1);
+        /// <summary>
+        /// Directory entry NTReserved matadata.
+        /// </summary>
         public static readonly FatDirectoryEntryMetadata NTReserved = new FatDirectoryEntryMetadata(0x0C, 1);
+        /// <summary>
+        /// Directory entry creation time tenths of a second matadata.
+        /// </summary>
         public static readonly FatDirectoryEntryMetadata CreationTimeTenthsOfASecond = new FatDirectoryEntryMetadata(0x0D, 1);
+        /// <summary>
+        /// Directory entry creation time matadata.
+        /// </summary>
         public static readonly FatDirectoryEntryMetadata CreationTime = new FatDirectoryEntryMetadata(0x0E, 2);
+        /// <summary>
+        /// Directory entry creation date matadata.
+        /// </summary>
         public static readonly FatDirectoryEntryMetadata CreationDate = new FatDirectoryEntryMetadata(0x10, 2);
+        /// <summary>
+        /// Directory entry last accessed date matadata.
+        /// </summary>
         public static readonly FatDirectoryEntryMetadata AccessedDate = new FatDirectoryEntryMetadata(0x12, 2);
+        /// <summary>
+        /// Directory entry first cluster high matadata.
+        /// </summary>
         public static readonly FatDirectoryEntryMetadata FirstClusterHigh = new FatDirectoryEntryMetadata(0x14, 2);
+        /// <summary>
+        /// Directory entry modified time matadata.
+        /// </summary>
         public static readonly FatDirectoryEntryMetadata ModifiedTime = new FatDirectoryEntryMetadata(0x16, 2);
+        /// <summary>
+        /// Directory entry modified date matadata.
+        /// </summary>
         public static readonly FatDirectoryEntryMetadata ModifiedDate = new FatDirectoryEntryMetadata(0x18, 2);
+        /// <summary>
+        /// Directory entry first cluster low matadata.
+        /// </summary>
         public static readonly FatDirectoryEntryMetadata FirstClusterLow = new FatDirectoryEntryMetadata(0x1A, 2);
+        /// <summary>
+        /// Directory entry size matadata.
+        /// </summary>
         public static readonly FatDirectoryEntryMetadata Size = new FatDirectoryEntryMetadata(0x1C, 4);

+        /// <summary>
+        /// Long filename entry metadata class. This class can not be inherited.
+        /// </summary>
         public sealed class LongFilenameEntryMetadata
         {
+            /// <summary>
+            /// Directory entry sequence number and allocation status matadata.
+            /// </summary>
             public static readonly FatDirectoryEntryMetadata SequenceNumberAndAllocationStatus = new FatDirectoryEntryMetadata(0x00, 1);
+            /// <summary>
+            /// Directory entry LongName1 matadata.
+            /// </summary>
             public static readonly FatDirectoryEntryMetadata LongName1 = new FatDirectoryEntryMetadata(0x01, 10);
+            /// <summary>
+            /// Directory entry attributes matadata.
+            /// </summary>
             public static readonly FatDirectoryEntryMetadata Attributes = new FatDirectoryEntryMetadata(0x0B, 1);
             //public static readonly FatDirectoryEntryMetadata Reserved1 = new FatDirectoryEntryMetadata(0x0C, 1);
+            /// <summary>
+            /// Directory entry checksum matadata.
+            /// </summary>
             public static readonly FatDirectoryEntryMetadata Checksum = new FatDirectoryEntryMetadata(0x0D, 1);
+            /// <summary>
+            /// Directory entry LongName2 matadata.
+            /// </summary>
             public static readonly FatDirectoryEntryMetadata LongName2 = new FatDirectoryEntryMetadata(0x0E, 12);
             //public static readonly FatDirectoryEntryMetadata Reserved2 = new FatDirectoryEntryMetadata(0x1A, 2);
+            /// <summary>
+            /// Directory entry LongName3 matadata.
+            /// </summary>
             public static readonly FatDirectoryEntryMetadata LongName3 = new FatDirectoryEntryMetadata(0x1C, 4);
         }
     }
-}+}


Guard against null root part on SQLite. This really needs to be fixed so SQLite loads roots before children like MySQL does.

--- OpenSim/Region/Framework/Scenes/SceneObjectPart.cs
+++ OpenSim/Region/Framework/Scenes/SceneObjectPart.cs
@@ -4285,7 +4285,7 @@

         public void aggregateScriptEvents()
         {
-            if (ParentGroup.RootPart == null)
+            if (ParentGroup == null || ParentGroup.RootPart == null)
                 return;

             AggregateScriptEvents = 0;


Added helperName to the HandlebarsRuntimeException message

More detailed error message here would help debug issues.

--- source/Handlebars/Compiler/Translation/Expression/HelperFunctionBinder.cs
+++ source/Handlebars/Compiler/Translation/Expression/HelperFunctionBinder.cs
@@ -98,7 +98,7 @@
             }
             else
             {
-                throw new HandlebarsRuntimeException("Template references a helper that is not registered");
+                throw new HandlebarsRuntimeException(string.Format("Template references a helper that is not registered. Could not find helper '{0}'", helperName));
             }
         }
     }


BulletSim: improve angularVerticalAttraction calculation to compute angular correction velocity rather than estimating correction (excuse to use trig functions).

--- OpenSim/Region/Physics/BulletSPlugin/BSDynamics.cs
+++ OpenSim/Region/Physics/BulletSPlugin/BSDynamics.cs
@@ -1003,7 +1003,6 @@
                 // Not colliding if the vehicle is off the ground
                 if (!Prim.IsColliding)
                 {
-                    // downForce = new Vector3(0, 0, (-distanceAboveGround / m_bankingTimescale) * pTimestep);
                     // downForce = new Vector3(0, 0, -distanceAboveGround / m_bankingTimescale);
                     ret = new Vector3(0, 0, -distanceAboveGround);
                 }
@@ -1135,31 +1134,30 @@
                 //         zero and one.
                 // The value of Z is how far the rotation is off with 1 meaning none and 0 being 90 degrees.

+                // Y error means needed rotation around X axis and visa versa.
+                // Since the error goes from zero to one, the asin is the corresponding angle.
+                ret.X = (float)Math.Asin(verticalError.Y);
+                ret.Y = (float)Math.Asin(verticalError.X);
+
                 // If verticalError.Z is negative, the vehicle is upside down. Add additional push.
                 if (verticalError.Z < 0f)
                 {
-                    verticalError.X = 2f - verticalError.X;
-                    verticalError.Y = 2f - verticalError.Y;
-                }
-
-                // Y error means needed rotation around X axis and visa versa.
-                ret.X =    verticalError.Y;
-                ret.Y =  - verticalError.X;
-                ret.Z = 0f;
-
-                // Scale the correction force by how far we're off from vertical.
-                // Z error of one says little error. As Z gets smaller, the vehicle is leaning farther over.
-                float clampedSqrZError = ClampInRange(0.01f, verticalError.Z * verticalError.Z, 1f);
-                float vertForce = 1f / clampedSqrZError;
-
-                ret *= vertForce;
-
-                // Correction happens over a number of seconds.
+                    ret.X += (float)Math.PI / 4f;
+                    ret.Y += (float)Math.PI / 4f;
+                }
+
+                // Put the signs back on so the rotation is in the correct direction.
+                ret.X *= (float)Math.Sign(verticalError.Y);
+                // (Tilt forward (positive X) needs to tilt back (rotate negative) around Y axis.)
+                ret.Y *= -(float)Math.Sign(verticalError.X);
+
+                // 'ret' is now the necessary velocity to correct tilt in one second.
+                //     Correction happens over a number of seconds.
                 Vector3 unscaledContrib = ret;
                 ret /= m_verticalAttractionTimescale;

-                VDetailLog("{0},  MoveAngular,verticalAttraction,,verticalError={1},unscaled={2},vertForce={3},eff={4},vertAttr={5}",
-                                Prim.LocalID, verticalError, unscaledContrib, vertForce, m_verticalAttractionEfficiency, ret);
+                VDetailLog("{0},  MoveAngular,verticalAttraction,,verticalError={1},unscaled={2},eff={3},vertAttr={4}",
+                                Prim.LocalID, verticalError, unscaledContrib, m_verticalAttractionEfficiency, ret);
             }
             return ret;
         }
@@ -1172,7 +1170,6 @@
         public Vector3 ComputeAngularDeflection()
         {
             Vector3 ret = Vector3.Zero;
-            return ret;     // DEBUG DEBUG DEBUG  debug one force at a time

             if (m_angularDeflectionEfficiency != 0)
             {


limit !download to spam channels

limit !download to spam channels

--- CompatBot/Commands/Psn.cs
+++ CompatBot/Commands/Psn.cs
@@ -62,12 +62,13 @@

         public static async Task SearchForGame(CommandContext ctx, [RemainingText] string search)
         {
+            var ch = await ctx.GetChannelForSpamAsync().ConfigureAwait(false);
             DiscordMessage msg = null;
             if (string.IsNullOrEmpty(search))
             {
                 var interact = ctx.Client.GetInteractivity();
-                msg = await msg.UpdateOrCreateMessageAsync(ctx.Channel, "What game are you looking for?").ConfigureAwait(false);
-                var response = await interact.WaitForMessageAsync(m => m.Author == ctx.User).ConfigureAwait(false);
+                msg = await msg.UpdateOrCreateMessageAsync(ch, "What game are you looking for?").ConfigureAwait(false);
+                var response = await interact.WaitForMessageAsync(m => m.Author == ctx.User && m.Channel == ch).ConfigureAwait(false);
                 await msg.DeleteAsync().ConfigureAwait(false);
                 msg = null;
                 if (string.IsNullOrEmpty(response?.Message?.Content))
@@ -77,7 +78,7 @@
                 }
                 search = response.Message.Content;
             }
-            var msgTask = msg.UpdateOrCreateMessageAsync(ctx.Channel, "⏳ Searching...");
+            var msgTask = msg.UpdateOrCreateMessageAsync(ch, "⏳ Searching...");
             var psnResponseUSTask = Client.SearchAsync("en-US", search, Config.Cts.Token);
             var psnResponseEUTask = Client.SearchAsync("en-GB", search, Config.Cts.Token);
             var psnResponseJPTask = Client.SearchAsync("ja-JP", search, Config.Cts.Token);
@@ -86,7 +87,7 @@
             var responseEU = await psnResponseEUTask.ConfigureAwait(false);
             var responseJP = await psnResponseJPTask.ConfigureAwait(false);
             msg = await msgTask.ConfigureAwait(false);
-            msg = await msg.UpdateOrCreateMessageAsync(ctx.Channel, "⌛ Preparing results...").ConfigureAwait(false);
+            msg = await msg.UpdateOrCreateMessageAsync(ch, "⌛ Preparing results...").ConfigureAwait(false);
             var usGame = GetBestMatch(responseUS.Included, search);
             var euGame = GetBestMatch(responseEU.Included, search);
             var jpGame = GetBestMatch(responseJP.Included, search);
@@ -107,12 +108,12 @@
                 result.WithFooter("Test instance");
 #endif
                 hasResults = true;
-                await ctx.RespondAsync(embed: result).ConfigureAwait(false);
+                await ch.SendMessageAsync(embed: result).ConfigureAwait(false);
             }
             if (hasResults)
                 await msg.DeleteAsync().ConfigureAwait(false);
             else
-                await msg.UpdateOrCreateMessageAsync(ctx.Channel, "No results").ConfigureAwait(false);
+                await msg.UpdateOrCreateMessageAsync(ch, "No results").ConfigureAwait(false);
         }

         private static ContainerIncluded GetBestMatch(ContainerIncluded[] included, string search)


Load correct build information when showing the match overview

--- HeroesParserData/ViewModels/Match/MatchOverviewContext.cs
+++ HeroesParserData/ViewModels/Match/MatchOverviewContext.cs
@@ -304,6 +304,9 @@
             Replay replay = Query.Replay.ReadReplayIncludeRecord(replayId);
             var playersList = replay.ReplayMatchPlayers.ToList();

+            // load up correct build information
+            HeroesInfo.ReInitializeSpecificHeroesXml(replay.ReplayBuild);
+
             FindPlayerParties(playersList);

             foreach (var player in playersList)


adding back ImHashMap.Branch3 and -170 loc

--- src/ImTools/ImTools.cs
+++ src/ImTools/ImTools.cs
@@ -2957,7 +2957,7 @@
         internal virtual Entry GetMaxHashEntryOrDefault() => null;

         /// Lookup for the entry by hash. If nothing the method returns `null`
-        internal virtual Entry GetEntryOrDefault(int hash) => null;
+        internal virtual Entry GetEntryOrNull(int hash) => null;

         /// Returns the found entry with the same hash or the new map with added new entry.
         /// Note that the empty map will return the entry the same as if the entry was found - so the consumer should check for the empty map.
@@ -2981,7 +2981,7 @@
             internal override Entry GetMinHashEntryOrDefault() => this;
             internal override Entry GetMaxHashEntryOrDefault() => this;

-            internal sealed override Entry GetEntryOrDefault(int hash) => hash == Hash ? this : null;
+            internal sealed override Entry GetEntryOrNull(int hash) => hash == Hash ? this : null;

             internal sealed override ImHashMap<K, V> AddOrGetEntry(int hash, Entry entry) =>
                 hash > Hash ? new Leaf2(this, entry) : hash < Hash ? new Leaf2(entry, this) : (ImHashMap<K, V>)this;
@@ -3012,7 +3012,7 @@
             internal override Entry GetMinHashEntryOrDefault() => Entry0;
             internal override Entry GetMaxHashEntryOrDefault() => Entry1;

-            internal override Entry GetEntryOrDefault(int hash) =>
+            internal override Entry GetEntryOrNull(int hash) =>
                 Entry0.Hash == hash ? Entry0 : Entry1.Hash == hash ? Entry1 : null;

             internal override ImHashMap<K, V> AddOrGetEntry(int hash, Entry entry) =>
@@ -3045,7 +3045,7 @@
             internal sealed override Entry GetMinHashEntryOrDefault() => Plus.Hash < L.Entry0.Hash ? Plus : L.Entry0;
             internal sealed override Entry GetMaxHashEntryOrDefault() => Plus.Hash > L.Entry1.Hash ? Plus : L.Entry1;

-            internal override Entry GetEntryOrDefault(int hash)
+            internal override Entry GetEntryOrNull(int hash)
             {
                 if (hash == Plus.Hash)
                     return Plus;
@@ -3102,7 +3102,7 @@
                 return Plus.Hash > m.Hash ? Plus : m;
             }

-            internal override Entry GetEntryOrDefault(int hash)
+            internal override Entry GetEntryOrNull(int hash)
             {
                 if (hash == Plus.Hash)
                     return Plus;
@@ -3214,7 +3214,7 @@
             internal sealed override Entry GetMinHashEntryOrDefault() => Entry0;
             internal sealed override Entry GetMaxHashEntryOrDefault() => Entry4;

-            internal override Entry GetEntryOrDefault(int hash) =>
+            internal override Entry GetEntryOrNull(int hash) =>
                 hash == Entry0.Hash ? Entry0 :
                 hash == Entry1.Hash ? Entry1 :
                 hash == Entry2.Hash ? Entry2 :
@@ -3265,7 +3265,7 @@
             internal sealed override Entry GetMinHashEntryOrDefault() => Plus.Hash < L.Entry0.Hash ? Plus : L.Entry0;
             internal sealed override Entry GetMaxHashEntryOrDefault() => Plus.Hash > L.Entry4.Hash ? Plus : L.Entry4;

-            internal override Entry GetEntryOrDefault(int hash)
+            internal override Entry GetEntryOrNull(int hash)
             {
                 if (hash == Plus.Hash)
                     return Plus;
@@ -3375,7 +3375,7 @@
                 return Plus.Hash > m.Hash ? Plus : m;
             }

-            internal override Entry GetEntryOrDefault(int hash)
+            internal override Entry GetEntryOrNull(int hash)
             {
                 if (hash == Plus.Hash)
                     return Plus;
@@ -3615,11 +3615,11 @@
             internal sealed override Entry GetMinHashEntryOrDefault() => Left .GetMinHashEntryOrDefault();
             internal sealed override Entry GetMaxHashEntryOrDefault() => Right.GetMaxHashEntryOrDefault();

-            internal override Entry GetEntryOrDefault(int hash)
-            {
-                var mh = MidEntry.Hash;
-                return hash > mh ? Right.GetEntryOrDefault(hash)
-                    :  hash < mh ? Left .GetEntryOrDefault(hash)
+            internal override Entry GetEntryOrNull(int hash)
+            {
+                var h = MidEntry.Hash;
+                return hash > h ? Right.GetEntryOrNull(hash)
+                    :  hash < h ? Left .GetEntryOrNull(hash)
                     :  MidEntry;
             }

@@ -3630,18 +3630,22 @@
                 {
                     var right = Right;
                     var newRight = right.AddOrGetEntry(hash, entry);
-                    return newRight is Entry ? newRight
-                         : right.GetType() != typeof(Branch2) && newRight.GetType() == typeof(Branch2)
-                         ? new RightyBranch3(Left, e, newRight) : new Branch2(Left, e, newRight);
+                    if (newRight is Entry)
+                        return newRight;
+                    if ((right is Branch3 || right is Leaf5Plus1Plus1) && newRight is Branch2 rb)
+                        return new Branch3(Left, e, rb.Left, rb.MidEntry, rb.Right); // todo: @perf reuse the destructed branch2 or don't create it in the first place
+                    return new Branch2(Left, e, newRight);
                 }

                 if (hash < e.Hash)
                 {
                     var left = Left;
                     var newLeft = left.AddOrGetEntry(hash, entry);
-                    return newLeft is Entry ? newLeft
-                         : left.GetType() != typeof(Branch2) && newLeft.GetType() == typeof(Branch2)
-                         ? new LeftyBranch3(newLeft, e, Right) : new Branch2(newLeft, e, Right);
+                    if (newLeft is Entry)
+                        return newLeft;
+                    if ((left is Branch3 || left is Leaf5Plus1Plus1) && newLeft is Branch2 lb)
+                        return new Branch3(lb.Left, lb.MidEntry, lb.Right, e, Right); // todo: @perf reuse the destructed branch2 or don't create it in the first place
+                    return new Branch2(newLeft, e, Right);
                 }

                 return e;
@@ -3678,21 +3682,15 @@
                     }

                     //*rebalance needed: the branch was merged from Br2 to Br3 or to the leaf and the height decreased
-                    if (Right.GetType() == typeof(Branch2) && newRight.GetType() != typeof(Branch2))
+                    if (Right is Branch2 && newRight is Branch2 == false)
                     {
                         // the the hole has a 2-node as a parent and a 3-node as a sibling.
-                        if (Left is LeftyBranch3 ll) //! the height does not change
-                            return new Branch2(ll.Left, ll.MidEntry, new Branch2(ll.Right, mid, newRight));
-
-                        // the the hole has a 2-node as a parent and a 3-node as a sibling.
-                        if (Left is RightyBranch3 lr) //! the height does not change
-                        {
-                            var lrr = (Branch2)lr.Right;
-                            return new Branch2(new Branch2(lr.Left, lr.MidEntry, lrr.Left), lrr.MidEntry, new Branch2(lrr.Right, mid, newRight));
-                        }
+                        if (Left is Branch3 lb3) //! the height does not change
+                            return new Branch2(new Branch2(lb3.Left, lb3.Entry0, lb3.Middle), lb3.Entry1, new Branch2(lb3.Right, mid, newRight));

                         // the the hole has a 2-node as a parent and a 2-node as a sibling.
-                        return new LeftyBranch3(Left, mid, newRight);
+                        var lb2 = (Branch2)Left;
+                        return new Branch3(lb2.Left, lb2.MidEntry, lb2.Right, mid, newRight);
                     }

                     return new Branch2(Left, mid, newRight);
@@ -3712,70 +3710,73 @@
                 }

                 //*rebalance needed: the branch was merged from Br2 to Br3 or to the leaf and the height decreased
-                if (Left.GetType() == typeof(Branch2) && newLeft.GetType() != typeof(Branch2))
+                if (Left is Branch2 && newLeft is Branch2 == false)
                 {
                     // the the hole has a 2-node as a parent and a 3-node as a sibling.
-                    if (Right is LeftyBranch3 rl) //! the height does not change
-                    {
-                        var rll = (Branch2)rl.Left;
-                        return new Branch2(new Branch2(newLeft, mid, rll.Left), rll.MidEntry, new Branch2(rll.Right, rl.MidEntry, rl.Right));
-                    }
-
-                    // the the hole has a 2-node as a parent and a 3-node as a sibling.
-                    if (Right is RightyBranch3 rr) //! the height does not change
-                        return new Branch2(new Branch2(newLeft, mid, rr.Left), rr.MidEntry, rr.Right);
+                    if (Right is Branch3 rb3) //! the height does not change
+                        return new Branch2(new Branch2(newLeft, mid, rb3.Left), rb3.Entry0, new Branch2(rb3.Middle, rb3.Entry0, rb3.Right));

                     // the the hole has a 2-node as a parent and a 2-node as a sibling.
-                    return new RightyBranch3(newLeft, mid, Right);
+                    var rb2 = (Branch2)Right;
+                    return new Branch3(newLeft, mid, rb2.Left, rb2.MidEntry, rb2.Right);
                 }

                 return new Branch2(newLeft, mid, Right);
             }
         }

-        /// Right-skewed Branch of 3 - actually a branch of 2 with the right branch of 2
-        internal sealed class RightyBranch3 : Branch2
-        {
-            public RightyBranch3(ImHashMap<K, V> left, Entry entry, ImHashMap<K, V>  rightBranch) : base(left, entry, rightBranch)
-            {
-                Debug.Assert(rightBranch is Branch2, $"Right:{rightBranch} is Branch2");
-            }
+        /// Branch of 3 with 2 nodes in between
+        internal sealed class Branch3 : ImHashMap<K, V>
+        {
+            public readonly Entry Entry0, Entry1;
+            public readonly ImHashMap<K, V> Left, Middle, Right;
+
+            public Branch3(ImHashMap<K, V> left, Entry e0, ImHashMap<K, V> middle, Entry e1, ImHashMap<K, V> right)
+            {
+                Debug.Assert(e0.Hash < e1.Hash, $"e0.Hash:{e0.Hash} < e1.Hash{e1.Hash}");
+                Left   = left;
+                Entry0 = e0;
+                Middle = middle;
+                Entry1 = e1;
+                Right  = right;
+            }
+
+            public override int Count() => Entry0.Count() + Entry1.Count() + Left.Count() + Middle.Count() + Right.Count();

 #if !DEBUG
-            public override string ToString() => "{RB3:{"  + base.ToString() + "}";
+            public override string ToString() => "{B3:{E0:" + Entry0 + ",E1:" + Entry0 + ",L:" + Left + ",M:" + Middle + ",R:" + Right + "}}";
 #endif

-            internal override Entry GetEntryOrDefault(int hash)
-            {
-                var mh = MidEntry.Hash;
-                if (mh > hash)
-                    return Left.GetEntryOrDefault(hash);
-                if (mh < hash)
-                {
-                    var r = (Branch2)Right;
-                    mh = r.MidEntry.Hash;
-                    return hash > mh ? r.Right.GetEntryOrDefault(hash)
-                        :  hash < mh ? r.Left .GetEntryOrDefault(hash)
-                        :  r.MidEntry;
-                }
-                return MidEntry;
+            internal override Entry GetMinHashEntryOrDefault() => Left .GetMinHashEntryOrDefault();
+            internal override Entry GetMaxHashEntryOrDefault() => Right.GetMaxHashEntryOrDefault();
+
+            internal override Entry GetEntryOrNull(int hash)
+            {
+                var h1 = Entry1.Hash;
+                if (hash > h1)
+                    return Right.GetEntryOrNull(hash);
+                var h0 = Entry0.Hash;
+                if (hash < h0)
+                    return Left.GetEntryOrNull(hash);
+                if (h0 == hash)
+                    return Entry0;
+                if (h1 == hash)
+                    return Entry1;
+                return Middle.GetEntryOrNull(hash);
             }

             internal override ImHashMap<K, V> AddOrGetEntry(int hash, Entry entry)
             {
-                var h0 = MidEntry.Hash;
-                var rb = (Branch2)Right;
-                var h1 = rb.MidEntry.Hash;
-
+                int h0 = Entry0.Hash, h1 = Entry1.Hash;
                 if (hash > h1)
                 {
-                    var right = rb.Right;
+                    var right = Right;
                     var newRight = right.AddOrGetEntry(hash, entry);
                     if (newRight is Entry)
                         return newRight;
-                    if (right.GetType() != typeof(Branch2) && newRight.GetType() == typeof(Branch2))
-                        return new Branch2(new Branch2(Left, MidEntry, rb.Left), rb.MidEntry, newRight);
-                    return new RightyBranch3(Left, MidEntry, new Branch2(rb.Left, rb.MidEntry, newRight));
+                    if ((right is Leaf5Plus1Plus1 || right is Branch3) && newRight is Branch2)
+                        return new Branch2(new Branch2(Left, Entry0, Middle), Entry1, newRight);
+                    return new Branch3(Left, Entry0, Middle, Entry1, newRight);
                 }

                 if (hash < h0)
@@ -3784,43 +3785,40 @@
                     var newLeft = left.AddOrGetEntry(hash, entry);
                     if (newLeft is Entry)
                         return newLeft;
-                    if (left.GetType() != typeof(Branch2) && newLeft.GetType() == typeof(Branch2))
-                        return new Branch2(newLeft, MidEntry, rb);
-                    return new RightyBranch3(newLeft, MidEntry, rb);
+                    if ((left is Leaf5Plus1Plus1 || left is Branch3) && newLeft is Branch2)
+                        return new Branch2(newLeft, Entry0, new Branch2(Middle, Entry1, Right));
+                    return new Branch3(newLeft, Entry0, Middle, Entry1, Right);
                 }

                 if (hash > h0 && hash < h1)
                 {
-                    var middle = rb.Left;
+                    var middle = Middle;
                     var newMiddle = middle.AddOrGetEntry(hash, entry);
                     if (newMiddle is Entry)
                         return newMiddle;
-                    if (middle.GetType() != typeof(Branch2) && newMiddle.GetType() == typeof(Branch2))
-                    {
-                        var nmb2 = (Branch2)newMiddle;
-                        return new Branch2(new Branch2(Left, MidEntry, nmb2.Left), nmb2.MidEntry, new Branch2(nmb2.Right, rb.MidEntry, rb.Right));
-                    }
-                    return new RightyBranch3(Left, MidEntry, new Branch2(newMiddle, rb.MidEntry, rb.Right));
-                }
-
-                return hash == h0 ? MidEntry : rb.MidEntry;
+                    if ((middle is Leaf5Plus1Plus1 || middle is Branch3) && newMiddle is Branch2 mb)
+                        return new Branch2(new Branch2(Left, Entry0, mb.Left), mb.MidEntry, new Branch2(mb.Right, Entry1, Right));
+                    return new Branch3(Left, Entry0, newMiddle, Entry1, Right);
+                }
+
+                return hash == h0 ? Entry0 : Entry1;
             }

             internal override ImHashMap<K, V> ReplaceEntry(int hash, Entry oldEntry, Entry newEntry)
             {
-                var h = MidEntry.Hash;
-                return hash > h ? new RightyBranch3(Left, MidEntry, Right.ReplaceEntry(hash, oldEntry, newEntry))
-                    :  hash < h ? new RightyBranch3(Left.ReplaceEntry(hash, oldEntry, newEntry), MidEntry, Right)
-                    :  new RightyBranch3(Left, newEntry, Right);
+                int h0 = Entry0.Hash, h1 = Entry1.Hash;
+                return hash > h1 ? new Branch3(Left, Entry0, Middle, Entry1, Right.ReplaceEntry(hash, oldEntry, newEntry))
+                    :  hash < h0 ? new Branch3(Left.ReplaceEntry(hash, oldEntry, newEntry), Entry0, Middle, Entry1, Right)
+                    :  hash > h0 && hash < h1 ? new Branch3(Left, Entry0, Middle.ReplaceEntry(hash, oldEntry, newEntry), Entry1, Right)
+                    :  oldEntry == Entry0 ? new Branch3(Left, newEntry, Middle, Entry1, Right) : new Branch3(Left, Entry0, Middle, newEntry, Right);
             }

             internal override ImHashMap<K, V> RemoveEntry(Entry removedEntry)
             {
-                var midLeft = MidEntry;
-                var rb = (Branch2)Right;
-                var middle   = rb.Left;
-                var midRight = rb.MidEntry;
-                var right    = rb.Right;
+                var midLeft  = Entry0;
+                var middle   = Middle;
+                var midRight = Entry1;
+                var right    = Right;

                 // case 1, downward: swap the predecessor entry (max left entry) with the mid entry, then proceed to remove the predecessor from the Left branch
                 if (removedEntry == midLeft)
@@ -3833,28 +3831,22 @@
                     {
                         if (middle is Leaf2Plus1Plus1 == false)
                             return new Branch2(middle.AddOrGetEntry(midLeft.Hash, midLeft), midRight, right); //! the height does not change
-                        return new RightyBranch3(midLeft, removedEntry = middle.GetMinHashEntryOrDefault(), new Branch2(middle.RemoveEntry(removedEntry), midRight, right)); //! the height does not change
+                        return new Branch3(midLeft, removedEntry = middle.GetMinHashEntryOrDefault(), middle.RemoveEntry(removedEntry), midRight, right); //! the height does not change
                     }

                     // rebalance is needed because the branch was merged from Br2 to Br3 or to Leaf and the height decrease
-                    if (Left.GetType() == typeof(Branch2) && newLeft.GetType() != typeof(Branch2))
+                    if (Left is Branch2 && newLeft is Branch2 == false)
                     {
                         // the hole has a 3-node as a parent and a 3-node as a sibling.
-                        if (middle is LeftyBranch3 leftyMiddle) //! the height does not change
-                        {
-                            var mlb = (Branch2)leftyMiddle.Left;
-                            return new RightyBranch3(new Branch2(newLeft, midLeft, mlb.Left), mlb.MidEntry, new Branch2(new Branch2(mlb.Right, leftyMiddle.MidEntry, leftyMiddle.Right), midRight, right));
-                        }
-
-                        // the hole has a 3-node as a parent and a 3-node as a sibling.
-                        if (middle is RightyBranch3 rightyMiddle) //! the height does not change
-                            return new RightyBranch3(new Branch2(newLeft, midLeft, rightyMiddle.Left), rightyMiddle.MidEntry, new Branch2(rightyMiddle.Right, midRight, right));
+                        if (middle is Branch3 mb3) //! the height does not change
+                            return new Branch3(new Branch2(newLeft, midLeft, mb3.Left), mb3.Entry0, new Branch2(mb3.Middle, mb3.Entry1, mb3.Right), midRight, right);

                         // the hole has a 3-node as a parent and a 2-node as a sibling.
-                        return new Branch2(new RightyBranch3(newLeft, midLeft, middle), midRight, right);
+                        var mb2 = (Branch2)middle;
+                        return new Branch2(new Branch3(newLeft, midLeft, mb2.Left, mb2.MidEntry, mb2.Right), midRight, right);
                     }

-                    return new RightyBranch3(newLeft, midLeft, rb); // no rebalance needed
+                    return new Branch3(newLeft, midLeft, middle, midRight, right); // no rebalance needed
                 }

                 if (removedEntry == midRight)
@@ -3867,27 +3859,21 @@
                     {
                         if (right is Leaf2Plus1Plus1 == false)
                             return new Branch2(Left, midLeft, right.AddOrGetEntry(midLeft.Hash, midLeft)); // the Br3 become the Br2 but the height did not change - so no rebalance needed
-                        return new RightyBranch3(Left, midLeft, new Branch2(midRight, removedEntry = right.GetMinHashEntryOrDefault(), right.RemoveEntry(removedEntry))); //! the height does not change
+                        return new Branch3(Left, midLeft, midRight, removedEntry = right.GetMinHashEntryOrDefault(), right.RemoveEntry(removedEntry)); //! the height does not change
                     }

-                    if (middle.GetType() == typeof(Branch2) && newMiddle.GetType() != typeof(Branch2))
+                    if (middle is Branch2 && newMiddle is Branch2 == false)
                     {
                         // the hole has a 3-node as a parent and a 3-node as a sibling.
-                        if (right is LeftyBranch3 leftyRight) //! the height does not change
-                        {
-                            var rlb = (Branch2)leftyRight.Left;
-                            return new RightyBranch3(Left, midLeft, new Branch2(new Branch2(newMiddle, midRight, rlb.Left), rlb.MidEntry, new Branch2(rlb.Right, leftyRight.MidEntry, leftyRight.Right)));
-                        }
-
-                        // the hole has a 3-node as a parent and a 3-node as a sibling.
-                        if (right is RightyBranch3 rightyRight) //! the height does not change
-                            return new RightyBranch3(Left, midLeft, new Branch2(new Branch2(newMiddle, midRight, rightyRight.Left), rightyRight.MidEntry, rightyRight.Right));
+                        if (right is Branch3 rb3) //! the height does not change
+                            return new Branch3(Left, midLeft, new Branch2(newMiddle, midRight, rb3.Left), rb3.Entry0, new Branch2(rb3.Middle, rb3.Entry1, rb3.Right));

                         // the hole has a 3-node as a parent and a 2-node as a sibling.
-                        return new Branch2(Left, midLeft, new RightyBranch3(newMiddle, midRight, right));
+                        var rb2 = (Branch2)right;
+                        return new Branch2(Left, midLeft, new Branch3(newMiddle, midRight, rb2.Left, rb2.MidEntry, rb2.Right));
                     }

-                    return new RightyBranch3(Left, midLeft, new Branch2(newMiddle, midRight, right));
+                    return new Branch3(Left, midLeft, newMiddle, midRight, right);
                 }

                 var newRight = right.RemoveEntry(removedEntry);
@@ -3895,216 +3881,22 @@
                 {
                     if (middle is Leaf2Plus1Plus1 == false)
                         return new Branch2(Left, midLeft, middle.AddOrGetEntry(midRight.Hash, midRight));
-                    return new RightyBranch3(Left, midLeft, new Branch2(middle.RemoveEntry(removedEntry = middle.GetMaxHashEntryOrDefault()), removedEntry, midRight));
+                    return new Branch3(Left, midLeft, middle.RemoveEntry(removedEntry = middle.GetMaxHashEntryOrDefault()), removedEntry, midRight);
                 }

                 // right was a Br2 but now is Leaf or Br3 - means the branch height is decrease
                 if (right.GetType() == typeof(Branch2) && newRight.GetType() != typeof(Branch2))
                 {
-                    // the hole has a 3-node as a parent and a 3-node as a sibling.
-                    if (middle is LeftyBranch3 leftyMiddle) //! the height does not change
-                        return new RightyBranch3(Left, midLeft, new Branch2(leftyMiddle.Left, leftyMiddle.MidEntry, new Branch2(leftyMiddle.Right, midRight, newRight)));
-
                     // the hole has a 3-node as a parent and a 3-node as a sibling.new
-                    if (middle is RightyBranch3 rightyMiddle) //! the height does not change
-                    {
-                        var rrb = (Branch2)rightyMiddle.Right;
-                        return new RightyBranch3(Left, midLeft, new Branch2(new Branch2(rightyMiddle.Left, rightyMiddle.MidEntry, rrb.Left), rrb.MidEntry, new Branch2(rrb.Right, midRight, newRight)));
-                    }
+                    if (middle is Branch3 mb3) //! the height does not change
+                        return new Branch3(Left, midLeft, new Branch2(mb3.Left, mb3.Entry0, mb3.Middle), mb3.Entry1, new Branch2(mb3.Right, midRight, newRight));

                     // the hole has a 3-node as a parent and a 2-node as a sibling.
-                    return new Branch2(Left, midLeft, new LeftyBranch3(middle, midRight, newRight));
-                }
-
-                return new RightyBranch3(Left, midLeft, new Branch2(middle, midRight, newRight));
-            }
-        }
-
-        /// Left-skewed Branch of 3 - actually a branch of 2 with the left branch of 2
-        internal sealed class LeftyBranch3 : Branch2
-        {
-            public LeftyBranch3(ImHashMap<K, V> leftBranch, Entry entry, ImHashMap<K, V> right) : base(leftBranch, entry, right)
-            {
-                Debug.Assert(leftBranch is Branch2, $"Left:{leftBranch} is Branch2");
-            }
-
-#if !DEBUG
-            public override string ToString() => "{LB3:{"  + base.ToString() + "}";
-#endif
-
-            internal override Entry GetEntryOrDefault(int hash)
-            {
-                var mh = MidEntry.Hash;
-                if (mh < hash)
-                    return Right.GetEntryOrDefault(hash);
-                if (mh > hash)
-                {
-                    var l = (Branch2)Left;
-                    mh = l.MidEntry.Hash;
-                    return hash > mh ? l.Right.GetEntryOrDefault(hash)
-                        :  hash < mh ? l.Left .GetEntryOrDefault(hash)
-                        :  l.MidEntry;
-                }
-                return MidEntry;
-            }
-
-            internal override ImHashMap<K, V> AddOrGetEntry(int hash, Entry entry)
-            {
-                var lb = (Branch2)Left;
-                var h0 = lb.MidEntry.Hash;
-                var h1 = MidEntry.Hash;
-
-                if (hash > h1)
-                {
-                    var right = Right;
-                    var newRight = right.AddOrGetEntry(hash, entry);
-                    if (newRight is Entry)
-                        return newRight;
-                    if (right.GetType() != typeof(Branch2) && newRight.GetType() == typeof(Branch2))
-                        return new Branch2(lb, MidEntry, newRight);
-                    return new LeftyBranch3(lb, MidEntry, newRight);
-                }
-
-                if (hash < h0)
-                {
-                    var left = lb.Left;
-                    var newLeft = left.AddOrGetEntry(hash, entry);
-                    if (newLeft is Entry)
-                        return newLeft;
-                    if (left.GetType() != typeof(Branch2) && newLeft.GetType() == typeof(Branch2))
-                        return new Branch2(newLeft, lb.MidEntry, new Branch2(lb.Right, MidEntry, Right));
-                    return new LeftyBranch3(new Branch2(newLeft, lb.MidEntry, lb.Right), MidEntry, Right);
-                }
-
-                if (hash > h0 && hash < h1)
-                {
-                    var middle = lb.Right;
-                    var newMiddle = middle.AddOrGetEntry(hash, entry);
-                    if (newMiddle is Entry)
-                        return newMiddle;
-                    if (middle.GetType() != typeof(Branch2) && newMiddle.GetType() == typeof(Branch2))
-                    {
-                        var nmb2 = (Branch2)newMiddle;
-                        return new Branch2(new Branch2(lb.Left, lb.MidEntry, nmb2.Left), nmb2.MidEntry, new Branch2(nmb2.Right, MidEntry, Right));
-                    }
-                    return new LeftyBranch3(new Branch2(lb.Left, lb.MidEntry, newMiddle), MidEntry, Right);
-                }
-
-                return hash == h0 ? lb.MidEntry : MidEntry;
-            }
-
-            internal override ImHashMap<K, V> ReplaceEntry(int hash, Entry oldEntry, Entry newEntry)
-            {
-                var h = MidEntry.Hash;
-                return hash > h ? new LeftyBranch3(Left, MidEntry, Right.ReplaceEntry(hash, oldEntry, newEntry))
-                    :  hash < h ? new LeftyBranch3(Left.ReplaceEntry(hash, oldEntry, newEntry), MidEntry, Right)
-                    :  new LeftyBranch3(Left, newEntry, Right);
-            }
-
-            internal override ImHashMap<K, V> RemoveEntry(Entry removedEntry)
-            {
-                var lb = (Branch2)Left;
-                var left     = lb.Left;
-                var midLeft  = lb.MidEntry;
-                var middle   = lb.Right;
-                var midRight = MidEntry;
-                var right    = Right;
-
-                // case 1, downward: swap the predecessor entry (max left entry) with the mid entry, then proceed to remove the predecessor from the Left branch
-                if (removedEntry == midLeft)
-                    removedEntry  = midLeft = left.GetMaxHashEntryOrDefault();
-
-                if (removedEntry.Hash <= midLeft.Hash) // `<=` here is because of check on the above line
-                {
-                    var newLeft = left.RemoveEntry(removedEntry);
-                    if (newLeft == Empty)
-                    {
-                        if (middle is Leaf2Plus1Plus1 == false)
-                            return new Branch2(middle.AddOrGetEntry(midLeft.Hash, midLeft), midRight, right); //! the height does not change
-                        return new RightyBranch3(midLeft, removedEntry = middle.GetMinHashEntryOrDefault(), new Branch2(middle.RemoveEntry(removedEntry), midRight, right)); //! the height does not change
-                    }
-
-                    // rebalance is needed because the branch was merged from Br2 to Br3 or to Leaf and the height decrease
-                    if (left.GetType() == typeof(Branch2) && newLeft.GetType() != typeof(Branch2))
-                    {
-                        // the hole has a 3-node as a parent and a 3-node as a sibling.
-                        if (middle is LeftyBranch3 leftyMiddle) //! the height does not change
-                        {
-                            var mlb = (Branch2)leftyMiddle.Left;
-                            return new RightyBranch3(new Branch2(newLeft, midLeft, mlb.Left), mlb.MidEntry, new Branch2(new Branch2(mlb.Right, leftyMiddle.MidEntry, leftyMiddle.Right), midRight, right));
-                        }
-
-                        // the hole has a 3-node as a parent and a 3-node as a sibling.
-                        if (middle is RightyBranch3 rightyMiddle) //! the height does not change
-                            return new RightyBranch3(new Branch2(newLeft, midLeft, rightyMiddle.Left), rightyMiddle.MidEntry, new Branch2(rightyMiddle.Right, midRight, right));
-
-                        // the hole has a 3-node as a parent and a 2-node as a sibling.
-                        return new Branch2(new RightyBranch3(newLeft, midLeft, middle), midRight, right);
-                    }
-
-                    return new LeftyBranch3(new Branch2(newLeft, midLeft, middle), midRight, Right); // no rebalance needed
-                }
-
-                if (removedEntry == midRight)
-                    removedEntry = midRight = middle.GetMaxHashEntryOrDefault();
-
-                if (removedEntry.Hash <= midRight.Hash) // `<=` here is because of check on the above line
-                {
-                    var newMiddle = middle.RemoveEntry(removedEntry);
-                    if (newMiddle == Empty)
-                    {
-                        if (right is Leaf2Plus1Plus1 == false)
-                            return new Branch2(left, midLeft, right.AddOrGetEntry(midLeft.Hash, midLeft)); // the Br3 become the Br2 but the height did not change - so no rebalance needed
-                        return new LeftyBranch3(new Branch2(left, midLeft, midRight), removedEntry = right.GetMinHashEntryOrDefault(), right.RemoveEntry(removedEntry)); //! the height does not change
-                    }
-
-                    if (middle.GetType() == typeof(Branch2) && newMiddle.GetType() != typeof(Branch2))
-                    {
-                        // the hole has a 3-node as a parent and a 3-node as a sibling.
-                        if (right is LeftyBranch3 leftyRight) //! the height does not change
-                        {
-                            var rlb = (Branch2)leftyRight.Left;
-                            return new RightyBranch3(left, midLeft, new Branch2(new Branch2(newMiddle, midRight, rlb.Left), rlb.MidEntry, new Branch2(rlb.Right, leftyRight.MidEntry, leftyRight.Right)));
-                        }
-
-                        // the hole has a 3-node as a parent and a 3-node as a sibling.
-                        if (right is RightyBranch3 rightyRight) //! the height does not change
-                            return new RightyBranch3(left, midLeft, new Branch2(new Branch2(newMiddle, midRight, rightyRight.Left), rightyRight.MidEntry, rightyRight.Right));
-
-                        // the hole has a 3-node as a parent and a 2-node as a sibling.
-                        return new Branch2(left, midLeft, new RightyBranch3(newMiddle, midRight, right));
-                    }
-
-                    return new LeftyBranch3(new Branch2(left, midLeft, newMiddle), midRight, right);
-                }
-
-                var newRight = right.RemoveEntry(removedEntry);
-                if (newRight == Empty)
-                {
-                    if (middle is Leaf2Plus1Plus1 == false)
-                        return new Branch2(left, midLeft, middle.AddOrGetEntry(midRight.Hash, midRight));
-                    return new RightyBranch3(left, midLeft, new Branch2(middle.RemoveEntry(removedEntry = middle.GetMaxHashEntryOrDefault()), removedEntry, midRight));
-                }
-
-                // right was a Br2 but now is Leaf or Br3 - means the branch height is decrease
-                if (right.GetType() == typeof(Branch2) && newRight.GetType() != typeof(Branch2))
-                {
-                    // the hole has a 3-node as a parent and a 3-node as a sibling.
-                    if (middle is LeftyBranch3 leftyMiddle) //! the height does not change
-                        return new RightyBranch3(left, midLeft, new Branch2(leftyMiddle.Left, leftyMiddle.MidEntry, new Branch2(leftyMiddle.Right, midRight, newRight)));
-
-                    // the hole has a 3-node as a parent and a 3-node as a sibling.new
-                    if (middle is RightyBranch3 rightyMiddle) //! the height does not change
-                    {
-                        var rrb = (Branch2)rightyMiddle.Right;
-                        return new RightyBranch3(left, midLeft, new Branch2(new Branch2(rightyMiddle.Left, rightyMiddle.MidEntry, rrb.Left), rrb.MidEntry, new Branch2(rrb.Right, midRight, newRight)));
-                    }
-
-                    // the hole has a 3-node as a parent and a 2-node as a sibling.
-                    return new Branch2(left, midLeft, new LeftyBranch3(middle, midRight, newRight));
-                }
-
-                return new LeftyBranch3(Left, midRight, newRight);
+                    var mb2 = (Branch2)middle;
+                    return new Branch2(Left, midLeft, new Branch3(mb2.Left, mb2.MidEntry, mb2.Right, midRight, newRight));
+                }
+
+                return new Branch3(Left, midLeft, middle, midRight, newRight);
             }
         }
     }
@@ -4929,7 +4721,7 @@
             }
         }

-        /// Branch of 3
+        /// Branch of 3 with 2 nodes in between
         internal sealed class Branch3 : ImMap<V>
         {
             public readonly ImMapEntry<V> Entry0, Entry1;
@@ -5106,44 +4898,13 @@
     }

     /// Helper stack wrapper for the array
-    public sealed class ThreadUnsafeStack<T>
-    {
-        private const int DefaultInitialCapacity = 4;
-        private T[] _items;
-
-        /// Creates the list of the `DefaultInitialCapacity`
-        public ThreadUnsafeStack() => _items = new T[DefaultInitialCapacity];
-
-        /// Pushes the item
-        public void Push(T item, int count)
-        {
-            if (count >= _items.Length)
-                _items = Expand(_items);
-            _items[count] = item;
-        }
-
-        /// Gets the item by index
-        public T Get(int index) => _items[index];
-
-        private static T[] Expand(T[] items)
-        {
-            var count = items.Length;
-            var newItems = new T[count << 1]; // count * 2
-            Array.Copy(items, 0, newItems, 0, count);
-            return newItems;
-        }
-    }
-
-    /// Helper stack wrapper for the array
     public sealed class MapParentStack
     {
         private const int DefaultInitialCapacity = 4;
         private object[] _items;

         /// Creates the list of the `DefaultInitialCapacity`
-        public static MapParentStack Create() => new MapParentStack();
-
-        private MapParentStack() => _items = new object[DefaultInitialCapacity];
+        public MapParentStack() => _items = new object[DefaultInitialCapacity];

         /// Pushes the item
         public void Push(object item, int count)
@@ -5190,10 +4951,15 @@
         public static ImHashMap<K, V> Create<K, V>(ImHashMapEntry<K, V> e0, ImHashMapEntry<K, V> e1, ImHashMapEntry<K, V> e2, ImHashMapEntry<K, V> e3) =>
             new ImHashMap<K, V>.Leaf2Plus1Plus1(e3, new ImHashMap<K, V>.Leaf2Plus1(e2, (ImHashMap<K, V>.Leaf2)Create(e0, e1)));

+        private sealed class GoRightInBranch3<K, V>
+        {
+            public ImHashMap<K, V>.Branch3 Br3;
+        }
+
         /// Enumerates all the map entries in the hash order.
         /// The `parents` parameter allow sto reuse the stack memory used for traversal between multiple enumerates.
         /// So you may pass the empty `parents` into the first `Enumerate` and then keep passing the same `parents` into the subsequent `Enumerate` calls
-        public static IEnumerable<ImHashMapEntry<K, V>> Enumerate<K, V>(this ImHashMap<K, V> map, ThreadUnsafeStack<ImHashMap<K, V>> parents = null)
+        public static IEnumerable<ImHashMapEntry<K, V>> Enumerate<K, V>(this ImHashMap<K, V> map, MapParentStack parents = null)
         {
             if (map == ImHashMap<K, V>.Empty)
                 yield break;
@@ -5205,17 +4971,26 @@
             }

             var count = 0;
+            GoRightInBranch3<K, V> br3Wrapper = null;
             while (true)
             {
                 if (map is ImHashMap<K, V>.Branch2 b2)
                 {
                     if (parents == null)
-                        parents = new ThreadUnsafeStack<ImHashMap<K, V>>();
+                        parents = new MapParentStack();
                     parents.Push(map, count++);
                     map = b2.Left;
                     continue;
                 }
-
+                if (map is ImHashMap<K, V>.Branch3 b3)
+                {
+                    if (parents == null)
+                        parents = new MapParentStack();
+                    parents.Push(map, count++);
+                    map = b3.Left;
+                    continue;
+                }
+
                 if (map is ImHashMap<K, V>.Entry l1)
                 {
                     if (l1 is ImHashMapEntry<K, V> v0) yield return v0;
@@ -5414,12 +5189,31 @@
                 if (count == 0)
                     break; // we yield the leaf and there is nothing in stack - we are DONE!

-                var pb2 = (ImHashMap<K, V>.Branch2)parents.Get(--count); // otherwise get the parent
-                if (pb2.MidEntry is ImHashMapEntry<K, V> v)
-                    yield return v;
-                else foreach (var c in ((HashConflictingEntry<K, V>)pb2.MidEntry).Conflicts)
-                    yield return c;
-                map = pb2.Right;
+                var b = parents.Get(--count); // otherwise get the parent
+                if (b is ImHashMap<K,V>.Branch2 pb2)
+                {
+                    if (pb2.MidEntry is ImHashMapEntry<K, V> v) yield return v;
+                    else foreach (var c in ((HashConflictingEntry<K, V>)pb2.MidEntry).Conflicts) yield return c;
+                    map = pb2.Right;
+                }
+                else if (b is ImHashMap<K, V>.Branch3 pb3)
+                {
+                    if (pb3.Entry0 is ImHashMapEntry<K, V> v) yield return v;
+                    else foreach (var c in ((HashConflictingEntry<K, V>)pb3.Entry0).Conflicts) yield return c;
+                    if (br3Wrapper == null)
+                        br3Wrapper = new GoRightInBranch3<K, V>();
+                    br3Wrapper.Br3 = pb3;
+                    parents.Set(count++, br3Wrapper);
+                    br3Wrapper = null; // set to null to mark that the wrapper is in use and longer shared
+                    map = pb3.Middle;
+                }
+                else
+                {
+                    br3Wrapper = (GoRightInBranch3<K, V>)b;
+                    if (br3Wrapper.Br3.Entry1 is ImHashMapEntry<K, V> v) yield return v;
+                    else foreach (var c in ((HashConflictingEntry<K, V>)br3Wrapper.Br3.Entry1).Conflicts) yield return c;
+                    map = br3Wrapper.Br3.Right;
+                }
             }
         }

@@ -5427,7 +5221,7 @@
         /// Depth-first in-order of hash traversal as described in http://en.wikipedia.org/wiki/Tree_traversal.
         /// The `parents` parameter allows to reuse the stack memory used for the traversal between multiple calls.
         /// So you may pass the empty `parents` into the first `Enumerate` and then keep passing the same `parents` into the subsequent calls
-        public static S ForEach<K, V, S>(this ImHashMap<K, V> map, S state, Action<ImHashMapEntry<K, V>, int, S> handler, ThreadUnsafeStack<ImHashMap<K, V>> parents = null)
+        public static S ForEach<K, V, S>(this ImHashMap<K, V> map, S state, Action<ImHashMapEntry<K, V>, int, S> handler, MapParentStack parents = null)
         {
             if (map == ImHashMap<K, V>.Empty)
                 return state;
@@ -5440,14 +5234,23 @@
             }

             var count = 0;
+            GoRightInBranch3<K, V> br3Wrapper = null;
             while (true)
             {
                 if (map is ImHashMap<K, V>.Branch2 b2)
                 {
                     if (parents == null)
-                        parents = new ThreadUnsafeStack<ImHashMap<K, V>>();
+                        parents = new MapParentStack();
                     parents.Push(map, count++);
                     map = b2.Left;
+                    continue;
+                }
+                if (map is ImHashMap<K, V>.Branch3 b3)
+                {
+                    if (parents == null)
+                        parents = new MapParentStack();
+                    parents.Push(map, count++);
+                    map = b3.Left;
                     continue;
                 }

@@ -5649,10 +5452,31 @@
                 if (count == 0)
                     break; // we yield the leaf and there is nothing in stack - we are DONE!

-                var pb2 = (ImHashMap<K, V>.Branch2)parents.Get(--count); // otherwise get the parent
-                if (pb2.MidEntry is ImHashMapEntry<K, V> kv) handler(kv, i++, state);
-                else foreach (var c in ((HashConflictingEntry<K, V>)pb2.MidEntry).Conflicts) handler(c, i++, state);
-                map = pb2.Right;
+                var b = parents.Get(--count); // otherwise get the parent
+                if (b is ImHashMap<K,V>.Branch2 pb2)
+                {
+                    if (pb2.MidEntry is ImHashMapEntry<K, V> v) handler(v, i++, state);
+                    else foreach (var c in ((HashConflictingEntry<K, V>)pb2.MidEntry).Conflicts) handler(c, i++, state);
+                    map = pb2.Right;
+                }
+                else if (b is ImHashMap<K, V>.Branch3 pb3)
+                {
+                    if (pb3.Entry0 is ImHashMapEntry<K, V> v) handler(v, i++, state);
+                    else foreach (var c in ((HashConflictingEntry<K, V>)pb3.Entry0).Conflicts) handler(c, i++, state);
+                    if (br3Wrapper == null)
+                        br3Wrapper = new GoRightInBranch3<K, V>();
+                    br3Wrapper.Br3 = pb3;
+                    parents.Set(count++, br3Wrapper);
+                    br3Wrapper = null; // set to null to mark that the wrapper is in use and longer shared
+                    map = pb3.Middle;
+                }
+                else
+                {
+                    br3Wrapper = (GoRightInBranch3<K, V>)b;
+                    if (br3Wrapper.Br3.Entry1 is ImHashMapEntry<K, V> v) handler(v, i++, state);
+                    else foreach (var c in ((HashConflictingEntry<K, V>)br3Wrapper.Br3.Entry1).Conflicts) handler(c, i++, state);
+                    map = br3Wrapper.Br3.Right;
+                }
             }

             return state;
@@ -5661,13 +5485,13 @@
         /// Do something for each entry.
         /// The `parents` parameter allows to reuse the stack memory used for the traversal between multiple calls.
         /// So you may pass the empty `parents` into the first `Enumerate` and then keep passing the same `parents` into the subsequent calls
-        public static void ForEach<K, V>(this ImHashMap<K, V> map, Action<ImHashMapEntry<K, V>, int> handler, ThreadUnsafeStack<ImHashMap<K, V>> parents = null) =>
+        public static void ForEach<K, V>(this ImHashMap<K, V> map, Action<ImHashMapEntry<K, V>, int> handler, MapParentStack parents = null) =>
             map.ForEach(handler, (e, i, r) => r(e, i), parents);

         /// Collect something for each entry.
         /// The `parents` parameter allows to reuse the stack memory used for the traversal between multiple calls.
         /// So you may pass the empty `parents` into the first `Enumerate` and then keep passing the same `parents` into the subsequent calls
-        public static S Fold<K, V, S>(this ImHashMap<K, V> map, S state, Func<ImHashMapEntry<K, V>, int, S, S> handler, ThreadUnsafeStack<ImHashMap<K, V>> parents = null) =>
+        public static S Fold<K, V, S>(this ImHashMap<K, V> map, S state, Func<ImHashMapEntry<K, V>, int, S, S> handler, MapParentStack parents = null) =>
             map.ForEach(St.Rent(state, handler), (e, i, s) => s.a = s.b(e, i, s.a), parents).ResetButGetA();

         /// Converts map to an array with the minimum allocations
@@ -5684,7 +5508,7 @@
         [MethodImpl((MethodImplOptions)256)]
         public static ImHashMapEntry<K, V> GetEntryOrDefault<K, V>(this ImHashMap<K, V> map, int hash, K key)
         {
-            var e = map.GetEntryOrDefault(hash);
+            var e = map.GetEntryOrNull(hash);

             if (e is ImHashMapEntry<K, V> kv)
                 return kv.Key.Equals(key) ? kv : null;
@@ -5715,7 +5539,7 @@
         [MethodImpl((MethodImplOptions)256)]
         public static ImHashMapEntry<K, V> GetSurePresentEntry<K, V>(this ImHashMap<K, V> map, int hash, K key)
         {
-            var e = map.GetEntryOrDefault(hash);
+            var e = map.GetEntryOrNull(hash);
             if (e is HashConflictingEntry<K, V> c)
                 foreach (var x in c.Conflicts)
                     if (x.Key.Equals(key))
@@ -5729,7 +5553,7 @@
         [MethodImpl((MethodImplOptions)256)]
         public static V GetValueOrDefault<K, V>(this ImHashMap<K, V> map, int hash, K key)
         {
-            var e = map.GetEntryOrDefault(hash);
+            var e = map.GetEntryOrNull(hash);
             if (e is ImHashMapEntry<K, V> kv)
             {
                 if (kv.Key.Equals(key))
@@ -5755,7 +5579,7 @@
         [MethodImpl((MethodImplOptions)256)]
         public static V GetValueOrDefaultByReferenceEquals<K, V>(this ImHashMap<K, V> map, int hash, K key) where K : class
         {
-            var e = map.GetEntryOrDefault(hash);
+            var e = map.GetEntryOrNull(hash);
             if (e is ImHashMapEntry<K, V> kv)
             {
                 if (kv.Key == key)
@@ -5775,7 +5599,7 @@
         [MethodImpl((MethodImplOptions)256)]
         public static bool TryFind<K, V>(this ImHashMap<K, V> map, int hash, K key, out V value)
         {
-            var e = map.GetEntryOrDefault(hash);
+            var e = map.GetEntryOrNull(hash);
             if (e is ImHashMapEntry<K, V> kv)
             {
                 if (kv.Key.Equals(key))
@@ -5803,7 +5627,7 @@
         [MethodImpl((MethodImplOptions)256)]
         public static bool TryFindByReferenceEquals<K, V>(this ImHashMap<K, V> map, int hash, K key, out V value) where K : class
         {
-            var e = map.GetEntryOrDefault(hash);
+            var e = map.GetEntryOrNull(hash);
             if (e is ImHashMapEntry<K, V> kv)
             {
                 if (kv.Key == key)
@@ -5961,7 +5785,7 @@
         /// Updates the map with the new value if the key is found otherwise returns the same unchanged map.
         public static ImHashMap<K, V> Update<K, V>(this ImHashMap<K, V> map, int hash, K key, V value)
         {
-            var entry = map.GetEntryOrDefault(hash);
+            var entry = map.GetEntryOrNull(hash);
             if (entry == null)
                 return map;

@@ -5991,7 +5815,7 @@
         /// Updates the map with the new value if the key is found otherwise returns the same unchanged map.
         public static ImHashMap<K, V> UpdateToDefault<K, V>(this ImHashMap<K, V> map, int hash, K key)
         {
-            var entry = map.GetEntryOrDefault(hash);
+            var entry = map.GetEntryOrNull(hash);
             if (entry == null)
                 return map;

@@ -6020,7 +5844,7 @@
         /// If `update` returns the same map if the updated result is the same
         public static ImHashMap<K, V> Update<K, V>(this ImHashMap<K, V> map, int hash, K key, V value, Update<K, V> update)
         {
-            var entry = map.GetEntryOrDefault(hash);
+            var entry = map.GetEntryOrNull(hash);
             if (entry == null)
                 return map;

@@ -6115,7 +5939,7 @@
         [MethodImpl((MethodImplOptions)256)]
         public static ImHashMap<K, V> Remove<K, V>(this ImHashMap<K, V> map, int hash, K key)
         {
-            var entryToRemove = map.GetEntryOrDefault(hash);
+            var entryToRemove = map.GetEntryOrNull(hash);
             if (entryToRemove is ImHashMapEntry<K, V>)
                 return map.RemoveEntry(entryToRemove);

@@ -6195,7 +6019,7 @@
                 if (map is ImMap<V>.Branch2 b2)
                 {
                     if (parents == null)
-                        parents = MapParentStack.Create();
+                        parents = new MapParentStack();
                     parents.Push(map, count++);
                     map = b2.Left;
                     continue;
@@ -6204,7 +6028,7 @@
                 if (map is ImMap<V>.Branch3 b3)
                 {
                     if (parents == null)
-                        parents = MapParentStack.Create();
+                        parents = new MapParentStack();
                     parents.Push(map, count++);
                     map = b3.Left;
                     continue;
@@ -6424,7 +6248,7 @@
                 if (map is ImMap<V>.Branch2 b2)
                 {
                     if (parents == null)
-                        parents = MapParentStack.Create();
+                        parents = new MapParentStack();
                     parents.Push(map, count++);
                     map = b2.Left;
                     continue;
@@ -6432,7 +6256,7 @@
                 if (map is ImMap<V>.Branch3 b3)
                 {
                     if (parents == null)
-                        parents = MapParentStack.Create();
+                        parents = new MapParentStack();
                     parents.Push(map, count++);
                     map = b3.Left;
                     continue;
@@ -6924,10 +6748,10 @@
         /// The `parents` parameter allows to reuse the stack memory used for the traversal between multiple calls.
         /// So you may pass the empty `parents` into the first `Enumerate` and then keep passing the same `parents` into the subsequent calls
         [MethodImpl((MethodImplOptions)256)]
-        public static IEnumerable<ImHashMapEntry<K, V>> Enumerate<K, V>(this ImHashMap<K, V>[] parts, ThreadUnsafeStack<ImHashMap<K, V>> parents = null)
+        public static IEnumerable<ImHashMapEntry<K, V>> Enumerate<K, V>(this ImHashMap<K, V>[] parts, MapParentStack parents = null)
         {
             if (parents == null)
-                parents = new ThreadUnsafeStack<ImHashMap<K, V>>();
+                parents = new MapParentStack();
             foreach (var map in parts)
             {
                 if (map == ImHashMap<K, V>.Empty)
@@ -6941,10 +6765,10 @@
         /// The `parents` parameter allows to reuse the stack memory used for the traversal between multiple calls.
         /// So you may pass the empty `parents` into the first `Enumerate` and then keep passing the same `parents` into the subsequent calls
         public static S ForEach<K, V, S>(this ImHashMap<K, V>[] parts, S state, Action<ImHashMapEntry<K, V>, int, S> handler,
-            ThreadUnsafeStack<ImHashMap<K, V>> parents = null)
+            MapParentStack parents = null)
         {
             if (parents == null)
-                parents = new ThreadUnsafeStack<ImHashMap<K, V>>();
+                parents = new MapParentStack();
             foreach (var map in parts)
             {
                 if (map == ImHashMap<K, V>.Empty)
@@ -7054,7 +6878,7 @@
         public static IEnumerable<ImMapEntry<V>> Enumerate<V>(this ImMap<V>[] parts, MapParentStack parents = null)
         {
             if (parents == null)
-                parents = MapParentStack.Create();
+                parents = new MapParentStack();
             foreach (var map in parts)
             {
                 if (map == ImMap<V>.Empty)
@@ -7070,7 +6894,7 @@
         public static S ForEach<V, S>(this ImMap<V>[] parts, S state, Action<ImMapEntry<V>, int, S> handler, MapParentStack parents = null)
         {
             if (parents == null)
-                parents = MapParentStack.Create();
+                parents = new MapParentStack();
             foreach (var map in parts)
             {
                 if (map == ImMap<V>.Empty)


Change namespace

Change namespace

--- Ifp.Validation.TestProxy.Tests/RuleBasedValidatorTests.cs
+++ Ifp.Validation.TestProxy.Tests/RuleBasedValidatorTests.cs
@@ -2,7 +2,7 @@
 using Ifp.Validation.TestProxy.Tests.SupportClasses;
 using Xunit;

-namespace Ifp.Validation.Tests
+namespace Ifp.Validation.TestProxy.Tests
 {
     public class RuleBasedValidatorTests
     {


Modified IForm in order to support callbacks

--- Core/IForm.cs
+++ Core/IForm.cs
@@ -1,20 +1,21 @@
 namespace GLIB.Core
 {
-
+    using System;
     using UnityEngine;
     using System.Collections;

     public interface IForm
     {
+
         bool IsModuleRunning { get; }

         bool DataSubmitted { get; }

-        void StartForm();
+        void StartForm(Action onFormStart);

-        void EndForm();
+        void EndForm(Action onFormEnd);

-        void SubmitData();
+        void SubmitData(Action<string> onDataSubmission);

     }


add default acceptable routes + Route#ctor which uses the defaults

--- Core/Route.cs
+++ Core/Route.cs
@@ -76,14 +76,37 @@
          * matches, because the unbounded variable is infixed.
          */

+        private static readonly IEnumerable<string> _defaultMethods;
+
         private readonly Lazy<IEnumerable<string>> _paramNames;
         private readonly string _formatOriginal;

         static Route()
         {
             Route.MethodComparer = StringComparer.OrdinalIgnoreCase;
-        }
-
+            _defaultMethods = new[]
+            {
+                "GET", "POST", "HEAD", "PUT", "DELETE",
+                "OPTIONS", "CONNECT", "TRACE"
+            };
+        }
+
+        /// <summary>
+        /// Creates a new route.
+        /// </summary>
+        /// <param name="format">The format to be associated with this route.</param>
+        /// <param name="handler">The handler to be associated with this route.</param>
+        /// <exception cref="System.ArgumentNullException">
+        ///     Thrown when at least one of the provided arguments is null.
+        /// </exception>
+        public Route(
+            string format,
+            RouteHandler handler
+            )
+            : this(format, handler, _defaultMethods)
+        {
+
+        }
         /// <summary>
         /// Creates a new route.
         /// </summary>
@@ -100,7 +123,8 @@
         public Route(
             string format,
             RouteHandler handler,
-            IEnumerable<string> methods)
+            IEnumerable<string> methods
+            )
         {
             #region Param validation
             if (format == null)


Added Expression copy constructor. Added test for the copy constructor. Removed the "Properties" folder that was created when the project is opened.

--- Expression.cs
+++ Expression.cs
@@ -74,9 +74,9 @@
 		/// <remarks><pre>
 		/// 20 Jul 2004 - Jeremy Roberts
 		/// </pre></remarks>
-		public Expression()
-		{
-		}
+        public Expression()
+        {
+        }

 		/// <summary>
 		/// Creation constructor.
@@ -99,7 +99,11 @@
 		/// <param name="function">The function to be evaluated.</param>
 		public Expression(Expression cloneMe)
 		{
-			// TODO!: Fill in the rest of the copy constructor
+            this.Function = cloneMe.Function;
+            foreach (string key in cloneMe.variables.Keys)
+            {
+                this.AddSetVariable(key, (double)cloneMe.variables[key]);
+            }
 		}
 		#endregion


Use predefined overloads

Use predefined overloads

--- Farity/Add.cs
+++ Farity/Add.cs
@@ -14,27 +14,27 @@
         public static TimeSpan Add(TimeSpan a, TimeSpan b) => a + b;

         public static decimal? Add(decimal? a, decimal? b)
-            => a == null && b == null ? (decimal?) null : (a ?? 0) + (b ?? 0);
+            => a == null && b == null ? (decimal?) null : Add(a ?? 0, b ?? 0);

         public static double? Add(double? a, double? b)
-            => a == null && b == null ? (double?) null : (a ?? 0) + (b ?? 0);
+            => a == null && b == null ? (double?) null : Add(a ?? 0, b ?? 0);

         public static float? Add(float? a, float? b)
-            => a == null && b == null ? (float?) null : (a ?? 0) + (b ?? 0);
+            => a == null && b == null ? (float?) null : Add(a ?? 0, b ?? 0);

         public static int? Add(int? a, int? b)
-            => a == null && b == null ? (int?) null : (a ?? 0) + (b ?? 0);
+            => a == null && b == null ? (int?) null : Add(a ?? 0, b ?? 0);

         public static long? Add(long? a, long? b)
-            => a == null && b == null ? (long?) null : (a ?? 0) + (b ?? 0);
+            => a == null && b == null ? (long?) null : Add(a ?? 0, b ?? 0);

         public static uint? Add(uint? a, uint? b)
-            => a == null && b == null ? (uint?) null : (a ?? 0) + (b ?? 0);
+            => a == null && b == null ? (uint?) null : Add(a ?? 0, b ?? 0);

         public static ulong? Add(ulong? a, ulong? b)
-            => a == null && b == null ? (ulong?) null : (a ?? 0) + (b ?? 0);
+            => a == null && b == null ? (ulong?) null : Add(a ?? 0, b ?? 0);

         public static TimeSpan? Add(TimeSpan? a, TimeSpan? b)
-            => a == null && b == null ? (TimeSpan?) null : (a ?? TimeSpan.Zero) + (b ?? TimeSpan.Zero);
+            => a == null && b == null ? (TimeSpan?) null : Add(a ?? TimeSpan.Zero, b ?? TimeSpan.Zero);
     }
 }


天候名取得アルゴリズム修正

天候名取得アルゴリズム修正

--- Inferno/InfernoScripts/Parupunte/Scripts/ChangeWeather.cs
+++ Inferno/InfernoScripts/Parupunte/Scripts/ChangeWeather.cs
@@ -26,7 +26,7 @@
                 .FirstOrDefault();

             var weatherName = GetWeatherName(weather);
-            name = "天候変更" + weatherName;
+            name = "天候変化" + (!string.IsNullOrEmpty(weatherName) ? "：" : "") + weatherName;
         }

         public override string Name
@@ -42,55 +42,39 @@

         private string GetWeatherName(Weather weather)
         {
-            string weatherName = null;
             switch (weather)
             {
                 case Weather.ExtraSunny:
-                    weatherName = "快晴";
-                    break;
+                    return "快晴";
                 case Weather.Clear:
-                    weatherName = "晴れ";
-                    break;
+                    return "晴れ";
                 case Weather.Clouds:
-                    weatherName = "くもり";
-                    break;
+                    return "くもり";
                 case Weather.Smog:
-                    weatherName = "スモッグ";
-                    break;
+                    return "スモッグ";
                 case Weather.Foggy:
-                    weatherName = "霧";
-                    break;
+                    return "霧";
                 case Weather.Overcast:
-                    weatherName = "くもり2";
-                    break;
-                case Weather.Raining:
-                    weatherName = "雨";
-                    break;
+                    return "くもり2";
+                  case Weather.Raining:
+                    return "雨";
                 case Weather.ThunderStorm:
-                    weatherName = "嵐";
-                    break;
+                    return "嵐";
                 case Weather.Clearing:
-                    weatherName = "天気雨";
-                    break;
+                    return "天気雨";
                 case Weather.Neutral:
-                    weatherName = "奇妙";
-                    break;
+                    return "奇妙";
                 case Weather.Snowing:
-                    weatherName = "雪";
-                    break;
+                    return "雪";
                 case Weather.Blizzard:
-                    weatherName = "吹雪";
-                    break;
+                    return "吹雪";
                 case Weather.Snowlight:
-                    weatherName = "雪明り";
-                    break;
+                    return "雪明り";
                 case Weather.Christmas:
-                    weatherName = "クリスマス";
-                    break;
+                    return "クリスマス";
                 default:
                     return string.Empty;
             }
-            return weatherName.Insert(0, "：");
         }
     }
 }


Added a couple tests. One to delete a record. Another to demonstrate .lift.update files are processes in the correct order.

--- Palaso.Lift.Tests/Merging/SynchronicMergerTests.cs
+++ Palaso.Lift.Tests/Merging/SynchronicMergerTests.cs
@@ -77,6 +77,72 @@
 			Assert.AreEqual(4, doc.SelectNodes("//entry").Count);
 		}

+		[Test]
+		public void NewEntriesAdded_MultipleFilesSucessiveChanges()
+		{
+			//This test demonstrates that LiftUpdate files are applied in the order they are created when time stamps are used to order the names up the LIFTUpdate files.
+			//Notice that the files names of the LIFT update files are purposely created so that the alphabetical ordering does not match the time stamp ordering.
+
+			//Create a LIFT file with 3 entries which will have updates applied to it.
+			WriteFile(_baseLiftFileName, s_LiftData1, _directory);
+			//Create a .lift.update file with three entries.  One to replace the second entry in the original LIFT file.
+			//The other two are new and should be appended to the original LIFT file.
+			WriteFile("LiftChangeFileB" + SynchronicMerger.ExtensionOfIncrementalFiles, s_LiftUpdate1, _directory);
+			//Create a .lift.update file with two entries.  One to replace one of the changes from the first LiftUpdate file and one new entry.
+			WriteFile("LiftChangeFileA" + SynchronicMerger.ExtensionOfIncrementalFiles, s_LiftUpdate2, _directory);
+			FileInfo[] files = SynchronicMerger.GetPendingUpdateFiles(Path.Combine(_directory, _baseLiftFileName));
+
+			XmlDocument doc = MergeAndGetResult(true, _directory, files);
+			Assert.AreEqual(6, doc.SelectNodes("//entry").Count);
+			Assert.AreEqual(1, doc.SelectNodes("//entry[@id='one']").Count);
+			Assert.AreEqual(0, doc.SelectNodes("//entry[@id='two']").Count);
+			Assert.AreEqual(1, doc.SelectNodes("//entry[@id='twoblatblat']").Count);
+			Assert.AreEqual(0, doc.SelectNodes("//entry[@id='four']").Count);
+			Assert.AreEqual(1, doc.SelectNodes("//entry[@id='fourChangedFirstAddition']").Count);
+			Assert.AreEqual(1, doc.SelectNodes("//entry[@id='six']").Count);
+		}
+
+		private static readonly string s_LiftData1 =
+			"<entry id='one' guid='0ae89610-fc01-4bfd-a0d6-1125b7281dd1'></entry>"
+			+ "<entry id='two' guid='0ae89610-fc01-4bfd-a0d6-1125b7281d22'><lexical-unit><form lang='nan'><text>test</text></form></lexical-unit></entry>"
+			+ "<entry id='three' guid='80677C8E-9641-486e-ADA1-9D20ED2F5B69'></entry>";
+
+		private static readonly string s_LiftUpdate1 =
+			"<entry id='four' guid='6216074D-AD4F-4dae-BE5F-8E5E748EF68A'></entry>"
+			+ "<entry id='twoblatblat' guid='0ae89610-fc01-4bfd-a0d6-1125b7281d22'></entry>"
+			+ "<entry id='five' guid='6D2EC48D-C3B5-4812-B130-5551DC4F13B6'></entry>";
+
+		private static readonly string s_LiftUpdate2 =
+			"<entry id='fourChangedFirstAddition' guid='6216074D-AD4F-4dae-BE5F-8E5E748EF68A'></entry>"
+			+ "<entry id='six' guid='107136D0-5108-4b6b-9846-8590F28937E8'></entry>";
+
+
+		[Test]
+		public void EntryDeleted_DeletionDateAdded()
+		{
+			//This test demonstrates that a deletion of an entry is applied to a LIFT file.
+			//Now 'tomb stoning' is done.  The entry is not actually deleted, but a dateDeleted attribute is added
+
+			//Create a LIFT file with 3 entries which will have updates applied to it.
+			WriteFile(_baseLiftFileName, s_LiftData1, _directory);
+			//Create a .lift.update file with and entry which is indicating that an entry was deleted (tombstone).
+			WriteFile("LiftChangeFile" + SynchronicMerger.ExtensionOfIncrementalFiles, s_LiftUpdateDeleteEntry, _directory);
+			FileInfo[] files = SynchronicMerger.GetPendingUpdateFiles(Path.Combine(_directory, _baseLiftFileName));
+			XmlDocument doc = MergeAndGetResult(true, _directory, files);
+			Assert.AreEqual(4, doc.SelectNodes("//entry").Count);
+			Assert.AreEqual(1, doc.SelectNodes("//entry[@id='one']").Count);
+			XmlNodeList nodesDeleted = doc.SelectNodes("//entry[@id='two' and @guid='0ae89610-fc01-4bfd-a0d6-1125b7281d22']");
+			Assert.AreEqual(1, nodesDeleted.Count);   //ensure there is only one entry with this guid
+			XmlNode nodeDeleted = nodesDeleted[0];
+			//Make sure the contents of the node was changed to match the deleted entry from the .lift.update file
+			Assert.AreEqual("2012-05-08T06:40:44Z", nodeDeleted.Attributes["dateDeleted"].Value);
+			Assert.IsNullOrEmpty(nodeDeleted.InnerXml);
+		}
+
+		private static readonly string s_LiftUpdateDeleteEntry =
+			"<entry id='two' dateCreated='2012-05-04T04:19:57Z' dateModified='2012-05-04T04:19:57Z' guid='0ae89610-fc01-4bfd-a0d6-1125b7281d22' dateDeleted='2012-05-08T06:40:44Z'></entry>"
+			+ "<entry id='six' guid='107136D0-5108-4b6b-9846-8590F28937E8'></entry>";
+
 		[Category("Fails on Linux because of New Lines as well as xml namespaces")]
 		[Test]
 		public void NewEntries_SingleLineXml_FormattedCorrectly()
@@ -519,6 +585,14 @@
 			return GetResult(directory);
 		}

+		private XmlDocument MergeAndGetResult(bool isBackupFileExpected, string directory, FileInfo[] files)
+		{
+			Merge(directory, files);
+			ExpectFileCount(isBackupFileExpected ? 2 : 1, directory);
+
+			return GetResult(directory);
+		}
+
 		private static XmlDocument GetResult(string directory) {
 			XmlDocument doc = new XmlDocument();
 			string outputPath = Path.Combine(directory,_baseLiftFileName);
@@ -529,6 +603,11 @@

 		private void Merge(string directory) {
 			this._merger.MergeUpdatesIntoFile(Path.Combine(directory, _baseLiftFileName));
+		}
+
+		private void Merge(string directory, FileInfo[] files)
+		{
+			this._merger.MergeUpdatesIntoFile(Path.Combine(directory, _baseLiftFileName), files);
 		}

 		static private void ExpectFileCount(int count, string directory)


* Put in crude poll waiting for 60 seconds for delayed user inventory delivery from the inventory service * This will reduce failures where the inventory server is simply slow * Still need to properly inform the user if the inventory service has failed altogether

--- OpenSim/Framework/Communications/Cache/UserProfileCacheService.cs
+++ OpenSim/Framework/Communications/Cache/UserProfileCacheService.cs
@@ -355,11 +355,36 @@
             if ((fold = libraryRoot.HasSubFolder(folderID)) != null)
             {
                 return fold.RequestListOfItems();
-            }
+            }

             CachedUserInfo userProfile;
             if (m_userProfiles.TryGetValue(agentID, out userProfile))
-            {
+            {
+                // XXX: When a client crosses into a scene, their entire inventory is fetched
+                // asynchronously.  If the client makes a request before the inventory is received, we need
+                // to give the inventory a chance to come in.
+                //
+                // This is a crude way of dealing with that by retrying the lookup.  It's not quite as bad
+                // in CAPS as doing this with the udp request, since here it won't hold up other packets.
+                // In fact, here we'll be generous and try for longer.
+                if (!userProfile.HasInventory)
+                {
+                    int attempts = 0;
+                    while (attempts++ < 20)
+                    {
+                        m_log.DebugFormat(
+                             "[INVENTORY CACHE]: Poll number {0} for inventory items in folder {1} for user {2}",
+                             attempts, folderID, agentID);
+
+                        Thread.Sleep(3000);
+
+                        if (userProfile.HasInventory)
+                        {
+                            break;
+                        }
+                    }
+                }
+
                 if (userProfile.HasInventory)
                 {
                     if (userProfile.RootFolder.ID == folderID)


fixed ListenerInfo registration on sim startup

--- OpenSim/Region/CoreModules/Scripting/WorldComm/WorldCommModule.cs
+++ OpenSim/Region/CoreModules/Scripting/WorldComm/WorldCommModule.cs
@@ -834,13 +834,8 @@
                 ListenerInfo info =
                         ListenerInfo.FromData(localID, itemID, hostID, item);

-                try
-                {
-                    m_listeners.AddIfNotExists((int)item[2], delegate() { return new ThreadedClasses.RwLockedList<ListenerInfo>(); });
-                }
-                catch(ThreadedClasses.RwLockedDictionary<int, ThreadedClasses.RwLockedList<ListenerInfo>>.KeyAlreadyExistsException)
-                {
-                }
+                ThreadedClasses.RwLockedList<ListenerInfo> li = m_listeners.GetOrAddIfNotExists((int)item[2], delegate() { return new ThreadedClasses.RwLockedList<ListenerInfo>(); });
+                li.Add(info);

                 idx += dataItemLength;
             }


A last fix for estate access by group. One should send the correct list.

--- OpenSim/Region/CoreModules/World/Estate/EstateManagementModule.cs
+++ OpenSim/Region/CoreModules/World/Estate/EstateManagementModule.cs
@@ -278,7 +278,7 @@
                 {
                     m_scene.RegionInfo.EstateSettings.AddEstateGroup(user);
                     m_scene.RegionInfo.EstateSettings.Save();
-                    remote_client.SendEstateList(invoice, (int)Constants.EstateAccessCodex.AllowedGroups, m_scene.RegionInfo.EstateSettings.EstateAccess, m_scene.RegionInfo.EstateSettings.EstateID);
+                    remote_client.SendEstateList(invoice, (int)Constants.EstateAccessCodex.AllowedGroups, m_scene.RegionInfo.EstateSettings.EstateGroups, m_scene.RegionInfo.EstateSettings.EstateID);
                 }
                 else
                 {
@@ -292,7 +292,7 @@
                     m_scene.RegionInfo.EstateSettings.RemoveEstateGroup(user);
                     m_scene.RegionInfo.EstateSettings.Save();

-                    remote_client.SendEstateList(invoice, (int)Constants.EstateAccessCodex.AllowedGroups, m_scene.RegionInfo.EstateSettings.EstateAccess, m_scene.RegionInfo.EstateSettings.EstateID);
+                    remote_client.SendEstateList(invoice, (int)Constants.EstateAccessCodex.AllowedGroups, m_scene.RegionInfo.EstateSettings.EstateGroups, m_scene.RegionInfo.EstateSettings.EstateID);
                 }
                 else
                 {