diff --git a/src/FactorioTools/OilField/Steps/AddPipes.0.cs b/src/FactorioTools/OilField/Steps/AddPipes.0.cs index b2b8dc47..a10c8b4e 100644 --- a/src/FactorioTools/OilField/Steps/AddPipes.0.cs +++ b/src/FactorioTools/OilField/Steps/AddPipes.0.cs @@ -107,6 +107,11 @@ private static Result SelectBestSolution(Context context, bool eli var result = GetBestSolution(context); if (result.Exception is NoPathBetweenTerminalsException && !eliminateStrandedTerminals) { + // GetBestSolution swaps in a per-strategy clone and mutates it, and does + // not put the originals back when it fails. Without this the recovery + // reasons about a half-solved field. + context.CenterToTerminals = centerToTerminals; + context.LocationToTerminals = locationToTerminals; EliminateStrandedTerminals(context); result = GetBestSolution(context); } @@ -625,49 +630,82 @@ private static Solution GetSolution( }; } + /// + /// Splits the terminal locations into connected components, keeps the one serving + /// the most pumpjacks, and eliminates every other component. + /// + /// This used to decide one component at a time. It walked a component, and if that + /// component did not cover every pumpjack it assumed the component was a stranded + /// pocket and eliminated it. That assumption only holds when the component it + /// happened to walk first is the minority one. + /// + /// Measured on big-list blueprint 227 under Factorio 2.1 terminal offsets: the + /// first walk reached 43 of 47 terminals, covering 21 of 23 pumpjacks, and the old + /// code eliminated all 43. It threw away the main network to keep a two-pumpjack + /// pocket, stranding 19 pumpjacks in a single step. + /// + /// Keeping the largest component instead makes the choice independent of which + /// terminal the walk started from. The branch that got this wrong never runs on the + /// current blueprint corpus - instrumented across all 1147 big-list blueprints, it + /// fired zero times - so this changes no existing plan. + /// private static void EliminateStrandedTerminals(Context context) { - var locationsToExplore = context.LocationToTerminals.Keys.ToReadOnlySet(context, allowEnumerate: true); + var remaining = context.LocationToTerminals.Keys.ToReadOnlySet(context, allowEnumerate: true); - while (locationsToExplore.Count > 0) + var components = new List(); + while (remaining.Count > 0) { - var goals = context.GetLocationSet(locationsToExplore); + var goals = context.GetLocationSet(remaining); var start = goals.EnumerateItems().First(); goals.Remove(start); var result = Dijkstras.GetShortestPaths(context, context.Grid, start, goals, stopOnFirstGoal: false, allowGoalEnumerate: true); - var reachedTerminals = result.ReachedGoals; - reachedTerminals.Add(start); + var component = context.GetLocationSet(result.ReachedGoals); + component.Add(start); + components.Add(component); - var unreachedTerminals = context.GetLocationSet(goals); - unreachedTerminals.ExceptWith(result.ReachedGoals); + remaining.ExceptWith(component); + } + + // One component means everything can already reach everything else. + if (components.Count < 2) + { + return; + } - var reachedPumpjacks = context.GetLocationSet(); - foreach (var location in result.ReachedGoals.EnumerateItems()) + var keep = 0; + var mostPumpjacks = -1; + for (var i = 0; i < components.Count; i++) + { + var centers = context.GetLocationSet(); + foreach (var location in components[i].EnumerateItems()) { var terminals = context.LocationToTerminals[location]; - for (var i = 0; i < terminals.Count; i++) + for (var j = 0; j < terminals.Count; j++) { - reachedPumpjacks.Add(terminals[i].Center); + centers.Add(terminals[j].Center); } } - ILocationSet terminalsToEliminate; - if (reachedPumpjacks.Count == context.CenterToTerminals.Count) + if (centers.Count > mostPumpjacks) { - terminalsToEliminate = unreachedTerminals; - locationsToExplore.Clear(); + mostPumpjacks = centers.Count; + keep = i; } - else + } + + Location strandedTerminal = Location.Invalid; + bool foundStranded = false; + for (var i = 0; i < components.Count; i++) + { + if (i == keep) { - terminalsToEliminate = reachedTerminals; - locationsToExplore = unreachedTerminals; + continue; } - Location strandedTerminal = Location.Invalid; - bool foundStranded = false; - foreach (var location in terminalsToEliminate.EnumerateItems()) + foreach (var location in components[i].EnumerateItems()) { foreach (var terminal in context.LocationToTerminals[location]) { @@ -683,17 +721,13 @@ private static void EliminateStrandedTerminals(Context context) context.LocationToTerminals.Remove(location); } - - if (foundStranded) - { - /* - var clone = new PipeGrid(context.Grid); - AddPipeEntities.Execute(clone, new(), context.CenterToTerminals, new ILocationSet(), undergroundPipes: null, allowMultipleTerminals: true); - Visualizer.Show(clone, new[] { strandedTerminal.Value, locationsToExplore.First() }.Select(x => (IPoint)new Point(x.X, x.Y)), Array.Empty()); - */ + } - throw new NoPathBetweenTerminalsException(strandedTerminal, locationsToExplore.EnumerateItems().First()); - } + // A pumpjack whose every terminal sat outside the kept component genuinely + // cannot join the network, so this is still the right place to give up. + if (foundStranded) + { + throw new NoPathBetweenTerminalsException(strandedTerminal, components[keep].EnumerateItems().First()); } } diff --git a/src/lua/FactorioTools/AddPipes_0.lua b/src/lua/FactorioTools/AddPipes_0.lua index 418e9f17..c47650af 100644 --- a/src/lua/FactorioTools/AddPipes_0.lua +++ b/src/lua/FactorioTools/AddPipes_0.lua @@ -8,6 +8,7 @@ local ListInt32 local ListPlanInfo local ListSolution local NullableInt32 +local ListILocationSet local ListOilFieldPlan local ListTerminalLocation local Comparer_1NullableInt32 @@ -24,6 +25,7 @@ System.import(function (out) ListPlanInfo = System.List(KnapcodeAddPipes.PlanInfo) ListSolution = System.List(KnapcodeAddPipes.Solution) NullableInt32 = System.Nullable(System.Int32) + ListILocationSet = System.List(KnapcodeOilField.ILocationSet) ListOilFieldPlan = System.List(KnapcodeOilField.OilFieldPlan) ListTerminalLocation = System.List(KnapcodeOilField.TerminalLocation) Comparer_1NullableInt32 = System.Comparer_1(NullableInt32) @@ -364,6 +366,11 @@ System.namespace("Knapcode.FactorioTools.OilField", function (namespace) local result = GetBestSolution(context) if System.is(result.Exception, KnapcodeFactorioTools.NoPathBetweenTerminalsException) and not eliminateStrandedTerminals then + -- GetBestSolution swaps in a per-strategy clone and mutates it, and does + -- not put the originals back when it fails. Without this the recovery + -- reasons about a half-solved field. + context.CenterToTerminals = centerToTerminals + context.LocationToTerminals = locationToTerminals EliminateStrandedTerminals(context) result = GetBestSolution(context) end @@ -827,65 +834,100 @@ System.namespace("Knapcode.FactorioTools.OilField", function (namespace) default.UncoveredCenters = uncoveredCenters return default end + -- + -- Splits the terminal locations into connected components, keeps the one serving + -- the most pumpjacks, and eliminates every other component. + -- + -- This used to decide one component at a time. It walked a component, and if that + -- component did not cover every pumpjack it assumed the component was a stranded + -- pocket and eliminated it. That assumption only holds when the component it + -- happened to walk first is the minority one. + -- + -- Measured on big-list blueprint 227 under Factorio 2.1 terminal offsets: the + -- first walk reached 43 of 47 terminals, covering 21 of 23 pumpjacks, and the old + -- code eliminated all 43. It threw away the main network to keep a two-pumpjack + -- pocket, stranding 19 pumpjacks in a single step. + -- + -- Keeping the largest component instead makes the choice independent of which + -- terminal the walk started from. The branch that got this wrong never runs on the + -- current blueprint corpus - instrumented across all 1147 big-list blueprints, it + -- fired zero times - so this changes no existing plan. + -- EliminateStrandedTerminals = function (context) - local locationsToExplore = KnapcodeFactorioTools.CollectionExtensions.ToReadOnlySet1(context.LocationToTerminals:getKeys(), context, true) + local remaining = KnapcodeFactorioTools.CollectionExtensions.ToReadOnlySet1(context.LocationToTerminals:getKeys(), context, true) - while locationsToExplore:getCount() > 0 do - local goals = context:GetLocationSet(locationsToExplore) + local components = ListILocationSet() + while remaining:getCount() > 0 do + local goals = context:GetLocationSet(remaining) local start = KnapcodeFactorioTools.CollectionExtensions.First(goals:EnumerateItems(), KnapcodeOilField.Location) goals:Remove(start) local result = KnapcodeOilField.Dijkstras.GetShortestPaths(context, context.Grid, start, goals, false, true) - local reachedTerminals = result.ReachedGoals - reachedTerminals:Add(start) + local component = context:GetLocationSet(result.ReachedGoals) + component:Add(start) + components:Add(component) - local unreachedTerminals = context:GetLocationSet(goals) - unreachedTerminals:ExceptWith(result.ReachedGoals) + remaining:ExceptWith(component) + end + + -- One component means everything can already reach everything else. + if #components < 2 then + return + end - local reachedPumpjacks = context:GetLocationSet1() - for _, location in System.each(result.ReachedGoals:EnumerateItems()) do + local keep = 0 + local mostPumpjacks = - 1 + for i = 0, #components - 1 do + local centers = context:GetLocationSet1() + for _, location in System.each(components:get(i):EnumerateItems()) do local terminals = context.LocationToTerminals:get(location) - for i = 0, #terminals - 1 do - reachedPumpjacks:Add(terminals:get(i).Center) + for j = 0, #terminals - 1 do + centers:Add(terminals:get(j).Center) end end - local terminalsToEliminate - if reachedPumpjacks:getCount() == context.CenterToTerminals:getCount() then - terminalsToEliminate = unreachedTerminals - locationsToExplore:Clear() - else - terminalsToEliminate = reachedTerminals - locationsToExplore = unreachedTerminals + if centers:getCount() > mostPumpjacks then + mostPumpjacks = centers:getCount() + keep = i end + end - local strandedTerminal = KnapcodeOilField.Location.getInvalid() - local foundStranded = false - for _, location in System.each(terminalsToEliminate:EnumerateItems()) do - for _, terminal in System.each(context.LocationToTerminals:get(location)) do - local terminals = context.CenterToTerminals:get(terminal.Center) - terminals:Remove(terminal) - - if #terminals == 0 then - strandedTerminal = terminal.Terminal - foundStranded = true - end + local strandedTerminal = KnapcodeOilField.Location.getInvalid() + local foundStranded = false + for i = 0, #components - 1 do + local continue + repeat + if i == keep then + continue = true + break end - context.LocationToTerminals:Remove(location) - end + for _, location in System.each(components:get(i):EnumerateItems()) do + for _, terminal in System.each(context.LocationToTerminals:get(location)) do + local terminals = context.CenterToTerminals:get(terminal.Center) + terminals:Remove(terminal) - if foundStranded then - --[[ - var clone = new PipeGrid(context.Grid); - AddPipeEntities.Execute(clone, new(), context.CenterToTerminals, new ILocationSet(), undergroundPipes: null, allowMultipleTerminals: true); - Visualizer.Show(clone, new[] { strandedTerminal.Value, locationsToExplore.First() }.Select(x => (IPoint)new Point(x.X, x.Y)), Array.Empty()); - ]] + if #terminals == 0 then + strandedTerminal = terminal.Terminal + foundStranded = true + end + end - System.throw(KnapcodeFactorioTools.NoPathBetweenTerminalsException(strandedTerminal, KnapcodeFactorioTools.CollectionExtensions.First(locationsToExplore:EnumerateItems(), KnapcodeOilField.Location))) + context.LocationToTerminals:Remove(location) + end + continue = true + until 1 + if not continue then + break end end + + -- A pumpjack whose every terminal sat outside the kept component genuinely + -- cannot join the network, so this is still the right place to give up. + if foundStranded then + System.throw(KnapcodeFactorioTools.NoPathBetweenTerminalsException(strandedTerminal, KnapcodeFactorioTools.CollectionExtensions.First(components:get(keep):EnumerateItems(), KnapcodeOilField.Location))) + end end class = { Execute = Execute diff --git a/test/FactorioTools.Test/OilField/AllowsBlueprintWithNonBlockingIsolatedArea/Theory.E_ConnectedCentersDelaunay.verified.txt b/test/FactorioTools.Test/OilField/AllowsBlueprintWithNonBlockingIsolatedArea/Theory.E_ConnectedCentersDelaunay.verified.txt index 0e37fbac..9db9af10 100644 --- a/test/FactorioTools.Test/OilField/AllowsBlueprintWithNonBlockingIsolatedArea/Theory.E_ConnectedCentersDelaunay.verified.txt +++ b/test/FactorioTools.Test/OilField/AllowsBlueprintWithNonBlockingIsolatedArea/Theory.E_ConnectedCentersDelaunay.verified.txt @@ -1,40 +1,41 @@ - CC-DT -> FBE* (effects: 112, beacons: 62, pipes: 82) - CC-DT -> snug (effects: 105, beacons: 58, pipes: 82) -S CC-DT -> optimize -> FBE* (effects: 114, beacons: 62, pipes: 77) - CC-DT -> optimize -> snug (effects: 101, beacons: 57, pipes: 77) + CC-DT -> FBE* (effects: 109, beacons: 62, pipes: 84) + CC-DT -> snug (effects: 103, beacons: 58, pipes: 84) +S CC-DT -> optimize -> FBE* (effects: 116, beacons: 64, pipes: 78) + CC-DT -> optimize -> snug (effects: 103, beacons: 58, pipes: 78) -. . . . . . . . . . . b b b b b b . b b b b b b . . b b b . . . . . b b b . . . . . . . . . -. . . . . . . . . . . b B b b B b . b B b b B b . . b B b . . . . . b B b . . . . . . . . . -. . . . . . . . . . . b b b b b b . b b b b b b . . b b b b b b . . b b b . . . . . . . . . -. . . . . . . . . E . . . . . . . . . . . . . . . . . . . b B b . . . . . . . . . . . . . . -. . . . b b b b b b b b b . . . . . E . . o < . . > + o o b b b . . . . . . . . . . . . . . -. . . . b B b b B b b B b b b b j j j + o o . E j j j . o j j j E b b b b b b . b b b b b b -E . . . b b b b b b b b b b B b j J j . . ^ . . j J j . o j J j . b B b b B b . b B b b B b -b b b b b b . . . . . . . b b b j j j . . . . . j j j . + j j j . b b b b b b . b b b b b b -b B b b B b . . . . . . . . . . . . . . . . j j j b b b o j j j . . . . . . . . . . . b b b -b b b b b b b b b j j j . . b b b . . . . v j J j b B b o j J j . . . . . . . . . . . b B b -b b b . . . b B b j J j . E b B b . o o o + j j j b b b + j j j . . b b b . j j j E . b b b -b B b . . . b b b j j j . . b b b . o j j j . . . j j j + . . . . . b B b . j J j . . . . . -b b b . . j j j + + < . . > o < . > + j J j . E . j J j o . . . . . b b b . j j j . . b b b +. . . . . . . . . . . b b b b b b . . b b b b b b . b b b b b b . . b b b . . . . . . . . . +. . . . . . . . . . . b B b b B b . . b B b b B b . b B b b B b . . b B b . . . . . . . . . +. . . . . . . . . . . b b b b b b . . b b b b b b . b b b b b b . . b b b . . . . . . . . . +. . . . . . . . . . . b b b . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . +. . . . b b b b b b . b B b . . . . . . . b b b . . . . . . . . . . . . . . . . . . . . . . +. . . . b B b b B b . b b b . E j j j E . b B b j j j + E j j j E b b b b b b . b b b b b b +. E . . b b b b b b b b b b b b j J j . . b b b j J j o . j J j . b B b b B b . b B b b B b +b b b b b b . . E . b B b b B b j j j b b b . . j j j o + j j j . b b b b b b . b b b b b b +b B b b B b . . . . b b b b b b ^ . . b B b j j j b b b o j j j . . . . . . . . . . . b b b +b b b b b b b b b j j j b b b . . . . b b b j J j b B b o j J j . . . . . . . . . . . b B b +b b b . . . b B b j J j b B b . . . o o o + j j j b b b + j j j . . b b b . j j j E . b b b +b B b . . . b b b j j j b b b . v . o j j j . . . j j j + . . . . . b B b . j J j . . . . . +b b b . . j j j + + < . . > o o o o + j J j . . . j J j o . . . . . b b b . j j j . . b b b . . . . . j J j . b b b . . o . j j j j j j j j j j j j o o o + < . > o o o + b b b E b B b -b b b . E j j j . b B b . . + . j J j . . . j J j . . . o j j j . . . o . . E b B b . b b b -b B b . . . . . . b b b j j j . j j j . . . j j j o o o + j J j . E . o . . . b b b . b b b -b b b . . . . E . b b b j J j b b b . j j j + o o o j j j j j j j j j + j j j . . . . b B b -b b b b b b b b b b B b j j j b B b . j J j ^ . E . j J j . . . j J j o j J j . . . . b b b -b B b b B b b B b b b b b b b b b b . j j j b b b . j j j . . . j j j + j j j . . b b b . . -b b b b b b b b b . . . b B b j j j + + . . b B b b b b b b b j j j b b b b b b . b B b . . -. . . . . . . b b b . E b b b j J j . o . . b b b b B b b B b j J j b B b b B b . b b b . . -. . . . . . . b B b b b b . E j j j . o . . v . E b b b b b b j j j b b b b b b . b b b . . +b b b . . j j j . b B b . E + o j J j . . . j J j E . . o j j j . . . o . E . b B b . b b b +b B b E . . . . . b b b j j j o j j j E . . j j j o o o + j J j . E . o . . . b b b . b b b +b b b . . . . . . b b b j J j o o o o j j j + o o o j j j j j j j j j + j j j . . . . b B b +b b b b b b b b b b B b j j j . . . o j J j ^ . . . j J j . . . j J j o j J j . . . . b b b +b B b b B b b B b b b b b b b . . . o j j j b b b . j j j . . . j j j + j j j . . b b b . . +b b b b b b b b b . . E b B b j j j + + . . b B b b b b b b b j j j b b b b b b . b B b . . +. . . . . . . b b b . . b b b j J j . o . . b b b b B b b B b j J j b B b b B b . b b b . . +. . . . . . . b B b b b b . . j j j . o . . v . . b b b b b b j j j b b b b b b . b b b . . . . . . . . . b b b b B b . b b b . . + o o o < . > + < . > o + . . . . . . E . . b B b . . . . . . . . . . . . b b b . b B b j j j b b b . j j j b b b ^ b b b . . . . . . . b b b . . -. . . . . . . . . . b b b . b b b j J j b B b . j J j b B b E b B b . . b b b . . . . . . . +. . . . . . . . . . b b b E b b b j J j b B b . j J j b B b E b B b . . b b b . . . . . . . . . . . . . . . . . b B b b b b . j j j b b b . j j j b b b . b b b . . b B b . . . . . . . -. . . . . . . . . . b b b b B b E . . . . . . . . b b b . . v . . b b b b b b . . . . . . . -. . . . . . . . . . . . . b b b . . . . . . . . . b B b j j j . . b B b . . . . . . . . . . +. . . . . . . . . . b b b b B b . . . . . . E . . b b b . . v . . b b b b b b . . . . . . . +. . . . . . . . . . . . E b b b . . . . . . . . . b B b j j j . . b B b . . . . . . . . . . . . . . . . . . . . . . b b b b b b . b b b b b b b b b j J j . . b b b . . . . . . . . . . -. . . . . . . . . . . . b B b b B b . b B b b B b b b b j j j . . b b b . . . . . . . . . . -. . . . . . . . . . . . b b b b b b . b b b b b b b B b . . E . . b B b . . . . . . . . . . -. . . . . . . . . . . . . . . . . . . . . . . . E b b b . . . . . b b b . . . . . . . . . . +. . . . . . . . . . . . b B b b B b . b B b b B b b b b j j j E . b b b . . . . . . . . . . +. . . . . . . . . . . . b b b b b b . b b b b b b b B b . . . . . b B b . . . . . . . . . . +. . . . . . . . . . . . . . . . . . . . . . . . . b b b . . . . . b b b . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . b b b b b b . b b b b b b . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . b B b b B b . b B b b B b . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . b b b b b b . b b b b b b . . . . . . . . . . +. . . . . . . . . . . . . . . . . . . . . . E . . . . . . . . . . . . . . . . . . . . . . . diff --git a/test/FactorioTools.Test/OilField/AllowsBlueprintWithNonBlockingIsolatedArea/Theory.E_ConnectedCentersFlute.verified.txt b/test/FactorioTools.Test/OilField/AllowsBlueprintWithNonBlockingIsolatedArea/Theory.E_ConnectedCentersFlute.verified.txt index 389a2ac4..9c582917 100644 --- a/test/FactorioTools.Test/OilField/AllowsBlueprintWithNonBlockingIsolatedArea/Theory.E_ConnectedCentersFlute.verified.txt +++ b/test/FactorioTools.Test/OilField/AllowsBlueprintWithNonBlockingIsolatedArea/Theory.E_ConnectedCentersFlute.verified.txt @@ -1,40 +1,41 @@ - CC-FLUTE -> FBE* (effects: 116, beacons: 64, pipes: 93) - CC-FLUTE -> snug (effects: 112, beacons: 61, pipes: 93) -S CC-FLUTE -> optimize -> FBE* (effects: 116, beacons: 64, pipes: 81) - CC-FLUTE -> optimize -> snug (effects: 106, beacons: 58, pipes: 81) + CC-FLUTE -> FBE* (effects: 114, beacons: 63, pipes: 87) + CC-FLUTE -> snug (effects: 102, beacons: 57, pipes: 87) +S CC-FLUTE -> optimize -> FBE* (effects: 116, beacons: 64, pipes: 77) + CC-FLUTE -> optimize -> snug (effects: 106, beacons: 59, pipes: 77) -. . . . . . . . . . . b b b b b b . b b b b b b . b b b . . . b b b b b b . . . . . . . . . . . -. . . . . . . . . . . b B b b B b . b B b b B b E b B b . . . b B b b B b E . . . . . . . . . . -. . . . . . . . . . . b b b b b b . b b b b b b . b b b b b b b b b b b b . . . . . . . . . . . -. . . . . . E . . . E b b b . . . . . . . . . . . . . . b B b . . . . . . . . . . . . . . . . . -. . . . b b b b b b . b B b . E . . . . b b b o < . > o b b b . . . . . . . . . . . . . . . . . -. . . . b B b b B b . b b b . . j j j . b B b o j j j + E j j j . b b b b b b . b b b b b b . . -. . . . b b b b b b b b b b b b j J j . b b b o j J j o . j J j . b B b b B b . b B b b B b . . -b b b b b b . . . . b B b b B b j j j . . o o o j j j o + j j j . b b b b b b . b b b b b b . . -b B b b B b . . . . b b b b b b + . . . . o j j j b b b o j j j . . . . . . . . . . . b b b . . -b b b b b b b b b j j j b b b . o . . E . o j J j b B b o j J j . . . . . E . . . . . b B b . . -b b b . . . b B b j J j b B b . o < . . > + j j j b b b + j j j . . b b b . j j j E . b b b . . -b B b . . E b b b j j j b b b . o . . j j j . . . j j j + E . . . . b B b . j J j . . . . . . . -b b b . . j j j + + < b b b > o o . . j J j . . . j J j o . . . . . b b b . j j j . . b b b . . -. . . . . j J j b b b b B b E o j j j j j j j j j j j j o o o + < . > o o o + b b b . b B b . . -b b b . . j j j b B b b b b . o j J j . . . j J j . . . o j j j . . . o . . . b B b . b b b . . -b B b . . . . . b b b . j j j + j j j . . . j j j . E . + j J j . . . o . . . b b b . b b b . . -b b b . . . . . . b b b j J j o + o o j j j ^ b b b j j j j j j j j j + j j j . . . . b B b . . -b b b b b b b b b b B b j j j . . . o j J j . b B b j J j E . . j J j o j J j . . . . b b b . . -b B b b B b b B b b b b b b b . . . + j j j v b b b j j j . . . j j j + j j j E . b b b . . . E -b b b b b b b b b . . E b B b j j j + o o o o . b b b . b b b j j j + o . b b b . b B b . . . . -. . . . . . . b b b . . b b b j J j E o E b b b b B b . b B b j J j b b b b B b . b b b . . . . -. . . . . E . b B b b b b . . j j j . o . b B b b b b . b b b j j j b B b b b b . b b b . . . . -. . . . . . . b b b b B b . b b b . . + o b b b . . . . . . . b b b b b b . . . . b B b . . . . -. . . . . . . . . . b b b . b B b j j j ^ b b b j j j b b b . b B b . . . . . . . b b b . . . . -. . . . . . . . . . b b b . b b b j J j . b B b j J j b B b E b b b . . b b b . . . . . . . . . -. . . . . . . . . . b B b b b b . j j j v b b b j j j b b b . . . b b b b B b . . . . . . . . . -. . . . . . . . . . b b b b B b . E . . o < . > + < . E . > + . . b B b b b b . . . . . . . . . -. . . . . . . . . . . . . b b b . . . . . . . . . b b b j j j . . b b b . . . . . . . . . . . . -. . . . . . . . E . . . b b b b b b . . b b b . . b B b j J j . . . . . . . . E . . . . . . . . -. . . . . . . . . . . . b B b b B b . . b B b . . b b b j j j E . b b b . . . . . . . . . . . . -. . . . . . . . . . . . b b b b b b . . b b b . . . . . . . . . . b B b . . . . . . . . . . . . -. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . b b b . . . . . . . . . . . . -. . . . . . . . . . . . . . . . . . . . . . . b b b b b b . b b b b b b . . . . . . . . . . . . -. . . . . . . . . . . . . . . . . . . . . . . b B b b B b . b B b b B b . . . . . . . . . . . . -. . . . . . . . . . . . . . . . . . . . . E . b b b b b b . b b b b b b . . . . . . . . . . . . +. . . . . . . . . . . b b b b b b . . b b b b b b . b b b b b b . . b b b . . . . . . . . . +. . . . . . . . . . . b B b b B b . . b B b b B b . b B b b B b . . b B b . . . . . . . . . +. . . . . . . . . . . b b b b b b . . b b b b b b . b b b b b b . . b b b . . . . . . . . . +. . . . . . . . . . . b b b . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . +. . . . b b b b b b . b B b . . . . . . . b b b . . . . . . . . . . . . . . . . . . . . . . +. . . . b B b b B b . b b b . E j j j E . b B b j j j + E j j j E b b b b b b . b b b b b b +. E . . b b b b b b b b b b b b j J j . . b b b j J j o . j J j . b B b b B b . b B b b B b +b b b b b b . . E . b B b b B b j j j b b b . . j j j o + j j j . b b b b b b . b b b b b b +b B b b B b . . . . b b b b b b ^ . . b B b j j j b b b o j j j . . . . . . . . . . . b b b +b b b b b b b b b j j j b b b . . . . b b b j J j b B b o j J j . . . . . . . . . . . b B b +b b b . . . b B b j J j b B b . . . o o o + j j j b b b + j j j . b b b . . j j j E . b b b +b B b . . . b b b j j j b b b . v . o j j j . . . j j j + . . . . b B b . . j J j . . . . . +b b b . . j j j + + < . . > o o o o + j J j . . . j J j o . . . . b b b . . j j j . . b b b +. . . . . j J j . b b b . . o . j j j j j j j j j j j j o o o + o o o . . . + b b b E b B b +b b b . . j j j . b B b . E + o j J j . . . j J j E . . o j j j . . o . . E o b B b . b b b +b B b E . . . . . b b b j j j o j j j E . . j j j o o o + j J j . E + < . > + b b b . b b b +b b b . . . . . . b b b j J j o o o o j j j + o o o j j j j j j j j j . j j j . . . . b B b +b b b b b b b b b b B b j j j . . . o j J j ^ . . . j J j . . . j J j . j J j . . . . b b b +b B b b B b b B b b b b b b b . . . o j j j b b b . j j j . . . j j j . j j j . . b b b . . +b b b b b b b b b . . E b B b j j j + + . . b B b b b b b b b j j j b b b b b b . b B b . . +. . . . . . . b b b . . b b b j J j . o . . b b b b B b b B b j J j b B b b B b . b b b . . +. . . . . . . b B b b b b . . j j j . o . . v . . b b b b b b j j j b b b b b b . b b b . . +. . . . . . . b b b b B b . b b b . . + o o o < . > + < . > o + . . . . . . E . . b B b . . +. . . . . . . . . . b b b . b B b j j j b b b . j j j b b b ^ b b b . . . . . . . b b b . . +. . . . . . . . . . b b b E b b b j J j b B b . j J j b B b E b B b . . b b b . . . . . . . +. . . . . . . . . . b B b b b b . j j j b b b . j j j b b b . b b b . . b B b . . . . . . . +. . . . . . . . . . b b b b B b . . . . . . E . . b b b . . v . . b b b b b b . . . . . . . +. . . . . . . . . . . . E b b b . . . . . . . . . b B b j j j . . b B b . . . . . . . . . . +. . . . . . . . . . . . b b b b b b . b b b b b b b b b j J j . . b b b . . . . . . . . . . +. . . . . . . . . . . . b B b b B b . b B b b B b b b b j j j E . b b b . . . . . . . . . . +. . . . . . . . . . . . b b b b b b . b b b b b b b B b . . . . . b B b . . . . . . . . . . +. . . . . . . . . . . . . . . . . . . . . . . . . b b b . . . . . b b b . . . . . . . . . . +. . . . . . . . . . . . . . . . . . . . . . . b b b b b b . b b b b b b . . . . . . . . . . +. . . . . . . . . . . . . . . . . . . . . . . b B b b B b . b B b b B b . . . . . . . . . . +. . . . . . . . . . . . . . . . . . . . . . . b b b b b b . b b b b b b . . . . . . . . . . +. . . . . . . . . . . . . . . . . . . . . . E . . . . . . . . . . . . . . . . . . . . . . .