From 98d88a68b4f9f8daee09a255501fa764670e6319 Mon Sep 17 00:00:00 2001 From: "Paul C. Scharf" Date: Mon, 10 Jan 2022 20:30:07 +0000 Subject: [PATCH] =?UTF-8?q?=E2=8F=AE=20Switch=20to=20file=20scope=20namesp?= =?UTF-8?q?aces=20(#251)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Linq/Extensions.cs | 83 +- Bearded.Utilities.Benchmarks/Program.cs | 11 +- .../Core/MaybeAssertionsTests.cs | 191 +++-- .../Monads/ResultAssertionsTests.cs | 319 ++++--- .../Core/MaybeAssertions.cs | 89 +- .../Core/MaybeExtensions.cs | 9 +- .../Geometry/Bivector2Assertions.cs | 61 +- .../Geometry/Bivector2Extensions.cs | 9 +- .../Geometry/Bivector3Assertions.cs | 89 +- .../Geometry/Bivector3Extensions.cs | 9 +- .../Monads/ResultAssertions.cs | 99 ++- .../Monads/ResultExtensions.cs | 11 +- .../Algorithms/BinPackingTests.cs | 189 +++-- .../Algorithms/CoffmanGrahamTests.cs | 251 +++--- .../Algorithms/HungarianAlgorithmTests.cs | 157 ++-- .../Assertions/Vector2Assertions.cs | 37 +- .../Assertions/Vector2Extensions.cs | 9 +- .../Collections/DeletableObjectListTests.cs | 753 +++++++++-------- .../Collections/LoopingQueue.cs | 33 +- .../Collections/PrefixTrieTests.cs | 255 +++--- .../Collections/PriorityQueueTests.cs | 170 ++-- .../Collections/StaticPriorityQueueTests.cs | 123 ++- Bearded.Utilities.Tests/Core/MaybeTests.cs | 407 +++++---- .../Core/Random/BaseTests.cs | 103 ++- .../Core/Random/RandomExtensionsTests.cs | 251 +++--- .../Core/Random/SharedTests.cs | 785 +++++++++--------- .../Core/Random/StaticRandomTests.cs | 255 +++--- .../Core/ResettableLazyTests.cs | 95 ++- .../BiLinearInterpolationTests.cs | 9 +- .../InterpolationMethod1dTests.cs | 43 +- .../InterpolationMethod2dTests.cs | 83 +- .../interpolation/LinearInterpolationTests.cs | 9 +- .../Nearest1InterpolationTests.cs | 9 +- .../Nearest2InterpolationTests.cs | 9 +- .../SmoothStepInterpolationTests.cs | 9 +- .../Generators/DirectionGenerators.cs | 13 +- .../Generators/DoubleGenerators.cs | 53 +- .../Generators/FloatGenerators.cs | 71 +- .../Generators/IntGenerators.cs | 19 +- .../Generators/Vector2Generators.cs | 15 +- .../Generators/Vector3Generators.cs | 15 +- .../Geometry/Bivector2Tests.cs | 211 +++-- .../Geometry/Bivector3Tests.cs | 269 +++--- .../Geometry/CircularArc2Tests.cs | 499 ++++++----- ...ectedAcyclicGraphTransitiveReducerTests.cs | 205 +++-- .../Graphs/DirectedGraphBuilderTests.cs | 559 +++++++------ .../Helpers/LimitedRangeFloatGenerator.cs | 15 +- .../Helpers/NumericAssertionsExtensions.cs | 31 +- .../Linq/ExtensionsTests.cs | 725 ++++++++-------- Bearded.Utilities.Tests/Monads/ResultTests.cs | 329 ++++---- Bearded.Utilities.Tests/Noise/NoiseTests.cs | 89 +- .../Noise/PerlinNoiseTests.cs | 9 +- .../Noise/UniformNoiseTests.cs | 9 +- .../SpaceTime/GravitationalConstantTests.cs | 211 +++-- .../Tilemaps/ExtensionsTests.cs | 393 +++++---- .../Tilemaps/RectangularTilemapTests.cs | 141 ++-- Bearded.Utilities.Tests/Tilemaps/TileTests.cs | 239 +++--- .../Algorithms/BinPacking.Algorithm.cs | 307 ++++--- .../Algorithms/BinPacking.BinPacking.Types.cs | 159 ++-- Bearded.Utilities/Algorithms/BinPacking.cs | 201 +++-- Bearded.Utilities/Algorithms/CoffmanGraham.cs | 332 ++++---- .../Algorithms/HungarianAlgorithm.cs | 645 +++++++------- .../Collections/DeletableObjectList.cs | 279 ++++--- .../DeletableObjectListEnumerator.cs | 87 +- Bearded.Utilities/Collections/IDeletable.cs | 19 +- Bearded.Utilities/Collections/IIdable.cs | 9 +- Bearded.Utilities/Collections/IdDictionary.cs | 13 +- .../Collections/MutableLinkedList.cs | 291 ++++--- .../MutableLinkedListEnumerator.cs | 143 ++-- .../Collections/MutableLinkedListItem.cs | 26 +- .../Collections/MutableLinkedListNode.cs | 183 ++-- Bearded.Utilities/Collections/PrefixTrie.cs | 355 ++++---- .../Collections/PriorityQueue.cs | 163 ++-- .../Collections/StaticPriorityQueue.cs | 357 ++++---- Bearded.Utilities/Core/AsyncAtomicUpdating.cs | 55 +- Bearded.Utilities/Core/Box.cs | 15 +- Bearded.Utilities/Core/Do.cs | 13 +- Bearded.Utilities/Core/Environment.cs | 143 ++-- Bearded.Utilities/Core/EventHandlers.cs | 15 +- Bearded.Utilities/Core/Id.cs | 35 +- Bearded.Utilities/Core/IdManager.cs | 21 +- Bearded.Utilities/Core/MathConstants.cs | 75 +- Bearded.Utilities/Core/MathExtensions.cs | 597 +++++++------ Bearded.Utilities/Core/Maybe.cs | 117 ++- Bearded.Utilities/Core/MoreMath.cs | 81 +- Bearded.Utilities/Core/RandomExtensions.cs | 279 ++++--- Bearded.Utilities/Core/ResettableLazy.cs | 67 +- Bearded.Utilities/Core/Singleton.cs | 19 +- Bearded.Utilities/Core/StaticRandom.cs | 237 +++--- Bearded.Utilities/Core/Void.cs | 33 +- .../interpolation/IInterpolationMethod1d.cs | 9 +- .../interpolation/IInterpolationMethod2d.cs | 9 +- .../Core/interpolation/Interpolate.cs | 361 ++++---- .../Core/interpolation/Interpolation1d.cs | 51 +- .../Core/interpolation/Interpolation2d.cs | 35 +- Bearded.Utilities/Geometry/Angle.cs | 745 +++++++++-------- Bearded.Utilities/Geometry/Bivector2.cs | 51 +- Bearded.Utilities/Geometry/Bivector3.cs | 71 +- Bearded.Utilities/Geometry/CircularArc2.cs | 207 +++-- Bearded.Utilities/Geometry/Direction2.cs | 407 +++++---- .../Geometry/GeometryExtensions.cs | 31 +- Bearded.Utilities/Geometry/Interval.cs | 31 +- Bearded.Utilities/Geometry/PolarPosition.cs | 237 +++--- Bearded.Utilities/Geometry/Rectangle.cs | 133 ++- Bearded.Utilities/Geometry/arcs/Arc.cs | 237 +++--- Bearded.Utilities/Geometry/arcs/Arc2.cs | 39 +- Bearded.Utilities/Geometry/arcs/Arc3.cs | 39 +- Bearded.Utilities/Geometry/arcs/Bezier2nd2.cs | 67 +- Bearded.Utilities/Geometry/arcs/Bezier2nd3.cs | 67 +- Bearded.Utilities/Geometry/arcs/Bezier3rd2.cs | 71 +- Bearded.Utilities/Geometry/arcs/Bezier3rd3.cs | 71 +- .../AdjacencyListDirectedAcyclicGraph.cs | 25 +- .../Graphs/AdjacencyListDirectedGraph.cs | 51 +- .../DirectedAcyclicGraphTransitiveReducer.cs | 95 ++- .../Graphs/DirectedGraphBuilder.cs | 221 +++-- .../Graphs/IDirectedAcyclicGraph.cs | 7 +- Bearded.Utilities/Graphs/IDirectedGraph.cs | 15 +- Bearded.Utilities/IO/FileModifiedWatcher.cs | 121 ++- Bearded.Utilities/IO/Logger.cs | 509 ++++++------ .../Input/Actions/DigitalAction.cs | 23 +- .../Input/Actions/DummyAction.cs | 35 +- .../Input/Actions/GamePadAction.cs | 1 + .../Input/Actions/KeyboardAction.cs | 29 +- .../Input/Actions/LambdaAction.cs | 41 +- .../Input/Actions/MouseAction.cs | 29 +- .../Input/GamePadStateManager.cs | 59 +- Bearded.Utilities/Input/IAction.cs | 17 +- Bearded.Utilities/Input/InputAction.cs | 197 +++-- .../Input/InputManager.Actions.Gamepad.cs | 1 + .../Input/InputManager.Actions.Keyboard.cs | 75 +- .../Input/InputManager.Actions.Mouse.cs | 49 +- .../Input/InputManager.Actions.cs | 53 +- Bearded.Utilities/Input/InputManager.cs | 149 ++-- Bearded.Utilities/Input/KeyboardEvents.cs | 99 ++- Bearded.Utilities/Linq/Extensions.cs | 595 +++++++------ Bearded.Utilities/Monads/Result.cs | 177 ++-- Bearded.Utilities/Noise/IProceduralTexture.cs | 85 +- Bearded.Utilities/Noise/NoiseUtils.cs | 25 +- Bearded.Utilities/Noise/PerlinNoise.cs | 205 +++-- Bearded.Utilities/Noise/ProceduralTexture.cs | 121 ++- Bearded.Utilities/Noise/UniformNoise.cs | 31 +- .../SpaceTime/2d/Acceleration2.cs | 313 ++++--- Bearded.Utilities/SpaceTime/2d/Difference2.cs | 309 ++++--- Bearded.Utilities/SpaceTime/2d/Position2.cs | 193 +++-- Bearded.Utilities/SpaceTime/2d/Velocity2.cs | 331 ++++---- .../SpaceTime/3d/Acceleration3.cs | 313 ++++--- .../SpaceTime/3d/AddDimensionExtensions.cs | 11 +- Bearded.Utilities/SpaceTime/3d/Difference3.cs | 301 ++++--- Bearded.Utilities/SpaceTime/3d/Position3.cs | 209 +++-- Bearded.Utilities/SpaceTime/3d/Velocity3.cs | 329 ++++---- Bearded.Utilities/SpaceTime/Extensions.cs | 57 +- Bearded.Utilities/SpaceTime/Squared.cs | 221 +++-- .../SpaceTime/angular/AngularAcceleration.cs | 263 +++--- .../SpaceTime/angular/AngularVelocity.cs | 273 +++--- .../gravity/GravitationalConstant.cs | 69 +- Bearded.Utilities/SpaceTime/gravity/Mass.cs | 121 ++- Bearded.Utilities/SpaceTime/time/Frequency.cs | 139 ++-- Bearded.Utilities/SpaceTime/time/Instant.cs | 175 ++-- Bearded.Utilities/SpaceTime/time/TimeSpan.cs | 145 ++-- .../SpaceTime/undirected/Acceleration.cs | 313 ++++--- .../SpaceTime/undirected/Speed.cs | 329 ++++---- .../SpaceTime/undirected/Unit.cs | 307 ++++--- .../Threading/BackgroundActionQueue.cs | 123 ++- Bearded.Utilities/Threading/IActionQueue.cs | 39 +- .../Threading/ManualActionQueue.cs | 169 ++-- .../Tilemaps/Rectangular/Direction.cs | 83 +- .../Tilemaps/Rectangular/Directions.cs | 93 +-- .../Tilemaps/Rectangular/Extensions.cs | 405 +++++---- .../Tilemaps/Rectangular/Tile.cs | 251 +++--- .../Tilemaps/Rectangular/Tilemap.cs | 203 +++-- Bearded.Utilities/Tilemaps/Step.cs | 19 +- 171 files changed, 13282 insertions(+), 13448 deletions(-) diff --git a/Bearded.Utilities.Benchmarks/Linq/Extensions.cs b/Bearded.Utilities.Benchmarks/Linq/Extensions.cs index 0b5fa491..925f5db6 100644 --- a/Bearded.Utilities.Benchmarks/Linq/Extensions.cs +++ b/Bearded.Utilities.Benchmarks/Linq/Extensions.cs @@ -4,64 +4,63 @@ using Bearded.Utilities.Linq; using BenchmarkDotNet.Attributes; -namespace Bearded.Utilities.Benchmarks.Linq +namespace Bearded.Utilities.Benchmarks.Linq; + +[SuppressMessage("ReSharper", "ClassCanBeSealed.Global")] +public sealed class Extensions { - [SuppressMessage("ReSharper", "ClassCanBeSealed.Global")] - public sealed class Extensions + private const int seed = 1337; + private const int count = 10000; + + public abstract class ListBenchmark { - private const int seed = 1337; - private const int count = 10000; + protected List List { get; } + protected Random Random { get; } - public abstract class ListBenchmark + protected ListBenchmark() { - protected List List { get; } - protected Random Random { get; } - - protected ListBenchmark() + List = new List(count); + Random = new Random(seed); + for (var i = 0; i < count; i++) { - List = new List(count); - Random = new Random(seed); - for (var i = 0; i < count; i++) - { - List.Add(Random.Next()); - } + List.Add(Random.Next()); } } + } - public class RandomElement : ListBenchmark + public class RandomElement : ListBenchmark + { + [Benchmark] + public int GetRandomElement() { - [Benchmark] - public int GetRandomElement() - { - return List.RandomElement(); - } + return List.RandomElement(); } + } - public class RandomSubset : ListBenchmark - { - [Params(1, 100, count / 2, count - 100, count)] - public int SubsetSize { get; set; } + public class RandomSubset : ListBenchmark + { + [Params(1, 100, count / 2, count - 100, count)] + public int SubsetSize { get; set; } - [Benchmark] - public List GetRandomSubset() - { - return List.RandomSubset(SubsetSize); - } + [Benchmark] + public List GetRandomSubset() + { + return List.RandomSubset(SubsetSize); } + } - public class Shuffle : ListBenchmark + public class Shuffle : ListBenchmark + { + [Benchmark] + public void ShuffleInPlace() { - [Benchmark] - public void ShuffleInPlace() - { - List.Shuffle(Random); - } + List.Shuffle(Random); + } - [Benchmark] - public IList Shuffled() - { - return List.Shuffled(Random); - } + [Benchmark] + public IList Shuffled() + { + return List.Shuffled(Random); } } } diff --git a/Bearded.Utilities.Benchmarks/Program.cs b/Bearded.Utilities.Benchmarks/Program.cs index f9fbf4b4..90c0e6f2 100644 --- a/Bearded.Utilities.Benchmarks/Program.cs +++ b/Bearded.Utilities.Benchmarks/Program.cs @@ -1,13 +1,12 @@ using BenchmarkDotNet.Configs; using BenchmarkDotNet.Running; -namespace Bearded.Utilities.Benchmarks +namespace Bearded.Utilities.Benchmarks; + +static class Program { - static class Program + static void Main(string[] args) { - static void Main(string[] args) - { - BenchmarkSwitcher.FromAssembly(typeof(Program).Assembly).Run(args, new DebugInProcessConfig()); - } + BenchmarkSwitcher.FromAssembly(typeof(Program).Assembly).Run(args, new DebugInProcessConfig()); } } diff --git a/Bearded.Utilities.Testing.Tests/Core/MaybeAssertionsTests.cs b/Bearded.Utilities.Testing.Tests/Core/MaybeAssertionsTests.cs index 263592f0..c0fda65f 100644 --- a/Bearded.Utilities.Testing.Tests/Core/MaybeAssertionsTests.cs +++ b/Bearded.Utilities.Testing.Tests/Core/MaybeAssertionsTests.cs @@ -3,147 +3,146 @@ using Xunit; using Xunit.Sdk; -namespace Bearded.Utilities.Testing.Tests +namespace Bearded.Utilities.Testing.Tests; + +public sealed class MaybeAssertionsTests { - public sealed class MaybeAssertionsTests + public sealed class BeJustWithNoParameter { - public sealed class BeJustWithNoParameter + [Fact] + public void SucceedsWhenJust() { - [Fact] - public void SucceedsWhenJust() - { - var maybe = Maybe.Just(100); + var maybe = Maybe.Just(100); - Action assertion = () => maybe.Should().BeJust(); + Action assertion = () => maybe.Should().BeJust(); - assertion.Should().NotThrow(); - } + assertion.Should().NotThrow(); + } - [Fact] - public void FailsWhenNothing() - { - var maybe = Maybe.Nothing; + [Fact] + public void FailsWhenNothing() + { + var maybe = Maybe.Nothing; - Action assertion = () => maybe.Should().BeJust(); + Action assertion = () => maybe.Should().BeJust(); - assertion.Should().Throw(); - } + assertion.Should().Throw(); + } - [Fact] - public void ReturnsAndConstraintThatSucceedsAsExpected() - { - var maybe = Maybe.Just(100); + [Fact] + public void ReturnsAndConstraintThatSucceedsAsExpected() + { + var maybe = Maybe.Just(100); - Action assertion = () => maybe.Should().BeJust().And.Should().NotBeNull(); + Action assertion = () => maybe.Should().BeJust().And.Should().NotBeNull(); - assertion.Should().NotThrow(); - } + assertion.Should().NotThrow(); + } - [Fact] - public void ReturnsAndConstraintThatFailsAsExpected() - { - var maybe = Maybe.Just(100); + [Fact] + public void ReturnsAndConstraintThatFailsAsExpected() + { + var maybe = Maybe.Just(100); - Action assertion = () => maybe.Should().BeJust().And.Should().BeNull(); + Action assertion = () => maybe.Should().BeJust().And.Should().BeNull(); - assertion.Should().Throw(); - } + assertion.Should().Throw(); + } - [Fact] - public void FailsWhenNothingEvenIfAndConstraintSucceeds() - { - var maybe = Maybe.Just(100); + [Fact] + public void FailsWhenNothingEvenIfAndConstraintSucceeds() + { + var maybe = Maybe.Just(100); - Action assertion = () => maybe.Should().BeJust().And.Should().NotBeNull(); + Action assertion = () => maybe.Should().BeJust().And.Should().NotBeNull(); - assertion.Should().NotThrow(); - } + assertion.Should().NotThrow(); + } - [Fact] - public void ReturnsWhichConstraintThatSucceedsAsExpected() - { - var maybe = Maybe.Just(100); + [Fact] + public void ReturnsWhichConstraintThatSucceedsAsExpected() + { + var maybe = Maybe.Just(100); - Action assertion = () => maybe.Should().BeJust().Which.Should().Be(100); + Action assertion = () => maybe.Should().BeJust().Which.Should().Be(100); - assertion.Should().NotThrow(); - } + assertion.Should().NotThrow(); + } - [Fact] - public void ReturnsWhichConstraintThatFailsAsExpected() - { - var maybe = Maybe.Just(100); + [Fact] + public void ReturnsWhichConstraintThatFailsAsExpected() + { + var maybe = Maybe.Just(100); - Action assertion = () => maybe.Should().BeJust().Which.Should().Be(200); + Action assertion = () => maybe.Should().BeJust().Which.Should().Be(200); - assertion.Should().Throw(); - } + assertion.Should().Throw(); + } - [Fact] - public void FailsWhenNothingEvenIfWhichConstraintSucceeds() - { - var maybe = Maybe.Just(100); + [Fact] + public void FailsWhenNothingEvenIfWhichConstraintSucceeds() + { + var maybe = Maybe.Just(100); - Action assertion = () => maybe.Should().BeJust().Which.Should().Be(100); + Action assertion = () => maybe.Should().BeJust().Which.Should().Be(100); - assertion.Should().NotThrow(); - } + assertion.Should().NotThrow(); } + } - public sealed class BeJustWithValue + public sealed class BeJustWithValue + { + [Fact] + public void SucceedsWhenJustComparedToSameValue() { - [Fact] - public void SucceedsWhenJustComparedToSameValue() - { - var maybe = Maybe.Just(100); + var maybe = Maybe.Just(100); - Action assertion = () => maybe.Should().BeJust(100); + Action assertion = () => maybe.Should().BeJust(100); - assertion.Should().NotThrow(); - } + assertion.Should().NotThrow(); + } - [Fact] - public void FailsWhenJustComparedToDifferentValue() - { - var maybe = Maybe.Just(100); + [Fact] + public void FailsWhenJustComparedToDifferentValue() + { + var maybe = Maybe.Just(100); - Action assertion = () => maybe.Should().BeJust(200); + Action assertion = () => maybe.Should().BeJust(200); - assertion.Should().Throw(); - } + assertion.Should().Throw(); + } - [Fact] - public void FailsWhenNothing() - { - var maybe = Maybe.Nothing; + [Fact] + public void FailsWhenNothing() + { + var maybe = Maybe.Nothing; - Action assertion = () => maybe.Should().BeJust(200); + Action assertion = () => maybe.Should().BeJust(200); - assertion.Should().Throw(); - } + assertion.Should().Throw(); } + } - public sealed class BeNothing + public sealed class BeNothing + { + [Fact] + public void SucceedsWhenNothing() { - [Fact] - public void SucceedsWhenNothing() - { - var maybe = Maybe.Nothing; + var maybe = Maybe.Nothing; - Action assertion = () => maybe.Should().BeNothing(); + Action assertion = () => maybe.Should().BeNothing(); - assertion.Should().NotThrow(); - } + assertion.Should().NotThrow(); + } - [Fact] - public void FailsWhenJust() - { - var maybe = Maybe.Just(100); + [Fact] + public void FailsWhenJust() + { + var maybe = Maybe.Just(100); - Action assertion = () => maybe.Should().BeNothing(); + Action assertion = () => maybe.Should().BeNothing(); - assertion.Should().Throw(); - } + assertion.Should().Throw(); } } } diff --git a/Bearded.Utilities.Testing.Tests/Monads/ResultAssertionsTests.cs b/Bearded.Utilities.Testing.Tests/Monads/ResultAssertionsTests.cs index 0694cfa8..217b1858 100644 --- a/Bearded.Utilities.Testing.Tests/Monads/ResultAssertionsTests.cs +++ b/Bearded.Utilities.Testing.Tests/Monads/ResultAssertionsTests.cs @@ -5,240 +5,239 @@ using Xunit; using Xunit.Sdk; -namespace Bearded.Utilities.Testing.Tests.Monads +namespace Bearded.Utilities.Testing.Tests.Monads; + +public sealed class ResultAssertionsTests { - public sealed class ResultAssertionsTests + public sealed class BeSuccess { - public sealed class BeSuccess + [Fact] + public void SucceedsWhenSuccess() { - [Fact] - public void SucceedsWhenSuccess() - { - var result = Result.Success(100); + var result = Result.Success(100); - Action assertion = () => result.Should().BeSuccess(); + Action assertion = () => result.Should().BeSuccess(); - assertion.Should().NotThrow(); - } + assertion.Should().NotThrow(); + } - [Fact] - public void FailsWhenFailure() - { - var result = Result.Failure("something went wrong"); + [Fact] + public void FailsWhenFailure() + { + var result = Result.Failure("something went wrong"); - Action assertion = () => result.Should().BeSuccess(); + Action assertion = () => result.Should().BeSuccess(); - assertion.Should().Throw(); - } + assertion.Should().Throw(); + } - [Fact] - public void ReturnsAndConstraintThatSucceedsAsExpected() - { - var result = Result.Success(100); + [Fact] + public void ReturnsAndConstraintThatSucceedsAsExpected() + { + var result = Result.Success(100); - Action assertion = () => result.Should().BeSuccess().And.Should().NotBeNull(); + Action assertion = () => result.Should().BeSuccess().And.Should().NotBeNull(); - assertion.Should().NotThrow(); - } + assertion.Should().NotThrow(); + } - [Fact] - public void ReturnsAndConstraintThatFailsAsExpected() - { - var result = Result.Success(100); + [Fact] + public void ReturnsAndConstraintThatFailsAsExpected() + { + var result = Result.Success(100); - Action assertion = () => result.Should().BeSuccess().And.Should().BeNull(); + Action assertion = () => result.Should().BeSuccess().And.Should().BeNull(); - assertion.Should().Throw(); - } + assertion.Should().Throw(); + } - [Fact] - public void FailsWhenFailureEvenIfAndConstraintSucceeds() - { - var result = Result.Failure("something went wrong"); + [Fact] + public void FailsWhenFailureEvenIfAndConstraintSucceeds() + { + var result = Result.Failure("something went wrong"); - Action assertion = () => result.Should().BeSuccess().And.Should().NotBeNull(); + Action assertion = () => result.Should().BeSuccess().And.Should().NotBeNull(); - assertion.Should().Throw(); - } + assertion.Should().Throw(); + } - [Fact] - public void ReturnsWhichConstraintThatSucceedsAsExpected() - { - var result = Result.Success(100); + [Fact] + public void ReturnsWhichConstraintThatSucceedsAsExpected() + { + var result = Result.Success(100); - Action assertion = () => result.Should().BeSuccess().Which.Should().Be(100); + Action assertion = () => result.Should().BeSuccess().Which.Should().Be(100); - assertion.Should().NotThrow(); - } + assertion.Should().NotThrow(); + } - [Fact] - public void ReturnsWhichConstraintThatFailsAsExpected() - { - var result = Result.Success(200); + [Fact] + public void ReturnsWhichConstraintThatFailsAsExpected() + { + var result = Result.Success(200); - Action assertion = () => result.Should().BeSuccess().Which.Should().Be(100); + Action assertion = () => result.Should().BeSuccess().Which.Should().Be(100); - assertion.Should().Throw(); - } + assertion.Should().Throw(); + } - [Fact] - public void FailsWhenFailureEvenIfWhichConstraintSucceeds() - { - var result = Result.Failure("something went wrong"); + [Fact] + public void FailsWhenFailureEvenIfWhichConstraintSucceeds() + { + var result = Result.Failure("something went wrong"); - Action assertion = () => result.Should().BeSuccess().Which.Should().Be(100); + Action assertion = () => result.Should().BeSuccess().Which.Should().Be(100); - assertion.Should().Throw(); - } + assertion.Should().Throw(); } + } - public sealed class BeSuccessWithResult + public sealed class BeSuccessWithResult + { + [Fact] + public void SucceedsWhenSuccessWithSameResult() { - [Fact] - public void SucceedsWhenSuccessWithSameResult() - { - var result = Result.Success(100); + var result = Result.Success(100); - Action assertion = () => result.Should().BeSuccessWithResult(100); + Action assertion = () => result.Should().BeSuccessWithResult(100); - assertion.Should().NotThrow(); - } + assertion.Should().NotThrow(); + } - [Fact] - public void FailsWhenSuccessWithDifferentResult() - { - var result = Result.Success(200); + [Fact] + public void FailsWhenSuccessWithDifferentResult() + { + var result = Result.Success(200); - Action assertion = () => result.Should().BeSuccessWithResult(100); + Action assertion = () => result.Should().BeSuccessWithResult(100); - assertion.Should().Throw(); - } + assertion.Should().Throw(); + } - [Fact] - public void FailsWhenFailure() - { - var result = Result.Failure("something went wrong"); + [Fact] + public void FailsWhenFailure() + { + var result = Result.Failure("something went wrong"); - Action assertion = () => result.Should().BeSuccessWithResult(100); + Action assertion = () => result.Should().BeSuccessWithResult(100); - assertion.Should().Throw(); - } + assertion.Should().Throw(); } + } - public sealed class BeFailure + public sealed class BeFailure + { + [Fact] + public void SucceedsWhenFailure() { - [Fact] - public void SucceedsWhenFailure() - { - var result = Result.Failure("something went wrong"); + var result = Result.Failure("something went wrong"); - Action assertion = () => result.Should().BeFailure(); + Action assertion = () => result.Should().BeFailure(); - assertion.Should().NotThrow(); - } + assertion.Should().NotThrow(); + } - [Fact] - public void FailsWhenSuccess() - { - var result = Result.Success(100); + [Fact] + public void FailsWhenSuccess() + { + var result = Result.Success(100); - Action assertion = () => result.Should().BeFailure(); + Action assertion = () => result.Should().BeFailure(); - assertion.Should().Throw(); - } + assertion.Should().Throw(); + } - [Fact] - public void ReturnsAndConstraintThatSucceedsAsExpected() - { - var result = Result.Failure("something went wrong"); + [Fact] + public void ReturnsAndConstraintThatSucceedsAsExpected() + { + var result = Result.Failure("something went wrong"); - Action assertion = () => result.Should().BeFailure().And.Should().NotBeNull(); + Action assertion = () => result.Should().BeFailure().And.Should().NotBeNull(); - assertion.Should().NotThrow(); - } + assertion.Should().NotThrow(); + } - [Fact] - public void ReturnsAndConstraintThatFailsAsExpected() - { - var result = Result.Failure("something went wrong"); + [Fact] + public void ReturnsAndConstraintThatFailsAsExpected() + { + var result = Result.Failure("something went wrong"); - Action assertion = () => result.Should().BeFailure().And.Should().BeNull(); + Action assertion = () => result.Should().BeFailure().And.Should().BeNull(); - assertion.Should().Throw(); - } + assertion.Should().Throw(); + } - [Fact] - public void FailsWhenSuccessEvenIfAndConstraintSucceeds() - { - var result = Result.Success(100); + [Fact] + public void FailsWhenSuccessEvenIfAndConstraintSucceeds() + { + var result = Result.Success(100); - Action assertion = () => result.Should().BeFailure().And.Should().NotBeNull(); + Action assertion = () => result.Should().BeFailure().And.Should().NotBeNull(); - assertion.Should().Throw(); - } + assertion.Should().Throw(); + } - [Fact] - public void ReturnsWhichConstraintThatSucceedsAsExpected() - { - var result = Result.Failure("something went wrong"); + [Fact] + public void ReturnsWhichConstraintThatSucceedsAsExpected() + { + var result = Result.Failure("something went wrong"); - Action assertion = () => result.Should().BeFailure().Which.Should().Be("something went wrong"); + Action assertion = () => result.Should().BeFailure().Which.Should().Be("something went wrong"); - assertion.Should().NotThrow(); - } + assertion.Should().NotThrow(); + } - [Fact] - public void ReturnsWhichConstraintThatFailsAsExpected() - { - var result = Result.Failure("something else went wrong"); + [Fact] + public void ReturnsWhichConstraintThatFailsAsExpected() + { + var result = Result.Failure("something else went wrong"); - Action assertion = () => result.Should().BeFailure().Which.Should().Be("something went wrong"); + Action assertion = () => result.Should().BeFailure().Which.Should().Be("something went wrong"); - assertion.Should().Throw(); - } + assertion.Should().Throw(); + } - [Fact] - public void FailsWhenSuccessEvenIfWhichConstraintSucceeds() - { - var result = Result.Success(100); + [Fact] + public void FailsWhenSuccessEvenIfWhichConstraintSucceeds() + { + var result = Result.Success(100); - Action assertion = () => result.Should().BeFailure().Which.Should().Be("something went wrong"); + Action assertion = () => result.Should().BeFailure().Which.Should().Be("something went wrong"); - assertion.Should().Throw(); - } + assertion.Should().Throw(); } + } - public sealed class BeFailureWithError + public sealed class BeFailureWithError + { + [Fact] + public void SucceedsWhenFailureWithSameError() { - [Fact] - public void SucceedsWhenFailureWithSameError() - { - var result = Result.Failure("something went wrong"); + var result = Result.Failure("something went wrong"); - Action assertion = () => result.Should().BeFailureWithError("something went wrong"); + Action assertion = () => result.Should().BeFailureWithError("something went wrong"); - assertion.Should().NotThrow(); - } + assertion.Should().NotThrow(); + } - [Fact] - public void FailsWhenFailureWithDifferentError() - { - var result = Result.Failure("something else went wrong"); + [Fact] + public void FailsWhenFailureWithDifferentError() + { + var result = Result.Failure("something else went wrong"); - Action assertion = () => result.Should().BeFailureWithError("something went wrong"); + Action assertion = () => result.Should().BeFailureWithError("something went wrong"); - assertion.Should().Throw(); - } + assertion.Should().Throw(); + } - [Fact] - public void FailsWhenSuccess() - { - var result = Result.Success(100); + [Fact] + public void FailsWhenSuccess() + { + var result = Result.Success(100); - Action assertion = () => result.Should().BeFailureWithError("something went wrong"); + Action assertion = () => result.Should().BeFailureWithError("something went wrong"); - assertion.Should().Throw(); - } + assertion.Should().Throw(); } } } diff --git a/Bearded.Utilities.Testing/Core/MaybeAssertions.cs b/Bearded.Utilities.Testing/Core/MaybeAssertions.cs index cc1ab463..0b654eaf 100644 --- a/Bearded.Utilities.Testing/Core/MaybeAssertions.cs +++ b/Bearded.Utilities.Testing/Core/MaybeAssertions.cs @@ -1,52 +1,51 @@ using FluentAssertions; using FluentAssertions.Execution; -namespace Bearded.Utilities.Testing +namespace Bearded.Utilities.Testing; + +public sealed class MaybeAssertions { - public sealed class MaybeAssertions + private readonly Maybe subject; + + public MaybeAssertions(Maybe instance) => subject = instance; + + [CustomAssertion] + public void BeJust(T value, string because = "", params object[] becauseArgs) + { + BeJust().Which.Should().Be(value, because, becauseArgs); + } + + [CustomAssertion] + public AndWhichConstraint, T> BeJust(string because = "", params object[] becauseArgs) + { + var onValueCalled = false; + var matched = default(T); + + subject.Match( + onValue: actual => + { + onValueCalled = true; + matched = actual; + }, onNothing: () => { }); + + Execute.Assertion + .BecauseOf(because, becauseArgs) + .ForCondition(onValueCalled) + .FailWith("Expected maybe to have value, but had none."); + + return new AndWhichConstraint, T>(this, matched); + } + + [CustomAssertion] + public void BeNothing(string because = "", params object[] becauseArgs) { - private readonly Maybe subject; - - public MaybeAssertions(Maybe instance) => subject = instance; - - [CustomAssertion] - public void BeJust(T value, string because = "", params object[] becauseArgs) - { - BeJust().Which.Should().Be(value, because, becauseArgs); - } - - [CustomAssertion] - public AndWhichConstraint, T> BeJust(string because = "", params object[] becauseArgs) - { - var onValueCalled = false; - var matched = default(T); - - subject.Match( - onValue: actual => - { - onValueCalled = true; - matched = actual; - }, onNothing: () => { }); - - Execute.Assertion - .BecauseOf(because, becauseArgs) - .ForCondition(onValueCalled) - .FailWith("Expected maybe to have value, but had none."); - - return new AndWhichConstraint, T>(this, matched); - } - - [CustomAssertion] - public void BeNothing(string because = "", params object[] becauseArgs) - { - var onNothingCalled = false; - - subject.Match(onValue: _ => { }, onNothing: () => onNothingCalled = true); - - Execute.Assertion - .BecauseOf(because, becauseArgs) - .ForCondition(onNothingCalled) - .FailWith("Expected maybe to be nothing, but had value."); - } + var onNothingCalled = false; + + subject.Match(onValue: _ => { }, onNothing: () => onNothingCalled = true); + + Execute.Assertion + .BecauseOf(because, becauseArgs) + .ForCondition(onNothingCalled) + .FailWith("Expected maybe to be nothing, but had value."); } } diff --git a/Bearded.Utilities.Testing/Core/MaybeExtensions.cs b/Bearded.Utilities.Testing/Core/MaybeExtensions.cs index 7099af47..f0f93478 100644 --- a/Bearded.Utilities.Testing/Core/MaybeExtensions.cs +++ b/Bearded.Utilities.Testing/Core/MaybeExtensions.cs @@ -1,7 +1,6 @@ -namespace Bearded.Utilities.Testing +namespace Bearded.Utilities.Testing; + +public static class MaybeExtensions { - public static class MaybeExtensions - { - public static MaybeAssertions Should(this Maybe instance) => new MaybeAssertions(instance); - } + public static MaybeAssertions Should(this Maybe instance) => new MaybeAssertions(instance); } diff --git a/Bearded.Utilities.Testing/Geometry/Bivector2Assertions.cs b/Bearded.Utilities.Testing/Geometry/Bivector2Assertions.cs index 9e82f067..945f030a 100644 --- a/Bearded.Utilities.Testing/Geometry/Bivector2Assertions.cs +++ b/Bearded.Utilities.Testing/Geometry/Bivector2Assertions.cs @@ -1,42 +1,41 @@ using Bearded.Utilities.Geometry; using FluentAssertions; -namespace Bearded.Utilities.Testing.Geometry +namespace Bearded.Utilities.Testing.Geometry; + +public sealed class Bivector2Assertions { - public sealed class Bivector2Assertions - { - private readonly Bivector2 subject; + private readonly Bivector2 subject; - public Bivector2Assertions(Bivector2 instance) => subject = instance; + public Bivector2Assertions(Bivector2 instance) => subject = instance; - [CustomAssertion] - public AndConstraint Be(Bivector2 other, string because = "", params object[] becauseArgs) - { - AssertionExtensions.Should(subject).Be(other, because, becauseArgs); - return new AndConstraint(this); - } + [CustomAssertion] + public AndConstraint Be(Bivector2 other, string because = "", params object[] becauseArgs) + { + AssertionExtensions.Should(subject).Be(other, because, becauseArgs); + return new AndConstraint(this); + } - [CustomAssertion] - public AndConstraint NotBe(Bivector2 other, string because = "", params object[] becauseArgs) - { - AssertionExtensions.Should(subject).NotBe(other, because, becauseArgs); - return new AndConstraint(this); - } + [CustomAssertion] + public AndConstraint NotBe(Bivector2 other, string because = "", params object[] becauseArgs) + { + AssertionExtensions.Should(subject).NotBe(other, because, becauseArgs); + return new AndConstraint(this); + } - [CustomAssertion] - public AndConstraint BeApproximately( - Bivector2 other, float precision, string because = "", params object[] becauseArgs) - { - subject.Magnitude.Should().BeApproximately(other.Magnitude, precision, because, becauseArgs); - return new AndConstraint(this); - } + [CustomAssertion] + public AndConstraint BeApproximately( + Bivector2 other, float precision, string because = "", params object[] becauseArgs) + { + subject.Magnitude.Should().BeApproximately(other.Magnitude, precision, because, becauseArgs); + return new AndConstraint(this); + } - [CustomAssertion] - public AndConstraint NotBeApproximately( - Bivector2 other, float precision, string because = "", params object[] becauseArgs) - { - subject.Magnitude.Should().NotBeApproximately(other.Magnitude, precision, because, becauseArgs); - return new AndConstraint(this); - } + [CustomAssertion] + public AndConstraint NotBeApproximately( + Bivector2 other, float precision, string because = "", params object[] becauseArgs) + { + subject.Magnitude.Should().NotBeApproximately(other.Magnitude, precision, because, becauseArgs); + return new AndConstraint(this); } } diff --git a/Bearded.Utilities.Testing/Geometry/Bivector2Extensions.cs b/Bearded.Utilities.Testing/Geometry/Bivector2Extensions.cs index ea6c0285..a760dba1 100644 --- a/Bearded.Utilities.Testing/Geometry/Bivector2Extensions.cs +++ b/Bearded.Utilities.Testing/Geometry/Bivector2Extensions.cs @@ -1,9 +1,8 @@ using Bearded.Utilities.Geometry; -namespace Bearded.Utilities.Testing.Geometry +namespace Bearded.Utilities.Testing.Geometry; + +public static class Bivector2Extensions { - public static class Bivector2Extensions - { - public static Bivector2Assertions Should(this Bivector2 instance) => new Bivector2Assertions(instance); - } + public static Bivector2Assertions Should(this Bivector2 instance) => new Bivector2Assertions(instance); } diff --git a/Bearded.Utilities.Testing/Geometry/Bivector3Assertions.cs b/Bearded.Utilities.Testing/Geometry/Bivector3Assertions.cs index 751ca17a..7d04bb3e 100644 --- a/Bearded.Utilities.Testing/Geometry/Bivector3Assertions.cs +++ b/Bearded.Utilities.Testing/Geometry/Bivector3Assertions.cs @@ -3,58 +3,57 @@ using FluentAssertions; using FluentAssertions.Execution; -namespace Bearded.Utilities.Testing.Geometry +namespace Bearded.Utilities.Testing.Geometry; + +public sealed class Bivector3Assertions { - public sealed class Bivector3Assertions - { - private readonly Bivector3 subject; + private readonly Bivector3 subject; - public Bivector3Assertions(Bivector3 instance) => subject = instance; + public Bivector3Assertions(Bivector3 instance) => subject = instance; - [CustomAssertion] - public AndConstraint Be(Bivector3 other, string because = "", params object[] becauseArgs) - { - AssertionExtensions.Should(subject).Be(other, because, becauseArgs); - return new AndConstraint(this); - } + [CustomAssertion] + public AndConstraint Be(Bivector3 other, string because = "", params object[] becauseArgs) + { + AssertionExtensions.Should(subject).Be(other, because, becauseArgs); + return new AndConstraint(this); + } - [CustomAssertion] - public AndConstraint NotBe(Bivector3 other, string because = "", params object[] becauseArgs) - { - AssertionExtensions.Should(subject).NotBe(other, because, becauseArgs); - return new AndConstraint(this); - } + [CustomAssertion] + public AndConstraint NotBe(Bivector3 other, string because = "", params object[] becauseArgs) + { + AssertionExtensions.Should(subject).NotBe(other, because, becauseArgs); + return new AndConstraint(this); + } - [CustomAssertion] - public AndConstraint BeApproximately( - Bivector3 other, float precision, string because = "", params object[] becauseArgs) + [CustomAssertion] + public AndConstraint BeApproximately( + Bivector3 other, float precision, string because = "", params object[] becauseArgs) + { + using (new AssertionScope()) { - using (new AssertionScope()) - { - subject.Xy.Should().BeApproximately(other.Xy, precision, because, becauseArgs); - subject.Yz.Should().BeApproximately(other.Yz, precision, because, becauseArgs); - subject.Xz.Should().BeApproximately(other.Xz, precision, because, becauseArgs); - } - return new AndConstraint(this); + subject.Xy.Should().BeApproximately(other.Xy, precision, because, becauseArgs); + subject.Yz.Should().BeApproximately(other.Yz, precision, because, becauseArgs); + subject.Xz.Should().BeApproximately(other.Xz, precision, because, becauseArgs); } + return new AndConstraint(this); + } - [CustomAssertion] - public AndConstraint NotBeApproximately( - Bivector3 other, float precision, string because = "", params object[] becauseArgs) - { - var xyDifference = Math.Abs(subject.Xy - other.Xy); - var yzDifference = Math.Abs(subject.Yz - other.Yz); - var xzDifference = Math.Abs(subject.Xz - other.Xz); - - Execute.Assertion - .BecauseOf(because, becauseArgs) - .ForCondition(xyDifference > precision || yzDifference > precision || xzDifference > precision) - .FailWith( - "Expected {context:value} to not approximate {1} +/- {2}{reason}, " + - "but {0}'s coordinates only differed by {3} (xy), {4} (yz), and {5} (xz).", - subject, other, precision, xyDifference, yzDifference, xzDifference); - - return new AndConstraint(this); - } + [CustomAssertion] + public AndConstraint NotBeApproximately( + Bivector3 other, float precision, string because = "", params object[] becauseArgs) + { + var xyDifference = Math.Abs(subject.Xy - other.Xy); + var yzDifference = Math.Abs(subject.Yz - other.Yz); + var xzDifference = Math.Abs(subject.Xz - other.Xz); + + Execute.Assertion + .BecauseOf(because, becauseArgs) + .ForCondition(xyDifference > precision || yzDifference > precision || xzDifference > precision) + .FailWith( + "Expected {context:value} to not approximate {1} +/- {2}{reason}, " + + "but {0}'s coordinates only differed by {3} (xy), {4} (yz), and {5} (xz).", + subject, other, precision, xyDifference, yzDifference, xzDifference); + + return new AndConstraint(this); } } diff --git a/Bearded.Utilities.Testing/Geometry/Bivector3Extensions.cs b/Bearded.Utilities.Testing/Geometry/Bivector3Extensions.cs index 69470e65..f6204907 100644 --- a/Bearded.Utilities.Testing/Geometry/Bivector3Extensions.cs +++ b/Bearded.Utilities.Testing/Geometry/Bivector3Extensions.cs @@ -1,9 +1,8 @@ using Bearded.Utilities.Geometry; -namespace Bearded.Utilities.Testing.Geometry +namespace Bearded.Utilities.Testing.Geometry; + +public static class Bivector3Extensions { - public static class Bivector3Extensions - { - public static Bivector3Assertions Should(this Bivector3 instance) => new Bivector3Assertions(instance); - } + public static Bivector3Assertions Should(this Bivector3 instance) => new Bivector3Assertions(instance); } diff --git a/Bearded.Utilities.Testing/Monads/ResultAssertions.cs b/Bearded.Utilities.Testing/Monads/ResultAssertions.cs index 25d72998..f7017cc3 100644 --- a/Bearded.Utilities.Testing/Monads/ResultAssertions.cs +++ b/Bearded.Utilities.Testing/Monads/ResultAssertions.cs @@ -2,69 +2,68 @@ using FluentAssertions; using FluentAssertions.Execution; -namespace Bearded.Utilities.Testing.Monads +namespace Bearded.Utilities.Testing.Monads; + +public sealed class ResultAssertions { - public sealed class ResultAssertions + private readonly Result subject; + + public ResultAssertions(Result subject) { - private readonly Result subject; + this.subject = subject; + } - public ResultAssertions(Result subject) - { - this.subject = subject; - } + [CustomAssertion] + public void BeSuccessWithResult(TResult result, string because = "", params object[] becauseArgs) + { + BeSuccess().Which.Should().Be(result, because, becauseArgs); + } - [CustomAssertion] - public void BeSuccessWithResult(TResult result, string because = "", params object[] becauseArgs) - { - BeSuccess().Which.Should().Be(result, because, becauseArgs); - } + [CustomAssertion] + public AndWhichConstraint, TResult> BeSuccess( + string because = "", params object[] becauseArgs) + { + var onResultCalled = false; + var matched = default(TResult); - [CustomAssertion] - public AndWhichConstraint, TResult> BeSuccess( - string because = "", params object[] becauseArgs) + subject.Match(actual => { - var onResultCalled = false; - var matched = default(TResult); + onResultCalled = true; + matched = actual; + }); - subject.Match(actual => - { - onResultCalled = true; - matched = actual; - }); + Execute.Assertion + .BecauseOf(because, becauseArgs) + .ForCondition(onResultCalled) + .FailWith("Expected result to be successful, but was not."); - Execute.Assertion - .BecauseOf(because, becauseArgs) - .ForCondition(onResultCalled) - .FailWith("Expected result to be successful, but was not."); + return new AndWhichConstraint, TResult>(this, matched); + } - return new AndWhichConstraint, TResult>(this, matched); - } + [CustomAssertion] + public void BeFailureWithError(TError error, string because = "", params object[] becauseArgs) + { + BeFailure().Which.Should().Be(error, because, becauseArgs); + } - [CustomAssertion] - public void BeFailureWithError(TError error, string because = "", params object[] becauseArgs) - { - BeFailure().Which.Should().Be(error, because, becauseArgs); - } + [CustomAssertion] + public AndWhichConstraint, TError> BeFailure( + string because = "", params object[] becauseArgs) + { + var onErrorCalled = false; + var matched = default(TError); - [CustomAssertion] - public AndWhichConstraint, TError> BeFailure( - string because = "", params object[] becauseArgs) + subject.Match(_ => {}, actual => { - var onErrorCalled = false; - var matched = default(TError); - - subject.Match(_ => {}, actual => - { - onErrorCalled = true; - matched = actual; - }); + onErrorCalled = true; + matched = actual; + }); - Execute.Assertion - .BecauseOf(because, becauseArgs) - .ForCondition(onErrorCalled) - .FailWith("Expected result to be erroneous, but was not."); + Execute.Assertion + .BecauseOf(because, becauseArgs) + .ForCondition(onErrorCalled) + .FailWith("Expected result to be erroneous, but was not."); - return new AndWhichConstraint, TError>(this, matched); - } + return new AndWhichConstraint, TError>(this, matched); } } diff --git a/Bearded.Utilities.Testing/Monads/ResultExtensions.cs b/Bearded.Utilities.Testing/Monads/ResultExtensions.cs index 423c611d..def9a440 100644 --- a/Bearded.Utilities.Testing/Monads/ResultExtensions.cs +++ b/Bearded.Utilities.Testing/Monads/ResultExtensions.cs @@ -1,10 +1,9 @@ using Bearded.Utilities.Monads; -namespace Bearded.Utilities.Testing.Monads +namespace Bearded.Utilities.Testing.Monads; + +public static class ResultExtensions { - public static class ResultExtensions - { - public static ResultAssertions Should(this Result subject) => - new ResultAssertions(subject); - } + public static ResultAssertions Should(this Result subject) => + new ResultAssertions(subject); } diff --git a/Bearded.Utilities.Tests/Algorithms/BinPackingTests.cs b/Bearded.Utilities.Tests/Algorithms/BinPackingTests.cs index edd155fe..39746d91 100644 --- a/Bearded.Utilities.Tests/Algorithms/BinPackingTests.cs +++ b/Bearded.Utilities.Tests/Algorithms/BinPackingTests.cs @@ -4,131 +4,130 @@ using Bearded.Utilities.Algorithms; using Xunit; -namespace Bearded.Utilities.Tests.Algorithms +namespace Bearded.Utilities.Tests.Algorithms; + +public class BinPackingTests { - public class BinPackingTests + [Fact] + public void TestPackedRectangles_IncludesAll() { - [Fact] - public void TestPackedRectangles_IncludesAll() - { - var random = new System.Random(0); - var input = Enumerable.Range(0, 100) - .Select(i => new BinPacking.Rectangle(i, random.Next(5, 20), random.Next(5, 20))) - .ToList(); + var random = new System.Random(0); + var input = Enumerable.Range(0, 100) + .Select(i => new BinPacking.Rectangle(i, random.Next(5, 20), random.Next(5, 20))) + .ToList(); - var result = BinPacking.Pack(input); + var result = BinPacking.Pack(input); - Assert.NotNull(result); - Assert.Equal(100, result!.Rectangles.Count); + Assert.NotNull(result); + Assert.Equal(100, result!.Rectangles.Count); - var checkedIds = new HashSet(); - - foreach (var rectangle in result.Rectangles) - { - Assert.True(checkedIds.Add(rectangle.Value)); + var checkedIds = new HashSet(); - Assert.Equal(rectangle.Width, input[rectangle.Value].Width); - Assert.Equal(rectangle.Height, input[rectangle.Value].Height); - } + foreach (var rectangle in result.Rectangles) + { + Assert.True(checkedIds.Add(rectangle.Value)); - Assert.Equal(100, checkedIds.Count); + Assert.Equal(rectangle.Width, input[rectangle.Value].Width); + Assert.Equal(rectangle.Height, input[rectangle.Value].Height); } - [Fact] - public void TestPackedRectangles_NoOverlap() - { - var random = new System.Random(0); - var input = Enumerable.Range(0, 100) - .Select(i => new BinPacking.Rectangle(i, random.Next(5, 20), random.Next(5, 20))) - .ToList(); + Assert.Equal(100, checkedIds.Count); + } + + [Fact] + public void TestPackedRectangles_NoOverlap() + { + var random = new System.Random(0); + var input = Enumerable.Range(0, 100) + .Select(i => new BinPacking.Rectangle(i, random.Next(5, 20), random.Next(5, 20))) + .ToList(); - var result = BinPacking.Pack(input); + var result = BinPacking.Pack(input); - Assert.NotNull(result); + Assert.NotNull(result); - foreach (var r1 in result!.Rectangles) + foreach (var r1 in result!.Rectangles) + { + foreach (var r2 in result.Rectangles) { - foreach (var r2 in result.Rectangles) - { - if (r1 == r2) - continue; - - Assert.True( - r1.X + r1.Width <= r2.X || - r2.X + r2.Width <= r1.X || - r1.Y + r1.Height <= r2.Y || - r2.Y + r2.Height <= r1.Y - ); - } + if (r1 == r2) + continue; + + Assert.True( + r1.X + r1.Width <= r2.X || + r2.X + r2.Width <= r1.X || + r1.Y + r1.Height <= r2.Y || + r2.Y + r2.Height <= r1.Y + ); } } + } - [Fact] - public void TestPackedRectangles_CorrectResultStatistics() - { - var random = new System.Random(0); - var input = Enumerable.Range(0, 100) - .Select(i => new BinPacking.Rectangle(i, random.Next(5, 20), random.Next(5, 20))) - .ToList(); + [Fact] + public void TestPackedRectangles_CorrectResultStatistics() + { + var random = new System.Random(0); + var input = Enumerable.Range(0, 100) + .Select(i => new BinPacking.Rectangle(i, random.Next(5, 20), random.Next(5, 20))) + .ToList(); - var result = BinPacking.Pack(input); + var result = BinPacking.Pack(input); - Assert.NotNull(result); + Assert.NotNull(result); - var totalArea = result!.Width * result.Height; + var totalArea = result!.Width * result.Height; - Assert.Equal(totalArea, result.Area); + Assert.Equal(totalArea, result.Area); - var coveredPixels = 0; + var coveredPixels = 0; - var maxX = 0; - var maxY = 0; + var maxX = 0; + var maxY = 0; - foreach (var r in result.Rectangles) - { - var area = r.Width * r.Height; - coveredPixels += area; + foreach (var r in result.Rectangles) + { + var area = r.Width * r.Height; + coveredPixels += area; - maxX = Math.Max(maxX, r.X + r.Width); - maxY = Math.Max(maxY, r.Y + r.Height); - } + maxX = Math.Max(maxX, r.X + r.Width); + maxY = Math.Max(maxY, r.Y + r.Height); + } - Assert.Equal(maxX, result.Width); - Assert.Equal(maxY, result.Height); + Assert.Equal(maxX, result.Width); + Assert.Equal(maxY, result.Height); - var emptyPixels = totalArea - coveredPixels; + var emptyPixels = totalArea - coveredPixels; - Assert.Equal(emptyPixels, result.EmptyPixels); - } + Assert.Equal(emptyPixels, result.EmptyPixels); + } - [Fact] - public void TestPackedRectangles_MultipleHeuristicsEqualOrBetter() - { - var random = new System.Random(0); - var input = Enumerable.Range(0, 100) - .Select(i => new BinPacking.Rectangle(i, random.Next(5, 20), random.Next(5, 20))) - .ToList(); + [Fact] + public void TestPackedRectangles_MultipleHeuristicsEqualOrBetter() + { + var random = new System.Random(0); + var input = Enumerable.Range(0, 100) + .Select(i => new BinPacking.Rectangle(i, random.Next(5, 20), random.Next(5, 20))) + .ToList(); - var resultSingle = BinPacking.Pack(input); - var resultMultiple = BinPacking.PackWithMultipleHeuristics(input); + var resultSingle = BinPacking.Pack(input); + var resultMultiple = BinPacking.PackWithMultipleHeuristics(input); - Assert.NotNull(resultSingle); - Assert.NotNull(resultMultiple); - Assert.True(resultSingle!.Filled <= resultMultiple!.Filled); - } + Assert.NotNull(resultSingle); + Assert.NotNull(resultMultiple); + Assert.True(resultSingle!.Filled <= resultMultiple!.Filled); + } - [Fact] - public void TestPackedRectangles_EmptyInputReturnsEmptyResult() - { - var result = BinPacking.Pack(new List>()); - - Assert.NotNull(result); - Assert.Empty(result.Rectangles); - Assert.Equal(0, result.Area); - Assert.Equal(0, result.Width); - Assert.Equal(0, result.Height); - Assert.Equal(0, result.EmptyPixels); - Assert.Equal(double.NaN, result.Filled); - } + [Fact] + public void TestPackedRectangles_EmptyInputReturnsEmptyResult() + { + var result = BinPacking.Pack(new List>()); + + Assert.NotNull(result); + Assert.Empty(result.Rectangles); + Assert.Equal(0, result.Area); + Assert.Equal(0, result.Width); + Assert.Equal(0, result.Height); + Assert.Equal(0, result.EmptyPixels); + Assert.Equal(double.NaN, result.Filled); } } diff --git a/Bearded.Utilities.Tests/Algorithms/CoffmanGrahamTests.cs b/Bearded.Utilities.Tests/Algorithms/CoffmanGrahamTests.cs index 0ffcd3e5..5b35182e 100644 --- a/Bearded.Utilities.Tests/Algorithms/CoffmanGrahamTests.cs +++ b/Bearded.Utilities.Tests/Algorithms/CoffmanGrahamTests.cs @@ -4,179 +4,178 @@ using FluentAssertions; using Xunit; -namespace Bearded.Utilities.Tests.Algorithms +namespace Bearded.Utilities.Tests.Algorithms; + +public class CoffmanGrahamTests { - public class CoffmanGrahamTests + [Fact] + public void Solve_EmptyInput_ReturnsEmptyOutput() { - [Fact] - public void Solve_EmptyInput_ReturnsEmptyOutput() - { - var graph = DirectedGraphBuilder.EmptyGraph(); - var solver = CoffmanGraham.SolverForArbitraryGraphs(1); + var graph = DirectedGraphBuilder.EmptyGraph(); + var solver = CoffmanGraham.SolverForArbitraryGraphs(1); - solver.Solve(graph).Should().BeEmpty(); - } + solver.Solve(graph).Should().BeEmpty(); + } - [Fact] - public void Solve_SingleInput_ReturnsSingleLayer() - { - var graph = DirectedGraphBuilder.NewBuilder() - .AddElement("element") - .CreateAcyclicGraphUnsafe(); - var solver = CoffmanGraham.SolverForArbitraryGraphs(1); + [Fact] + public void Solve_SingleInput_ReturnsSingleLayer() + { + var graph = DirectedGraphBuilder.NewBuilder() + .AddElement("element") + .CreateAcyclicGraphUnsafe(); + var solver = CoffmanGraham.SolverForArbitraryGraphs(1); - var solution = solver.Solve(graph); + var solution = solver.Solve(graph); - solution.Should().HaveCount(1); - solution[0].Should().Contain("element"); - } - - [Fact] - public void Solve_NoArrows_DoesNotCreatesLayersTooLarge() - { - const int width = 2; - const int numElements = 3; + solution.Should().HaveCount(1); + solution[0].Should().Contain("element"); + } - var graph = createGraphWithoutArrows(numElements); - var solver = CoffmanGraham.SolverForArbitraryGraphs(width); + [Fact] + public void Solve_NoArrows_DoesNotCreatesLayersTooLarge() + { + const int width = 2; + const int numElements = 3; - var solution = solver.Solve(graph); + var graph = createGraphWithoutArrows(numElements); + var solver = CoffmanGraham.SolverForArbitraryGraphs(width); - foreach (var layer in solution) - { - layer.Should().HaveCountLessOrEqualTo(width); - } - } + var solution = solver.Solve(graph); - [Fact] - public void Solve_MoreChildrenThanMaxWidth_DoesNotCreateLayersTooLarge() + foreach (var layer in solution) { - const int width = 3; - const int numChildren = width * 3; + layer.Should().HaveCountLessOrEqualTo(width); + } + } - var graph = createGraphWithManyChildren(numChildren); - var solver = CoffmanGraham.SolverForArbitraryGraphs(width); + [Fact] + public void Solve_MoreChildrenThanMaxWidth_DoesNotCreateLayersTooLarge() + { + const int width = 3; + const int numChildren = width * 3; - var solution = solver.Solve(graph); + var graph = createGraphWithManyChildren(numChildren); + var solver = CoffmanGraham.SolverForArbitraryGraphs(width); - foreach (var layer in solution) - { - layer.Should().HaveCountLessOrEqualTo(width); - } - } + var solution = solver.Solve(graph); - [Fact] - public void Solve_NoArrows_UsesTheMinimumNumberOfLayers() + foreach (var layer in solution) { - const int width = 3; - const int numElements = 18; - const int expectedLayers = 6; + layer.Should().HaveCountLessOrEqualTo(width); + } + } - var graph = createGraphWithoutArrows(numElements); - var solver = CoffmanGraham.SolverForArbitraryGraphs(width); + [Fact] + public void Solve_NoArrows_UsesTheMinimumNumberOfLayers() + { + const int width = 3; + const int numElements = 18; + const int expectedLayers = 6; - var solution = solver.Solve(graph); + var graph = createGraphWithoutArrows(numElements); + var solver = CoffmanGraham.SolverForArbitraryGraphs(width); - solution.Should().HaveCount(expectedLayers); - } + var solution = solver.Solve(graph); - [Fact] - public void Solve_MoreChildrenThanMaxWidth_UsesTheMinimumNumberOfLayers() - { - const int width = 4; - const int numChildren = 17; - const int expectedLayers = 6; // 1 [root] + ceil(17 / 4) [children] + solution.Should().HaveCount(expectedLayers); + } - var graph = createGraphWithManyChildren(numChildren); - var solver = CoffmanGraham.SolverForArbitraryGraphs(width); + [Fact] + public void Solve_MoreChildrenThanMaxWidth_UsesTheMinimumNumberOfLayers() + { + const int width = 4; + const int numChildren = 17; + const int expectedLayers = 6; // 1 [root] + ceil(17 / 4) [children] - var solution = solver.Solve(graph); + var graph = createGraphWithManyChildren(numChildren); + var solver = CoffmanGraham.SolverForArbitraryGraphs(width); - solution.Should().HaveCount(expectedLayers); - } + var solution = solver.Solve(graph); - [Fact] - public void Solve_NoArrows_IncludesAllElementsInLayers() - { - const int width = 3; - const int numElements = 100; + solution.Should().HaveCount(expectedLayers); + } - var graph = createGraphWithoutArrows(numElements); - var solver = CoffmanGraham.SolverForArbitraryGraphs(width); + [Fact] + public void Solve_NoArrows_IncludesAllElementsInLayers() + { + const int width = 3; + const int numElements = 100; - var solution = solver.Solve(graph); + var graph = createGraphWithoutArrows(numElements); + var solver = CoffmanGraham.SolverForArbitraryGraphs(width); - solution.SelectMany(s => s).Should().Contain(graph.Elements); - } + var solution = solver.Solve(graph); - [Fact] - public void Solve_MoreChildrenThanMaxWidth_IncludesAllElementsInLayers() - { - const int width = 3; - const int numChildren = width * 3; + solution.SelectMany(s => s).Should().Contain(graph.Elements); + } - var graph = createGraphWithManyChildren(numChildren); - var solver = CoffmanGraham.SolverForArbitraryGraphs(width); + [Fact] + public void Solve_MoreChildrenThanMaxWidth_IncludesAllElementsInLayers() + { + const int width = 3; + const int numChildren = width * 3; - var solution = solver.Solve(graph); + var graph = createGraphWithManyChildren(numChildren); + var solver = CoffmanGraham.SolverForArbitraryGraphs(width); - solution.SelectMany(s => s).Should().Contain(graph.Elements); - } + var solution = solver.Solve(graph); - [Fact] - public void Solve_PutsChildrenInSeparateLayers() - { - const int numElements = 10; + solution.SelectMany(s => s).Should().Contain(graph.Elements); + } - var graph = createLine(numElements); - var solver = CoffmanGraham.SolverForArbitraryGraphs(100); + [Fact] + public void Solve_PutsChildrenInSeparateLayers() + { + const int numElements = 10; - var solution = solver.Solve(graph); + var graph = createLine(numElements); + var solver = CoffmanGraham.SolverForArbitraryGraphs(100); - for (var i = 0; i < solution.Count; i++) - { - solution[i].Should().ContainSingle().Which.Should().Be(i); - } - } + var solution = solver.Solve(graph); - private static IDirectedAcyclicGraph createGraphWithoutArrows(int numElements) + for (var i = 0; i < solution.Count; i++) { - var builder = DirectedGraphBuilder.NewBuilder(); + solution[i].Should().ContainSingle().Which.Should().Be(i); + } + } - for (var i = 0; i < numElements; i++) - { - builder.AddElement(i); - } + private static IDirectedAcyclicGraph createGraphWithoutArrows(int numElements) + { + var builder = DirectedGraphBuilder.NewBuilder(); - var graph = builder.CreateAcyclicGraphUnsafe(); - return graph; + for (var i = 0; i < numElements; i++) + { + builder.AddElement(i); } - private static IDirectedAcyclicGraph createGraphWithManyChildren(int numDirectSuccessors) - { - var builder = DirectedGraphBuilder.NewBuilder().AddElement(-1); + var graph = builder.CreateAcyclicGraphUnsafe(); + return graph; + } - for (var i = 0; i < numDirectSuccessors; i++) - { - builder.AddElement(i).AddArrow(-1, i); - } + private static IDirectedAcyclicGraph createGraphWithManyChildren(int numDirectSuccessors) + { + var builder = DirectedGraphBuilder.NewBuilder().AddElement(-1); - var graph = builder.CreateAcyclicGraphUnsafe(); - return graph; + for (var i = 0; i < numDirectSuccessors; i++) + { + builder.AddElement(i).AddArrow(-1, i); } - private static IDirectedAcyclicGraph createLine(int numElements) - { - var builder = DirectedGraphBuilder.NewBuilder(); + var graph = builder.CreateAcyclicGraphUnsafe(); + return graph; + } - for (var i = 0; i < numElements; i++) - { - builder.AddElement(i); - if (i > 0) builder.AddArrow(i - 1, i); - } + private static IDirectedAcyclicGraph createLine(int numElements) + { + var builder = DirectedGraphBuilder.NewBuilder(); - var graph = builder.CreateAcyclicGraphUnsafe(); - return graph; + for (var i = 0; i < numElements; i++) + { + builder.AddElement(i); + if (i > 0) builder.AddArrow(i - 1, i); } + + var graph = builder.CreateAcyclicGraphUnsafe(); + return graph; } } diff --git a/Bearded.Utilities.Tests/Algorithms/HungarianAlgorithmTests.cs b/Bearded.Utilities.Tests/Algorithms/HungarianAlgorithmTests.cs index 29a85794..730ea274 100644 --- a/Bearded.Utilities.Tests/Algorithms/HungarianAlgorithmTests.cs +++ b/Bearded.Utilities.Tests/Algorithms/HungarianAlgorithmTests.cs @@ -6,98 +6,97 @@ using Xunit; using static System.Math; -namespace Bearded.Utilities.Tests.Algorithms +namespace Bearded.Utilities.Tests.Algorithms; + +public class HungarianAlgorithmTests { - public class HungarianAlgorithmTests + [Fact] + public void Run_EmptyCostMatrix_DoesNotFail() { - [Fact] - public void Run_EmptyCostMatrix_DoesNotFail() - { - HungarianAlgorithm.Run(new float[0, 0]); - } + HungarianAlgorithm.Run(new float[0, 0]); + } - [Fact] - public void Run_EmptyCostMatrix_ReturnsEmptyResult() - { - Assert.Empty(HungarianAlgorithm.Run(new float[0, 0])); - } + [Fact] + public void Run_EmptyCostMatrix_ReturnsEmptyResult() + { + Assert.Empty(HungarianAlgorithm.Run(new float[0, 0])); + } - [Property(Arbitrary = new [] { typeof(FloatGenerators.NonInfiniteNonNaN) })] - public void Run_OneSource_AssignsTarget(float cost) - { - Assert.Equal(new[] { 0 }, HungarianAlgorithm.Run(new[,] { { cost } })); - } + [Property(Arbitrary = new [] { typeof(FloatGenerators.NonInfiniteNonNaN) })] + public void Run_OneSource_AssignsTarget(float cost) + { + Assert.Equal(new[] { 0 }, HungarianAlgorithm.Run(new[,] { { cost } })); + } - [Fact] - public void Run_InfiniteCost_ThrowsException() - { - Assert.Throws(() => HungarianAlgorithm.Run(new[,] { { float.PositiveInfinity } })); - Assert.Throws(() => HungarianAlgorithm.Run(new[,] { { float.NegativeInfinity } })); - } + [Fact] + public void Run_InfiniteCost_ThrowsException() + { + Assert.Throws(() => HungarianAlgorithm.Run(new[,] { { float.PositiveInfinity } })); + Assert.Throws(() => HungarianAlgorithm.Run(new[,] { { float.NegativeInfinity } })); + } - [Fact] - public void Run_NaNCost_ThrowsException() - { - Assert.Throws(() => HungarianAlgorithm.Run(new[,] { { float.NaN } })); - } + [Fact] + public void Run_NaNCost_ThrowsException() + { + Assert.Throws(() => HungarianAlgorithm.Run(new[,] { { float.NaN } })); + } - [Property(Arbitrary = new[] { typeof(FloatGenerators.NonInfiniteNonNaN) })] - public void Run_TwoSources_AssignsLowestCostTarget(float f, short s1, short s2, short s3, short s4) - { - // The behaviour for equal matching is not defined. - if (Abs(s1 + s4 - s3 - s2) == 0 || Abs(f) < 1e-10 || Abs(f) > 1e10) - return; - var (f1, f2, f3, f4) = (f * s1, f * s2, f * s3, f * s4); + [Property(Arbitrary = new[] { typeof(FloatGenerators.NonInfiniteNonNaN) })] + public void Run_TwoSources_AssignsLowestCostTarget(float f, short s1, short s2, short s3, short s4) + { + // The behaviour for equal matching is not defined. + if (Abs(s1 + s4 - s3 - s2) == 0 || Abs(f) < 1e-10 || Abs(f) > 1e10) + return; + var (f1, f2, f3, f4) = (f * s1, f * s2, f * s3, f * s4); - var result = HungarianAlgorithm.Run(new[,] {{f1, f2}, {f3, f4}}); + var result = HungarianAlgorithm.Run(new[,] {{f1, f2}, {f3, f4}}); - Assert.Equal(f1 + f4 < f3 + f2 ? new[] {0, 1} : new[] {1, 0}, result); - } + Assert.Equal(f1 + f4 < f3 + f2 ? new[] {0, 1} : new[] {1, 0}, result); + } - [Fact] - public void Run_MoreSourcesThanTargets_ThrowsException() - { - Assert.Throws(() => HungarianAlgorithm.Run( - new float[,] - { - {1, 3}, - {9, 9}, - {3, 1} - } - )); - } + [Fact] + public void Run_MoreSourcesThanTargets_ThrowsException() + { + Assert.Throws(() => HungarianAlgorithm.Run( + new float[,] + { + {1, 3}, + {9, 9}, + {3, 1} + } + )); + } - [Fact] - public void Run_MoreTargetsThanSources_AssignsSourcesToCheapest() - { - Assert.Throws(() => HungarianAlgorithm.Run( - new float[,] - { - {1, 9, 3}, - {3, 9, 1} - } - )); - } + [Fact] + public void Run_MoreTargetsThanSources_AssignsSourcesToCheapest() + { + Assert.Throws(() => HungarianAlgorithm.Run( + new float[,] + { + {1, 9, 3}, + {3, 9, 1} + } + )); + } - [Fact] - public void Run_Metric_MatchClosest() - { - Assert.Equal(new[] { 1, 2, 0 }, - HungarianAlgorithm.Run(new[] { -100, 0, 100 }, new[] { 111, -111, 42 }, (i1, i2) => Abs(i2 - i1))); - } + [Fact] + public void Run_Metric_MatchClosest() + { + Assert.Equal(new[] { 1, 2, 0 }, + HungarianAlgorithm.Run(new[] { -100, 0, 100 }, new[] { 111, -111, 42 }, (i1, i2) => Abs(i2 - i1))); + } - [Fact] - public void Run_Vector2s_MatchClosest() - { - Assert.Equal(new[] {1, 0}, - HungarianAlgorithm.Run(new[] {Vector2.Zero, Vector2.UnitY}, new[] {Vector2.One, Vector2.UnitX})); - } + [Fact] + public void Run_Vector2s_MatchClosest() + { + Assert.Equal(new[] {1, 0}, + HungarianAlgorithm.Run(new[] {Vector2.Zero, Vector2.UnitY}, new[] {Vector2.One, Vector2.UnitX})); + } - [Fact] - public void Run_Vector3s_MathClosest() - { - Assert.Equal(new[] { 1, 0 }, - HungarianAlgorithm.Run(new[] { Vector3.Zero, Vector3.UnitY }, new[] { Vector3.One, Vector3.Zero })); - } + [Fact] + public void Run_Vector3s_MathClosest() + { + Assert.Equal(new[] { 1, 0 }, + HungarianAlgorithm.Run(new[] { Vector3.Zero, Vector3.UnitY }, new[] { Vector3.One, Vector3.Zero })); } } diff --git a/Bearded.Utilities.Tests/Assertions/Vector2Assertions.cs b/Bearded.Utilities.Tests/Assertions/Vector2Assertions.cs index ea2fa547..af446f49 100644 --- a/Bearded.Utilities.Tests/Assertions/Vector2Assertions.cs +++ b/Bearded.Utilities.Tests/Assertions/Vector2Assertions.cs @@ -1,27 +1,26 @@ using FluentAssertions; using OpenTK.Mathematics; -namespace Bearded.Utilities.Tests.Assertions +namespace Bearded.Utilities.Tests.Assertions; + +sealed class Vector2Assertions { - sealed class Vector2Assertions - { - private readonly Vector2 subject; + private readonly Vector2 subject; - public Vector2Assertions(Vector2 subject) - { - this.subject = subject; - } + public Vector2Assertions(Vector2 subject) + { + this.subject = subject; + } - [CustomAssertion] - public AndConstraint BeApproximately( - Vector2 expectedValue, - float precision, - string because = "", - params object[] becauseArgs) - { - subject.X.Should().BeApproximately(expectedValue.X, precision, because, becauseArgs); - subject.Y.Should().BeApproximately(expectedValue.Y, precision, because, becauseArgs); - return new AndConstraint(this); - } + [CustomAssertion] + public AndConstraint BeApproximately( + Vector2 expectedValue, + float precision, + string because = "", + params object[] becauseArgs) + { + subject.X.Should().BeApproximately(expectedValue.X, precision, because, becauseArgs); + subject.Y.Should().BeApproximately(expectedValue.Y, precision, because, becauseArgs); + return new AndConstraint(this); } } diff --git a/Bearded.Utilities.Tests/Assertions/Vector2Extensions.cs b/Bearded.Utilities.Tests/Assertions/Vector2Extensions.cs index ed570454..fea1cf75 100644 --- a/Bearded.Utilities.Tests/Assertions/Vector2Extensions.cs +++ b/Bearded.Utilities.Tests/Assertions/Vector2Extensions.cs @@ -1,9 +1,8 @@ using OpenTK.Mathematics; -namespace Bearded.Utilities.Tests.Assertions +namespace Bearded.Utilities.Tests.Assertions; + +static class Vector2Extensions { - static class Vector2Extensions - { - public static Vector2Assertions Should(this Vector2 subject) => new Vector2Assertions(subject); - } + public static Vector2Assertions Should(this Vector2 subject) => new Vector2Assertions(subject); } diff --git a/Bearded.Utilities.Tests/Collections/DeletableObjectListTests.cs b/Bearded.Utilities.Tests/Collections/DeletableObjectListTests.cs index a2a33cba..226d761c 100644 --- a/Bearded.Utilities.Tests/Collections/DeletableObjectListTests.cs +++ b/Bearded.Utilities.Tests/Collections/DeletableObjectListTests.cs @@ -11,529 +11,528 @@ using Xunit; // ReSharper disable AssignmentIsFullyDiscarded -namespace Bearded.Utilities.Tests.Collections +namespace Bearded.Utilities.Tests.Collections; + +public class DeletableObjectListTests { - public class DeletableObjectListTests - { - private static readonly Func, EquivalencyAssertionOptions> - withExactSameItems = o => o.WithStrictOrdering().ComparingByValue(); + private static readonly Func, EquivalencyAssertionOptions> + withExactSameItems = o => o.WithStrictOrdering().ComparingByValue(); - public static TheoryData PositiveCounts => new TheoryData {1, 2, 3, 4, 5, 7, 10, 13, 37, 42, 1337}; + public static TheoryData PositiveCounts => new TheoryData {1, 2, 3, 4, 5, 7, 10, 13, 37, 42, 1337}; - private static List getDeletables(int itemsToAdd) - => Enumerable.Range(0, itemsToAdd).Select(i => new TestDeletable()).ToList(); + private static List getDeletables(int itemsToAdd) + => Enumerable.Range(0, itemsToAdd).Select(i => new TestDeletable()).ToList(); - private static (DeletableObjectList List, List Items) - createPopulatedList(int itemsToAdd) - { - var list = new DeletableObjectList(); - var items = getDeletables(itemsToAdd); - foreach (var item in items) - list.Add(item); - return (list, items); - } + private static (DeletableObjectList List, List Items) + createPopulatedList(int itemsToAdd) + { + var list = new DeletableObjectList(); + var items = getDeletables(itemsToAdd); + foreach (var item in items) + list.Add(item); + return (list, items); + } - public class TestDeletable : IDeletable - { - public bool Deleted { get; set; } - } + public class TestDeletable : IDeletable + { + public bool Deleted { get; set; } + } - public class TheParameterlessConstructor + public class TheParameterlessConstructor + { + [Fact] + public void CreatesEmptyList() { - [Fact] - public void CreatesEmptyList() - { - var list = new DeletableObjectList(); + var list = new DeletableObjectList(); - list.Should().BeEmpty(); - } + list.Should().BeEmpty(); } + } - public class TheIntParameterConstructor + public class TheIntParameterConstructor + { + [Fact] + public void CreatesEmptyListForZeroValue() { - [Fact] - public void CreatesEmptyListForZeroValue() - { - var list = new DeletableObjectList(0); + var list = new DeletableObjectList(0); - list.Should().BeEmpty(); - } + list.Should().BeEmpty(); + } - [Theory] - [MemberData(nameof(PositiveCounts), MemberType = typeof(DeletableObjectListTests))] - public void CreatesEmptyListForPositiveValues(int count) - { - var list = new DeletableObjectList(count); + [Theory] + [MemberData(nameof(PositiveCounts), MemberType = typeof(DeletableObjectListTests))] + public void CreatesEmptyListForPositiveValues(int count) + { + var list = new DeletableObjectList(count); - list.Should().BeEmpty(); - } + list.Should().BeEmpty(); + } - [Theory] - [MemberData(nameof(PositiveCounts), MemberType = typeof(DeletableObjectListTests))] - public void ThrowsForNegativeValues(int count) - { - Action createListWithNegativeValue = () => _ = new DeletableObjectList(-count); + [Theory] + [MemberData(nameof(PositiveCounts), MemberType = typeof(DeletableObjectListTests))] + public void ThrowsForNegativeValues(int count) + { + Action createListWithNegativeValue = () => _ = new DeletableObjectList(-count); - createListWithNegativeValue.Should().Throw(); - } + createListWithNegativeValue.Should().Throw(); } + } - public abstract class MethodThatDoesNotThrowsWhenEnumeratingTests - { - protected abstract void CallMethod(DeletableObjectList list); + public abstract class MethodThatDoesNotThrowsWhenEnumeratingTests + { + protected abstract void CallMethod(DeletableObjectList list); - public static TheoryData IndicesFromZeroToNineteen + public static TheoryData IndicesFromZeroToNineteen + { + get { - get + var data = new TheoryData(); + foreach (var i in Enumerable.Range(0, 20)) { - var data = new TheoryData(); - foreach (var i in Enumerable.Range(0, 20)) - { - data.Add(i); - } - return data; + data.Add(i); } + return data; } + } - [Theory] - [MemberData(nameof(IndicesFromZeroToNineteen))] - public void DoesNotThrowWhenCallingDuringEnumeration(int index) - { - var (list, _) = createPopulatedList(20); + [Theory] + [MemberData(nameof(IndicesFromZeroToNineteen))] + public void DoesNotThrowWhenCallingDuringEnumeration(int index) + { + var (list, _) = createPopulatedList(20); - foreach (var i in list.Select((item, i) => i)) - { - if (i == index) - CallMethod(list); - } + foreach (var i in list.Select((item, i) => i)) + { + if (i == index) + CallMethod(list); } } + } - public class TheAddMethod : MethodThatDoesNotThrowsWhenEnumeratingTests + public class TheAddMethod : MethodThatDoesNotThrowsWhenEnumeratingTests + { + protected override void CallMethod(DeletableObjectList list) + => list.Add(new TestDeletable()); + + [Fact] + public void AddsObjectToList() { - protected override void CallMethod(DeletableObjectList list) - => list.Add(new TestDeletable()); + var list = new DeletableObjectList(); + var deletable = new TestDeletable(); - [Fact] - public void AddsObjectToList() - { - var list = new DeletableObjectList(); - var deletable = new TestDeletable(); + list.Add(deletable); - list.Add(deletable); + list.Should().ContainSingle().Which.Should().Be(deletable); + } - list.Should().ContainSingle().Which.Should().Be(deletable); - } + [Fact] + public void ThrowsOnNull() + { + var list = new DeletableObjectList(); - [Fact] - public void ThrowsOnNull() - { - var list = new DeletableObjectList(); + Action addingNull = () => list.Add(null); - Action addingNull = () => list.Add(null); + addingNull.Should().Throw(); + } - addingNull.Should().Throw(); - } + [Theory] + [MemberData(nameof(PositiveCounts), MemberType = typeof(DeletableObjectListTests))] + public void CanAddMultipleItems(int itemsToAdd) + { + var list = new DeletableObjectList(); + var items = getDeletables(itemsToAdd); - [Theory] - [MemberData(nameof(PositiveCounts), MemberType = typeof(DeletableObjectListTests))] - public void CanAddMultipleItems(int itemsToAdd) - { - var list = new DeletableObjectList(); - var items = getDeletables(itemsToAdd); + foreach (var item in items) + list.Add(item); - foreach (var item in items) - list.Add(item); + list.Should().BeEquivalentTo(items, withExactSameItems); + } - list.Should().BeEquivalentTo(items, withExactSameItems); - } + [Fact] + public void EnumeratesElementsAddedDuringEnumeration() + { + var (list, items) = createPopulatedList(1); + var addItems = 3; - [Fact] - public void EnumeratesElementsAddedDuringEnumeration() + foreach (var _ in list) { - var (list, items) = createPopulatedList(1); - var addItems = 3; - - foreach (var _ in list) + if (addItems > 0) { - if (addItems > 0) - { - var newItem = new TestDeletable(); - items.Add(newItem); - list.Add(newItem); - addItems--; - } + var newItem = new TestDeletable(); + items.Add(newItem); + list.Add(newItem); + addItems--; } - - list.Should().HaveCount(4).And.BeEquivalentTo(items, withExactSameItems); } + + list.Should().HaveCount(4).And.BeEquivalentTo(items, withExactSameItems); } + } - public class TheRemoveMethod : MethodThatDoesNotThrowsWhenEnumeratingTests + public class TheRemoveMethod : MethodThatDoesNotThrowsWhenEnumeratingTests + { + private readonly System.Random random = new System.Random(0); + + protected override void CallMethod(DeletableObjectList list) + => list.Remove(list.RandomElement(random)); + + [Fact] + public void ReturnsFalseOnANewList() { - private readonly System.Random random = new System.Random(0); + var list = new DeletableObjectList(); - protected override void CallMethod(DeletableObjectList list) - => list.Remove(list.RandomElement(random)); + var returnValue = list.Remove(new TestDeletable()); - [Fact] - public void ReturnsFalseOnANewList() - { - var list = new DeletableObjectList(); + returnValue.Should().BeFalse(); + } - var returnValue = list.Remove(new TestDeletable()); + [Fact] + public void ReturnsFalseForUnknownElement() + { + var (list, _) = createPopulatedList(1); - returnValue.Should().BeFalse(); - } + var returnValue = list.Remove(new TestDeletable()); - [Fact] - public void ReturnsFalseForUnknownElement() - { - var (list, _) = createPopulatedList(1); + returnValue.Should().BeFalse(); + } - var returnValue = list.Remove(new TestDeletable()); + [Fact] + public void ReturnsFalseForNull() + { + var (list, _) = createPopulatedList(1); - returnValue.Should().BeFalse(); - } + var returnValue = list.Remove(null); - [Fact] - public void ReturnsFalseForNull() - { - var (list, _) = createPopulatedList(1); + returnValue.Should().BeFalse(); + } - var returnValue = list.Remove(null); + [Fact] + public void ReturnsTrueForKnownItem() + { + var (list, items) = createPopulatedList(1); - returnValue.Should().BeFalse(); - } + var returnValue = list.Remove(items[0]); - [Fact] - public void ReturnsTrueForKnownItem() - { - var (list, items) = createPopulatedList(1); + returnValue.Should().BeTrue(); + } - var returnValue = list.Remove(items[0]); + [Fact] + public void ReturnsFalseForRepeatedCall() + { + var (list, items) = createPopulatedList(1); - returnValue.Should().BeTrue(); - } + list.Remove(items[0]); + var returnValue = list.Remove(items[0]); - [Fact] - public void ReturnsFalseForRepeatedCall() - { - var (list, items) = createPopulatedList(1); + returnValue.Should().BeFalse(); + } - list.Remove(items[0]); - var returnValue = list.Remove(items[0]); + [Fact] + public void ReturnsFalseForClearedList() + { + var (list, items) = createPopulatedList(1); + list.Clear(); - returnValue.Should().BeFalse(); - } + var returnValue = list.Remove(items[0]); - [Fact] - public void ReturnsFalseForClearedList() - { - var (list, items) = createPopulatedList(1); - list.Clear(); + returnValue.Should().BeFalse(); + } - var returnValue = list.Remove(items[0]); + [Theory] + [MemberData(nameof(PositiveCounts), MemberType = typeof(DeletableObjectListTests))] + public void CanRemoveMultipleItems(int itemsToAdd) + { + var (list, items) = createPopulatedList(itemsToAdd); - returnValue.Should().BeFalse(); + foreach (var item in items) + { + var returnValue = list.Remove(item); + returnValue.Should().BeTrue(); } - [Theory] - [MemberData(nameof(PositiveCounts), MemberType = typeof(DeletableObjectListTests))] - public void CanRemoveMultipleItems(int itemsToAdd) - { - var (list, items) = createPopulatedList(itemsToAdd); + list.Should().BeEmpty(); + } - foreach (var item in items) - { - var returnValue = list.Remove(item); - returnValue.Should().BeTrue(); - } + [Fact] + public void DoesNotInfluenceEnumerationIfRemovedItemsHaveAlreadyBeenEnumerated() + { + var (list, items) = createPopulatedList(20); + var enumeratedItems = new List(); - list.Should().BeEmpty(); + foreach (var item in list) + { + enumeratedItems.Add(item); + list.Remove(item); } - [Fact] - public void DoesNotInfluenceEnumerationIfRemovedItemsHaveAlreadyBeenEnumerated() - { - var (list, items) = createPopulatedList(20); - var enumeratedItems = new List(); + enumeratedItems.Should().BeEquivalentTo(items, withExactSameItems); + } - foreach (var item in list) - { - enumeratedItems.Add(item); - list.Remove(item); - } + [Fact] + public void PreventsRemovedItemsFromBeingEnumeratedIfRemovedItemsHaveNotBeenEnumeratedYet() + { + var (list, items) = createPopulatedList(20); + var enumeratedItems = new List(); - enumeratedItems.Should().BeEquivalentTo(items, withExactSameItems); + foreach (var item in list) + { + var toRemove = list.Last(); + enumeratedItems.Add(item); + list.Remove(toRemove); } - [Fact] - public void PreventsRemovedItemsFromBeingEnumeratedIfRemovedItemsHaveNotBeenEnumeratedYet() - { - var (list, items) = createPopulatedList(20); - var enumeratedItems = new List(); + enumeratedItems.Should().BeEquivalentTo(items.Take(10), withExactSameItems); + } + } - foreach (var item in list) - { - var toRemove = list.Last(); - enumeratedItems.Add(item); - list.Remove(toRemove); - } + public class TheClearMethod : MethodThatDoesNotThrowsWhenEnumeratingTests + { + protected override void CallMethod(DeletableObjectList list) + => list.Clear(); - enumeratedItems.Should().BeEquivalentTo(items.Take(10), withExactSameItems); - } + [Fact] + public void DoesNotThrowForEmptyList() + { + var list = new DeletableObjectList(); + + list.Clear(); } - public class TheClearMethod : MethodThatDoesNotThrowsWhenEnumeratingTests + [Theory] + [MemberData(nameof(PositiveCounts), MemberType = typeof(DeletableObjectListTests))] + public void ClearsAllItems(int itemsToAdd) { - protected override void CallMethod(DeletableObjectList list) - => list.Clear(); + var (list, _) = createPopulatedList(itemsToAdd); - [Fact] - public void DoesNotThrowForEmptyList() - { - var list = new DeletableObjectList(); + list.Clear(); - list.Clear(); - } + list.Should().BeEmpty(); + } + } - [Theory] - [MemberData(nameof(PositiveCounts), MemberType = typeof(DeletableObjectListTests))] - public void ClearsAllItems(int itemsToAdd) - { - var (list, _) = createPopulatedList(itemsToAdd); + public abstract class NonMutatingMethodThatThrowsWhenEnumeratingTests + { + protected abstract void CallMethod(DeletableObjectList list); - list.Clear(); + [Fact] + public void DoesNotThrowForEmptyList() + { + var list = new DeletableObjectList(); - list.Should().BeEmpty(); - } + CallMethod(list); } - public abstract class NonMutatingMethodThatThrowsWhenEnumeratingTests + [Theory] + [MemberData(nameof(PositiveCounts), MemberType = typeof(DeletableObjectListTests))] + public void DoesNotRemoveItems(int itemsToAdd) { - protected abstract void CallMethod(DeletableObjectList list); + var (list, items) = createPopulatedList(itemsToAdd); - [Fact] - public void DoesNotThrowForEmptyList() - { - var list = new DeletableObjectList(); + CallMethod(list); - CallMethod(list); - } + list.Should().BeEquivalentTo(items, withExactSameItems); + } - [Theory] - [MemberData(nameof(PositiveCounts), MemberType = typeof(DeletableObjectListTests))] - public void DoesNotRemoveItems(int itemsToAdd) - { - var (list, items) = createPopulatedList(itemsToAdd); + [Fact] + public void ThrowsIfEnumerating() + { + var list = new DeletableObjectList(); + _ = list.GetEnumerator(); - CallMethod(list); + Action callingWhileEnumerating = () => CallMethod(list); - list.Should().BeEquivalentTo(items, withExactSameItems); - } + callingWhileEnumerating.Should().Throw(); + } - [Fact] - public void ThrowsIfEnumerating() - { - var list = new DeletableObjectList(); - _ = list.GetEnumerator(); + [Fact] + public void DoesNotThrowAfterDisposingEnumerator() + { + var list = new DeletableObjectList(); + var enumerator = list.GetEnumerator(); + enumerator.Dispose(); - Action callingWhileEnumerating = () => CallMethod(list); + CallMethod(list); + } - callingWhileEnumerating.Should().Throw(); - } + [Fact] + public void ThrowsIfAtLeastOneEnumeratorIsNotDisposed() + { + var list = new DeletableObjectList(); + var enumerators = Enumerable.Range(0, 20).Select(i => list.GetEnumerator()).ToList(); + var arbitraryEnumerator = enumerators[7]; - [Fact] - public void DoesNotThrowAfterDisposingEnumerator() + foreach (var enumerator in enumerators.Except(new [] { arbitraryEnumerator })) { - var list = new DeletableObjectList(); - var enumerator = list.GetEnumerator(); enumerator.Dispose(); - - CallMethod(list); } - [Fact] - public void ThrowsIfAtLeastOneEnumeratorIsNotDisposed() - { - var list = new DeletableObjectList(); - var enumerators = Enumerable.Range(0, 20).Select(i => list.GetEnumerator()).ToList(); - var arbitraryEnumerator = enumerators[7]; + Action callingWhileEnumerating = () => CallMethod(list); - foreach (var enumerator in enumerators.Except(new [] { arbitraryEnumerator })) - { - enumerator.Dispose(); - } + callingWhileEnumerating.Should().Throw(); + } - Action callingWhileEnumerating = () => CallMethod(list); + [Fact] + public void DoesNotThrowIfAllEnumeratorsAreDisposed() + { + var list = new DeletableObjectList(); + var enumerators = Enumerable.Range(0, 20).Select(i => list.GetEnumerator()).ToList(); - callingWhileEnumerating.Should().Throw(); + foreach (var enumerator in enumerators) + { + enumerator.Dispose(); } - [Fact] - public void DoesNotThrowIfAllEnumeratorsAreDisposed() - { - var list = new DeletableObjectList(); - var enumerators = Enumerable.Range(0, 20).Select(i => list.GetEnumerator()).ToList(); + CallMethod(list); + } + } - foreach (var enumerator in enumerators) - { - enumerator.Dispose(); - } + public class TheTrimExcessMethod : NonMutatingMethodThatThrowsWhenEnumeratingTests + { + protected override void CallMethod(DeletableObjectList list) => list.TrimExcess(); + } - CallMethod(list); - } - } + public class TheForceCompactMethod : NonMutatingMethodThatThrowsWhenEnumeratingTests + { + protected override void CallMethod(DeletableObjectList list) => list.ForceCompact(); + } - public class TheTrimExcessMethod : NonMutatingMethodThatThrowsWhenEnumeratingTests + public class Enumeration + { + [Property] + public void SkipsDeletedItems(NonEmptyArray someBools) { - protected override void CallMethod(DeletableObjectList list) => list.TrimExcess(); - } + var boolQueue = someBools.Get.Looping(); + var (list, items) = createPopulatedList(20); - public class TheForceCompactMethod : NonMutatingMethodThatThrowsWhenEnumeratingTests - { - protected override void CallMethod(DeletableObjectList list) => list.ForceCompact(); + foreach (var item in list) + { + if (boolQueue.Next()) + item.Deleted = true; + } + + list.Should().BeEquivalentTo(items.Where(i => !i.Deleted), withExactSameItems); } - public class Enumeration + [Fact] + public void SkipsItemsDeletedAheadOfEnumeration() { - [Property] - public void SkipsDeletedItems(NonEmptyArray someBools) - { - var boolQueue = someBools.Get.Looping(); - var (list, items) = createPopulatedList(20); - - foreach (var item in list) - { - if (boolQueue.Next()) - item.Deleted = true; - } + var (list, items) = createPopulatedList(20); + var enumeratedItems = new List(); - list.Should().BeEquivalentTo(items.Where(i => !i.Deleted), withExactSameItems); + foreach (var item in list) + { + enumeratedItems.Add(item); + list.Last().Deleted = true; } - [Fact] - public void SkipsItemsDeletedAheadOfEnumeration() - { - var (list, items) = createPopulatedList(20); - var enumeratedItems = new List(); + enumeratedItems.Should().BeEquivalentTo(items.Take(10), withExactSameItems); + list.Should().BeEquivalentTo(items.Take(10), withExactSameItems); + } - foreach (var item in list) - { - enumeratedItems.Add(item); - list.Last().Deleted = true; - } + [Fact] + public void IsNotAffectedByItemsBeingDeletedAfterEnumeratingThem() + { + var (list, items) = createPopulatedList(20); + var enumeratedItems = new List(); - enumeratedItems.Should().BeEquivalentTo(items.Take(10), withExactSameItems); - list.Should().BeEquivalentTo(items.Take(10), withExactSameItems); + foreach (var item in list) + { + enumeratedItems.Add(item); + list.First().Deleted = true; } - [Fact] - public void IsNotAffectedByItemsBeingDeletedAfterEnumeratingThem() - { - var (list, items) = createPopulatedList(20); - var enumeratedItems = new List(); + enumeratedItems.Should().BeEquivalentTo(items, withExactSameItems); + list.Should().BeEmpty(); + } - foreach (var item in list) - { - enumeratedItems.Add(item); - list.First().Deleted = true; - } + [Fact] + public void CanBeResetTakingIntoAccountDeletedAndRemovedItems() + { + var (list, items) = createPopulatedList(20); + var enumeratedItems = new List(); - enumeratedItems.Should().BeEquivalentTo(items, withExactSameItems); - list.Should().BeEmpty(); - } + // ReSharper disable once GenericEnumeratorNotDisposed + var enumerator = list.GetEnumerator(); + enumerator.MoveNext(); + enumerator.MoveNext(); + enumerator.MoveNext(); - [Fact] - public void CanBeResetTakingIntoAccountDeletedAndRemovedItems() - { - var (list, items) = createPopulatedList(20); - var enumeratedItems = new List(); + list.Remove(items[0]); + items[1].Deleted = true; - // ReSharper disable once GenericEnumeratorNotDisposed - var enumerator = list.GetEnumerator(); - enumerator.MoveNext(); - enumerator.MoveNext(); - enumerator.MoveNext(); + enumerator.Reset(); - list.Remove(items[0]); - items[1].Deleted = true; + while (enumerator.MoveNext()) + { + enumeratedItems.Add(enumerator.Current); + } - enumerator.Reset(); + enumeratedItems.Should().BeEquivalentTo(items.Skip(2), withExactSameItems); + } - while (enumerator.MoveNext()) - { - enumeratedItems.Add(enumerator.Current); - } + [Fact] + public void WorksNonGenerically() + { + var (list, items) = createPopulatedList(20); + var nonGenericList = (IEnumerable) list; - enumeratedItems.Should().BeEquivalentTo(items.Skip(2), withExactSameItems); - } + var enumeratedItems = nonGenericList.Cast().ToList(); - [Fact] - public void WorksNonGenerically() - { - var (list, items) = createPopulatedList(20); - var nonGenericList = (IEnumerable) list; + enumeratedItems.Should().BeEquivalentTo(items, withExactSameItems); + } + } - var enumeratedItems = nonGenericList.Cast().ToList(); + public class TheApproximateCountProperty + { + [Fact] + public void IsZeroForEmptyList() + { + var list = new DeletableObjectList(); - enumeratedItems.Should().BeEquivalentTo(items, withExactSameItems); - } + list.ApproximateCount.Should().Be(0); } - public class TheApproximateCountProperty + [Fact] + public void IsZeroForClearedList() { - [Fact] - public void IsZeroForEmptyList() - { - var list = new DeletableObjectList(); + var (list, _) = createPopulatedList(10); - list.ApproximateCount.Should().Be(0); - } + list.Clear(); - [Fact] - public void IsZeroForClearedList() - { - var (list, _) = createPopulatedList(10); + list.ApproximateCount.Should().Be(0); + } - list.Clear(); + [Property] + public void IsAccurateWhenAddingAndRemoving(PositiveInt itemsToAdd, int randomSeed) + { + var itemCount = itemsToAdd.Get; + var random = new System.Random(randomSeed); + var list = new DeletableObjectList(); - list.ApproximateCount.Should().Be(0); + foreach (var i in Enumerable.Range(1, itemCount)) + { + list.Add(new TestDeletable()); + list.ApproximateCount.Should().Be(i); } - [Property] - public void IsAccurateWhenAddingAndRemoving(PositiveInt itemsToAdd, int randomSeed) + foreach (var i in Enumerable.Range(1, itemCount)) { - var itemCount = itemsToAdd.Get; - var random = new System.Random(randomSeed); - var list = new DeletableObjectList(); - - foreach (var i in Enumerable.Range(1, itemCount)) - { - list.Add(new TestDeletable()); - list.ApproximateCount.Should().Be(i); - } - - foreach (var i in Enumerable.Range(1, itemCount)) - { - list.Remove(list.RandomElement(random)); - list.ApproximateCount.Should().Be(itemCount - i); - } + list.Remove(list.RandomElement(random)); + list.ApproximateCount.Should().Be(itemCount - i); } + } - [Property] - public void IsAccurateAfterEnumerating(PositiveInt itemsToAdd, int randomSeed) - { - var itemCount = itemsToAdd.Get; - var random = new System.Random(randomSeed); - var (list, items) = createPopulatedList(itemCount); - items.RandomElement(random).Deleted = true; + [Property] + public void IsAccurateAfterEnumerating(PositiveInt itemsToAdd, int randomSeed) + { + var itemCount = itemsToAdd.Get; + var random = new System.Random(randomSeed); + var (list, items) = createPopulatedList(itemCount); + items.RandomElement(random).Deleted = true; - _ = list.Count(); + _ = list.Count(); - list.ApproximateCount.Should().Be(itemCount - 1); - } + list.ApproximateCount.Should().Be(itemCount - 1); } } } diff --git a/Bearded.Utilities.Tests/Collections/LoopingQueue.cs b/Bearded.Utilities.Tests/Collections/LoopingQueue.cs index 7796c7e0..5ea70e9c 100644 --- a/Bearded.Utilities.Tests/Collections/LoopingQueue.cs +++ b/Bearded.Utilities.Tests/Collections/LoopingQueue.cs @@ -1,26 +1,25 @@ using System.Collections.Generic; -namespace Bearded.Utilities.Tests.Collections +namespace Bearded.Utilities.Tests.Collections; + +public static class LoopingQueue +{ + public static LoopingQueue Looping(this IEnumerable items) => new LoopingQueue(items); +} + +public class LoopingQueue { - public static class LoopingQueue + private readonly Queue queue; + + public LoopingQueue(IEnumerable items) { - public static LoopingQueue Looping(this IEnumerable items) => new LoopingQueue(items); + queue = new Queue(items); } - public class LoopingQueue + public T Next() { - private readonly Queue queue; - - public LoopingQueue(IEnumerable items) - { - queue = new Queue(items); - } - - public T Next() - { - var item = queue.Dequeue(); - queue.Enqueue(item); - return item; - } + var item = queue.Dequeue(); + queue.Enqueue(item); + return item; } } diff --git a/Bearded.Utilities.Tests/Collections/PrefixTrieTests.cs b/Bearded.Utilities.Tests/Collections/PrefixTrieTests.cs index 775bd9aa..20ac7b81 100644 --- a/Bearded.Utilities.Tests/Collections/PrefixTrieTests.cs +++ b/Bearded.Utilities.Tests/Collections/PrefixTrieTests.cs @@ -4,179 +4,178 @@ using FluentAssertions; using Xunit; -namespace Bearded.Utilities.Tests.Collections +namespace Bearded.Utilities.Tests.Collections; + +public class PrefixTrieTests { - public class PrefixTrieTests + #region construction + + [Fact] + public void TestCount() { - #region construction + var trie = new PrefixTrie( + new[] { "one", "two", "three" } + ); + trie.Count.Should().Be(3); + + trie = new PrefixTrie( + new string[0] + ); + trie.Count.Should().Be(0); + } - [Fact] - public void TestCount() - { - var trie = new PrefixTrie( - new[] { "one", "two", "three" } - ); - trie.Count.Should().Be(3); - - trie = new PrefixTrie( - new string[0] - ); - trie.Count.Should().Be(0); - } + [Fact] + public void TestIgnoreNull() + { + var trie = new PrefixTrie( + new[] { "one", "two", "three", null } + ); + trie.Should().HaveCount(3); + } - [Fact] - public void TestIgnoreNull() - { - var trie = new PrefixTrie( - new[] { "one", "two", "three", null } - ); - trie.Should().HaveCount(3); - } + [Fact] + public void TestIgnoreDouble() + { + var trie = new PrefixTrie( + new[] { "one", "two", "three", "two" } + ); + trie.Should().HaveCount(3); + } - [Fact] - public void TestIgnoreDouble() - { - var trie = new PrefixTrie( - new[] { "one", "two", "three", "two" } - ); - trie.Should().HaveCount(3); - } + #endregion - #endregion + #region Contains - #region Contains + [Fact] + public void TestContains() + { + var trie = new PrefixTrie( + new[] { "one", "two", "three" } + ); - [Fact] - public void TestContains() + foreach (var contained in new[] {"one", "two", "three"}) { - var trie = new PrefixTrie( - new[] { "one", "two", "three" } - ); - - foreach (var contained in new[] {"one", "two", "three"}) - { - trie.Contains(contained).Should().BeTrue(); - } - - foreach (var notContained in new[] {"four", "threeAndSome"}) - { - trie.Contains(notContained).Should().BeFalse(); - } + trie.Contains(contained).Should().BeTrue(); } - [Fact] - public void TestContains_DoesNotContainPrefixes() + foreach (var notContained in new[] {"four", "threeAndSome"}) { - var trie = new PrefixTrie( - new[] { "one", "two", "three" } - ); - - foreach (var notContained in new[] {"", "t", "tw"}) - { - trie.Contains(notContained).Should().BeFalse(); - } + trie.Contains(notContained).Should().BeFalse(); } + } - #endregion - - #region ExtendPrefix + [Fact] + public void TestContains_DoesNotContainPrefixes() + { + var trie = new PrefixTrie( + new[] { "one", "two", "three" } + ); - [Fact] - public void TestExtendPrefix() + foreach (var notContained in new[] {"", "t", "tw"}) { - var trie = new PrefixTrie( - new[] { "one", "two", "three", "threshing" } - ); - - trie.ExtendPrefix("o").Should().Be("one"); - trie.ExtendPrefix("one").Should().Be("one"); - - trie.ExtendPrefix("t").Should().Be("t"); - trie.ExtendPrefix("th").Should().Be("thre"); + trie.Contains(notContained).Should().BeFalse(); } + } - [Fact] - public void TestExtendPrefix_InvalidPrefix() - { - var trie = new PrefixTrie( - new[] { "one", "two", "three" } - ); + #endregion - trie.ExtendPrefix("a").Should().BeNull(); - trie.ExtendPrefix("twofold").Should().BeNull(); - } + #region ExtendPrefix - #endregion + [Fact] + public void TestExtendPrefix() + { + var trie = new PrefixTrie( + new[] { "one", "two", "three", "threshing" } + ); - #region AllKeys + trie.ExtendPrefix("o").Should().Be("one"); + trie.ExtendPrefix("one").Should().Be("one"); - [Fact] - public void TestAllKeys() - { - var words = new[] { "one", "two", "three", "threshing" }; + trie.ExtendPrefix("t").Should().Be("t"); + trie.ExtendPrefix("th").Should().Be("thre"); + } - var trie = new PrefixTrie(words); + [Fact] + public void TestExtendPrefix_InvalidPrefix() + { + var trie = new PrefixTrie( + new[] { "one", "two", "three" } + ); - var withPrefixO = trie.AllKeys("o").ToList(); + trie.ExtendPrefix("a").Should().BeNull(); + trie.ExtendPrefix("twofold").Should().BeNull(); + } - withPrefixO.Should().BeEquivalentTo(new[] { "one" }); + #endregion - var withPrefixTh = trie.AllKeys("th").ToList(); + #region AllKeys - withPrefixTh.Should().BeEquivalentTo(words.Where(w => w.StartsWith("th"))); - } + [Fact] + public void TestAllKeys() + { + var words = new[] { "one", "two", "three", "threshing" }; - [Fact] - public void TestAllKeys_EmptyPrefix() - { - var words = new[] { "one", "two", "three", "threshing" }; + var trie = new PrefixTrie(words); - var trie = new PrefixTrie(words); + var withPrefixO = trie.AllKeys("o").ToList(); - var withPrefixEmptyString = trie.AllKeys("").ToList(); + withPrefixO.Should().BeEquivalentTo(new[] { "one" }); - withPrefixEmptyString.Should().BeEquivalentTo(words); - } + var withPrefixTh = trie.AllKeys("th").ToList(); - [Fact] - public void TestAllKeys_InvalidPrefix() - { - var words = new[] { "one", "two", "three", "threshing" }; + withPrefixTh.Should().BeEquivalentTo(words.Where(w => w.StartsWith("th"))); + } + + [Fact] + public void TestAllKeys_EmptyPrefix() + { + var words = new[] { "one", "two", "three", "threshing" }; - var trie = new PrefixTrie(words); + var trie = new PrefixTrie(words); - var withPrefixEmptyString = trie.AllKeys("twofold").ToList(); + var withPrefixEmptyString = trie.AllKeys("").ToList(); - withPrefixEmptyString.Should().BeEmpty(); - } + withPrefixEmptyString.Should().BeEquivalentTo(words); + } - #endregion + [Fact] + public void TestAllKeys_InvalidPrefix() + { + var words = new[] { "one", "two", "three", "threshing" }; - #region enumeration + var trie = new PrefixTrie(words); - [Fact] - public void TestGetEnumerator() - { - var words = new[] { "one", "two", "three", "threshing" }; + var withPrefixEmptyString = trie.AllKeys("twofold").ToList(); + + withPrefixEmptyString.Should().BeEmpty(); + } - var trie = new PrefixTrie(words); + #endregion - var list = new List(words.Length); + #region enumeration - using (var enumerator = trie.GetEnumerator()) + [Fact] + public void TestGetEnumerator() + { + var words = new[] { "one", "two", "three", "threshing" }; + + var trie = new PrefixTrie(words); + + var list = new List(words.Length); + + using (var enumerator = trie.GetEnumerator()) + { + for (int i = 0; i < words.Length; i++) { - for (int i = 0; i < words.Length; i++) - { - enumerator.MoveNext().Should().BeTrue(); - list.Add(enumerator.Current); - } - - enumerator.MoveNext().Should().BeFalse(); + enumerator.MoveNext().Should().BeTrue(); + list.Add(enumerator.Current); } - list.Should().Contain(words); + enumerator.MoveNext().Should().BeFalse(); } - #endregion + list.Should().Contain(words); } + + #endregion } diff --git a/Bearded.Utilities.Tests/Collections/PriorityQueueTests.cs b/Bearded.Utilities.Tests/Collections/PriorityQueueTests.cs index 3b634dac..5c2886cf 100644 --- a/Bearded.Utilities.Tests/Collections/PriorityQueueTests.cs +++ b/Bearded.Utilities.Tests/Collections/PriorityQueueTests.cs @@ -6,107 +6,107 @@ using FsCheck.Xunit; using Xunit; -namespace Bearded.Utilities.Tests.Collections +namespace Bearded.Utilities.Tests.Collections; + +using Bearded.Utilities.Collections; + +public class PriorityQueueTests { - using Bearded.Utilities.Collections; + [Fact] + public void TestDecreasePriority() + { + var q = new PriorityQueue(); + q.Enqueue(2, "first item"); + q.Enqueue(3, "second item"); + q.Enqueue(1, "third item"); + Assert.Equal("third item", q.Peek().Value); + q.DecreasePriority("first item", 0); + Assert.Equal("first item", q.Peek().Value); + Assert.Equal(0, q.Peek().Key); + } - public class PriorityQueueTests + [Fact] + public void TestDecreasePriorityInitialData() { - [Fact] - public void TestDecreasePriority() - { - var q = new PriorityQueue(); - q.Enqueue(2, "first item"); - q.Enqueue(3, "second item"); - q.Enqueue(1, "third item"); - Assert.Equal("third item", q.Peek().Value); - q.DecreasePriority("first item", 0); - Assert.Equal("first item", q.Peek().Value); - Assert.Equal(0, q.Peek().Key); - } + var data = new[] { 5, 9, 2, 13, 1, 4, 7, 11 }; + var q = new PriorityQueue(data.Select(i => new KeyValuePair(i, i))); + Assert.Equal(data.Length, q.Count); + q.DecreasePriority(5, 0); + Assert.Equal(5, q.Dequeue().Value); + Assert.Equal(1, q.Dequeue().Value); + q.DecreasePriority(7, 1); + Assert.Equal(7, q.Dequeue().Value); + } - [Fact] - public void TestDecreasePriorityInitialData() - { - var data = new[] { 5, 9, 2, 13, 1, 4, 7, 11 }; - var q = new PriorityQueue(data.Select(i => new KeyValuePair(i, i))); - Assert.Equal(data.Length, q.Count); - q.DecreasePriority(5, 0); - Assert.Equal(5, q.Dequeue().Value); - Assert.Equal(1, q.Dequeue().Value); - q.DecreasePriority(7, 1); - Assert.Equal(7, q.Dequeue().Value); - } + [Fact] + public void TestDecreasePriorityEmptyQueue() + { + var q = new PriorityQueue(); + Assert.Throws(() => q.DecreasePriority(0, 0)); + } - [Fact] - public void TestDecreasePriorityEmptyQueue() - { - var q = new PriorityQueue(); - Assert.Throws(() => q.DecreasePriority(0, 0)); - } + [Fact] + public void TestDecreasePriorityDequeuedElement() + { + var q = new PriorityQueue(); + q.Enqueue(2, "first item"); + q.Enqueue(3, "second item"); + q.Enqueue(1, "third item"); - [Fact] - public void TestDecreasePriorityDequeuedElement() - { - var q = new PriorityQueue(); - q.Enqueue(2, "first item"); - q.Enqueue(3, "second item"); - q.Enqueue(1, "third item"); + q.Dequeue(); - q.Dequeue(); + Assert.Throws(() => q.DecreasePriority("third item", 0)); + } - Assert.Throws(() => q.DecreasePriority("third item", 0)); - } + [Fact] + public void TestDecreasePriorityClearedQueue() + { + var q = new PriorityQueue(); + q.Enqueue(2, "first item"); + q.Enqueue(3, "second item"); + q.Enqueue(1, "third item"); - [Fact] - public void TestDecreasePriorityClearedQueue() - { - var q = new PriorityQueue(); - q.Enqueue(2, "first item"); - q.Enqueue(3, "second item"); - q.Enqueue(1, "third item"); + q.Clear(); - q.Clear(); + Assert.Throws(() => q.DecreasePriority("third item", 0)); + } - Assert.Throws(() => q.DecreasePriority("third item", 0)); - } + [Fact] + public void TestIncreasePriority() + { + var q = new PriorityQueue(); + q.Enqueue(2, "item"); + Assert.Throws(() => q.DecreasePriority("item", 4)); + } - [Fact] - public void TestIncreasePriority() + [Property] + public void TestEnumerationContainsCorrectItems(int seed, byte itemsToEnumerate, byte otherItems) + { + var random = new System.Random(seed); + var totalItems = itemsToEnumerate + otherItems; + var q = new PriorityQueue(); + var items = Enumerable + .Range(0, totalItems) + .Select(i => KeyValuePair.Create(random.NextDouble(), i)) + .Shuffled(); + foreach (var (priority, value) in items) { - var q = new PriorityQueue(); - q.Enqueue(2, "item"); - Assert.Throws(() => q.DecreasePriority("item", 4)); + q.Enqueue(priority, value); } + for (var i = 0; i < otherItems; i++) + { + q.Dequeue(); + } + + // ReSharper disable once RedundantCast to make sure we're not using a ToList() that we may add to the queue + var enumerated = ((IEnumerable>)q).ToList(); - [Property] - public void TestEnumerationContainsCorrectItems(int seed, byte itemsToEnumerate, byte otherItems) + enumerated.Should().HaveCount(itemsToEnumerate).And.BeSubsetOf(items); + while (q.Count > 0) { - var random = new System.Random(seed); - var totalItems = itemsToEnumerate + otherItems; - var q = new PriorityQueue(); - var items = Enumerable - .Range(0, totalItems) - .Select(i => KeyValuePair.Create(random.NextDouble(), i)) - .Shuffled(); - foreach (var (priority, value) in items) - { - q.Enqueue(priority, value); - } - for (var i = 0; i < otherItems; i++) - { - q.Dequeue(); - } - - // ReSharper disable once RedundantCast to make sure we're not using a ToList() that we may add to the queue - var enumerated = ((IEnumerable>)q).ToList(); - - enumerated.Should().HaveCount(itemsToEnumerate).And.BeSubsetOf(items); - while (q.Count > 0) - { - var dequeued = q.Dequeue(); - enumerated.Should().Contain(dequeued); - } + var dequeued = q.Dequeue(); + enumerated.Should().Contain(dequeued); } } } + diff --git a/Bearded.Utilities.Tests/Collections/StaticPriorityQueueTests.cs b/Bearded.Utilities.Tests/Collections/StaticPriorityQueueTests.cs index 38affc49..e959d30e 100644 --- a/Bearded.Utilities.Tests/Collections/StaticPriorityQueueTests.cs +++ b/Bearded.Utilities.Tests/Collections/StaticPriorityQueueTests.cs @@ -4,81 +4,80 @@ using Bearded.Utilities.Collections; using Xunit; -namespace Bearded.Utilities.Tests.Collections +namespace Bearded.Utilities.Tests.Collections; + +public class StaticPriorityQueueTests { - public class StaticPriorityQueueTests + [Fact] + public void TestEnqueueing() { - [Fact] - public void TestEnqueueing() - { - var q = new StaticPriorityQueue(); - q.Enqueue(2, "first item"); - q.Enqueue(3, "second item"); - q.Enqueue(1, "third item"); - Assert.Equal(3, q.Count); - } + var q = new StaticPriorityQueue(); + q.Enqueue(2, "first item"); + q.Enqueue(3, "second item"); + q.Enqueue(1, "third item"); + Assert.Equal(3, q.Count); + } - [Fact] - public void TestDequeueing() - { - var q = new StaticPriorityQueue(); - q.Enqueue(2, "first item"); - q.Enqueue(3, "second item"); - q.Enqueue(1, "third item"); + [Fact] + public void TestDequeueing() + { + var q = new StaticPriorityQueue(); + q.Enqueue(2, "first item"); + q.Enqueue(3, "second item"); + q.Enqueue(1, "third item"); - var peek = q.Peek(); - Assert.Equal("third item", peek.Value); - Assert.Equal(3, q.Count); + var peek = q.Peek(); + Assert.Equal("third item", peek.Value); + Assert.Equal(3, q.Count); - var deq = q.Dequeue(); - Assert.Equal("third item", deq.Value); - Assert.Equal(2, q.Count); + var deq = q.Dequeue(); + Assert.Equal("third item", deq.Value); + Assert.Equal(2, q.Count); - deq = q.Dequeue(); - Assert.Equal("first item", deq.Value); - Assert.Equal(1, q.Count); - } + deq = q.Dequeue(); + Assert.Equal("first item", deq.Value); + Assert.Equal(1, q.Count); + } - [Fact] - public void TestDequeueOnEmptyQueue() - { - Assert.Throws(() => new StaticPriorityQueue().Dequeue()); + [Fact] + public void TestDequeueOnEmptyQueue() + { + Assert.Throws(() => new StaticPriorityQueue().Dequeue()); - } + } - [Fact] - public void TestPeekOnEmptyQueue() - { - Assert.Throws(() => new StaticPriorityQueue().Peek()); - } + [Fact] + public void TestPeekOnEmptyQueue() + { + Assert.Throws(() => new StaticPriorityQueue().Peek()); + } - [Fact] - public void TestGrowing() + [Fact] + public void TestGrowing() + { + var q = new StaticPriorityQueue(); + for (int i = 0; i < 32; i++) + q.Enqueue(i, i.ToString()); + Assert.Equal(32, q.Count); + for (int i = 0; i < 32; i++) { - var q = new StaticPriorityQueue(); - for (int i = 0; i < 32; i++) - q.Enqueue(i, i.ToString()); - Assert.Equal(32, q.Count); - for (int i = 0; i < 32; i++) - { - var deq = q.Dequeue(); - Assert.Equal(i.ToString(), deq.Value); - } + var deq = q.Dequeue(); + Assert.Equal(i.ToString(), deq.Value); } + } - [Fact] - public void TestInitialData() - { - var data = new[] { 5, 9, 2, 13, 1, 4, 7, 11, 2 }; - var q = new StaticPriorityQueue(data.Select(i => new KeyValuePair(i, i))); - Assert.Equal(data.Length, q.Count); + [Fact] + public void TestInitialData() + { + var data = new[] { 5, 9, 2, 13, 1, 4, 7, 11, 2 }; + var q = new StaticPriorityQueue(data.Select(i => new KeyValuePair(i, i))); + Assert.Equal(data.Length, q.Count); - Array.Sort(data); - for (int i = 0; i < data.Length; i++) - { - var deq = q.Dequeue(); - Assert.Equal(data[i], deq.Value); - } + Array.Sort(data); + for (int i = 0; i < data.Length; i++) + { + var deq = q.Dequeue(); + Assert.Equal(data[i], deq.Value); } } -} \ No newline at end of file +} diff --git a/Bearded.Utilities.Tests/Core/MaybeTests.cs b/Bearded.Utilities.Tests/Core/MaybeTests.cs index 0fe4d4ba..0e48046b 100644 --- a/Bearded.Utilities.Tests/Core/MaybeTests.cs +++ b/Bearded.Utilities.Tests/Core/MaybeTests.cs @@ -4,293 +4,292 @@ using Xunit; using Xunit.Sdk; -namespace Bearded.Utilities.Tests +namespace Bearded.Utilities.Tests; + +[SuppressMessage("ReSharper", "ArgumentsStyleAnonymousFunction")] +public sealed class MaybeTests { - [SuppressMessage("ReSharper", "ArgumentsStyleAnonymousFunction")] - public sealed class MaybeTests + public sealed class ValueOrDefault { - public sealed class ValueOrDefault + public sealed class WithEagerDefault { - public sealed class WithEagerDefault + [Fact] + public void ReturnsDefaultOnNothing() { - [Fact] - public void ReturnsDefaultOnNothing() - { - var maybe = Maybe.Nothing; - - maybe.ValueOrDefault(100).Should().Be(100); - } - - [Fact] - public void ReturnsValueOnJust() - { - var maybe = Maybe.Just(200); + var maybe = Maybe.Nothing; - maybe.ValueOrDefault(100).Should().Be(200); - } + maybe.ValueOrDefault(100).Should().Be(100); } - public sealed class WithLazyDefault + [Fact] + public void ReturnsValueOnJust() { - [Fact] - public void ReturnsDefaultOnNothing() - { - var maybe = Maybe.Nothing; + var maybe = Maybe.Just(200); - maybe.ValueOrDefault(() => 100).Should().Be(100); - } - - [Fact] - public void ReturnsValueOnJust() - { - var maybe = Maybe.Just(200); - - maybe.ValueOrDefault(() => 100).Should().Be(200); - } + maybe.ValueOrDefault(100).Should().Be(200); } } - public sealed class ValueOrError + public sealed class WithLazyDefault { - public sealed class WithEagerError + [Fact] + public void ReturnsDefaultOnNothing() { - [Fact] - public void ReturnsFailureOnNothing() - { - var maybe = Maybe.Nothing; - - maybe.ValueOrFailure("something went wrong").Should().BeFailureWithError("something went wrong"); - } - - [Fact] - public void ReturnsSuccessOnJust() - { - var maybe = Maybe.Just(200); + var maybe = Maybe.Nothing; - maybe.ValueOrFailure("something went wrong").Should().BeSuccessWithResult(200); - } + maybe.ValueOrDefault(() => 100).Should().Be(100); } - public sealed class WithLazyError + [Fact] + public void ReturnsValueOnJust() { - [Fact] - public void ReturnsFailureOnNothing() - { - var maybe = Maybe.Nothing; - - maybe.ValueOrFailure(() => "something went wrong").Should() - .BeFailureWithError("something went wrong"); - } + var maybe = Maybe.Just(200); - [Fact] - public void ReturnsSuccessOnJust() - { - var maybe = Maybe.Just(200); - - maybe.ValueOrFailure(() => "something went wrong").Should().BeSuccessWithResult(200); - } + maybe.ValueOrDefault(() => 100).Should().Be(200); } } + } - public sealed class Select + public sealed class ValueOrError + { + public sealed class WithEagerError { [Fact] - public void MapsNothingToNothing() + public void ReturnsFailureOnNothing() { var maybe = Maybe.Nothing; - maybe.Select(i => i * 2).Should().Be(Maybe.Nothing); + maybe.ValueOrFailure("something went wrong").Should().BeFailureWithError("something went wrong"); } [Fact] - public void MapsValueToJust() + public void ReturnsSuccessOnJust() { - var maybe = Maybe.Just(100); + var maybe = Maybe.Just(200); - maybe.Select(i => i * 2).Should().Be(Maybe.Just(200)); + maybe.ValueOrFailure("something went wrong").Should().BeSuccessWithResult(200); } } - public sealed class SelectMany + public sealed class WithLazyError { [Fact] - public void MapsNothingToNothing() + public void ReturnsFailureOnNothing() { var maybe = Maybe.Nothing; - maybe.SelectMany(i => Maybe.Just(i * 2)).Should().Be(Maybe.Nothing); + maybe.ValueOrFailure(() => "something went wrong").Should() + .BeFailureWithError("something went wrong"); } [Fact] - public void MapsValueToJust() + public void ReturnsSuccessOnJust() { - var maybe = Maybe.Just(100); + var maybe = Maybe.Just(200); - maybe.SelectMany(i => Maybe.Just(i * 2)).Should().Be(Maybe.Just(200)); + maybe.ValueOrFailure(() => "something went wrong").Should().BeSuccessWithResult(200); } + } + } - [Fact] - public void MapsValueToNothing() - { - var maybe = Maybe.Just(100); + public sealed class Select + { + [Fact] + public void MapsNothingToNothing() + { + var maybe = Maybe.Nothing; - maybe.SelectMany(i => Maybe.Nothing).Should().Be(Maybe.Nothing); - } + maybe.Select(i => i * 2).Should().Be(Maybe.Nothing); } - public sealed class Where + [Fact] + public void MapsValueToJust() { - [Fact] - public void MapsNothingToNothingIfPredicateReturnsFalse() - { - var maybe = Maybe.Nothing; + var maybe = Maybe.Just(100); - maybe.Where(_ => false).Should().Be(Maybe.Nothing); - } + maybe.Select(i => i * 2).Should().Be(Maybe.Just(200)); + } + } - [Fact] - public void MapsNothingToNothingIfPredicateReturnsTrue() - { - var maybe = Maybe.Nothing; + public sealed class SelectMany + { + [Fact] + public void MapsNothingToNothing() + { + var maybe = Maybe.Nothing; - maybe.Where(_ => true).Should().Be(Maybe.Nothing); - } + maybe.SelectMany(i => Maybe.Just(i * 2)).Should().Be(Maybe.Nothing); + } - [Fact] - public void MapsJustToNothingIfPredicateReturnsFalse() - { - var maybe = Maybe.Just(100); + [Fact] + public void MapsValueToJust() + { + var maybe = Maybe.Just(100); - maybe.Where(_ => false).Should().Be(Maybe.Nothing); - } + maybe.SelectMany(i => Maybe.Just(i * 2)).Should().Be(Maybe.Just(200)); + } - [Fact] - public void MapsJustToJustIfPredicateReturnsTrue() - { - var maybe = Maybe.Just(100); + [Fact] + public void MapsValueToNothing() + { + var maybe = Maybe.Just(100); - maybe.Where(_ => true).Should().Be(Maybe.Just(100)); - } + maybe.SelectMany(i => Maybe.Nothing).Should().Be(Maybe.Nothing); } + } - public sealed class MatchWithOneParameter + public sealed class Where + { + [Fact] + public void MapsNothingToNothingIfPredicateReturnsFalse() { - [Fact] - public void DoesNotCallOnValueWithNothing() - { - var maybe = Maybe.Nothing; + var maybe = Maybe.Nothing; - maybe.Match(onValue: (val) => throw new XunitException("Wrong method called")); - } + maybe.Where(_ => false).Should().Be(Maybe.Nothing); + } - [Fact] - public void CallsOnValueWithValueOnJust() - { - var maybe = Maybe.Just(100); + [Fact] + public void MapsNothingToNothingIfPredicateReturnsTrue() + { + var maybe = Maybe.Nothing; - var isCalled = false; - maybe.Match( - onValue: (val) => - { - val.Should().Be(100); - isCalled = true; - }); + maybe.Where(_ => true).Should().Be(Maybe.Nothing); + } - isCalled.Should().BeTrue("onValue should have been called"); - } + [Fact] + public void MapsJustToNothingIfPredicateReturnsFalse() + { + var maybe = Maybe.Just(100); + + maybe.Where(_ => false).Should().Be(Maybe.Nothing); } - public sealed class MatchWithTwoParameters + [Fact] + public void MapsJustToJustIfPredicateReturnsTrue() { - [Fact] - public void CallsOnNothingOnNothing() - { - var maybe = Maybe.Nothing; + var maybe = Maybe.Just(100); - var isCalled = false; - maybe.Match( - onValue: (val) => throw new XunitException("Wrong method called"), - onNothing: () => isCalled = true); + maybe.Where(_ => true).Should().Be(Maybe.Just(100)); + } + } - isCalled.Should().BeTrue("onNothing should have been called"); - } + public sealed class MatchWithOneParameter + { + [Fact] + public void DoesNotCallOnValueWithNothing() + { + var maybe = Maybe.Nothing; - [Fact] - public void CallsOnValueWithValueOnJust() - { - var maybe = Maybe.Just(100); - - var isCalled = false; - maybe.Match( - onValue: (val) => - { - val.Should().Be(100); - isCalled = true; - }, - onNothing: () => throw new XunitException("Wrong method called")); - - isCalled.Should().BeTrue("onValue should have been called"); - } + maybe.Match(onValue: (val) => throw new XunitException("Wrong method called")); } - public sealed class ReturningMatch + [Fact] + public void CallsOnValueWithValueOnJust() { - [Fact] - public void CallsOnNothingOnNothingAndReturnsItsValue() - { - var maybe = Maybe.Nothing; - var expectedResult = "expected result"; + var maybe = Maybe.Just(100); - var actualResult = maybe.Match( - onValue: (val) => throw new XunitException("Wrong method called"), - onNothing: () => expectedResult); + var isCalled = false; + maybe.Match( + onValue: (val) => + { + val.Should().Be(100); + isCalled = true; + }); - actualResult.Should().Be(expectedResult, "onNothing should have been called"); - } + isCalled.Should().BeTrue("onValue should have been called"); + } + } - [Fact] - public void CallsOnValueWithValueOnJustAndReturnsItsValue() - { - var maybe = Maybe.Just(100); - var expectedResult = "expected result"; - - var actualResult = maybe.Match( - onValue: (val) => - { - val.Should().Be(100); - return expectedResult; - }, - onNothing: () => throw new XunitException("Wrong method called")); - - actualResult.Should().Be(expectedResult, "onValue should have been called"); - } + public sealed class MatchWithTwoParameters + { + [Fact] + public void CallsOnNothingOnNothing() + { + var maybe = Maybe.Nothing; + + var isCalled = false; + maybe.Match( + onValue: (val) => throw new XunitException("Wrong method called"), + onNothing: () => isCalled = true); + + isCalled.Should().BeTrue("onNothing should have been called"); } - public sealed class FromNullable + [Fact] + public void CallsOnValueWithValueOnJust() { - [Fact] - public void ReturnsNothingOnReferenceTypeNull() - { - Maybe.FromNullable((string?) null).Should().Be(Maybe.Nothing); - } + var maybe = Maybe.Just(100); - [Fact] - public void ReturnsJustOnReferenceTypeSet() - { - Maybe.FromNullable("the game").Should().Be(Maybe.Just("the game")); - } + var isCalled = false; + maybe.Match( + onValue: (val) => + { + val.Should().Be(100); + isCalled = true; + }, + onNothing: () => throw new XunitException("Wrong method called")); - [Fact] - public void ReturnsNothingOnNullableNoValue() - { - Maybe.FromNullable((int?) null).Should().Be(Maybe.Nothing); - } + isCalled.Should().BeTrue("onValue should have been called"); + } + } - [Fact] - public void ReturnsJustOnNullableWithValue() - { - Maybe.FromNullable((int?) 10).Should().Be(Maybe.Just(10)); - } + public sealed class ReturningMatch + { + [Fact] + public void CallsOnNothingOnNothingAndReturnsItsValue() + { + var maybe = Maybe.Nothing; + var expectedResult = "expected result"; + + var actualResult = maybe.Match( + onValue: (val) => throw new XunitException("Wrong method called"), + onNothing: () => expectedResult); + + actualResult.Should().Be(expectedResult, "onNothing should have been called"); + } + + [Fact] + public void CallsOnValueWithValueOnJustAndReturnsItsValue() + { + var maybe = Maybe.Just(100); + var expectedResult = "expected result"; + + var actualResult = maybe.Match( + onValue: (val) => + { + val.Should().Be(100); + return expectedResult; + }, + onNothing: () => throw new XunitException("Wrong method called")); + + actualResult.Should().Be(expectedResult, "onValue should have been called"); + } + } + + public sealed class FromNullable + { + [Fact] + public void ReturnsNothingOnReferenceTypeNull() + { + Maybe.FromNullable((string?) null).Should().Be(Maybe.Nothing); + } + + [Fact] + public void ReturnsJustOnReferenceTypeSet() + { + Maybe.FromNullable("the game").Should().Be(Maybe.Just("the game")); + } + + [Fact] + public void ReturnsNothingOnNullableNoValue() + { + Maybe.FromNullable((int?) null).Should().Be(Maybe.Nothing); + } + + [Fact] + public void ReturnsJustOnNullableWithValue() + { + Maybe.FromNullable((int?) 10).Should().Be(Maybe.Just(10)); } } } diff --git a/Bearded.Utilities.Tests/Core/Random/BaseTests.cs b/Bearded.Utilities.Tests/Core/Random/BaseTests.cs index e28cb6cc..1ed9e371 100644 --- a/Bearded.Utilities.Tests/Core/Random/BaseTests.cs +++ b/Bearded.Utilities.Tests/Core/Random/BaseTests.cs @@ -1,73 +1,72 @@ using System; -namespace Bearded.Utilities.Tests.Random +namespace Bearded.Utilities.Tests.Random; + +public class BaseTests { - public class BaseTests + public abstract class RandomMethodBase { - public abstract class RandomMethodBase - { - // ReSharper disable once VirtualMemberCallInConstructor - // it's magic to make actual tests more concise, yet enforce implementing setup - // (though we can't enforce _correct_ implementation) - protected RandomMethodBase() => Setup(); + // ReSharper disable once VirtualMemberCallInConstructor + // it's magic to make actual tests more concise, yet enforce implementing setup + // (though we can't enforce _correct_ implementation) + protected RandomMethodBase() => Setup(); - protected abstract void Setup(); + protected abstract void Setup(); - protected void Setup(Action seed, TMethod method) - { - Seed = seed; - CallMethod = method; - } + protected void Setup(Action seed, TMethod method) + { + Seed = seed; + CallMethod = method; + } - public TMethod CallMethod { get; set; } + public TMethod CallMethod { get; set; } - public Action Seed { get; set; } - } + public Action Seed { get; set; } + } - public abstract class RandomMethodWithoutParameters : RandomMethodBase> - where T : struct, IComparable - { - protected Func CallingWith(int seed) - => () => ResultOfCallingWith(seed); + public abstract class RandomMethodWithoutParameters : RandomMethodBase> + where T : struct, IComparable + { + protected Func CallingWith(int seed) + => () => ResultOfCallingWith(seed); - protected T ResultOfCallingWith(int seed) - { - Seed(seed); - return CallMethod(); - } + protected T ResultOfCallingWith(int seed) + { + Seed(seed); + return CallMethod(); } + } - public abstract class RandomMethodWithOneParameter - : RandomMethodWithOneParameter - where T : struct, IComparable - { + public abstract class RandomMethodWithOneParameter + : RandomMethodWithOneParameter + where T : struct, IComparable + { - } + } - public abstract class RandomMethodWithOneParameter : RandomMethodBase> - where T : struct, IComparable - { - protected Func CallingWith(int seed, TParameter parameter) - => () => ResultOfCallingWith(seed, parameter); + public abstract class RandomMethodWithOneParameter : RandomMethodBase> + where T : struct, IComparable + { + protected Func CallingWith(int seed, TParameter parameter) + => () => ResultOfCallingWith(seed, parameter); - protected T ResultOfCallingWith(int seed, TParameter parameter) - { - Seed(seed); - return CallMethod(parameter); - } + protected T ResultOfCallingWith(int seed, TParameter parameter) + { + Seed(seed); + return CallMethod(parameter); } + } - public abstract class RandomMethodWithTwoParameters : RandomMethodBase> - where T : struct, IComparable - { - protected Func CallingWith(int seed, T parameter1, T parameter2) - => () => ResultOfCallingWith(seed, parameter1, parameter2); + public abstract class RandomMethodWithTwoParameters : RandomMethodBase> + where T : struct, IComparable + { + protected Func CallingWith(int seed, T parameter1, T parameter2) + => () => ResultOfCallingWith(seed, parameter1, parameter2); - protected T ResultOfCallingWith(int seed, T parameter1, T parameter2) - { - Seed(seed); - return CallMethod(parameter1, parameter2); - } + protected T ResultOfCallingWith(int seed, T parameter1, T parameter2) + { + Seed(seed); + return CallMethod(parameter1, parameter2); } } } diff --git a/Bearded.Utilities.Tests/Core/Random/RandomExtensionsTests.cs b/Bearded.Utilities.Tests/Core/Random/RandomExtensionsTests.cs index 5efa8cd0..c1b0ff61 100644 --- a/Bearded.Utilities.Tests/Core/Random/RandomExtensionsTests.cs +++ b/Bearded.Utilities.Tests/Core/Random/RandomExtensionsTests.cs @@ -1,138 +1,137 @@ using System; -namespace Bearded.Utilities.Tests.Random +namespace Bearded.Utilities.Tests.Random; + +public class RandomExtensionsTests { - public class RandomExtensionsTests + private static void setup(BaseTests.RandomMethodBase tests, Func getMethod) { - private static void setup(BaseTests.RandomMethodBase tests, Func getMethod) - { - tests.Seed = seed => tests.CallMethod = getMethod(new System.Random(seed)); - } + tests.Seed = seed => tests.CallMethod = getMethod(new System.Random(seed)); + } - public class TheSeedWithMethod : SharedTests.TheSeedMethod - { - protected override void Setup() - => setup(this, r => r.Next); - } - - public class TheIntMethod : SharedTests.TheIntMethod - { - protected override void Setup() - => setup(this, r => r.Next); - } + public class TheSeedWithMethod : SharedTests.TheSeedMethod + { + protected override void Setup() + => setup(this, r => r.Next); + } + + public class TheIntMethod : SharedTests.TheIntMethod + { + protected override void Setup() + => setup(this, r => r.Next); + } - public class TheIntWithMaxMethod : SharedTests.TheIntWithMaxMethod - { - protected override void Setup() - => setup(this, r => r.Next); - } - - public class TheIntWithMinAndMaxMethod : SharedTests.TheIntWithMinAndMaxMethod - { - protected override void Setup() - => setup(this, r => r.Next); - } + public class TheIntWithMaxMethod : SharedTests.TheIntWithMaxMethod + { + protected override void Setup() + => setup(this, r => r.Next); + } + + public class TheIntWithMinAndMaxMethod : SharedTests.TheIntWithMinAndMaxMethod + { + protected override void Setup() + => setup(this, r => r.Next); + } - public class TheLongMethod : SharedTests.TheLongMethod - { - protected override void Setup() - => setup(this, r => r.NextLong); - } + public class TheLongMethod : SharedTests.TheLongMethod + { + protected override void Setup() + => setup(this, r => r.NextLong); + } - public class TheLongWithMaxMethod : SharedTests.TheLongWithMaxMethod - { - protected override void Setup() - => setup(this, r => r.NextLong); - } - - public class TheLongWithMinAndMaxMethod : SharedTests.TheLongWithMinAndMaxMethod - { - protected override void Setup() - => setup(this, r => r.NextLong); - } - - public class TheFloatMethod : SharedTests.TheFloatMethod - { - protected override void Setup() - => setup(this, r => r.NextFloat); - } - - public class TheFloatWithOneBoundMethod : SharedTests.TheFloatWithOneBoundMethod - { - protected override void Setup() - => setup(this, r => r.NextFloat); - } - - public class TheFloatWithTwoBoundsMethod : SharedTests.TheFloatWithTwoBoundsMethod - { - protected override void Setup() - => setup(this, r => r.NextFloat); - } - - public class TheDoubleMethod : SharedTests.TheDoubleMethod - { - protected override void Setup() - => setup(this, r => r.NextDouble); - } - - public class TheDoubleWithOneBoundMethod : SharedTests.TheDoubleWithOneBoundMethod - { - protected override void Setup() - => setup(this, r => r.NextDouble); - } - - public class TheDoubleWithTwoBoundsMethod : SharedTests.TheDoubleWithTwoBoundsMethod - { - protected override void Setup() - => setup(this, r => r.NextDouble); - } - - public class TheSignMethod : SharedTests.TheSignMethod - { - protected override void Setup() - => setup(this, r => r.NextSign); - } - - public class TheBoolMethod : SharedTests.TheBoolMethod - { - protected override void Setup() - => setup(this, r => r.NextBool); - } + public class TheLongWithMaxMethod : SharedTests.TheLongWithMaxMethod + { + protected override void Setup() + => setup(this, r => r.NextLong); + } + + public class TheLongWithMinAndMaxMethod : SharedTests.TheLongWithMinAndMaxMethod + { + protected override void Setup() + => setup(this, r => r.NextLong); + } + + public class TheFloatMethod : SharedTests.TheFloatMethod + { + protected override void Setup() + => setup(this, r => r.NextFloat); + } + + public class TheFloatWithOneBoundMethod : SharedTests.TheFloatWithOneBoundMethod + { + protected override void Setup() + => setup(this, r => r.NextFloat); + } + + public class TheFloatWithTwoBoundsMethod : SharedTests.TheFloatWithTwoBoundsMethod + { + protected override void Setup() + => setup(this, r => r.NextFloat); + } + + public class TheDoubleMethod : SharedTests.TheDoubleMethod + { + protected override void Setup() + => setup(this, r => r.NextDouble); + } + + public class TheDoubleWithOneBoundMethod : SharedTests.TheDoubleWithOneBoundMethod + { + protected override void Setup() + => setup(this, r => r.NextDouble); + } + + public class TheDoubleWithTwoBoundsMethod : SharedTests.TheDoubleWithTwoBoundsMethod + { + protected override void Setup() + => setup(this, r => r.NextDouble); + } + + public class TheSignMethod : SharedTests.TheSignMethod + { + protected override void Setup() + => setup(this, r => r.NextSign); + } + + public class TheBoolMethod : SharedTests.TheBoolMethod + { + protected override void Setup() + => setup(this, r => r.NextBool); + } - public class TheBoolWithParameterMethod : SharedTests.TheBoolWithParameterMethod - { - protected override void Setup() - => setup(this, r => r.NextBool); - } - - public class TheDiscretiseMethod : SharedTests.TheDiscretiseMethod - { - protected override void Setup() - => setup(this, r => r.Discretise); - } - - public class TheNormalFloatMethod : SharedTests.TheNormalFloatMethod - { - protected override void Setup() - => setup(this, r => r.NormalFloat); - } - - public class TheNormalFloatWithParametersMethod : SharedTests.TheNormalFloatWithParametersMethod - { - protected override void Setup() - => setup(this, r => r.NormalFloat); - } - - public class TheNormalDoubleMethod : SharedTests.TheNormalDoubleMethod - { - protected override void Setup() - => setup(this, r => r.NormalDouble); - } + public class TheBoolWithParameterMethod : SharedTests.TheBoolWithParameterMethod + { + protected override void Setup() + => setup(this, r => r.NextBool); + } + + public class TheDiscretiseMethod : SharedTests.TheDiscretiseMethod + { + protected override void Setup() + => setup(this, r => r.Discretise); + } + + public class TheNormalFloatMethod : SharedTests.TheNormalFloatMethod + { + protected override void Setup() + => setup(this, r => r.NormalFloat); + } + + public class TheNormalFloatWithParametersMethod : SharedTests.TheNormalFloatWithParametersMethod + { + protected override void Setup() + => setup(this, r => r.NormalFloat); + } + + public class TheNormalDoubleMethod : SharedTests.TheNormalDoubleMethod + { + protected override void Setup() + => setup(this, r => r.NormalDouble); + } - public class TheNormalDoubleWithParametersMethod : SharedTests.TheNormalDoubleWithParametersMethod - { - protected override void Setup() - => setup(this, r => r.NormalDouble); - } + public class TheNormalDoubleWithParametersMethod : SharedTests.TheNormalDoubleWithParametersMethod + { + protected override void Setup() + => setup(this, r => r.NormalDouble); } } diff --git a/Bearded.Utilities.Tests/Core/Random/SharedTests.cs b/Bearded.Utilities.Tests/Core/Random/SharedTests.cs index 7b75e711..d9fd3895 100644 --- a/Bearded.Utilities.Tests/Core/Random/SharedTests.cs +++ b/Bearded.Utilities.Tests/Core/Random/SharedTests.cs @@ -6,438 +6,437 @@ using FsCheck.Xunit; // ReSharper disable CompareOfFloatsByEqualityOperator because we mean to test equality below -namespace Bearded.Utilities.Tests.Random +namespace Bearded.Utilities.Tests.Random; + +public class SharedTests { - public class SharedTests - { - private const int sequenceCount = 100; - private const int diviationOffset = 100; + private const int sequenceCount = 100; + private const int diviationOffset = 100; - private static IEnumerable sequence(int count, Func function) - => Enumerable.Range(0, count).Select(_ => function()); + private static IEnumerable sequence(int count, Func function) + => Enumerable.Range(0, count).Select(_ => function()); - private static List list(int count, Func function) - => sequence(count, function).ToList(); + private static List list(int count, Func function) + => sequence(count, function).ToList(); - public abstract class TheSeedMethod : BaseTests.RandomMethodWithoutParameters + public abstract class TheSeedMethod : BaseTests.RandomMethodWithoutParameters + { + [Property] + public void LeadsToPredictableResultsWithSameSeed(int seed) { - [Property] - public void LeadsToPredictableResultsWithSameSeed(int seed) - { - Seed(seed); - var sequence1 = list(sequenceCount, CallMethod); + Seed(seed); + var sequence1 = list(sequenceCount, CallMethod); - Seed(seed); - var sequence2 = list(sequenceCount, CallMethod); + Seed(seed); + var sequence2 = list(sequenceCount, CallMethod); - sequence1.Should().BeEquivalentTo(sequence2, - options => options.WithStrictOrdering()); - } + sequence1.Should().BeEquivalentTo(sequence2, + options => options.WithStrictOrdering()); } + } - public abstract class TheIntMethod : BaseTests.RandomMethodWithoutParameters - { - [Property] - public void ShouldReturnDifferentValues(int seed) - { - Seed(seed); - var first = CallMethod(); - - sequence(sequenceCount, CallMethod) - .Should().Contain(i => i != first); - } - } - - public abstract class TheIntWithMaxMethod : BaseTests.RandomMethodWithOneParameter - { - [Property] - public void ShouldReturnValueInRange(int seed, PositiveInt max) - { - ResultOfCallingWith(seed, max.Get) - .Should().BeInRange(0, max.Get); - } - - [Property] - public void ShouldThrowForNegativeMaximum(int seed, NegativeInt max) - { - CallingWith(seed, max.Get).Should().Throw(); - } - - [Property] - public void ShouldReturnZeroForZeroMaximum(int seed) - { - ResultOfCallingWith(seed, 0).Should().Be(0); - } - } - - public abstract class TheIntWithMinAndMaxMethod : BaseTests.RandomMethodWithTwoParameters - { - [Property] - public void ShouldReturnValueInRange(int seed, int min, int max) - { - if (max < min) - (min, max) = (max, min); - - ResultOfCallingWith(seed, min, max) - .Should().BeInRange(min, max); - } - - [Property] - public void ShouldThrowIfMaximumLessThanMinimum(int seed, int min, int max) - { - if (max == min) - max--; - if (max > min) - (min, max) = (max, min); - - CallingWith(seed, min, max) - .Should().Throw(); - } - - [Property] - public void ShouldReturnMinimumIfMinimumEqualsMaximum(int seed, int minMax) - { - ResultOfCallingWith(seed, minMax, minMax) - .Should().Be(minMax); - } + public abstract class TheIntMethod : BaseTests.RandomMethodWithoutParameters + { + [Property] + public void ShouldReturnDifferentValues(int seed) + { + Seed(seed); + var first = CallMethod(); + + sequence(sequenceCount, CallMethod) + .Should().Contain(i => i != first); + } + } + + public abstract class TheIntWithMaxMethod : BaseTests.RandomMethodWithOneParameter + { + [Property] + public void ShouldReturnValueInRange(int seed, PositiveInt max) + { + ResultOfCallingWith(seed, max.Get) + .Should().BeInRange(0, max.Get); + } + + [Property] + public void ShouldThrowForNegativeMaximum(int seed, NegativeInt max) + { + CallingWith(seed, max.Get).Should().Throw(); + } + + [Property] + public void ShouldReturnZeroForZeroMaximum(int seed) + { + ResultOfCallingWith(seed, 0).Should().Be(0); + } + } + + public abstract class TheIntWithMinAndMaxMethod : BaseTests.RandomMethodWithTwoParameters + { + [Property] + public void ShouldReturnValueInRange(int seed, int min, int max) + { + if (max < min) + (min, max) = (max, min); + + ResultOfCallingWith(seed, min, max) + .Should().BeInRange(min, max); } + + [Property] + public void ShouldThrowIfMaximumLessThanMinimum(int seed, int min, int max) + { + if (max == min) + max--; + if (max > min) + (min, max) = (max, min); + + CallingWith(seed, min, max) + .Should().Throw(); + } + + [Property] + public void ShouldReturnMinimumIfMinimumEqualsMaximum(int seed, int minMax) + { + ResultOfCallingWith(seed, minMax, minMax) + .Should().Be(minMax); + } + } - public abstract class TheLongMethod : BaseTests.RandomMethodWithoutParameters - { - [Property] - public void ShouldReturnDifferentValues(int seed) - { - Seed(seed); - var first = CallMethod(); - - sequence(sequenceCount, CallMethod) - .Should().Contain(i => i != first); - } - } - - public abstract class TheLongWithMaxMethod : BaseTests.RandomMethodWithOneParameter - { - [Property] - public void ShouldReturnValueInRange(int seed, long max) - { - max = Math.Max(1, max); - ResultOfCallingWith(seed, max) - .Should().BeInRange(0, max); - } - - [Property] - public void ShouldThrowForNegativeMaximum(int seed, long max) - { - max = Math.Min(-1, max); - CallingWith(seed, max).Should().Throw(); - } - - [Property] - public void ShouldReturnZeroForZeroMaximum(int seed) - { - ResultOfCallingWith(seed, 0).Should().Be(0); - } - } - - public abstract class TheLongWithMinAndMaxMethod : BaseTests.RandomMethodWithTwoParameters - { - [Property] - public void ShouldReturnValueInRange(int seed, long min, long max) - { - if (max < min) - (min, max) = (max, min); - - ResultOfCallingWith(seed, min, max) - .Should().BeInRange(min, max); - } - - [Property] - public void ShouldThrowIfMaximumLessThanMinimum(int seed, long min, long max) - { - if (max == min) - max--; - if (max > min) - (min, max) = (max, min); - - CallingWith(seed, min, max) - .Should().Throw(); - } - - [Property] - public void ShouldReturnMinimumIfMinimumEqualsMaximum(int seed, long minMax) - { - ResultOfCallingWith(seed, minMax, minMax) - .Should().Be(minMax); - } + public abstract class TheLongMethod : BaseTests.RandomMethodWithoutParameters + { + [Property] + public void ShouldReturnDifferentValues(int seed) + { + Seed(seed); + var first = CallMethod(); + + sequence(sequenceCount, CallMethod) + .Should().Contain(i => i != first); } + } + + public abstract class TheLongWithMaxMethod : BaseTests.RandomMethodWithOneParameter + { + [Property] + public void ShouldReturnValueInRange(int seed, long max) + { + max = Math.Max(1, max); + ResultOfCallingWith(seed, max) + .Should().BeInRange(0, max); + } + + [Property] + public void ShouldThrowForNegativeMaximum(int seed, long max) + { + max = Math.Min(-1, max); + CallingWith(seed, max).Should().Throw(); + } + + [Property] + public void ShouldReturnZeroForZeroMaximum(int seed) + { + ResultOfCallingWith(seed, 0).Should().Be(0); + } + } + + public abstract class TheLongWithMinAndMaxMethod : BaseTests.RandomMethodWithTwoParameters + { + [Property] + public void ShouldReturnValueInRange(int seed, long min, long max) + { + if (max < min) + (min, max) = (max, min); + + ResultOfCallingWith(seed, min, max) + .Should().BeInRange(min, max); + } + + [Property] + public void ShouldThrowIfMaximumLessThanMinimum(int seed, long min, long max) + { + if (max == min) + max--; + if (max > min) + (min, max) = (max, min); + + CallingWith(seed, min, max) + .Should().Throw(); + } + + [Property] + public void ShouldReturnMinimumIfMinimumEqualsMaximum(int seed, long minMax) + { + ResultOfCallingWith(seed, minMax, minMax) + .Should().Be(minMax); + } + } - public abstract class TheFloatMethod : BaseTests.RandomMethodWithoutParameters + public abstract class TheFloatMethod : BaseTests.RandomMethodWithoutParameters + { + [Property] + public void ShouldReturnDifferentValues(int seed) { - [Property] - public void ShouldReturnDifferentValues(int seed) - { - Seed(seed); - var first = CallMethod(); + Seed(seed); + var first = CallMethod(); - sequence(sequenceCount, CallMethod) - .Should().Contain(i => i != first); - } + sequence(sequenceCount, CallMethod) + .Should().Contain(i => i != first); + } - [Property] - public void ShouldReturnValuesInUnitRange(int seed) - { - ResultOfCallingWith(seed).Should().BeInRange(0, 1); - } + [Property] + public void ShouldReturnValuesInUnitRange(int seed) + { + ResultOfCallingWith(seed).Should().BeInRange(0, 1); } + } - public abstract class TheFloatWithOneBoundMethod : BaseTests.RandomMethodWithOneParameter - { - [Property] - public void ShouldReturnValueInRange(int seed, NormalFloat bound) - { - var b = (float) bound.Get; - var (minimumValue, maximumValue) = b > 0 - ? (0f, b) - : (b, 0f); + public abstract class TheFloatWithOneBoundMethod : BaseTests.RandomMethodWithOneParameter + { + [Property] + public void ShouldReturnValueInRange(int seed, NormalFloat bound) + { + var b = (float) bound.Get; + var (minimumValue, maximumValue) = b > 0 + ? (0f, b) + : (b, 0f); - ResultOfCallingWith(seed, b) - .Should().BeInRange(minimumValue, maximumValue); - } - - [Property] - public void ShouldReturnZeroForZeroBound(int seed) - { - ResultOfCallingWith(seed, 0).Should().Be(0); - } - } - - public abstract class TheFloatWithTwoBoundsMethod : BaseTests.RandomMethodWithTwoParameters - { - [Property] - public void ShouldReturnValueInRange(int seed, NormalFloat bound1, NormalFloat bound2) - { - var (b1, b2) = ((float)bound1.Get, (float)bound2.Get); - var (minimumValue, maximumValue) = b1 < b2 - ? (b1, b2) - : (b2, b1); - - ResultOfCallingWith(seed, b1, b2) - .Should().BeInRange(minimumValue, maximumValue); - } - - [Property] - public void ShouldReturnBoundIfBoundsAreEqual(int seed, NormalFloat bound) - { - var b = (float) bound.Get; - ResultOfCallingWith(seed, b, b).Should().Be(b); - } - } - public abstract class TheDoubleMethod : BaseTests.RandomMethodWithoutParameters - { - [Property] - public void ShouldReturnDifferentValues(int seed) - { - Seed(seed); - var first = CallMethod(); - - sequence(sequenceCount, CallMethod) - .Should().Contain(i => i != first); - } + ResultOfCallingWith(seed, b) + .Should().BeInRange(minimumValue, maximumValue); + } + + [Property] + public void ShouldReturnZeroForZeroBound(int seed) + { + ResultOfCallingWith(seed, 0).Should().Be(0); + } + } + + public abstract class TheFloatWithTwoBoundsMethod : BaseTests.RandomMethodWithTwoParameters + { + [Property] + public void ShouldReturnValueInRange(int seed, NormalFloat bound1, NormalFloat bound2) + { + var (b1, b2) = ((float)bound1.Get, (float)bound2.Get); + var (minimumValue, maximumValue) = b1 < b2 + ? (b1, b2) + : (b2, b1); + + ResultOfCallingWith(seed, b1, b2) + .Should().BeInRange(minimumValue, maximumValue); + } + + [Property] + public void ShouldReturnBoundIfBoundsAreEqual(int seed, NormalFloat bound) + { + var b = (float) bound.Get; + ResultOfCallingWith(seed, b, b).Should().Be(b); + } + } + public abstract class TheDoubleMethod : BaseTests.RandomMethodWithoutParameters + { + [Property] + public void ShouldReturnDifferentValues(int seed) + { + Seed(seed); + var first = CallMethod(); + + sequence(sequenceCount, CallMethod) + .Should().Contain(i => i != first); + } - [Property] - public void ShouldReturnValuesInUnitRange(int seed) - { - ResultOfCallingWith(seed).Should().BeInRange(0, 1); - } + [Property] + public void ShouldReturnValuesInUnitRange(int seed) + { + ResultOfCallingWith(seed).Should().BeInRange(0, 1); } + } - public abstract class TheDoubleWithOneBoundMethod : BaseTests.RandomMethodWithOneParameter - { - [Property] - public void ShouldReturnValueInRange(int seed, NormalFloat bound) - { - var b = bound.Get; - var (minimumValue, maximumValue) = b > 0 - ? (0d, b) - : (b, 0d); + public abstract class TheDoubleWithOneBoundMethod : BaseTests.RandomMethodWithOneParameter + { + [Property] + public void ShouldReturnValueInRange(int seed, NormalFloat bound) + { + var b = bound.Get; + var (minimumValue, maximumValue) = b > 0 + ? (0d, b) + : (b, 0d); - ResultOfCallingWith(seed, b) - .Should().BeInRange(minimumValue, maximumValue); - } - - [Property] - public void ShouldReturnZeroForZeroBound(int seed) - { - ResultOfCallingWith(seed, 0).Should().Be(0); - } - } - - public abstract class TheDoubleWithTwoBoundsMethod : BaseTests.RandomMethodWithTwoParameters - { - [Property] - public void ShouldReturnValueInRange(int seed, NormalFloat bound1, NormalFloat bound2) - { - var (b1, b2) = (bound1.Get, bound2.Get); - var (minimumValue, maximumValue) = b1 < b2 - ? (b1, b2) - : (b2, b1); - - ResultOfCallingWith(seed, b1, b2) - .Should().BeInRange(minimumValue, maximumValue); - } - - [Property] - public void ShouldReturnBoundIfBoundsAreEqual(int seed, NormalFloat bound) - { - var b = bound.Get; - ResultOfCallingWith(seed, b, b).Should().Be(b); - } - } - - public abstract class TheSignMethod : BaseTests.RandomMethodWithoutParameters - { - [Property] - public void ReturnsPlusOrMinusOne(int seed) - { - ResultOfCallingWith(seed).Should().BeOneOf(-1, 1); - } - - [Property] - public void ReturnsBothValidValues(int seed) - { - Seed(seed); - sequence(sequenceCount, CallMethod) - .Should().Contain(new []{-1, 1}); - } - } - - public abstract class TheBoolMethod : BaseTests.RandomMethodWithoutParameters - { - [Property] - public void ReturnsBothPossibleValues(int seed) - { - Seed(seed); - sequence(sequenceCount, CallMethod) - .Should().Contain(new []{true, false}); - } + ResultOfCallingWith(seed, b) + .Should().BeInRange(minimumValue, maximumValue); + } + + [Property] + public void ShouldReturnZeroForZeroBound(int seed) + { + ResultOfCallingWith(seed, 0).Should().Be(0); + } + } + + public abstract class TheDoubleWithTwoBoundsMethod : BaseTests.RandomMethodWithTwoParameters + { + [Property] + public void ShouldReturnValueInRange(int seed, NormalFloat bound1, NormalFloat bound2) + { + var (b1, b2) = (bound1.Get, bound2.Get); + var (minimumValue, maximumValue) = b1 < b2 + ? (b1, b2) + : (b2, b1); + + ResultOfCallingWith(seed, b1, b2) + .Should().BeInRange(minimumValue, maximumValue); + } + + [Property] + public void ShouldReturnBoundIfBoundsAreEqual(int seed, NormalFloat bound) + { + var b = bound.Get; + ResultOfCallingWith(seed, b, b).Should().Be(b); + } + } + + public abstract class TheSignMethod : BaseTests.RandomMethodWithoutParameters + { + [Property] + public void ReturnsPlusOrMinusOne(int seed) + { + ResultOfCallingWith(seed).Should().BeOneOf(-1, 1); + } + + [Property] + public void ReturnsBothValidValues(int seed) + { + Seed(seed); + sequence(sequenceCount, CallMethod) + .Should().Contain(new []{-1, 1}); + } + } + + public abstract class TheBoolMethod : BaseTests.RandomMethodWithoutParameters + { + [Property] + public void ReturnsBothPossibleValues(int seed) + { + Seed(seed); + sequence(sequenceCount, CallMethod) + .Should().Contain(new []{true, false}); } + } - public abstract class TheBoolWithParameterMethod : BaseTests.RandomMethodWithOneParameter + public abstract class TheBoolWithParameterMethod : BaseTests.RandomMethodWithOneParameter + { + [Property] + public void ReturnsOnlyFalseForZeroParameter(int seed) { - [Property] - public void ReturnsOnlyFalseForZeroParameter(int seed) - { - ResultOfCallingWith(seed, 0).Should().Be(false); - } + ResultOfCallingWith(seed, 0).Should().Be(false); + } - [Property] - public void ReturnsOnlyTrueForOneParameter(int seed) - { - ResultOfCallingWith(seed, 1).Should().Be(true); - } + [Property] + public void ReturnsOnlyTrueForOneParameter(int seed) + { + ResultOfCallingWith(seed, 1).Should().Be(true); + } - [Property] - public void ReturnsOnlyFalseForNegativeParameter(int seed, NegativeInt negativeInt) - { - ResultOfCallingWith(seed, negativeInt.Get).Should().Be(false); - } + [Property] + public void ReturnsOnlyFalseForNegativeParameter(int seed, NegativeInt negativeInt) + { + ResultOfCallingWith(seed, negativeInt.Get).Should().Be(false); + } - [Property] - public void ReturnsOnlyTrueForParameterLargerThanOne(int seed, PositiveInt positiveInt) - { - ResultOfCallingWith(seed, positiveInt.Get).Should().Be(true); - } + [Property] + public void ReturnsOnlyTrueForParameterLargerThanOne(int seed, PositiveInt positiveInt) + { + ResultOfCallingWith(seed, positiveInt.Get).Should().Be(true); + } - [Property] - public void ReturnsBothPossibleValues(int seed, byte parameter) - { - // ensure 0< CallMethod(p)) - .Should().Contain(new []{true, false}); - } - } - - public abstract class TheDiscretiseMethod : BaseTests.RandomMethodWithOneParameter - { - [Property] - public void ReturnsTheSameValueForIntegerParameters(int seed, int parameter) - { - ResultOfCallingWith(seed, parameter).Should().Be(parameter); - } - - [Property] - public void ReturnsEitherFloorOrCeilOfParameter(int seed, NormalFloat parameter) - { - var f = (float)parameter.Get; - ResultOfCallingWith(seed, f) - .Should().BeOneOf((int)Math.Floor(f), (int)Math.Ceiling(f)); - } - } - - public abstract class TheNormalFloatMethod : BaseTests.RandomMethodWithoutParameters - { - [Property] - public void ReturnsDifferentValues(int seed) - { - Seed(seed); - var first = CallMethod(); + [Property] + public void ReturnsBothPossibleValues(int seed, byte parameter) + { + // ensure 0< CallMethod(p)) + .Should().Contain(new []{true, false}); + } + } + + public abstract class TheDiscretiseMethod : BaseTests.RandomMethodWithOneParameter + { + [Property] + public void ReturnsTheSameValueForIntegerParameters(int seed, int parameter) + { + ResultOfCallingWith(seed, parameter).Should().Be(parameter); + } + + [Property] + public void ReturnsEitherFloorOrCeilOfParameter(int seed, NormalFloat parameter) + { + var f = (float)parameter.Get; + ResultOfCallingWith(seed, f) + .Should().BeOneOf((int)Math.Floor(f), (int)Math.Ceiling(f)); + } + } + + public abstract class TheNormalFloatMethod : BaseTests.RandomMethodWithoutParameters + { + [Property] + public void ReturnsDifferentValues(int seed) + { + Seed(seed); + var first = CallMethod(); - sequence(sequenceCount, CallMethod) - .Should().Contain(f => f != first); - } + sequence(sequenceCount, CallMethod) + .Should().Contain(f => f != first); } + } - public abstract class TheNormalFloatWithParametersMethod : BaseTests.RandomMethodWithTwoParameters - { - [Property] - public void ReturnsValuesOtherThanMeanForNonZeroDeviation(int seed, int mean, short deviation) - { - var m = (float) mean; - var d = (float) deviation + deviation > 0 ? diviationOffset : -diviationOffset; - Seed(seed); - sequence(sequenceCount, () => CallMethod(m, d)) - .Should().Contain(f => f != m); - } - - [Property] - public void ReturnsMeanForZeroDeviation(int seed, float mean) - { - ResultOfCallingWith(seed, mean, 0).Should().Be(mean); - } + public abstract class TheNormalFloatWithParametersMethod : BaseTests.RandomMethodWithTwoParameters + { + [Property] + public void ReturnsValuesOtherThanMeanForNonZeroDeviation(int seed, int mean, short deviation) + { + var m = (float) mean; + var d = (float) deviation + deviation > 0 ? diviationOffset : -diviationOffset; + Seed(seed); + sequence(sequenceCount, () => CallMethod(m, d)) + .Should().Contain(f => f != m); + } + + [Property] + public void ReturnsMeanForZeroDeviation(int seed, float mean) + { + ResultOfCallingWith(seed, mean, 0).Should().Be(mean); } + } - public abstract class TheNormalDoubleMethod : BaseTests.RandomMethodWithoutParameters + public abstract class TheNormalDoubleMethod : BaseTests.RandomMethodWithoutParameters + { + [Property] + public void ReturnsDifferentValues(int seed) { - [Property] - public void ReturnsDifferentValues(int seed) - { - Seed(seed); - var first = CallMethod(); + Seed(seed); + var first = CallMethod(); - sequence(sequenceCount, CallMethod) - .Should().Contain(d => d != first); - } + sequence(sequenceCount, CallMethod) + .Should().Contain(d => d != first); } + } - public abstract class TheNormalDoubleWithParametersMethod : BaseTests.RandomMethodWithTwoParameters - { - [Property] - public void ReturnsValuesOtherThanMeanForNonZeroDeviation(int seed, int mean, short deviation) - { - var m = (double) mean; - var d = (double) deviation + deviation > 0 ? diviationOffset : -diviationOffset; - Seed(seed); - sequence(sequenceCount, () => CallMethod(m, d)) - .Should().Contain(f => f != m); - } - - [Property] - public void ReturnsMeanForZeroDeviation(int seed, double mean) - { - ResultOfCallingWith(seed, mean, 0).Should().Be(mean); - } + public abstract class TheNormalDoubleWithParametersMethod : BaseTests.RandomMethodWithTwoParameters + { + [Property] + public void ReturnsValuesOtherThanMeanForNonZeroDeviation(int seed, int mean, short deviation) + { + var m = (double) mean; + var d = (double) deviation + deviation > 0 ? diviationOffset : -diviationOffset; + Seed(seed); + sequence(sequenceCount, () => CallMethod(m, d)) + .Should().Contain(f => f != m); + } + + [Property] + public void ReturnsMeanForZeroDeviation(int seed, double mean) + { + ResultOfCallingWith(seed, mean, 0).Should().Be(mean); } } } diff --git a/Bearded.Utilities.Tests/Core/Random/StaticRandomTests.cs b/Bearded.Utilities.Tests/Core/Random/StaticRandomTests.cs index 0ba43159..4b71c90a 100644 --- a/Bearded.Utilities.Tests/Core/Random/StaticRandomTests.cs +++ b/Bearded.Utilities.Tests/Core/Random/StaticRandomTests.cs @@ -1,133 +1,132 @@ using static Bearded.Utilities.StaticRandom; -namespace Bearded.Utilities.Tests.Random +namespace Bearded.Utilities.Tests.Random; + +public class StaticRandomTests { - public class StaticRandomTests - { - public class TheSeedWithMethod : SharedTests.TheSeedMethod - { - protected override void Setup() - => Setup(SeedWith, Int); - } - - public class TheIntMethod : SharedTests.TheIntMethod - { - protected override void Setup() - => Setup(SeedWith, Int); - } - - public class TheIntWithMaxMethod : SharedTests.TheIntWithMaxMethod - { - protected override void Setup() - => Setup(SeedWith, Int); - } - - public class TheIntWithMinAndMaxMethod : SharedTests.TheIntWithMinAndMaxMethod - { - protected override void Setup() - => Setup(SeedWith, Int); - } - - public class TheLongMethod : SharedTests.TheLongMethod - { - protected override void Setup() - => Setup(SeedWith, Long); - } - - public class TheLongWithMaxMethod : SharedTests.TheLongWithMaxMethod - { - protected override void Setup() - => Setup(SeedWith, Long); - } - - public class TheLongWithMinAndMaxMethod : SharedTests.TheLongWithMinAndMaxMethod - { - protected override void Setup() - => Setup(SeedWith, Long); - } - - public class TheFloatMethod : SharedTests.TheFloatMethod - { - protected override void Setup() - => Setup(SeedWith, Float); - } - - public class TheFloatWithOneBoundMethod : SharedTests.TheFloatWithOneBoundMethod - { - protected override void Setup() - => Setup(SeedWith, Float); - } - - public class TheFloatWithTwoBoundsMethod : SharedTests.TheFloatWithTwoBoundsMethod - { - protected override void Setup() - => Setup(SeedWith, Float); - } - - public class TheDoubleMethod : SharedTests.TheDoubleMethod - { - protected override void Setup() - => Setup(SeedWith, Double); - } - - public class TheDoubleWithOneBoundMethod : SharedTests.TheDoubleWithOneBoundMethod - { - protected override void Setup() - => Setup(SeedWith, Double); - } - - public class TheDoubleWithTwoBoundsMethod : SharedTests.TheDoubleWithTwoBoundsMethod - { - protected override void Setup() - => Setup(SeedWith, Double); - } - - public class TheSignMethod : SharedTests.TheSignMethod - { - protected override void Setup() - => Setup(SeedWith, Sign); - } - - public class TheBoolMethod : SharedTests.TheBoolMethod - { - protected override void Setup() - => Setup(SeedWith, Bool); - } - - public class TheBoolWithParameterMethod : SharedTests.TheBoolWithParameterMethod - { - protected override void Setup() - => Setup(SeedWith, Bool); - } - - public class TheDiscretiseMethod : SharedTests.TheDiscretiseMethod - { - protected override void Setup() - => Setup(SeedWith, Discretise); - } - - public class TheNormalFloatMethod : SharedTests.TheNormalFloatMethod - { - protected override void Setup() - => Setup(SeedWith, NormalFloat); - } - - public class TheNormalFloatWithParametersMethod : SharedTests.TheNormalFloatWithParametersMethod - { - protected override void Setup() - => Setup(SeedWith, NormalFloat); - } - - public class TheNormalDoubleMethod : SharedTests.TheNormalDoubleMethod - { - protected override void Setup() - => Setup(SeedWith, NormalDouble); - } - - public class TheNormalDoubleWithParametersMethod : SharedTests.TheNormalDoubleWithParametersMethod - { - protected override void Setup() - => Setup(SeedWith, NormalDouble); - } + public class TheSeedWithMethod : SharedTests.TheSeedMethod + { + protected override void Setup() + => Setup(SeedWith, Int); + } + + public class TheIntMethod : SharedTests.TheIntMethod + { + protected override void Setup() + => Setup(SeedWith, Int); + } + + public class TheIntWithMaxMethod : SharedTests.TheIntWithMaxMethod + { + protected override void Setup() + => Setup(SeedWith, Int); + } + + public class TheIntWithMinAndMaxMethod : SharedTests.TheIntWithMinAndMaxMethod + { + protected override void Setup() + => Setup(SeedWith, Int); + } + + public class TheLongMethod : SharedTests.TheLongMethod + { + protected override void Setup() + => Setup(SeedWith, Long); + } + + public class TheLongWithMaxMethod : SharedTests.TheLongWithMaxMethod + { + protected override void Setup() + => Setup(SeedWith, Long); + } + + public class TheLongWithMinAndMaxMethod : SharedTests.TheLongWithMinAndMaxMethod + { + protected override void Setup() + => Setup(SeedWith, Long); + } + + public class TheFloatMethod : SharedTests.TheFloatMethod + { + protected override void Setup() + => Setup(SeedWith, Float); + } + + public class TheFloatWithOneBoundMethod : SharedTests.TheFloatWithOneBoundMethod + { + protected override void Setup() + => Setup(SeedWith, Float); + } + + public class TheFloatWithTwoBoundsMethod : SharedTests.TheFloatWithTwoBoundsMethod + { + protected override void Setup() + => Setup(SeedWith, Float); + } + + public class TheDoubleMethod : SharedTests.TheDoubleMethod + { + protected override void Setup() + => Setup(SeedWith, Double); + } + + public class TheDoubleWithOneBoundMethod : SharedTests.TheDoubleWithOneBoundMethod + { + protected override void Setup() + => Setup(SeedWith, Double); + } + + public class TheDoubleWithTwoBoundsMethod : SharedTests.TheDoubleWithTwoBoundsMethod + { + protected override void Setup() + => Setup(SeedWith, Double); + } + + public class TheSignMethod : SharedTests.TheSignMethod + { + protected override void Setup() + => Setup(SeedWith, Sign); + } + + public class TheBoolMethod : SharedTests.TheBoolMethod + { + protected override void Setup() + => Setup(SeedWith, Bool); + } + + public class TheBoolWithParameterMethod : SharedTests.TheBoolWithParameterMethod + { + protected override void Setup() + => Setup(SeedWith, Bool); + } + + public class TheDiscretiseMethod : SharedTests.TheDiscretiseMethod + { + protected override void Setup() + => Setup(SeedWith, Discretise); + } + + public class TheNormalFloatMethod : SharedTests.TheNormalFloatMethod + { + protected override void Setup() + => Setup(SeedWith, NormalFloat); + } + + public class TheNormalFloatWithParametersMethod : SharedTests.TheNormalFloatWithParametersMethod + { + protected override void Setup() + => Setup(SeedWith, NormalFloat); + } + + public class TheNormalDoubleMethod : SharedTests.TheNormalDoubleMethod + { + protected override void Setup() + => Setup(SeedWith, NormalDouble); + } + + public class TheNormalDoubleWithParametersMethod : SharedTests.TheNormalDoubleWithParametersMethod + { + protected override void Setup() + => Setup(SeedWith, NormalDouble); } } diff --git a/Bearded.Utilities.Tests/Core/ResettableLazyTests.cs b/Bearded.Utilities.Tests/Core/ResettableLazyTests.cs index 35b34f68..de2b5f6f 100644 --- a/Bearded.Utilities.Tests/Core/ResettableLazyTests.cs +++ b/Bearded.Utilities.Tests/Core/ResettableLazyTests.cs @@ -1,68 +1,67 @@ using FluentAssertions; using Xunit; -namespace Bearded.Utilities.Tests +namespace Bearded.Utilities.Tests; + +public sealed class ResettableLazyTests { - public sealed class ResettableLazyTests + [Fact] + public void ValueReturnsReferenceTypeFromFunc() { - [Fact] - public void ValueReturnsReferenceTypeFromFunc() - { - var obj = new object(); - var lazy = ResettableLazy.From(() => obj); + var obj = new object(); + var lazy = ResettableLazy.From(() => obj); - lazy.Value.Should().Be(obj); - } + lazy.Value.Should().Be(obj); + } - [Fact] - public void ValueReturnsNullFromFunc() - { - var lazy = ResettableLazy.From(() => (object?) null); + [Fact] + public void ValueReturnsNullFromFunc() + { + var lazy = ResettableLazy.From(() => (object?) null); - lazy.Value.Should().Be(null); - } + lazy.Value.Should().Be(null); + } - [Fact] - public void ValueReturnsValueTypeFromFunc() - { - const int num = 5; - var lazy = ResettableLazy.From(() => num); + [Fact] + public void ValueReturnsValueTypeFromFunc() + { + const int num = 5; + var lazy = ResettableLazy.From(() => num); - lazy.Value.Should().Be(num); - } + lazy.Value.Should().Be(num); + } - [Fact] - public void ValueReturnsSameInstanceEveryTime() - { - var lazy = ResettableLazy.From(() => new object()); + [Fact] + public void ValueReturnsSameInstanceEveryTime() + { + var lazy = ResettableLazy.From(() => new object()); - var value1 = lazy.Value; - var value2 = lazy.Value; + var value1 = lazy.Value; + var value2 = lazy.Value; - value1.Should().BeSameAs(value2); - } + value1.Should().BeSameAs(value2); + } - [Fact] - public void FuncShouldNotBeCalledIfValueIsNotCalled() + [Fact] + public void FuncShouldNotBeCalledIfValueIsNotCalled() + { + var funcWasCalled = false; + ResettableLazy.From(() => { - var funcWasCalled = false; - ResettableLazy.From(() => - { - funcWasCalled = true; - return new object(); - }); + funcWasCalled = true; + return new object(); + }); - funcWasCalled.Should().BeFalse(); - } + funcWasCalled.Should().BeFalse(); + } - [Fact] - public void ValueReturnsNewInstanceAfterResetting() - { - var lazy = ResettableLazy.From(() => new object()); - var valueBeforeResetting = lazy.Value; - lazy.Reset(); + [Fact] + public void ValueReturnsNewInstanceAfterResetting() + { + var lazy = ResettableLazy.From(() => new object()); + var valueBeforeResetting = lazy.Value; + lazy.Reset(); - lazy.Value.Should().NotBeSameAs(valueBeforeResetting); - } + lazy.Value.Should().NotBeSameAs(valueBeforeResetting); } } diff --git a/Bearded.Utilities.Tests/Core/interpolation/BiLinearInterpolationTests.cs b/Bearded.Utilities.Tests/Core/interpolation/BiLinearInterpolationTests.cs index d1705682..f58dc998 100644 --- a/Bearded.Utilities.Tests/Core/interpolation/BiLinearInterpolationTests.cs +++ b/Bearded.Utilities.Tests/Core/interpolation/BiLinearInterpolationTests.cs @@ -1,7 +1,6 @@ -namespace Bearded.Utilities.Tests +namespace Bearded.Utilities.Tests; + +public sealed class BiLinearInterpolationTests : InterpolationMethod2dTests { - public sealed class BiLinearInterpolationTests : InterpolationMethod2dTests - { - protected override IInterpolationMethod2d Interpolation => Interpolation2d.BiLinear; - } + protected override IInterpolationMethod2d Interpolation => Interpolation2d.BiLinear; } diff --git a/Bearded.Utilities.Tests/Core/interpolation/InterpolationMethod1dTests.cs b/Bearded.Utilities.Tests/Core/interpolation/InterpolationMethod1dTests.cs index d2659b93..44ebc8bc 100644 --- a/Bearded.Utilities.Tests/Core/interpolation/InterpolationMethod1dTests.cs +++ b/Bearded.Utilities.Tests/Core/interpolation/InterpolationMethod1dTests.cs @@ -3,32 +3,31 @@ using FluentAssertions; using FsCheck.Xunit; -namespace Bearded.Utilities.Tests +namespace Bearded.Utilities.Tests; + +public abstract class InterpolationMethod1dTests { - public abstract class InterpolationMethod1dTests - { - private const double epsilon = 1e-6; + private const double epsilon = 1e-6; - protected abstract IInterpolationMethod1d Interpolation { get; } + protected abstract IInterpolationMethod1d Interpolation { get; } - [Property(Arbitrary = new[] {typeof(DoubleGenerators.NonInfiniteNonNaN)})] - public void ReturnsFromAtStart(double from, double to) - { - Interpolation.Interpolate(from, to, 0).Should().BeApproximately(from, epsilon); - } + [Property(Arbitrary = new[] {typeof(DoubleGenerators.NonInfiniteNonNaN)})] + public void ReturnsFromAtStart(double from, double to) + { + Interpolation.Interpolate(from, to, 0).Should().BeApproximately(from, epsilon); + } - [Property(Arbitrary = new[] {typeof(DoubleGenerators.NonInfiniteNonNaN)})] - public void ReturnsToAtEnd(double from, double to) - { - from = 1; - to = 0; - Interpolation.Interpolate(from, to, 1).Should().BeApproximately(to, epsilon); - } + [Property(Arbitrary = new[] {typeof(DoubleGenerators.NonInfiniteNonNaN)})] + public void ReturnsToAtEnd(double from, double to) + { + from = 1; + to = 0; + Interpolation.Interpolate(from, to, 1).Should().BeApproximately(to, epsilon); + } - [Property(Arbitrary = new[] {typeof(DoubleGenerators.UnitIntervalBoundsInclusive)})] - public void ReturnsValuesBetweenFromAndTo(double from, double to, double t) - { - Interpolation.Interpolate(from, to, t).Should().BeInRange(Math.Min(from, to), Math.Max(from, to)); - } + [Property(Arbitrary = new[] {typeof(DoubleGenerators.UnitIntervalBoundsInclusive)})] + public void ReturnsValuesBetweenFromAndTo(double from, double to, double t) + { + Interpolation.Interpolate(from, to, t).Should().BeInRange(Math.Min(from, to), Math.Max(from, to)); } } diff --git a/Bearded.Utilities.Tests/Core/interpolation/InterpolationMethod2dTests.cs b/Bearded.Utilities.Tests/Core/interpolation/InterpolationMethod2dTests.cs index f4108dac..7fe7b05e 100644 --- a/Bearded.Utilities.Tests/Core/interpolation/InterpolationMethod2dTests.cs +++ b/Bearded.Utilities.Tests/Core/interpolation/InterpolationMethod2dTests.cs @@ -3,49 +3,48 @@ using FluentAssertions; using FsCheck.Xunit; -namespace Bearded.Utilities.Tests +namespace Bearded.Utilities.Tests; + +public abstract class InterpolationMethod2dTests { - public abstract class InterpolationMethod2dTests + private const double epsilon = 1e-6; + + protected abstract IInterpolationMethod2d Interpolation { get; } + + [Property(Arbitrary = new[] {typeof(DoubleGenerators.NonInfiniteNonNaN)})] + public void ReturnsValue00At00(double value00, double value10, double value01, double value11) + { + Interpolation.Interpolate(value00, value10, value01, value11, 0, 0) + .Should().BeApproximately(value00, epsilon); + } + + [Property(Arbitrary = new[] {typeof(DoubleGenerators.NonInfiniteNonNaN)})] + public void ReturnsValue10At10(double value00, double value10, double value01, double value11) + { + Interpolation.Interpolate(value00, value10, value01, value11, 1, 0) + .Should().BeApproximately(value10, epsilon); + } + + [Property(Arbitrary = new[] {typeof(DoubleGenerators.NonInfiniteNonNaN)})] + public void ReturnsValue01At01(double value00, double value10, double value01, double value11) + { + Interpolation.Interpolate(value00, value10, value01, value11, 0, 1) + .Should().BeApproximately(value01, epsilon); + } + + [Property(Arbitrary = new[] {typeof(DoubleGenerators.NonInfiniteNonNaN)})] + public void ReturnsValue11At11(double value00, double value10, double value01, double value11) { - private const double epsilon = 1e-6; - - protected abstract IInterpolationMethod2d Interpolation { get; } - - [Property(Arbitrary = new[] {typeof(DoubleGenerators.NonInfiniteNonNaN)})] - public void ReturnsValue00At00(double value00, double value10, double value01, double value11) - { - Interpolation.Interpolate(value00, value10, value01, value11, 0, 0) - .Should().BeApproximately(value00, epsilon); - } - - [Property(Arbitrary = new[] {typeof(DoubleGenerators.NonInfiniteNonNaN)})] - public void ReturnsValue10At10(double value00, double value10, double value01, double value11) - { - Interpolation.Interpolate(value00, value10, value01, value11, 1, 0) - .Should().BeApproximately(value10, epsilon); - } - - [Property(Arbitrary = new[] {typeof(DoubleGenerators.NonInfiniteNonNaN)})] - public void ReturnsValue01At01(double value00, double value10, double value01, double value11) - { - Interpolation.Interpolate(value00, value10, value01, value11, 0, 1) - .Should().BeApproximately(value01, epsilon); - } - - [Property(Arbitrary = new[] {typeof(DoubleGenerators.NonInfiniteNonNaN)})] - public void ReturnsValue11At11(double value00, double value10, double value01, double value11) - { - Interpolation.Interpolate(value00, value10, value01, value11, 1, 1) - .Should().BeApproximately(value11, epsilon); - } - - [Property(Arbitrary = new[] {typeof(DoubleGenerators.UnitIntervalBoundsInclusive)})] - public void ReturnsValuesBetweenValues(double value00, double value10, double value01, double value11, double u, double v) - { - var min = new[] { value00, value10, value01, value11 }.Min(); - var max = new[] { value00, value10, value01, value11 }.Max(); - - Interpolation.Interpolate(value00, value10, value01, value11, u, v).Should().BeInRange(min, max); - } + Interpolation.Interpolate(value00, value10, value01, value11, 1, 1) + .Should().BeApproximately(value11, epsilon); + } + + [Property(Arbitrary = new[] {typeof(DoubleGenerators.UnitIntervalBoundsInclusive)})] + public void ReturnsValuesBetweenValues(double value00, double value10, double value01, double value11, double u, double v) + { + var min = new[] { value00, value10, value01, value11 }.Min(); + var max = new[] { value00, value10, value01, value11 }.Max(); + + Interpolation.Interpolate(value00, value10, value01, value11, u, v).Should().BeInRange(min, max); } } diff --git a/Bearded.Utilities.Tests/Core/interpolation/LinearInterpolationTests.cs b/Bearded.Utilities.Tests/Core/interpolation/LinearInterpolationTests.cs index be851569..f5e96038 100644 --- a/Bearded.Utilities.Tests/Core/interpolation/LinearInterpolationTests.cs +++ b/Bearded.Utilities.Tests/Core/interpolation/LinearInterpolationTests.cs @@ -1,7 +1,6 @@ -namespace Bearded.Utilities.Tests +namespace Bearded.Utilities.Tests; + +public sealed class LinearInterpolationTests : InterpolationMethod1dTests { - public sealed class LinearInterpolationTests : InterpolationMethod1dTests - { - protected override IInterpolationMethod1d Interpolation => Interpolation1d.Linear; - } + protected override IInterpolationMethod1d Interpolation => Interpolation1d.Linear; } diff --git a/Bearded.Utilities.Tests/Core/interpolation/Nearest1InterpolationTests.cs b/Bearded.Utilities.Tests/Core/interpolation/Nearest1InterpolationTests.cs index 56351ec2..d1fb0668 100644 --- a/Bearded.Utilities.Tests/Core/interpolation/Nearest1InterpolationTests.cs +++ b/Bearded.Utilities.Tests/Core/interpolation/Nearest1InterpolationTests.cs @@ -1,7 +1,6 @@ -namespace Bearded.Utilities.Tests +namespace Bearded.Utilities.Tests; + +public sealed class Nearest1InterpolationTests : InterpolationMethod1dTests { - public sealed class Nearest1InterpolationTests : InterpolationMethod1dTests - { - protected override IInterpolationMethod1d Interpolation => Interpolation1d.Nearest; - } + protected override IInterpolationMethod1d Interpolation => Interpolation1d.Nearest; } diff --git a/Bearded.Utilities.Tests/Core/interpolation/Nearest2InterpolationTests.cs b/Bearded.Utilities.Tests/Core/interpolation/Nearest2InterpolationTests.cs index 0351fe67..d92e3e1d 100644 --- a/Bearded.Utilities.Tests/Core/interpolation/Nearest2InterpolationTests.cs +++ b/Bearded.Utilities.Tests/Core/interpolation/Nearest2InterpolationTests.cs @@ -1,7 +1,6 @@ -namespace Bearded.Utilities.Tests +namespace Bearded.Utilities.Tests; + +public sealed class Nearest2InterpolationTests : InterpolationMethod2dTests { - public sealed class Nearest2InterpolationTests : InterpolationMethod2dTests - { - protected override IInterpolationMethod2d Interpolation => Interpolation2d.Nearest; - } + protected override IInterpolationMethod2d Interpolation => Interpolation2d.Nearest; } diff --git a/Bearded.Utilities.Tests/Core/interpolation/SmoothStepInterpolationTests.cs b/Bearded.Utilities.Tests/Core/interpolation/SmoothStepInterpolationTests.cs index 41cc4923..3e9ab854 100644 --- a/Bearded.Utilities.Tests/Core/interpolation/SmoothStepInterpolationTests.cs +++ b/Bearded.Utilities.Tests/Core/interpolation/SmoothStepInterpolationTests.cs @@ -1,7 +1,6 @@ -namespace Bearded.Utilities.Tests +namespace Bearded.Utilities.Tests; + +public sealed class SmoothStepInterpolationTests : InterpolationMethod1dTests { - public sealed class SmoothStepInterpolationTests : InterpolationMethod1dTests - { - protected override IInterpolationMethod1d Interpolation => Interpolation1d.SmoothStep; - } + protected override IInterpolationMethod1d Interpolation => Interpolation1d.SmoothStep; } diff --git a/Bearded.Utilities.Tests/Generators/DirectionGenerators.cs b/Bearded.Utilities.Tests/Generators/DirectionGenerators.cs index 354cb247..c990a729 100644 --- a/Bearded.Utilities.Tests/Generators/DirectionGenerators.cs +++ b/Bearded.Utilities.Tests/Generators/DirectionGenerators.cs @@ -1,14 +1,13 @@ using Bearded.Utilities.Geometry; using FsCheck; -namespace Bearded.Utilities.Tests.Generators +namespace Bearded.Utilities.Tests.Generators; + +static class DirectionGenerators { - static class DirectionGenerators + public sealed class All { - public sealed class All - { - public static Arbitrary Directions() => - Arb.Generate().Select(i => Direction2.FromDegrees(i * 360f / (1L << 32))).ToArbitrary(); - } + public static Arbitrary Directions() => + Arb.Generate().Select(i => Direction2.FromDegrees(i * 360f / (1L << 32))).ToArbitrary(); } } diff --git a/Bearded.Utilities.Tests/Generators/DoubleGenerators.cs b/Bearded.Utilities.Tests/Generators/DoubleGenerators.cs index ed7ec597..58b5d56d 100644 --- a/Bearded.Utilities.Tests/Generators/DoubleGenerators.cs +++ b/Bearded.Utilities.Tests/Generators/DoubleGenerators.cs @@ -1,39 +1,38 @@ using FsCheck; -namespace Bearded.Utilities.Tests.Generators +namespace Bearded.Utilities.Tests.Generators; + +static class DoubleGenerators { - static class DoubleGenerators + public static class NonInfiniteNonNaN { - public static class NonInfiniteNonNaN - { - public static Arbitrary Doubles() - => Arb.Default.Float().Filter(d => !double.IsInfinity(d) && !double.IsNaN(d)); - } + public static Arbitrary Doubles() + => Arb.Default.Float().Filter(d => !double.IsInfinity(d) && !double.IsNaN(d)); + } - public static class UnitIntervalBoundsInclusive + public static class UnitIntervalBoundsInclusive + { + public static Arbitrary Doubles() { - public static Arbitrary Doubles() - { - var gen = Gen.Frequency( - // Make "0" and "1" return more often to ensure it's likely to be covered by each test run. - new WeightAndValue>(1, Gen.OneOf(Gen.Constant(0.0), Gen.Constant(1.0))), - new WeightAndValue>(9, - Gen.Choose(0, int.MaxValue).Select(i => (double) i / int.MaxValue))); - return gen.ToArbitrary(); - } + var gen = Gen.Frequency( + // Make "0" and "1" return more often to ensure it's likely to be covered by each test run. + new WeightAndValue>(1, Gen.OneOf(Gen.Constant(0.0), Gen.Constant(1.0))), + new WeightAndValue>(9, + Gen.Choose(0, int.MaxValue).Select(i => (double) i / int.MaxValue))); + return gen.ToArbitrary(); } + } - public static class UnitIntervalUpperBoundExclusive + public static class UnitIntervalUpperBoundExclusive + { + public static Arbitrary Doubles() { - public static Arbitrary Doubles() - { - var gen = Gen.Frequency( - // Make "0" return more often to ensure it's likely to be covered by each test run. - new WeightAndValue>(1, Gen.Constant(0.0)), - new WeightAndValue>(9, - Gen.Choose(0, int.MaxValue - 1).Select(i => (double) i / int.MaxValue))); - return gen.ToArbitrary(); - } + var gen = Gen.Frequency( + // Make "0" return more often to ensure it's likely to be covered by each test run. + new WeightAndValue>(1, Gen.Constant(0.0)), + new WeightAndValue>(9, + Gen.Choose(0, int.MaxValue - 1).Select(i => (double) i / int.MaxValue))); + return gen.ToArbitrary(); } } } diff --git a/Bearded.Utilities.Tests/Generators/FloatGenerators.cs b/Bearded.Utilities.Tests/Generators/FloatGenerators.cs index fea5337a..806a24b3 100644 --- a/Bearded.Utilities.Tests/Generators/FloatGenerators.cs +++ b/Bearded.Utilities.Tests/Generators/FloatGenerators.cs @@ -1,46 +1,45 @@ using System; using FsCheck; -namespace Bearded.Utilities.Tests.Generators +namespace Bearded.Utilities.Tests.Generators; + +static class FloatGenerators { - static class FloatGenerators + public static class NonInfiniteNonNaN { - public static class NonInfiniteNonNaN - { - public static Arbitrary Floats() - => Arb.Default.Float32().Filter(f => !float.IsInfinity(f) && !float.IsNaN(f)); - } + public static Arbitrary Floats() + => Arb.Default.Float32().Filter(f => !float.IsInfinity(f) && !float.IsNaN(f)); + } - public static class PositiveNonInfiniteNonNaN - { - public static Arbitrary Floats() - => NonInfiniteNonNaN.Floats().Generator - .Where(f => f != 0) - .Select(Math.Abs) - .ToArbitrary(); - } + public static class PositiveNonInfiniteNonNaN + { + public static Arbitrary Floats() + => NonInfiniteNonNaN.Floats().Generator + .Where(f => f != 0) + .Select(Math.Abs) + .ToArbitrary(); + } - /// - /// Generates a positive (specifically, non-zero) circle radius with a reasonable maximum radius to avoid - /// positive infinities when doing geometric arithmetic with them. - /// - public static class PositiveCircleRadius - { - public static Arbitrary Floats - => Arb.Default.UInt32().Generator - .Select(i => ((float) i + 1) / 1000) - .ToArbitrary(); - } + /// + /// Generates a positive (specifically, non-zero) circle radius with a reasonable maximum radius to avoid + /// positive infinities when doing geometric arithmetic with them. + /// + public static class PositiveCircleRadius + { + public static Arbitrary Floats + => Arb.Default.UInt32().Generator + .Select(i => ((float) i + 1) / 1000) + .ToArbitrary(); + } - /// - /// Generates floats with reasonable maximum magnitude to avoid positive infinities when doing arithmetic. - /// - public sealed class ForArithmetic - { - public static Arbitrary Floats() - => Arb.Default.Int32().Generator - .Select(i => 0.001f * i) - .ToArbitrary(); - } + /// + /// Generates floats with reasonable maximum magnitude to avoid positive infinities when doing arithmetic. + /// + public sealed class ForArithmetic + { + public static Arbitrary Floats() + => Arb.Default.Int32().Generator + .Select(i => 0.001f * i) + .ToArbitrary(); } } diff --git a/Bearded.Utilities.Tests/Generators/IntGenerators.cs b/Bearded.Utilities.Tests/Generators/IntGenerators.cs index 732d947d..5601a2bb 100644 --- a/Bearded.Utilities.Tests/Generators/IntGenerators.cs +++ b/Bearded.Utilities.Tests/Generators/IntGenerators.cs @@ -1,15 +1,14 @@ using FsCheck; -namespace Bearded.Utilities.Tests.Generators +namespace Bearded.Utilities.Tests.Generators; + +internal static class IntGenerators { - internal static class IntGenerators + public static class PositiveInt { - public static class PositiveInt - { - public static Arbitrary Integers() - => Arb.Default.Int32().Generator - .Where(i => i > 0) - .ToArbitrary(); - } + public static Arbitrary Integers() + => Arb.Default.Int32().Generator + .Where(i => i > 0) + .ToArbitrary(); } -} \ No newline at end of file +} diff --git a/Bearded.Utilities.Tests/Generators/Vector2Generators.cs b/Bearded.Utilities.Tests/Generators/Vector2Generators.cs index 13c4f103..bd6f6b3c 100644 --- a/Bearded.Utilities.Tests/Generators/Vector2Generators.cs +++ b/Bearded.Utilities.Tests/Generators/Vector2Generators.cs @@ -1,15 +1,14 @@ using FsCheck; using OpenTK.Mathematics; -namespace Bearded.Utilities.Tests.Generators +namespace Bearded.Utilities.Tests.Generators; + +sealed class Vector2Generators { - sealed class Vector2Generators + public sealed class All { - public sealed class All - { - public static Arbitrary Vectors() => - Arb.From(FloatGenerators.ForArithmetic.Floats().Generator.Two() - .Select(tuple => new Vector2(tuple.Item1, tuple.Item2))); - } + public static Arbitrary Vectors() => + Arb.From(FloatGenerators.ForArithmetic.Floats().Generator.Two() + .Select(tuple => new Vector2(tuple.Item1, tuple.Item2))); } } diff --git a/Bearded.Utilities.Tests/Generators/Vector3Generators.cs b/Bearded.Utilities.Tests/Generators/Vector3Generators.cs index df39a44a..9857bc58 100644 --- a/Bearded.Utilities.Tests/Generators/Vector3Generators.cs +++ b/Bearded.Utilities.Tests/Generators/Vector3Generators.cs @@ -1,15 +1,14 @@ using FsCheck; using OpenTK.Mathematics; -namespace Bearded.Utilities.Tests.Generators +namespace Bearded.Utilities.Tests.Generators; + +sealed class Vector3Generators { - sealed class Vector3Generators + public sealed class All { - public sealed class All - { - public static Arbitrary Vectors() => - Arb.From(FloatGenerators.ForArithmetic.Floats().Generator.Three() - .Select(tuple => new Vector3(tuple.Item1, tuple.Item2, tuple.Item3))); - } + public static Arbitrary Vectors() => + Arb.From(FloatGenerators.ForArithmetic.Floats().Generator.Three() + .Select(tuple => new Vector3(tuple.Item1, tuple.Item2, tuple.Item3))); } } diff --git a/Bearded.Utilities.Tests/Geometry/Bivector2Tests.cs b/Bearded.Utilities.Tests/Geometry/Bivector2Tests.cs index 634e3471..e55fdd8a 100644 --- a/Bearded.Utilities.Tests/Geometry/Bivector2Tests.cs +++ b/Bearded.Utilities.Tests/Geometry/Bivector2Tests.cs @@ -8,113 +8,112 @@ using OpenTK.Mathematics; using Xunit; -namespace Bearded.Utilities.Tests.Geometry +namespace Bearded.Utilities.Tests.Geometry; + +public sealed class Bivector2Tests { - public sealed class Bivector2Tests + private const float epsilon = 1E-6f; + + public Bivector2Tests() + { + Arb.Register(); + Arb.Register(); + } + + [Fact] + public void WedgeOfUnitVectorsIsUnitBivector() + { + var wedge = Bivector2.Wedge(Vector2.UnitX, Vector2.UnitY); + + wedge.Should().BeApproximately(Bivector2.Unit, epsilon); + } + + [Property] + public void WedgeOfVectorWithSelfIsZero(Vector2 vector) + { + var wedge = Bivector2.Wedge(vector, vector); + + wedge.Should().BeApproximately(Bivector2.Zero, epsilon); + } + + [Property] + public void WedgeOfCollinearVectorsIsZero(Vector2 vector, float scalar) + { + var wedge = Bivector2.Wedge(vector, vector * scalar); + + wedge.Should().BeApproximately(Bivector2.Zero, epsilon); + } + + [Property] + public void WedgeOfNonCollinearVectorsIsNonZero(Vector2 left, Vector2 right) + { + if (areCollinear(left, right)) return; + + var wedge = Bivector2.Wedge(left, right); + + wedge.Should().NotBe(Bivector2.Zero); + } + + [Property] + public void WedgeIsAntiSymmetric(Vector2 left, Vector2 right) + { + var wedge1 = Bivector2.Wedge(left, right); + var wedge2 = Bivector2.Wedge(right, left); + + wedge1.Should().Be(-wedge2); + } + + [Property] + public void AddingBivectorsAddsMagnitudes(float f1, float f2) + { + var bivector1 = new Bivector2(f1); + var bivector2 = new Bivector2(f2); + var sum = bivector1 + bivector2; + + sum.Should().BeApproximately(new Bivector2(f1 + f2), epsilon); + } + + [Property] + public void SubtractingBivectorsSubtractsMagnitudes(float f1, float f2) + { + var bivector1 = new Bivector2(f1); + var bivector2 = new Bivector2(f2); + var difference = bivector1 - bivector2; + + difference.Should().BeApproximately(new Bivector2(f1 - f2), epsilon); + } + + [Property] + public void BivectorsWithSameMagnitudeAreEqual(float magnitude) + { + var bivector1 = new Bivector2(magnitude); + var bivector2 = new Bivector2(magnitude); + + bivector1.Equals(bivector2).Should().BeTrue(); + (bivector1 == bivector2).Should().BeTrue(); + (bivector1 != bivector2).Should().BeFalse(); + bivector1.GetHashCode().Should().Be(bivector2.GetHashCode()); + } + + [Property] + public void BivectorsWithDifferentMagnitudeAreNotEqual(float f1, float f2) + { + // ReSharper disable once CompareOfFloatsByEqualityOperator + if (f1 == f2) f2++; + + var bivector1 = new Bivector2(f1); + var bivector2 = new Bivector2(f2); + + bivector1.Equals(bivector2).Should().BeFalse(); + (bivector1 == bivector2).Should().BeFalse(); + (bivector1 != bivector2).Should().BeTrue(); + } + + private static bool areCollinear(Vector2 v1, Vector2 v2) { - private const float epsilon = 1E-6f; - - public Bivector2Tests() - { - Arb.Register(); - Arb.Register(); - } - - [Fact] - public void WedgeOfUnitVectorsIsUnitBivector() - { - var wedge = Bivector2.Wedge(Vector2.UnitX, Vector2.UnitY); - - wedge.Should().BeApproximately(Bivector2.Unit, epsilon); - } - - [Property] - public void WedgeOfVectorWithSelfIsZero(Vector2 vector) - { - var wedge = Bivector2.Wedge(vector, vector); - - wedge.Should().BeApproximately(Bivector2.Zero, epsilon); - } - - [Property] - public void WedgeOfCollinearVectorsIsZero(Vector2 vector, float scalar) - { - var wedge = Bivector2.Wedge(vector, vector * scalar); - - wedge.Should().BeApproximately(Bivector2.Zero, epsilon); - } - - [Property] - public void WedgeOfNonCollinearVectorsIsNonZero(Vector2 left, Vector2 right) - { - if (areCollinear(left, right)) return; - - var wedge = Bivector2.Wedge(left, right); - - wedge.Should().NotBe(Bivector2.Zero); - } - - [Property] - public void WedgeIsAntiSymmetric(Vector2 left, Vector2 right) - { - var wedge1 = Bivector2.Wedge(left, right); - var wedge2 = Bivector2.Wedge(right, left); - - wedge1.Should().Be(-wedge2); - } - - [Property] - public void AddingBivectorsAddsMagnitudes(float f1, float f2) - { - var bivector1 = new Bivector2(f1); - var bivector2 = new Bivector2(f2); - var sum = bivector1 + bivector2; - - sum.Should().BeApproximately(new Bivector2(f1 + f2), epsilon); - } - - [Property] - public void SubtractingBivectorsSubtractsMagnitudes(float f1, float f2) - { - var bivector1 = new Bivector2(f1); - var bivector2 = new Bivector2(f2); - var difference = bivector1 - bivector2; - - difference.Should().BeApproximately(new Bivector2(f1 - f2), epsilon); - } - - [Property] - public void BivectorsWithSameMagnitudeAreEqual(float magnitude) - { - var bivector1 = new Bivector2(magnitude); - var bivector2 = new Bivector2(magnitude); - - bivector1.Equals(bivector2).Should().BeTrue(); - (bivector1 == bivector2).Should().BeTrue(); - (bivector1 != bivector2).Should().BeFalse(); - bivector1.GetHashCode().Should().Be(bivector2.GetHashCode()); - } - - [Property] - public void BivectorsWithDifferentMagnitudeAreNotEqual(float f1, float f2) - { - // ReSharper disable once CompareOfFloatsByEqualityOperator - if (f1 == f2) f2++; - - var bivector1 = new Bivector2(f1); - var bivector2 = new Bivector2(f2); - - bivector1.Equals(bivector2).Should().BeFalse(); - (bivector1 == bivector2).Should().BeFalse(); - (bivector1 != bivector2).Should().BeTrue(); - } - - private static bool areCollinear(Vector2 v1, Vector2 v2) - { - if (v1.Y == 0 && v2.Y == 0) return true; - if (v1 == Vector2.Zero || v2 == Vector2.Zero) return true; - if (v1.Y == 0 || v2.Y == 0) return false; - return Math.Abs(v1.X / v1.Y - v2.X / v2.Y) < epsilon; - } + if (v1.Y == 0 && v2.Y == 0) return true; + if (v1 == Vector2.Zero || v2 == Vector2.Zero) return true; + if (v1.Y == 0 || v2.Y == 0) return false; + return Math.Abs(v1.X / v1.Y - v2.X / v2.Y) < epsilon; } } diff --git a/Bearded.Utilities.Tests/Geometry/Bivector3Tests.cs b/Bearded.Utilities.Tests/Geometry/Bivector3Tests.cs index 57dcc6e2..c942aa78 100644 --- a/Bearded.Utilities.Tests/Geometry/Bivector3Tests.cs +++ b/Bearded.Utilities.Tests/Geometry/Bivector3Tests.cs @@ -7,142 +7,141 @@ using OpenTK.Mathematics; using Xunit; -namespace Bearded.Utilities.Tests.Geometry +namespace Bearded.Utilities.Tests.Geometry; + +public sealed class Bivector3Tests { - public sealed class Bivector3Tests + private const float epsilon = 1E-6f; + + public Bivector3Tests() + { + Arb.Register(); + Arb.Register(); + } + + [Fact] + public void WedgeOfUnitVectorsIsUnitBivector() + { + Bivector3.Wedge(Vector3.UnitX, Vector3.UnitY).Should().BeApproximately(Bivector3.UnitXy, epsilon); + Bivector3.Wedge(Vector3.UnitY, Vector3.UnitZ).Should().BeApproximately(Bivector3.UnitYz, epsilon); + Bivector3.Wedge(Vector3.UnitX, Vector3.UnitZ).Should().BeApproximately(Bivector3.UnitXz, epsilon); + } + + [Property] + public void WedgeOfVectorWithSelfIsZero(Vector3 vector) + { + var wedge = Bivector3.Wedge(vector, vector); + + wedge.Should().BeApproximately(Bivector3.Zero, epsilon); + } + + [Property] + public void WedgeOfCollinearVectorsIsZero(Vector3 vector, float scalar) + { + var wedge = Bivector3.Wedge(vector, vector * scalar); + + wedge.Should().BeApproximately(Bivector3.Zero, epsilon); + } + + [Property] + public void WedgeOfNonCollinearVectorsIsNonZero(Vector3 left, Vector3 right) + { + if (areCollinear(left, right)) return; + + var wedge = Bivector3.Wedge(left, right); + + wedge.Should().NotBe(Bivector3.Zero); + } + + [Property] + public void WedgeIsAntiSymmetric(Vector3 left, Vector3 right) + { + var wedge1 = Bivector3.Wedge(left, right); + var wedge2 = Bivector3.Wedge(right, left); + + wedge1.Should().Be(-wedge2); + } + + [Property] + public void AddingBivectorsAddsComponents(float xy1, float xy2, float yz1, float yz2, float xz1, float xz2) + { + var bivector1 = new Bivector3(xy1, yz1, xz1); + var bivector2 = new Bivector3(xy2, yz2, xz2); + var sum = bivector1 + bivector2; + + sum.Should().BeApproximately(new Bivector3(xy1 + xy2, yz1 + yz2, xz1 + xz2), epsilon); + } + + [Property] + public void SubtractingBivectorsSubtractsComponents( + float xy1, float xy2, float yz1, float yz2, float xz1, float xz2) + { + var bivector1 = new Bivector3(xy1, yz1, xz1); + var bivector2 = new Bivector3(xy2, yz2, xz2); + var sum = bivector1 - bivector2; + + sum.Should().BeApproximately(new Bivector3(xy1 - xy2, yz1 - yz2, xz1 - xz2), epsilon); + } + + [Property] + public void BivectorsWithSameComponentsAreEqual(float xy, float yz, float xz) + { + var bivector1 = new Bivector3(xy, yz, xz); + var bivector2 = new Bivector3(xy, yz, xz); + + bivector1.Equals(bivector2).Should().BeTrue(); + (bivector1 == bivector2).Should().BeTrue(); + (bivector1 != bivector2).Should().BeFalse(); + bivector1.GetHashCode().Should().Be(bivector2.GetHashCode()); + } + + [Property] + public void BivectorsWithDifferentXyComponentAreNotEqual( + float xy1, float xy2, float yz, float xz) + { + // ReSharper disable once CompareOfFloatsByEqualityOperator + if (xy1 == xy2) xy2++; + + var bivector1 = new Bivector3(xy1, yz, xz); + var bivector2 = new Bivector3(xy2, yz, xz); + + bivector1.Equals(bivector2).Should().BeFalse(); + (bivector1 == bivector2).Should().BeFalse(); + (bivector1 != bivector2).Should().BeTrue(); + } + + [Property] + public void BivectorsWithDifferentYzComponentAreNotEqual( + float xy, float yz1, float yz2, float xz) + { + // ReSharper disable once CompareOfFloatsByEqualityOperator + if (yz1 == yz2) yz2++; + + var bivector1 = new Bivector3(xy, yz1, xz); + var bivector2 = new Bivector3(xy, yz2, xz); + + bivector1.Equals(bivector2).Should().BeFalse(); + (bivector1 == bivector2).Should().BeFalse(); + (bivector1 != bivector2).Should().BeTrue(); + } + + [Property] + public void BivectorsWithDifferentXzComponentAreNotEqual( + float xy, float yz, float xz1, float xz2) + { + // ReSharper disable once CompareOfFloatsByEqualityOperator + if (xz1 == xz2) xz2++; + + var bivector1 = new Bivector3(xy, yz, xz1); + var bivector2 = new Bivector3(xy, yz, xz2); + + bivector1.Equals(bivector2).Should().BeFalse(); + (bivector1 == bivector2).Should().BeFalse(); + (bivector1 != bivector2).Should().BeTrue(); + } + + private static bool areCollinear(Vector3 v1, Vector3 v2) { - private const float epsilon = 1E-6f; - - public Bivector3Tests() - { - Arb.Register(); - Arb.Register(); - } - - [Fact] - public void WedgeOfUnitVectorsIsUnitBivector() - { - Bivector3.Wedge(Vector3.UnitX, Vector3.UnitY).Should().BeApproximately(Bivector3.UnitXy, epsilon); - Bivector3.Wedge(Vector3.UnitY, Vector3.UnitZ).Should().BeApproximately(Bivector3.UnitYz, epsilon); - Bivector3.Wedge(Vector3.UnitX, Vector3.UnitZ).Should().BeApproximately(Bivector3.UnitXz, epsilon); - } - - [Property] - public void WedgeOfVectorWithSelfIsZero(Vector3 vector) - { - var wedge = Bivector3.Wedge(vector, vector); - - wedge.Should().BeApproximately(Bivector3.Zero, epsilon); - } - - [Property] - public void WedgeOfCollinearVectorsIsZero(Vector3 vector, float scalar) - { - var wedge = Bivector3.Wedge(vector, vector * scalar); - - wedge.Should().BeApproximately(Bivector3.Zero, epsilon); - } - - [Property] - public void WedgeOfNonCollinearVectorsIsNonZero(Vector3 left, Vector3 right) - { - if (areCollinear(left, right)) return; - - var wedge = Bivector3.Wedge(left, right); - - wedge.Should().NotBe(Bivector3.Zero); - } - - [Property] - public void WedgeIsAntiSymmetric(Vector3 left, Vector3 right) - { - var wedge1 = Bivector3.Wedge(left, right); - var wedge2 = Bivector3.Wedge(right, left); - - wedge1.Should().Be(-wedge2); - } - - [Property] - public void AddingBivectorsAddsComponents(float xy1, float xy2, float yz1, float yz2, float xz1, float xz2) - { - var bivector1 = new Bivector3(xy1, yz1, xz1); - var bivector2 = new Bivector3(xy2, yz2, xz2); - var sum = bivector1 + bivector2; - - sum.Should().BeApproximately(new Bivector3(xy1 + xy2, yz1 + yz2, xz1 + xz2), epsilon); - } - - [Property] - public void SubtractingBivectorsSubtractsComponents( - float xy1, float xy2, float yz1, float yz2, float xz1, float xz2) - { - var bivector1 = new Bivector3(xy1, yz1, xz1); - var bivector2 = new Bivector3(xy2, yz2, xz2); - var sum = bivector1 - bivector2; - - sum.Should().BeApproximately(new Bivector3(xy1 - xy2, yz1 - yz2, xz1 - xz2), epsilon); - } - - [Property] - public void BivectorsWithSameComponentsAreEqual(float xy, float yz, float xz) - { - var bivector1 = new Bivector3(xy, yz, xz); - var bivector2 = new Bivector3(xy, yz, xz); - - bivector1.Equals(bivector2).Should().BeTrue(); - (bivector1 == bivector2).Should().BeTrue(); - (bivector1 != bivector2).Should().BeFalse(); - bivector1.GetHashCode().Should().Be(bivector2.GetHashCode()); - } - - [Property] - public void BivectorsWithDifferentXyComponentAreNotEqual( - float xy1, float xy2, float yz, float xz) - { - // ReSharper disable once CompareOfFloatsByEqualityOperator - if (xy1 == xy2) xy2++; - - var bivector1 = new Bivector3(xy1, yz, xz); - var bivector2 = new Bivector3(xy2, yz, xz); - - bivector1.Equals(bivector2).Should().BeFalse(); - (bivector1 == bivector2).Should().BeFalse(); - (bivector1 != bivector2).Should().BeTrue(); - } - - [Property] - public void BivectorsWithDifferentYzComponentAreNotEqual( - float xy, float yz1, float yz2, float xz) - { - // ReSharper disable once CompareOfFloatsByEqualityOperator - if (yz1 == yz2) yz2++; - - var bivector1 = new Bivector3(xy, yz1, xz); - var bivector2 = new Bivector3(xy, yz2, xz); - - bivector1.Equals(bivector2).Should().BeFalse(); - (bivector1 == bivector2).Should().BeFalse(); - (bivector1 != bivector2).Should().BeTrue(); - } - - [Property] - public void BivectorsWithDifferentXzComponentAreNotEqual( - float xy, float yz, float xz1, float xz2) - { - // ReSharper disable once CompareOfFloatsByEqualityOperator - if (xz1 == xz2) xz2++; - - var bivector1 = new Bivector3(xy, yz, xz1); - var bivector2 = new Bivector3(xy, yz, xz2); - - bivector1.Equals(bivector2).Should().BeFalse(); - (bivector1 == bivector2).Should().BeFalse(); - (bivector1 != bivector2).Should().BeTrue(); - } - - private static bool areCollinear(Vector3 v1, Vector3 v2) - { - return Vector3.Cross(v1, v2).LengthSquared < epsilon; - } + return Vector3.Cross(v1, v2).LengthSquared < epsilon; } } diff --git a/Bearded.Utilities.Tests/Geometry/CircularArc2Tests.cs b/Bearded.Utilities.Tests/Geometry/CircularArc2Tests.cs index 97d204cb..deeb89d7 100644 --- a/Bearded.Utilities.Tests/Geometry/CircularArc2Tests.cs +++ b/Bearded.Utilities.Tests/Geometry/CircularArc2Tests.cs @@ -7,333 +7,332 @@ using FsCheck.Xunit; using OpenTK.Mathematics; -namespace Bearded.Utilities.Tests.Geometry +namespace Bearded.Utilities.Tests.Geometry; + +public sealed class CircularArc2Tests { - public sealed class CircularArc2Tests + private const float epsilon = 0.001f; + + public CircularArc2Tests() { - private const float epsilon = 0.001f; + Arb.Register(); + } - public CircularArc2Tests() - { - Arb.Register(); - } + [Property] + public void ShortArcBetweenIdenticalDirectionsIsZeroLength(Direction2 fromTo) + { + var arc = CircularArc2.ShortArcBetweenDirections(Vector2.Zero, 1, fromTo, fromTo); - [Property] - public void ShortArcBetweenIdenticalDirectionsIsZeroLength(Direction2 fromTo) - { - var arc = CircularArc2.ShortArcBetweenDirections(Vector2.Zero, 1, fromTo, fromTo); + arc.Angle.Radians.Should().BeApproximately(0, epsilon); + } - arc.Angle.Radians.Should().BeApproximately(0, epsilon); - } + [Property] + public void ShortArcBetweenOppositeDirectionsHasMinusPiAngle(Direction2 from) + { + var arc = CircularArc2.ShortArcBetweenDirections(Vector2.Zero, 1, from, -from); - [Property] - public void ShortArcBetweenOppositeDirectionsHasMinusPiAngle(Direction2 from) - { - var arc = CircularArc2.ShortArcBetweenDirections(Vector2.Zero, 1, from, -from); + arc.Angle.Radians.Should().BeApproximately(-MathConstants.Pi, epsilon); + } - arc.Angle.Radians.Should().BeApproximately(-MathConstants.Pi, epsilon); - } + [Property] + public void ShortArcHasAngleWithMagnitudeSmallerThanPi(Direction2 from, Direction2 to) + { + if (from == to) return; - [Property] - public void ShortArcHasAngleWithMagnitudeSmallerThanPi(Direction2 from, Direction2 to) - { - if (from == to) return; + var arc = CircularArc2.ShortArcBetweenDirections(Vector2.Zero, 1, from, to); - var arc = CircularArc2.ShortArcBetweenDirections(Vector2.Zero, 1, from, to); - - arc.Angle.MagnitudeInRadians.Should().BeInRange(0, MathConstants.Pi); - } + arc.Angle.MagnitudeInRadians.Should().BeInRange(0, MathConstants.Pi); + } - [Property] - public void LongArcBetweenIdenticalDirectionsIsFullCircle(Direction2 fromTo) - { - var arc = CircularArc2.LongArcBetweenDirections(Vector2.Zero, 1, fromTo, fromTo); - - arc.Angle.Radians.Should().BeApproximately(MathConstants.TwoPi, epsilon); - } + [Property] + public void LongArcBetweenIdenticalDirectionsIsFullCircle(Direction2 fromTo) + { + var arc = CircularArc2.LongArcBetweenDirections(Vector2.Zero, 1, fromTo, fromTo); - [Property] - public void LongArcBetweenOppositeDirectionsHasPiAngle(Direction2 from) - { - var arc = CircularArc2.LongArcBetweenDirections(Vector2.Zero, 1, from, -from); - - arc.Angle.Radians.Should().BeApproximately(MathConstants.Pi, epsilon); - } + arc.Angle.Radians.Should().BeApproximately(MathConstants.TwoPi, epsilon); + } - [Property] - public void LongArcHasAngleWithMagnitudeLargerThanPi(Direction2 from, Direction2 to) - { - if (from == to) return; - - var arc = CircularArc2.LongArcBetweenDirections(Vector2.Zero, 1, from, to); - - arc.Angle.MagnitudeInRadians.Should().BeInRange(MathConstants.Pi, MathConstants.TwoPi); - } - - [Property] - public void ArcWithAngleSmallerThanMinusTwoPiIsInvalid(Direction2 from) - { - Action action = () => CircularArc2.FromStartAndAngle( - Vector2.Zero, 1, from, Angle.FromRadians(-7)); + [Property] + public void LongArcBetweenOppositeDirectionsHasPiAngle(Direction2 from) + { + var arc = CircularArc2.LongArcBetweenDirections(Vector2.Zero, 1, from, -from); - action.Should().Throw(); - } + arc.Angle.Radians.Should().BeApproximately(MathConstants.Pi, epsilon); + } - [Property] - public void ArcWithAngleMinusTwoPiIsValid(Direction2 from) - { - Action action = () => CircularArc2.FromStartAndAngle( - Vector2.Zero, 1, from, Angle.FromRadians(-MathConstants.TwoPi)); - - action.Should().NotThrow(); - } + [Property] + public void LongArcHasAngleWithMagnitudeLargerThanPi(Direction2 from, Direction2 to) + { + if (from == to) return; - [Property] - public void ArcWithAngleLessThanMinusPiIsLongArc(Direction2 from) - { - var arc = CircularArc2.FromStartAndAngle( - Vector2.Zero, 1, from, Angle.FromRadians(-4)); + var arc = CircularArc2.LongArcBetweenDirections(Vector2.Zero, 1, from, to); - arc.IsLongArc.Should().BeTrue(); - } + arc.Angle.MagnitudeInRadians.Should().BeInRange(MathConstants.Pi, MathConstants.TwoPi); + } - [Property] - public void ArcWithAngleMinusPiIsShortArc(Direction2 from) - { - var arc = CircularArc2.FromStartAndAngle( - Vector2.Zero, 1, from, Angle.FromRadians(-MathConstants.Pi)); + [Property] + public void ArcWithAngleSmallerThanMinusTwoPiIsInvalid(Direction2 from) + { + Action action = () => CircularArc2.FromStartAndAngle( + Vector2.Zero, 1, from, Angle.FromRadians(-7)); - arc.IsShortArc.Should().BeTrue(); - } + action.Should().Throw(); + } - [Property] - public void ArcWithAngleMoreThanMinusPiIsShortArc(Direction2 from) - { - var arc = CircularArc2.FromStartAndAngle( - Vector2.Zero, 1, from, Angle.FromRadians(-2)); + [Property] + public void ArcWithAngleMinusTwoPiIsValid(Direction2 from) + { + Action action = () => CircularArc2.FromStartAndAngle( + Vector2.Zero, 1, from, Angle.FromRadians(-MathConstants.TwoPi)); - arc.IsShortArc.Should().BeTrue(); - } + action.Should().NotThrow(); + } - [Property] - public void ArcWithAngleZeroIsShortArc(Direction2 from) - { - var arc = CircularArc2.FromStartAndAngle( - Vector2.Zero, 1, from, Angle.Zero); + [Property] + public void ArcWithAngleLessThanMinusPiIsLongArc(Direction2 from) + { + var arc = CircularArc2.FromStartAndAngle( + Vector2.Zero, 1, from, Angle.FromRadians(-4)); + + arc.IsLongArc.Should().BeTrue(); + } + + [Property] + public void ArcWithAngleMinusPiIsShortArc(Direction2 from) + { + var arc = CircularArc2.FromStartAndAngle( + Vector2.Zero, 1, from, Angle.FromRadians(-MathConstants.Pi)); + + arc.IsShortArc.Should().BeTrue(); + } + + [Property] + public void ArcWithAngleMoreThanMinusPiIsShortArc(Direction2 from) + { + var arc = CircularArc2.FromStartAndAngle( + Vector2.Zero, 1, from, Angle.FromRadians(-2)); - arc.IsShortArc.Should().BeTrue(); - } + arc.IsShortArc.Should().BeTrue(); + } - [Property] - public void ArcWithAngleLessThanPiIsShortArc(Direction2 from) - { - var arc = CircularArc2.FromStartAndAngle( - Vector2.Zero, 1, from, Angle.FromRadians(2)); + [Property] + public void ArcWithAngleZeroIsShortArc(Direction2 from) + { + var arc = CircularArc2.FromStartAndAngle( + Vector2.Zero, 1, from, Angle.Zero); - arc.IsShortArc.Should().BeTrue(); - } + arc.IsShortArc.Should().BeTrue(); + } - [Property] - public void ArcWithAnglePiIsLongArc(Direction2 from) - { - var arc = CircularArc2.FromStartAndAngle( - Vector2.Zero, 1, from, Angle.FromRadians(MathConstants.Pi)); + [Property] + public void ArcWithAngleLessThanPiIsShortArc(Direction2 from) + { + var arc = CircularArc2.FromStartAndAngle( + Vector2.Zero, 1, from, Angle.FromRadians(2)); - arc.IsLongArc.Should().BeTrue(); - } + arc.IsShortArc.Should().BeTrue(); + } + + [Property] + public void ArcWithAnglePiIsLongArc(Direction2 from) + { + var arc = CircularArc2.FromStartAndAngle( + Vector2.Zero, 1, from, Angle.FromRadians(MathConstants.Pi)); + + arc.IsLongArc.Should().BeTrue(); + } - [Property] - public void ArcWithAngleMoreThanPiIsLongArc(Direction2 from) - { - var arc = CircularArc2.FromStartAndAngle( - Vector2.Zero, 1, from, Angle.FromRadians(4)); + [Property] + public void ArcWithAngleMoreThanPiIsLongArc(Direction2 from) + { + var arc = CircularArc2.FromStartAndAngle( + Vector2.Zero, 1, from, Angle.FromRadians(4)); - arc.IsLongArc.Should().BeTrue(); - } + arc.IsLongArc.Should().BeTrue(); + } - [Property] - public void ArcWithAngleTwoPiIsInvalid(Direction2 from) - { - Action action = () => CircularArc2.FromStartAndAngle( - Vector2.Zero, 1, from, Angle.FromRadians(MathConstants.TwoPi)); + [Property] + public void ArcWithAngleTwoPiIsInvalid(Direction2 from) + { + Action action = () => CircularArc2.FromStartAndAngle( + Vector2.Zero, 1, from, Angle.FromRadians(MathConstants.TwoPi)); - action.Should().Throw(); - } + action.Should().Throw(); + } - [Property] - public void ArcWithAngleMoreThanPiIsInvalid(Direction2 from) - { - Action action = () => CircularArc2.FromStartAndAngle( - Vector2.Zero, 1, from, Angle.FromRadians(7)); + [Property] + public void ArcWithAngleMoreThanPiIsInvalid(Direction2 from) + { + Action action = () => CircularArc2.FromStartAndAngle( + Vector2.Zero, 1, from, Angle.FromRadians(7)); - action.Should().Throw(); - } + action.Should().Throw(); + } - [Property] - public void OppositeOfALongArcIsAShortArc(Direction2 from, Direction2 to) - { - if (from == to) return; + [Property] + public void OppositeOfALongArcIsAShortArc(Direction2 from, Direction2 to) + { + if (from == to) return; - var arc = CircularArc2.LongArcBetweenDirections(Vector2.Zero, 1, from, to); + var arc = CircularArc2.LongArcBetweenDirections(Vector2.Zero, 1, from, to); - var actual = arc.Opposite; + var actual = arc.Opposite; - var expected = CircularArc2.ShortArcBetweenDirections(Vector2.Zero, 1, from, to); - actual.Should().Be(expected); - } + var expected = CircularArc2.ShortArcBetweenDirections(Vector2.Zero, 1, from, to); + actual.Should().Be(expected); + } - [Property] - public void OppositeOfAShortArcIsALongArc(Direction2 from, Direction2 to) - { - if (from == to) return; + [Property] + public void OppositeOfAShortArcIsALongArc(Direction2 from, Direction2 to) + { + if (from == to) return; - var arc = CircularArc2.ShortArcBetweenDirections(Vector2.Zero, 1, from, to); + var arc = CircularArc2.ShortArcBetweenDirections(Vector2.Zero, 1, from, to); - var actual = arc.Opposite; + var actual = arc.Opposite; - var expected = CircularArc2.LongArcBetweenDirections(Vector2.Zero, 1, from, to); - actual.Should().Be(expected); - } + var expected = CircularArc2.LongArcBetweenDirections(Vector2.Zero, 1, from, to); + actual.Should().Be(expected); + } - [Property] - public void OppositeOfAZeroLengthArcIsAFullLengthArc(Direction2 fromTo) - { - var arc = CircularArc2.ShortArcBetweenDirections(Vector2.Zero, 1, fromTo, fromTo); + [Property] + public void OppositeOfAZeroLengthArcIsAFullLengthArc(Direction2 fromTo) + { + var arc = CircularArc2.ShortArcBetweenDirections(Vector2.Zero, 1, fromTo, fromTo); - var actual = arc.Opposite; + var actual = arc.Opposite; - var expected = CircularArc2.LongArcBetweenDirections(Vector2.Zero, 1, fromTo, fromTo); - actual.Should().Be(expected); - } + var expected = CircularArc2.LongArcBetweenDirections(Vector2.Zero, 1, fromTo, fromTo); + actual.Should().Be(expected); + } - [Property] - public void OppositeOfAFullLengthArcIsAZeroLengthArc(Direction2 fromTo) - { - var arc = CircularArc2.LongArcBetweenDirections(Vector2.Zero, 1, fromTo, fromTo); + [Property] + public void OppositeOfAFullLengthArcIsAZeroLengthArc(Direction2 fromTo) + { + var arc = CircularArc2.LongArcBetweenDirections(Vector2.Zero, 1, fromTo, fromTo); - var actual = arc.Opposite; + var actual = arc.Opposite; - var expected = CircularArc2.ShortArcBetweenDirections(Vector2.Zero, 1, fromTo, fromTo); - actual.Should().Be(expected); - } + var expected = CircularArc2.ShortArcBetweenDirections(Vector2.Zero, 1, fromTo, fromTo); + actual.Should().Be(expected); + } - [Property] - public void ReverseOfAShortArcIsTheShortArcWithDirectionsSwitched(Direction2 from, Direction2 to) - { - if (from == to) return; + [Property] + public void ReverseOfAShortArcIsTheShortArcWithDirectionsSwitched(Direction2 from, Direction2 to) + { + if (from == to) return; - var arc = CircularArc2.ShortArcBetweenDirections(Vector2.Zero, 1, from, to); + var arc = CircularArc2.ShortArcBetweenDirections(Vector2.Zero, 1, from, to); - var actual = arc.Reversed; + var actual = arc.Reversed; - var expected = CircularArc2.ShortArcBetweenDirections(Vector2.Zero, 1, to, from); - actual.Should().Be(expected); - } + var expected = CircularArc2.ShortArcBetweenDirections(Vector2.Zero, 1, to, from); + actual.Should().Be(expected); + } - [Property] - public void ReverseOfALongArcIsTheLongArcWithDirectionsSwitched(Direction2 from, Direction2 to) - { - if (from == to) return; + [Property] + public void ReverseOfALongArcIsTheLongArcWithDirectionsSwitched(Direction2 from, Direction2 to) + { + if (from == to) return; - var arc = CircularArc2.LongArcBetweenDirections(Vector2.Zero, 1, from, to); + var arc = CircularArc2.LongArcBetweenDirections(Vector2.Zero, 1, from, to); - var actual = arc.Reversed; + var actual = arc.Reversed; - var expected = CircularArc2.LongArcBetweenDirections(Vector2.Zero, 1, to, from); - actual.Should().Be(expected); - } + var expected = CircularArc2.LongArcBetweenDirections(Vector2.Zero, 1, to, from); + actual.Should().Be(expected); + } - [Property] - public void ReverseOfAZeroLengthArcIsTheOriginalArc(Direction2 fromTo) - { - var arc = CircularArc2.ShortArcBetweenDirections(Vector2.Zero, 1, fromTo, fromTo); + [Property] + public void ReverseOfAZeroLengthArcIsTheOriginalArc(Direction2 fromTo) + { + var arc = CircularArc2.ShortArcBetweenDirections(Vector2.Zero, 1, fromTo, fromTo); - var actual = arc.Reversed; + var actual = arc.Reversed; - actual.Should().Be(arc); - } + actual.Should().Be(arc); + } - [Property] - public void ReverseOfAFullCircleArcIsTheOriginalArc(Direction2 fromTo) - { - var arc = CircularArc2.LongArcBetweenDirections(Vector2.Zero, 1, fromTo, fromTo); + [Property] + public void ReverseOfAFullCircleArcIsTheOriginalArc(Direction2 fromTo) + { + var arc = CircularArc2.LongArcBetweenDirections(Vector2.Zero, 1, fromTo, fromTo); - var actual = arc.Reversed; + var actual = arc.Reversed; - actual.Should().Be(arc); - } + actual.Should().Be(arc); + } - [Property(Arbitrary = new[] { typeof(FloatGenerators.PositiveCircleRadius) })] - public void StartPointIsPointOnArc(Direction2 from, Direction2 to, float radius) - { - if (from == to) return; + [Property(Arbitrary = new[] { typeof(FloatGenerators.PositiveCircleRadius) })] + public void StartPointIsPointOnArc(Direction2 from, Direction2 to, float radius) + { + if (from == to) return; - var arc = CircularArc2.ShortArcBetweenDirections(Vector2.Zero, radius, from, to); + var arc = CircularArc2.ShortArcBetweenDirections(Vector2.Zero, radius, from, to); - arc.StartPoint.LengthSquared.Should().BeApproximately(radius * radius, epsilon); - } + arc.StartPoint.LengthSquared.Should().BeApproximately(radius * radius, epsilon); + } - [Property(Arbitrary = new[] { typeof(FloatGenerators.PositiveCircleRadius) })] - public void ArcStartingAtDirectionZeroHasStartPointOnXAxis(Direction2 to, float radius) - { - var arc = CircularArc2.ShortArcBetweenDirections(Vector2.Zero, radius, Direction2.Zero, to); + [Property(Arbitrary = new[] { typeof(FloatGenerators.PositiveCircleRadius) })] + public void ArcStartingAtDirectionZeroHasStartPointOnXAxis(Direction2 to, float radius) + { + var arc = CircularArc2.ShortArcBetweenDirections(Vector2.Zero, radius, Direction2.Zero, to); - arc.StartPoint.Should().BeApproximately(radius * Vector2.UnitX, epsilon); - } + arc.StartPoint.Should().BeApproximately(radius * Vector2.UnitX, epsilon); + } - [Property(Arbitrary = new[] { typeof(FloatGenerators.PositiveCircleRadius) })] - public void ArcStartingAtDirection90DegHasStartPointOnYAxis(Direction2 to, float radius) - { - var arc = CircularArc2.ShortArcBetweenDirections(Vector2.Zero, radius, Direction2.FromDegrees(90), to); + [Property(Arbitrary = new[] { typeof(FloatGenerators.PositiveCircleRadius) })] + public void ArcStartingAtDirection90DegHasStartPointOnYAxis(Direction2 to, float radius) + { + var arc = CircularArc2.ShortArcBetweenDirections(Vector2.Zero, radius, Direction2.FromDegrees(90), to); - arc.StartPoint.Should().BeApproximately(radius * Vector2.UnitY, epsilon); - } + arc.StartPoint.Should().BeApproximately(radius * Vector2.UnitY, epsilon); + } - [Property(Arbitrary = new[] { typeof(FloatGenerators.PositiveCircleRadius) })] - public void ArcStartingAtDirection135HasStartPointInThirdQuadrant(Direction2 to, float radius) - { - var arc = CircularArc2.ShortArcBetweenDirections( - Vector2.Zero, radius, Direction2.FromDegrees(-135), to); + [Property(Arbitrary = new[] { typeof(FloatGenerators.PositiveCircleRadius) })] + public void ArcStartingAtDirection135HasStartPointInThirdQuadrant(Direction2 to, float radius) + { + var arc = CircularArc2.ShortArcBetweenDirections( + Vector2.Zero, radius, Direction2.FromDegrees(-135), to); - arc.StartPoint.X.Should().BeNegative(); - arc.StartPoint.Y.Should().BeNegative(); - } + arc.StartPoint.X.Should().BeNegative(); + arc.StartPoint.Y.Should().BeNegative(); + } - [Property(Arbitrary = new[] { typeof(FloatGenerators.PositiveCircleRadius) })] - public void EndPointIsPointOnArc(Direction2 from, Direction2 to, float radius) - { - if (from == to) return; + [Property(Arbitrary = new[] { typeof(FloatGenerators.PositiveCircleRadius) })] + public void EndPointIsPointOnArc(Direction2 from, Direction2 to, float radius) + { + if (from == to) return; - var arc = CircularArc2.ShortArcBetweenDirections(Vector2.Zero, radius, from, to); + var arc = CircularArc2.ShortArcBetweenDirections(Vector2.Zero, radius, from, to); - arc.EndPoint.LengthSquared.Should().BeApproximately(radius * radius, epsilon); - } + arc.EndPoint.LengthSquared.Should().BeApproximately(radius * radius, epsilon); + } - [Property(Arbitrary = new[] { typeof(FloatGenerators.PositiveCircleRadius) })] - public void ArcEndingAtDirectionZeroHasEndPointOnXAxis(Direction2 from, float radius) - { - var arc = CircularArc2.ShortArcBetweenDirections(Vector2.Zero, radius, from, Direction2.Zero); + [Property(Arbitrary = new[] { typeof(FloatGenerators.PositiveCircleRadius) })] + public void ArcEndingAtDirectionZeroHasEndPointOnXAxis(Direction2 from, float radius) + { + var arc = CircularArc2.ShortArcBetweenDirections(Vector2.Zero, radius, from, Direction2.Zero); - arc.EndPoint.Should().BeApproximately(radius * Vector2.UnitX, epsilon); - } + arc.EndPoint.Should().BeApproximately(radius * Vector2.UnitX, epsilon); + } - [Property(Arbitrary = new[] { typeof(FloatGenerators.PositiveCircleRadius) })] - public void ArcEndingAtDirection90DegHasEndPointOnYAxis(Direction2 from, float radius) - { - var arc = - CircularArc2.ShortArcBetweenDirections(Vector2.Zero, radius, from, Direction2.FromDegrees(90)); + [Property(Arbitrary = new[] { typeof(FloatGenerators.PositiveCircleRadius) })] + public void ArcEndingAtDirection90DegHasEndPointOnYAxis(Direction2 from, float radius) + { + var arc = + CircularArc2.ShortArcBetweenDirections(Vector2.Zero, radius, from, Direction2.FromDegrees(90)); - arc.EndPoint.Should().BeApproximately(radius * Vector2.UnitY, epsilon); - } + arc.EndPoint.Should().BeApproximately(radius * Vector2.UnitY, epsilon); + } - [Property(Arbitrary = new[] { typeof(FloatGenerators.PositiveCircleRadius) })] - public void ArcEndingAtDirection135DegHasEndPointInThirdQuadrant(Direction2 from, float radius) - { - var arc = CircularArc2.ShortArcBetweenDirections( - Vector2.Zero, radius, from, Direction2.FromDegrees(-135)); + [Property(Arbitrary = new[] { typeof(FloatGenerators.PositiveCircleRadius) })] + public void ArcEndingAtDirection135DegHasEndPointInThirdQuadrant(Direction2 from, float radius) + { + var arc = CircularArc2.ShortArcBetweenDirections( + Vector2.Zero, radius, from, Direction2.FromDegrees(-135)); - arc.EndPoint.X.Should().BeNegative(); - arc.EndPoint.Y.Should().BeNegative(); - } + arc.EndPoint.X.Should().BeNegative(); + arc.EndPoint.Y.Should().BeNegative(); } } diff --git a/Bearded.Utilities.Tests/Graphs/DirectedAcyclicGraphTransitiveReducerTests.cs b/Bearded.Utilities.Tests/Graphs/DirectedAcyclicGraphTransitiveReducerTests.cs index affba641..4a5c2434 100644 --- a/Bearded.Utilities.Tests/Graphs/DirectedAcyclicGraphTransitiveReducerTests.cs +++ b/Bearded.Utilities.Tests/Graphs/DirectedAcyclicGraphTransitiveReducerTests.cs @@ -2,120 +2,119 @@ using FluentAssertions; using Xunit; -namespace Bearded.Utilities.Tests.Graphs +namespace Bearded.Utilities.Tests.Graphs; + +public class DirectedAcyclicGraphTransitiveReducerTests { - public class DirectedAcyclicGraphTransitiveReducerTests + [Fact] + public void GetTransitiveReduction_DoesNotRemoveElements() { - [Fact] - public void GetTransitiveReduction_DoesNotRemoveElements() - { - /* - * ----------| - * | V - * 1 -> 2 -> 3 - */ - var graph = DirectedGraphBuilder.NewBuilder() - .AddElement("one") - .AddElement("two") - .AddElement("three") - .AddArrow("one", "two") - .AddArrow("two", "three") - .AddArrow("one", "three") - .CreateAcyclicGraphUnsafe(); + /* + * ----------| + * | V + * 1 -> 2 -> 3 + */ + var graph = DirectedGraphBuilder.NewBuilder() + .AddElement("one") + .AddElement("two") + .AddElement("three") + .AddArrow("one", "two") + .AddArrow("two", "three") + .AddArrow("one", "three") + .CreateAcyclicGraphUnsafe(); - /* - * 1 -> 2 -> 3 - */ - var reducedGraph = DirectedAcyclicGraphTransitiveReducer.ReduceGraph(graph); + /* + * 1 -> 2 -> 3 + */ + var reducedGraph = DirectedAcyclicGraphTransitiveReducer.ReduceGraph(graph); - reducedGraph.Elements.Should().Contain(graph.Elements); - } + reducedGraph.Elements.Should().Contain(graph.Elements); + } - [Fact] - public void GetTransitiveReduction_RemovesTransitiveEdge() - { - /* - * ----------| - * | V - * 1 -> 2 -> 3 - */ - var graph = DirectedGraphBuilder.NewBuilder() - .AddElement("one") - .AddElement("two") - .AddElement("three") - .AddArrow("one", "two") - .AddArrow("two", "three") - .AddArrow("one", "three") - .CreateAcyclicGraphUnsafe(); + [Fact] + public void GetTransitiveReduction_RemovesTransitiveEdge() + { + /* + * ----------| + * | V + * 1 -> 2 -> 3 + */ + var graph = DirectedGraphBuilder.NewBuilder() + .AddElement("one") + .AddElement("two") + .AddElement("three") + .AddArrow("one", "two") + .AddArrow("two", "three") + .AddArrow("one", "three") + .CreateAcyclicGraphUnsafe(); - /* - * 1 -> 2 -> 3 - */ - var reducedGraph = DirectedAcyclicGraphTransitiveReducer.ReduceGraph(graph); + /* + * 1 -> 2 -> 3 + */ + var reducedGraph = DirectedAcyclicGraphTransitiveReducer.ReduceGraph(graph); - reducedGraph.GetDirectSuccessorsOf("one").Should().NotContain("three"); - reducedGraph.GetDirectPredecessorsOf("three").Should().NotContain("one"); - } + reducedGraph.GetDirectSuccessorsOf("one").Should().NotContain("three"); + reducedGraph.GetDirectPredecessorsOf("three").Should().NotContain("one"); + } - [Fact] - public void GetTransitiveReduction_KeepsRequiredEdges() - { - /* - * 1 -> 2 -> 3 - */ - var graph = DirectedGraphBuilder.NewBuilder() - .AddElement("one") - .AddElement("two") - .AddElement("three") - .AddArrow("one", "two") - .AddArrow("two", "three") - .AddArrow("one", "three") - .CreateAcyclicGraphUnsafe(); + [Fact] + public void GetTransitiveReduction_KeepsRequiredEdges() + { + /* + * 1 -> 2 -> 3 + */ + var graph = DirectedGraphBuilder.NewBuilder() + .AddElement("one") + .AddElement("two") + .AddElement("three") + .AddArrow("one", "two") + .AddArrow("two", "three") + .AddArrow("one", "three") + .CreateAcyclicGraphUnsafe(); - /* - * 1 -> 2 -> 3 - */ - var reducedGraph = DirectedAcyclicGraphTransitiveReducer.ReduceGraph(graph); + /* + * 1 -> 2 -> 3 + */ + var reducedGraph = DirectedAcyclicGraphTransitiveReducer.ReduceGraph(graph); - reducedGraph.GetDirectSuccessorsOf("one").Should().Contain("two"); - reducedGraph.GetDirectPredecessorsOf("two").Should().Contain("one"); - reducedGraph.GetDirectSuccessorsOf("two").Should().Contain("three"); - reducedGraph.GetDirectPredecessorsOf("three").Should().Contain("two"); - } + reducedGraph.GetDirectSuccessorsOf("one").Should().Contain("two"); + reducedGraph.GetDirectPredecessorsOf("two").Should().Contain("one"); + reducedGraph.GetDirectSuccessorsOf("two").Should().Contain("three"); + reducedGraph.GetDirectPredecessorsOf("three").Should().Contain("two"); + } - [Fact] - public void GetTransitiveReduction_HandlesCasesWithMultiplePaths() - { - /* - * ----------| - * | V - * 1 -> 2 -> 3 -> 4 - * | ^ - * ----------| - */ - var graph = DirectedGraphBuilder.NewBuilder() - .AddElement("one") - .AddElement("two") - .AddElement("three") - .AddElement("four") - .AddArrow("one", "two") - .AddArrow("two", "three") - .AddArrow("three", "four") - .AddArrow("one", "three") - .AddArrow("two", "four") - .CreateAcyclicGraphUnsafe(); + [Fact] + public void GetTransitiveReduction_HandlesCasesWithMultiplePaths() + { + /* + * ----------| + * | V + * 1 -> 2 -> 3 -> 4 + * | ^ + * ----------| + */ + var graph = DirectedGraphBuilder.NewBuilder() + .AddElement("one") + .AddElement("two") + .AddElement("three") + .AddElement("four") + .AddArrow("one", "two") + .AddArrow("two", "three") + .AddArrow("three", "four") + .AddArrow("one", "three") + .AddArrow("two", "four") + .CreateAcyclicGraphUnsafe(); - /* - * 1 -> 2 -> 3 -> 4 - */ - var reducedGraph = DirectedAcyclicGraphTransitiveReducer.ReduceGraph(graph); + /* + * 1 -> 2 -> 3 -> 4 + */ + var reducedGraph = DirectedAcyclicGraphTransitiveReducer.ReduceGraph(graph); - reducedGraph.GetDirectSuccessorsOf("one").Should().Contain("two"); - reducedGraph.GetDirectPredecessorsOf("two").Should().Contain("one"); - reducedGraph.GetDirectSuccessorsOf("two").Should().Contain("three"); - reducedGraph.GetDirectPredecessorsOf("three").Should().Contain("two"); - reducedGraph.GetDirectSuccessorsOf("three").Should().Contain("four"); - reducedGraph.GetDirectPredecessorsOf("four").Should().Contain("three"); - } + reducedGraph.GetDirectSuccessorsOf("one").Should().Contain("two"); + reducedGraph.GetDirectPredecessorsOf("two").Should().Contain("one"); + reducedGraph.GetDirectSuccessorsOf("two").Should().Contain("three"); + reducedGraph.GetDirectPredecessorsOf("three").Should().Contain("two"); + reducedGraph.GetDirectSuccessorsOf("three").Should().Contain("four"); + reducedGraph.GetDirectPredecessorsOf("four").Should().Contain("three"); } -} \ No newline at end of file +} diff --git a/Bearded.Utilities.Tests/Graphs/DirectedGraphBuilderTests.cs b/Bearded.Utilities.Tests/Graphs/DirectedGraphBuilderTests.cs index 533947e3..b274d08b 100644 --- a/Bearded.Utilities.Tests/Graphs/DirectedGraphBuilderTests.cs +++ b/Bearded.Utilities.Tests/Graphs/DirectedGraphBuilderTests.cs @@ -4,332 +4,331 @@ using Xunit; // ReSharper disable ConvertToLocalFunction -namespace Bearded.Utilities.Tests.Graphs +namespace Bearded.Utilities.Tests.Graphs; + +public class DirectedGraphBuilderTests { - public class DirectedGraphBuilderTests + [Fact] + public void NewBuilder_ReturnsEmptyBuilder() { - [Fact] - public void NewBuilder_ReturnsEmptyBuilder() - { - var graph = DirectedGraphBuilder.NewBuilder().CreateGraph(); - - graph.Count.Should().Be(0); - graph.Elements.Should().BeEmpty(); - } + var graph = DirectedGraphBuilder.NewBuilder().CreateGraph(); + + graph.Count.Should().Be(0); + graph.Elements.Should().BeEmpty(); + } - [Fact] - public void FromExistingGraph_CopiesElements() - { - var original = DirectedGraphBuilder.NewBuilder() - .AddElement("element 1") - .AddElement("element 2") - .CreateGraph(); - - var copyOfGraph = DirectedGraphBuilder.FromExistingGraph(original).CreateGraph(); - - copyOfGraph.Elements.Should().Contain(original.Elements); - } + [Fact] + public void FromExistingGraph_CopiesElements() + { + var original = DirectedGraphBuilder.NewBuilder() + .AddElement("element 1") + .AddElement("element 2") + .CreateGraph(); + + var copyOfGraph = DirectedGraphBuilder.FromExistingGraph(original).CreateGraph(); + + copyOfGraph.Elements.Should().Contain(original.Elements); + } - [Fact] - public void FromExistingGraph_CopiesDirectSuccessors() - { - var original = DirectedGraphBuilder.NewBuilder() - .AddElement("from") - .AddElement("to") - .AddArrow("from", "to") - .CreateGraph(); - - var copyOfGraph = DirectedGraphBuilder.FromExistingGraph(original).CreateGraph(); - - copyOfGraph.GetDirectSuccessorsOf("from").Should().Contain("to"); - } - - [Fact] - public void FromExistingGraph_CopiesDirectPredecessors() - { - var original = DirectedGraphBuilder.NewBuilder() - .AddElement("from") - .AddElement("to") - .AddArrow("from", "to") - .CreateGraph(); - - var copyOfGraph = DirectedGraphBuilder.FromExistingGraph(original).CreateGraph(); - - copyOfGraph.GetDirectPredecessorsOf("to").Should().Contain("from"); - } - - [Fact] - public void EmptyGraph_ReturnsEmptyGraph() - { - var graph = DirectedGraphBuilder.EmptyGraph(); - - graph.Count.Should().Be(0); - graph.Elements.Should().BeEmpty(); - } - - [Fact] - public void AddElement_AddsElement() - { - var graph = DirectedGraphBuilder.NewBuilder() - .AddElement("element") - .CreateGraph(); - - graph.Elements.Should().Contain("element"); - } - - [Fact] - public void AddElement_SetsCorrectCount() - { - var graph = DirectedGraphBuilder.NewBuilder() - .AddElement("element") - .CreateGraph(); - - graph.Count.Should().Be(1); - } - - [Fact] - public void AddElement_ShouldThrowOnNull() - { - var builder = DirectedGraphBuilder.NewBuilder(); - - Action addNullElement = () => builder.AddElement(null); + [Fact] + public void FromExistingGraph_CopiesDirectSuccessors() + { + var original = DirectedGraphBuilder.NewBuilder() + .AddElement("from") + .AddElement("to") + .AddArrow("from", "to") + .CreateGraph(); + + var copyOfGraph = DirectedGraphBuilder.FromExistingGraph(original).CreateGraph(); + + copyOfGraph.GetDirectSuccessorsOf("from").Should().Contain("to"); + } + + [Fact] + public void FromExistingGraph_CopiesDirectPredecessors() + { + var original = DirectedGraphBuilder.NewBuilder() + .AddElement("from") + .AddElement("to") + .AddArrow("from", "to") + .CreateGraph(); + + var copyOfGraph = DirectedGraphBuilder.FromExistingGraph(original).CreateGraph(); + + copyOfGraph.GetDirectPredecessorsOf("to").Should().Contain("from"); + } + + [Fact] + public void EmptyGraph_ReturnsEmptyGraph() + { + var graph = DirectedGraphBuilder.EmptyGraph(); + + graph.Count.Should().Be(0); + graph.Elements.Should().BeEmpty(); + } + + [Fact] + public void AddElement_AddsElement() + { + var graph = DirectedGraphBuilder.NewBuilder() + .AddElement("element") + .CreateGraph(); + + graph.Elements.Should().Contain("element"); + } + + [Fact] + public void AddElement_SetsCorrectCount() + { + var graph = DirectedGraphBuilder.NewBuilder() + .AddElement("element") + .CreateGraph(); + + graph.Count.Should().Be(1); + } + + [Fact] + public void AddElement_ShouldThrowOnNull() + { + var builder = DirectedGraphBuilder.NewBuilder(); + + Action addNullElement = () => builder.AddElement(null); - addNullElement.Should().Throw(); - } + addNullElement.Should().Throw(); + } - [Fact] - public void AddElement_ShouldThrowOnDuplicate() - { - var builder = DirectedGraphBuilder.NewBuilder().AddElement("element"); + [Fact] + public void AddElement_ShouldThrowOnDuplicate() + { + var builder = DirectedGraphBuilder.NewBuilder().AddElement("element"); - Action addDuplicateElement = () => builder.AddElement("element"); + Action addDuplicateElement = () => builder.AddElement("element"); - addDuplicateElement.Should().Throw(); - } - - [Fact] - public void AddElement_DoesNotAddDirectSuccessors() - { - var graph = DirectedGraphBuilder.NewBuilder() - .AddElement("element") - .CreateGraph(); - - graph.GetDirectSuccessorsOf("element").Should().BeEmpty(); - } - - [Fact] - public void AddElement_DoesNotAddDirectPredecessor() - { - var graph = DirectedGraphBuilder.NewBuilder() - .AddElement("element") - .CreateGraph(); - - graph.GetDirectPredecessorsOf("element").Should().BeEmpty(); - } - - [Fact] - public void AddArrow_AddsDirectSuccessor() - { - var graph = DirectedGraphBuilder.NewBuilder() - .AddElement("from") - .AddElement("to") - .AddArrow("from", "to") - .CreateGraph(); - - graph.GetDirectSuccessorsOf("from").Should().Contain("to"); - } - - [Fact] - public void AddArrow_AddsDirectPredecessor() - { - var graph = DirectedGraphBuilder.NewBuilder() - .AddElement("from") - .AddElement("to") - .AddArrow("from", "to") - .CreateGraph(); - - graph.GetDirectPredecessorsOf("to").Should().Contain("from"); - } - - [Fact] - public void AddArrow_ThrowsOnMissingFrom() - { - var builder = DirectedGraphBuilder.NewBuilder().AddElement("from"); - - Action addArrowFromFromToTo = () => builder.AddArrow("from", "to"); + addDuplicateElement.Should().Throw(); + } + + [Fact] + public void AddElement_DoesNotAddDirectSuccessors() + { + var graph = DirectedGraphBuilder.NewBuilder() + .AddElement("element") + .CreateGraph(); + + graph.GetDirectSuccessorsOf("element").Should().BeEmpty(); + } + + [Fact] + public void AddElement_DoesNotAddDirectPredecessor() + { + var graph = DirectedGraphBuilder.NewBuilder() + .AddElement("element") + .CreateGraph(); + + graph.GetDirectPredecessorsOf("element").Should().BeEmpty(); + } + + [Fact] + public void AddArrow_AddsDirectSuccessor() + { + var graph = DirectedGraphBuilder.NewBuilder() + .AddElement("from") + .AddElement("to") + .AddArrow("from", "to") + .CreateGraph(); + + graph.GetDirectSuccessorsOf("from").Should().Contain("to"); + } + + [Fact] + public void AddArrow_AddsDirectPredecessor() + { + var graph = DirectedGraphBuilder.NewBuilder() + .AddElement("from") + .AddElement("to") + .AddArrow("from", "to") + .CreateGraph(); + + graph.GetDirectPredecessorsOf("to").Should().Contain("from"); + } + + [Fact] + public void AddArrow_ThrowsOnMissingFrom() + { + var builder = DirectedGraphBuilder.NewBuilder().AddElement("from"); + + Action addArrowFromFromToTo = () => builder.AddArrow("from", "to"); - addArrowFromFromToTo.Should().Throw(); - } + addArrowFromFromToTo.Should().Throw(); + } - [Fact] - public void AddArrow_ThrowsOnMissingTo() - { - var builder = DirectedGraphBuilder.NewBuilder().AddElement("to"); + [Fact] + public void AddArrow_ThrowsOnMissingTo() + { + var builder = DirectedGraphBuilder.NewBuilder().AddElement("to"); - Action addArrowFromFromToTo = () => builder.AddArrow("from", "to"); + Action addArrowFromFromToTo = () => builder.AddArrow("from", "to"); - addArrowFromFromToTo.Should().Throw(); - } + addArrowFromFromToTo.Should().Throw(); + } - [Fact] - public void AddArrow_ThrowsOnNullFrom() - { - var builder = DirectedGraphBuilder.NewBuilder().AddElement("to"); + [Fact] + public void AddArrow_ThrowsOnNullFrom() + { + var builder = DirectedGraphBuilder.NewBuilder().AddElement("to"); - Action addArrowFromNullToTo = () => builder.AddArrow(null, "to"); + Action addArrowFromNullToTo = () => builder.AddArrow(null, "to"); - addArrowFromNullToTo.Should().Throw(); - } + addArrowFromNullToTo.Should().Throw(); + } - [Fact] - public void AddArrow_ThrowsOnNullTo() - { - var builder = DirectedGraphBuilder.NewBuilder().AddElement("from"); + [Fact] + public void AddArrow_ThrowsOnNullTo() + { + var builder = DirectedGraphBuilder.NewBuilder().AddElement("from"); - Action addArrowFromFromToNull = () => builder.AddArrow("from", null); + Action addArrowFromFromToNull = () => builder.AddArrow("from", null); - addArrowFromFromToNull.Should().Throw(); - } - - [Fact] - public void AddArrow_ThrowsOnDuplicate() - { - var builder = DirectedGraphBuilder.NewBuilder() - .AddElement("from") - .AddElement("to") - .AddArrow("from", "to"); - - Action addDuplicateArrow = () => builder.AddArrow("from", "to"); + addArrowFromFromToNull.Should().Throw(); + } + + [Fact] + public void AddArrow_ThrowsOnDuplicate() + { + var builder = DirectedGraphBuilder.NewBuilder() + .AddElement("from") + .AddElement("to") + .AddArrow("from", "to"); + + Action addDuplicateArrow = () => builder.AddArrow("from", "to"); - addDuplicateArrow.Should().Throw(); - } - - [Fact] - public void AddArrow_AddsSelfEdges() - { - var graph = DirectedGraphBuilder.NewBuilder() - .AddElement("from_and_to") - .AddArrow("from_and_to", "from_and_to") - .CreateGraph(); - - graph.GetDirectSuccessorsOf("from_and_to").Should().Contain("from_and_to"); - graph.GetDirectPredecessorsOf("from_and_to").Should().Contain("from_and_to"); - } + addDuplicateArrow.Should().Throw(); + } + + [Fact] + public void AddArrow_AddsSelfEdges() + { + var graph = DirectedGraphBuilder.NewBuilder() + .AddElement("from_and_to") + .AddArrow("from_and_to", "from_and_to") + .CreateGraph(); + + graph.GetDirectSuccessorsOf("from_and_to").Should().Contain("from_and_to"); + graph.GetDirectPredecessorsOf("from_and_to").Should().Contain("from_and_to"); + } - [Fact] - public void CreateGraph_DoesNotThrowOnAcyclicGraph() - { - var builder = createBuilderForAcyclicGraph(); + [Fact] + public void CreateGraph_DoesNotThrowOnAcyclicGraph() + { + var builder = createBuilderForAcyclicGraph(); - Action createGraph = () => builder.CreateGraph(); + Action createGraph = () => builder.CreateGraph(); - createGraph.Should().NotThrow(); - } + createGraph.Should().NotThrow(); + } - [Fact] - public void CreateGraph_DoesNotThrowOnCycle() - { - var builder = createBuilderForTriangle(); + [Fact] + public void CreateGraph_DoesNotThrowOnCycle() + { + var builder = createBuilderForTriangle(); - Action createGraph = () => builder.CreateGraph(); + Action createGraph = () => builder.CreateGraph(); - createGraph.Should().NotThrow(); - } + createGraph.Should().NotThrow(); + } - [Fact] - public void CreateGraph_DoesNotThrowOnSelfEdge() - { - var builder = createBuilderForSelfEdge(); + [Fact] + public void CreateGraph_DoesNotThrowOnSelfEdge() + { + var builder = createBuilderForSelfEdge(); - Action createGraph = () => builder.CreateGraph(); + Action createGraph = () => builder.CreateGraph(); - createGraph.Should().NotThrow(); - } + createGraph.Should().NotThrow(); + } - [Fact] - public void CreateAcyclicGraph_DoesNotThrowOnAcyclicGraph() - { - var builder = createBuilderForAcyclicGraph(); + [Fact] + public void CreateAcyclicGraph_DoesNotThrowOnAcyclicGraph() + { + var builder = createBuilderForAcyclicGraph(); - Action createAcyclicGraph = () => builder.CreateAcyclicGraph(); + Action createAcyclicGraph = () => builder.CreateAcyclicGraph(); - createAcyclicGraph.Should().NotThrow(); - } + createAcyclicGraph.Should().NotThrow(); + } - [Fact] - public void CreateAcyclicGraph_ThrowsOnCycle() - { - var builder = createBuilderForTriangle(); + [Fact] + public void CreateAcyclicGraph_ThrowsOnCycle() + { + var builder = createBuilderForTriangle(); - Action createAcyclicGraph = () => builder.CreateAcyclicGraph(); + Action createAcyclicGraph = () => builder.CreateAcyclicGraph(); - createAcyclicGraph.Should().Throw(); - } + createAcyclicGraph.Should().Throw(); + } - [Fact] - public void CreateAcyclicGraph_ThrowsOnSelfEdge() - { - var builder = createBuilderForSelfEdge(); + [Fact] + public void CreateAcyclicGraph_ThrowsOnSelfEdge() + { + var builder = createBuilderForSelfEdge(); - Action createAcyclicGraph = () => builder.CreateAcyclicGraph(); + Action createAcyclicGraph = () => builder.CreateAcyclicGraph(); - createAcyclicGraph.Should().Throw(); - } + createAcyclicGraph.Should().Throw(); + } - [Fact] - public void CreateAcyclicGraphUnsafe_DoesNotThrowOnAcyclicGraph() - { - var builder = createBuilderForAcyclicGraph(); + [Fact] + public void CreateAcyclicGraphUnsafe_DoesNotThrowOnAcyclicGraph() + { + var builder = createBuilderForAcyclicGraph(); - Action createAcyclicGraphUnsafe = () => builder.CreateAcyclicGraphUnsafe(); + Action createAcyclicGraphUnsafe = () => builder.CreateAcyclicGraphUnsafe(); - createAcyclicGraphUnsafe.Should().NotThrow(); - } + createAcyclicGraphUnsafe.Should().NotThrow(); + } - [Fact] - public void CreateAcyclicGraphUnsafe_DoesNotThrowOnCycle() - { - var builder = createBuilderForTriangle(); + [Fact] + public void CreateAcyclicGraphUnsafe_DoesNotThrowOnCycle() + { + var builder = createBuilderForTriangle(); - Action createAcyclicGraphUnsafe = () => builder.CreateAcyclicGraphUnsafe(); + Action createAcyclicGraphUnsafe = () => builder.CreateAcyclicGraphUnsafe(); - createAcyclicGraphUnsafe.Should().NotThrow(); - } + createAcyclicGraphUnsafe.Should().NotThrow(); + } - [Fact] - public void CreateAcyclicGraphUnsafe_DoesNotThrowOnSelfEdge() - { - var builder = createBuilderForSelfEdge(); + [Fact] + public void CreateAcyclicGraphUnsafe_DoesNotThrowOnSelfEdge() + { + var builder = createBuilderForSelfEdge(); - Action createAcyclicGraphUnsafe = () => builder.CreateAcyclicGraphUnsafe(); + Action createAcyclicGraphUnsafe = () => builder.CreateAcyclicGraphUnsafe(); - createAcyclicGraphUnsafe.Should().NotThrow(); - } + createAcyclicGraphUnsafe.Should().NotThrow(); + } - private static DirectedGraphBuilder createBuilderForAcyclicGraph() - { - return DirectedGraphBuilder.NewBuilder() - .AddElement("A") - .AddElement("B") - .AddArrow("A", "B"); - } - - private static DirectedGraphBuilder createBuilderForTriangle() - { - return DirectedGraphBuilder.NewBuilder() - .AddElement("A") - .AddElement("B") - .AddElement("C") - .AddArrow("A", "B") - .AddArrow("B", "C") - .AddArrow("C", "A"); - } - - private static DirectedGraphBuilder createBuilderForSelfEdge() - { - return DirectedGraphBuilder.NewBuilder() - .AddElement("A") - .AddArrow("A", "A"); - } + private static DirectedGraphBuilder createBuilderForAcyclicGraph() + { + return DirectedGraphBuilder.NewBuilder() + .AddElement("A") + .AddElement("B") + .AddArrow("A", "B"); + } + + private static DirectedGraphBuilder createBuilderForTriangle() + { + return DirectedGraphBuilder.NewBuilder() + .AddElement("A") + .AddElement("B") + .AddElement("C") + .AddArrow("A", "B") + .AddArrow("B", "C") + .AddArrow("C", "A"); + } + + private static DirectedGraphBuilder createBuilderForSelfEdge() + { + return DirectedGraphBuilder.NewBuilder() + .AddElement("A") + .AddArrow("A", "A"); } } diff --git a/Bearded.Utilities.Tests/Helpers/LimitedRangeFloatGenerator.cs b/Bearded.Utilities.Tests/Helpers/LimitedRangeFloatGenerator.cs index 13b1f196..c7c3d71b 100644 --- a/Bearded.Utilities.Tests/Helpers/LimitedRangeFloatGenerator.cs +++ b/Bearded.Utilities.Tests/Helpers/LimitedRangeFloatGenerator.cs @@ -1,14 +1,13 @@ using System; using FsCheck; -namespace Bearded.Utilities.Tests.Helpers +namespace Bearded.Utilities.Tests.Helpers; + +public static class LimitedRangeFloatGenerator { - public static class LimitedRangeFloatGenerator + public static Arbitrary Generate() { - public static Arbitrary Generate() - { - return Arb.Default.Float32() - .Filter(f => Math.Abs(f) < 1e20 && Math.Abs(f) > 1e-20); - } + return Arb.Default.Float32() + .Filter(f => Math.Abs(f) < 1e20 && Math.Abs(f) > 1e-20); } -} \ No newline at end of file +} diff --git a/Bearded.Utilities.Tests/Helpers/NumericAssertionsExtensions.cs b/Bearded.Utilities.Tests/Helpers/NumericAssertionsExtensions.cs index 8abe9ea4..b3b8a861 100644 --- a/Bearded.Utilities.Tests/Helpers/NumericAssertionsExtensions.cs +++ b/Bearded.Utilities.Tests/Helpers/NumericAssertionsExtensions.cs @@ -4,25 +4,24 @@ using static System.Single; using static System.Math; -namespace Bearded.Utilities.Tests.Helpers +namespace Bearded.Utilities.Tests.Helpers; + +public static class NumericAssertionsExtensions { - public static class NumericAssertionsExtensions + public static AndConstraint> BeApproximatelyOrBothNaNOrInfinity( + this NumericAssertions parent, + float expectedValue, + int precisionDigits = 5, + string because = "", + params object[] becauseArgs) { - public static AndConstraint> BeApproximatelyOrBothNaNOrInfinity( - this NumericAssertions parent, - float expectedValue, - int precisionDigits = 5, - string because = "", - params object[] becauseArgs) - { - if (bothMatch(IsNaN) || bothMatch(IsPositiveInfinity) || bothMatch(IsNegativeInfinity)) - return new AndConstraint>(parent); + if (bothMatch(IsNaN) || bothMatch(IsPositiveInfinity) || bothMatch(IsNegativeInfinity)) + return new AndConstraint>(parent); - var acceptedPrecision = Max(Epsilon, Abs(expectedValue) * (float) Pow(0.1, precisionDigits)); + var acceptedPrecision = Max(Epsilon, Abs(expectedValue) * (float) Pow(0.1, precisionDigits)); - return parent.BeApproximately(expectedValue, acceptedPrecision, because, becauseArgs); + return parent.BeApproximately(expectedValue, acceptedPrecision, because, becauseArgs); - bool bothMatch(Func predicate) => predicate(expectedValue) && predicate((float)parent.Subject); - } + bool bothMatch(Func predicate) => predicate(expectedValue) && predicate((float)parent.Subject); } -} \ No newline at end of file +} diff --git a/Bearded.Utilities.Tests/Linq/ExtensionsTests.cs b/Bearded.Utilities.Tests/Linq/ExtensionsTests.cs index 8de01c49..b11a38df 100644 --- a/Bearded.Utilities.Tests/Linq/ExtensionsTests.cs +++ b/Bearded.Utilities.Tests/Linq/ExtensionsTests.cs @@ -8,449 +8,448 @@ using System.Collections.Generic; using Xunit; -namespace Bearded.Utilities.Tests.Linq +namespace Bearded.Utilities.Tests.Linq; + +public sealed class ExtensionsTests { - public sealed class ExtensionsTests + [Fact] + public void Yield() { - [Fact] - public void Yield() + var obj = new object(); + obj.Yield().Should().HaveCount(1) + .And.Contain(obj); + } + + [Fact] + public void AppendOnEmptyReturnsObject() + { + var obj = new object(); + Array.Empty().Append(obj).Should().HaveCount(1) + .And.Contain(obj); + } + + [Fact] + public void AppendOnNotEmptyReturnsObjectAsLastElement() + { + var obj = new object(); + var array = new[] { new object() }; + array.Append(obj).Should().HaveCount(2) + .And.ContainInOrder(array[0], obj); + } + + [Fact] + public void PrependOnEmptyReturnsObject() + { + var obj = new object(); + Array.Empty().Prepend(obj).Should().HaveCount(1) + .And.Contain(obj); + } + + [Fact] + public void PrependOnNotEmptyReturnsObjectAsFirstElement() + { + var obj = new object(); + var array = new[] { new object() }; + array.Prepend(obj).Should().HaveCount(2) + .And.ContainInOrder(obj, array[0]); + } + + [Theory] + [InlineData(1, 2, 3, 3)] + [InlineData(3, 2, 1, 1)] + public void MaxByFindsGreaterElementBySelector(int value1, int value2, int value3, int expectedId) + { + var array = new[] { + new Entity(1, value1), + new Entity(2, value2), + new Entity(3, value3) + }; + array.MaxBy(e => e.Value).Id.Should().Be(expectedId); + } + + [Theory] + [InlineData(1, 2, 3, 1)] + [InlineData(3, 2, 1, 3)] + public void MinByFindsSmallerElementBySelector(int value1, int value2, int value3, int expectedId) + { + var array = new[] { + new Entity(1, value1), + new Entity(2, value2), + new Entity(3, value3) + }; + array.MinBy(e => e.Value).Id.Should().Be(expectedId); + } + + [Fact] + public void TryGetTransformedValueTransformPresentValue() + { + var entity = new Entity(2, 0); + var dictionary = new Dictionary() { - var obj = new object(); - obj.Yield().Should().HaveCount(1) - .And.Contain(obj); - } + {1, entity } + }; + var result = dictionary.TryGetTransformedValue(1, out var value, e => e.Id * 2); + result.Should().BeTrue(); + value.Should().Be(4); + } - [Fact] - public void AppendOnEmptyReturnsObject() + [Fact] + public void TryGetTransformedValueReturnsDefaultOnMissingValue() + { + var entity = new Entity(2, 0); + var dictionary = new Dictionary() { - var obj = new object(); - Array.Empty().Append(obj).Should().HaveCount(1) - .And.Contain(obj); - } + {1, entity } + }; + var result = dictionary.TryGetTransformedValue(2, out var value, e => e.Id * 2); + result.Should().BeFalse(); + value.Should().Be(default(int)); + } - [Fact] - public void AppendOnNotEmptyReturnsObjectAsLastElement() + [Fact] + public void AddRangeAddsPairsToDictionary() + { + var entity = new Entity(2, 0); + var firstDictionary = new Dictionary() + { + {1, entity } + }; + var secondDictionary = new Dictionary() { - var obj = new object(); - var array = new[] { new object() }; - array.Append(obj).Should().HaveCount(2) - .And.ContainInOrder(array[0], obj); + {2, entity }, + {3, entity } + }; + firstDictionary.AddRange(secondDictionary); + firstDictionary.Keys + .Should().Contain(1) + .And.Contain(2) + .And.Contain(3); + } + + public class AddSorted + { + [Theory] + [InlineData(0, 0)] + [InlineData(2, 1)] + [InlineData(4, 2)] + [InlineData(6, 3)] + public void AddsInExpectedPosition(int id, int index) + { + var entity = new Entity(id, 0); + var list = new List() { + new Entity(1, 0), + new Entity(3, 0), + new Entity(5, 0) + }; + list.AddSorted(entity, Entity.ById); + list[index].Should().Be(entity); } - [Fact] - public void PrependOnEmptyReturnsObject() + [Theory] + [InlineData(0, 0)] + [InlineData(2, 1)] + [InlineData(4, 2)] + [InlineData(6, 3)] + public void AddsInExpectedPositionUsingDefaultComparer(int id, int index) { - var obj = new object(); - Array.Empty().Prepend(obj).Should().HaveCount(1) - .And.Contain(obj); + var list = new List() { + 1,3,5 + }; + list.AddSorted(id); + list[index].Should().Be(id); } [Fact] - public void PrependOnNotEmptyReturnsObjectAsFirstElement() + public void ThrowsOnNullList() { - var obj = new object(); - var array = new[] { new object() }; - array.Prepend(obj).Should().HaveCount(2) - .And.ContainInOrder(obj, array[0]); + List? list = null; + Action action = () => list.AddSorted(new Entity(1, 1), Entity.ById); + action.Should().Throw(); } - [Theory] - [InlineData(1, 2, 3, 3)] - [InlineData(3, 2, 1, 1)] - public void MaxByFindsGreaterElementBySelector(int value1, int value2, int value3, int expectedId) - { - var array = new[] { - new Entity(1, value1), - new Entity(2, value2), - new Entity(3, value3) - }; - array.MaxBy(e => e.Value).Id.Should().Be(expectedId); + [Fact] + public void ThrowsOnNullComparer() + { + var list = new List(); + Action action = () => list.AddSorted(new Entity(1, 1), null); + action.Should().Throw(); } - [Theory] - [InlineData(1, 2, 3, 1)] - [InlineData(3, 2, 1, 3)] - public void MinByFindsSmallerElementBySelector(int value1, int value2, int value3, int expectedId) - { - var array = new[] { - new Entity(1, value1), - new Entity(2, value2), - new Entity(3, value3) - }; - array.MinBy(e => e.Value).Id.Should().Be(expectedId); + [Fact] + public void OnEmptyListAddsAtStart() + { + var entity = new Entity(1, 0); + var list = new List(); + list.AddSorted(entity, Entity.ById); + list[0].Should().Be(entity); } + } + public sealed class ValueOrNull + { [Fact] - public void TryGetTransformedValueTransformPresentValue() + public void ReturnsNullOnMissingValue() { var entity = new Entity(2, 0); var dictionary = new Dictionary() - { - {1, entity } - }; - var result = dictionary.TryGetTransformedValue(1, out var value, e => e.Id * 2); - result.Should().BeTrue(); - value.Should().Be(4); + { + {1, entity } + }; + var value = dictionary.ValueOrNull(2); + value.Should().BeNull(); } [Fact] - public void TryGetTransformedValueReturnsDefaultOnMissingValue() + public void ReturnsValueIfPresent() { var entity = new Entity(2, 0); var dictionary = new Dictionary() - { - {1, entity } - }; - var result = dictionary.TryGetTransformedValue(2, out var value, e => e.Id * 2); - result.Should().BeFalse(); - value.Should().Be(default(int)); + { + {1, entity } + }; + var value = dictionary.ValueOrNull(1); + value.Should().Be(entity); } + } + public sealed class ValueOrDefault + { [Fact] - public void AddRangeAddsPairsToDictionary() + public void ReturnsValueIfPresent() { - var entity = new Entity(2, 0); - var firstDictionary = new Dictionary() - { - {1, entity } - }; - var secondDictionary = new Dictionary() - { - {2, entity }, - {3, entity } - }; - firstDictionary.AddRange(secondDictionary); - firstDictionary.Keys - .Should().Contain(1) - .And.Contain(2) - .And.Contain(3); + var dictionary = new Dictionary() + { + {1, 1 } + }; + var value = dictionary.ValueOrDefault(1); + value.Should().Be(1); } - public class AddSorted + [Fact] + public void ReturnsDefaultOnMissingValue() { - [Theory] - [InlineData(0, 0)] - [InlineData(2, 1)] - [InlineData(4, 2)] - [InlineData(6, 3)] - public void AddsInExpectedPosition(int id, int index) + var dictionary = new Dictionary() { - var entity = new Entity(id, 0); - var list = new List() { - new Entity(1, 0), - new Entity(3, 0), - new Entity(5, 0) - }; - list.AddSorted(entity, Entity.ById); - list[index].Should().Be(entity); - } - - [Theory] - [InlineData(0, 0)] - [InlineData(2, 1)] - [InlineData(4, 2)] - [InlineData(6, 3)] - public void AddsInExpectedPositionUsingDefaultComparer(int id, int index) - { - var list = new List() { - 1,3,5 - }; - list.AddSorted(id); - list[index].Should().Be(id); - } - - [Fact] - public void ThrowsOnNullList() - { - List? list = null; - Action action = () => list.AddSorted(new Entity(1, 1), Entity.ById); - action.Should().Throw(); - } - - [Fact] - public void ThrowsOnNullComparer() - { - var list = new List(); - Action action = () => list.AddSorted(new Entity(1, 1), null); - action.Should().Throw(); - } - - [Fact] - public void OnEmptyListAddsAtStart() - { - var entity = new Entity(1, 0); - var list = new List(); - list.AddSorted(entity, Entity.ById); - list[0].Should().Be(entity); - } + {1, 1 } + }; + var value = dictionary.ValueOrDefault(2); + value.Should().Be(0); } + } - public sealed class ValueOrNull + public sealed class RandomElement + { + [Property] + public void ReturnsAnElementFromTheCollection(int seed) { - [Fact] - public void ReturnsNullOnMissingValue() - { - var entity = new Entity(2, 0); - var dictionary = new Dictionary() - { - {1, entity } - }; - var value = dictionary.ValueOrNull(2); - value.Should().BeNull(); - } - - [Fact] - public void ReturnsValueIfPresent() - { - var entity = new Entity(2, 0); - var dictionary = new Dictionary() - { - {1, entity } - }; - var value = dictionary.ValueOrNull(1); - value.Should().Be(entity); - } + var list = new List { 1, 2, 3 }; + var result = list.RandomElement(new System.Random(seed)); + list.Should().Contain(result); } - public sealed class ValueOrDefault + [Fact] + public void ReturnsAnElementFromTheCollectionWithDefaultRandom() { - [Fact] - public void ReturnsValueIfPresent() - { - var dictionary = new Dictionary() - { - {1, 1 } - }; - var value = dictionary.ValueOrDefault(1); - value.Should().Be(1); - } - - [Fact] - public void ReturnsDefaultOnMissingValue() - { - var dictionary = new Dictionary() - { - {1, 1 } - }; - var value = dictionary.ValueOrDefault(2); - value.Should().Be(0); - } + var list = new List { 1, 2, 3 }; + var result = list.RandomElement(); + list.Should().Contain(result); } - public sealed class RandomElement + [Fact] + public void ThrowsOnSourceNull() { - [Property] - public void ReturnsAnElementFromTheCollection(int seed) - { - var list = new List { 1, 2, 3 }; - var result = list.RandomElement(new System.Random(seed)); - list.Should().Contain(result); - } + List? list = null; + Action action = () => list.RandomElement(new System.Random()); + action.Should().Throw(); + } - [Fact] - public void ReturnsAnElementFromTheCollectionWithDefaultRandom() - { - var list = new List { 1, 2, 3 }; - var result = list.RandomElement(); - list.Should().Contain(result); - } + [Fact] + public void ThrowsOnRandomNull() + { + var list = new List { 1, 2, 3 }; + Action action = () => list.RandomElement(null); + action.Should().Throw(); + } - [Fact] - public void ThrowsOnSourceNull() - { - List? list = null; - Action action = () => list.RandomElement(new System.Random()); - action.Should().Throw(); - } + [Fact] + public void ThrowsOnEmptyList() + { + var list = new List(); + Action action = () => list.RandomElement(new System.Random()); + action.Should().Throw(); + } - [Fact] - public void ThrowsOnRandomNull() - { - var list = new List { 1, 2, 3 }; - Action action = () => list.RandomElement(null); - action.Should().Throw(); - } + [Fact] + public void ThrowsOnEmptyEnumerable() + { + var list = System.Linq.Enumerable.Select(new List(), a => a); + Action action = () => list.RandomElement(new System.Random()); + action.Should().Throw(); + } + } - [Fact] - public void ThrowsOnEmptyList() - { - var list = new List(); - Action action = () => list.RandomElement(new System.Random()); - action.Should().Throw(); - } + public sealed class RandomSubset + { - [Fact] - public void ThrowsOnEmptyEnumerable() - { - var list = System.Linq.Enumerable.Select(new List(), a => a); - Action action = () => list.RandomElement(new System.Random()); - action.Should().Throw(); - } + [Property(Arbitrary = new [] { typeof(IntGenerators.PositiveInt) })] + public void ReturnsAnElementFromTheCollection(int seed, int length) + { + var list = new List { 1, 2, 3 }; + var result = list.RandomSubset(length, new System.Random(seed)); + foreach (var r in result) + list.Should().Contain(r); + result.Should().HaveCount(Math.Min(length, list.Count)); } - public sealed class RandomSubset + [Fact] + public void ReturnsAnElementFromTheCollectionWithDefaultRandom() { + var list = new List { 1, 2, 3 }; + var result = list.RandomSubset(1); + list.Should().Contain(result); + } - [Property(Arbitrary = new [] { typeof(IntGenerators.PositiveInt) })] - public void ReturnsAnElementFromTheCollection(int seed, int length) - { - var list = new List { 1, 2, 3 }; - var result = list.RandomSubset(length, new System.Random(seed)); - foreach (var r in result) - list.Should().Contain(r); - result.Should().HaveCount(Math.Min(length, list.Count)); - } - - [Fact] - public void ReturnsAnElementFromTheCollectionWithDefaultRandom() - { - var list = new List { 1, 2, 3 }; - var result = list.RandomSubset(1); - list.Should().Contain(result); - } - - [Theory] - [InlineData(0)] - [InlineData(-1)] - public void ReturnsAnEmptyListIfCountIsLessThanOrEqualToZero(int count) - { - var list = new List { 1, 2, 3 }; - var result = list.RandomSubset(count); - result.Should().BeEmpty(); - } + [Theory] + [InlineData(0)] + [InlineData(-1)] + public void ReturnsAnEmptyListIfCountIsLessThanOrEqualToZero(int count) + { + var list = new List { 1, 2, 3 }; + var result = list.RandomSubset(count); + result.Should().BeEmpty(); + } - [Fact] - public void ThrowsOnSourceNull() - { - List? list = null; - Action action = () => list.RandomSubset(1, new System.Random()); - action.Should().Throw(); - } + [Fact] + public void ThrowsOnSourceNull() + { + List? list = null; + Action action = () => list.RandomSubset(1, new System.Random()); + action.Should().Throw(); + } - [Fact] - public void ThrowsOnRandomNull() - { - var list = new List { 1, 2, 3 }; - Action action = () => list.RandomSubset(1, null); - action.Should().Throw(); - } + [Fact] + public void ThrowsOnRandomNull() + { + var list = new List { 1, 2, 3 }; + Action action = () => list.RandomSubset(1, null); + action.Should().Throw(); + } - [Fact] - public void ThrowsOnEmptyList() - { - var list = new List(); - var result = list.RandomSubset(1, new System.Random()); - result.Should().BeEmpty(); - } + [Fact] + public void ThrowsOnEmptyList() + { + var list = new List(); + var result = list.RandomSubset(1, new System.Random()); + result.Should().BeEmpty(); + } - [Fact] - public void ThrowsOnEmptyEnumerable() - { - var list = System.Linq.Enumerable.Select(new List(), a => a); - var result = list.RandomSubset(1, new System.Random()); - result.Should().BeEmpty(); - } + [Fact] + public void ThrowsOnEmptyEnumerable() + { + var list = System.Linq.Enumerable.Select(new List(), a => a); + var result = list.RandomSubset(1, new System.Random()); + result.Should().BeEmpty(); } + } - public sealed class Shuffle + public sealed class Shuffle + { + [Fact] + public void ThrowsOnSourceNull() { - [Fact] - public void ThrowsOnSourceNull() - { - List? list = null; - Action action = () => list.Shuffle(new System.Random()); - action.Should().Throw(); - } + List? list = null; + Action action = () => list.Shuffle(new System.Random()); + action.Should().Throw(); + } - [Fact] - public void ThrowsOnRandomNull() - { - var list = new List { 1, 2, 3 }; - Action action = () => list.Shuffle(null); - action.Should().Throw(); - } + [Fact] + public void ThrowsOnRandomNull() + { + var list = new List { 1, 2, 3 }; + Action action = () => list.Shuffle(null); + action.Should().Throw(); + } - [Fact] - public void ShufflesInPlaceList() - { - var list = new List { 1, 2, 3 }; - list.Shuffle(); - list.Should() - .Contain(1) - .And.Contain(2) - .And.Contain(3) - .And.HaveCount(3); - } + [Fact] + public void ShufflesInPlaceList() + { + var list = new List { 1, 2, 3 }; + list.Shuffle(); + list.Should() + .Contain(1) + .And.Contain(2) + .And.Contain(3) + .And.HaveCount(3); + } - [Property] - public void ShufflesInPlaceListWithGivenSeed(int seed) - { - var list = new List { 1, 2, 3 }; - list.Shuffle(new System.Random(seed)); - list.Should() - .HaveCount(3) - .And.Contain(1) - .And.Contain(2) - .And.Contain(3); - } + [Property] + public void ShufflesInPlaceListWithGivenSeed(int seed) + { + var list = new List { 1, 2, 3 }; + list.Shuffle(new System.Random(seed)); + list.Should() + .HaveCount(3) + .And.Contain(1) + .And.Contain(2) + .And.Contain(3); } + } - public sealed class Shuffled + public sealed class Shuffled + { + [Fact] + public void ThrowsOnSourceNull() { - [Fact] - public void ThrowsOnSourceNull() - { - List? list = null; - Action action = () => list.Shuffled(new System.Random()); - action.Should().Throw(); - } + List? list = null; + Action action = () => list.Shuffled(new System.Random()); + action.Should().Throw(); + } - [Fact] - public void ThrowsOnRandomNull() - { - var list = new List { 1, 2, 3 }; - Action action = () => list.Shuffled(null); - action.Should().Throw(); - } + [Fact] + public void ThrowsOnRandomNull() + { + var list = new List { 1, 2, 3 }; + Action action = () => list.Shuffled(null); + action.Should().Throw(); + } - [Fact] - public void ShufflesInPlaceList() + [Fact] + public void ShufflesInPlaceList() + { + var list = new List { 1, 2, 3 }; + var result = list.Shuffled(); + foreach (var e in list) { - var list = new List { 1, 2, 3 }; - var result = list.Shuffled(); - foreach (var e in list) - { - result.Should().Contain(e); - } - result.Should().HaveCount(list.Count); + result.Should().Contain(e); } + result.Should().HaveCount(list.Count); } + } - private class Entity - { - public static IComparer ById = new IdComparer(); + private class Entity + { + public static IComparer ById = new IdComparer(); - public Entity(int id, int value) - { - Id = id; - Value = value; - } + public Entity(int id, int value) + { + Id = id; + Value = value; + } - public int Id { get; set; } - public int Value { get; set; } + public int Id { get; set; } + public int Value { get; set; } - public class IdComparer : IComparer + public class IdComparer : IComparer + { + public int Compare(Entity? x, Entity? y) { - public int Compare(Entity? x, Entity? y) - { - if (x == null) - return -1; - if (y == null) - return 1; - return x.Id.CompareTo(y.Id); - } + if (x == null) + return -1; + if (y == null) + return 1; + return x.Id.CompareTo(y.Id); } } } -} \ No newline at end of file +} diff --git a/Bearded.Utilities.Tests/Monads/ResultTests.cs b/Bearded.Utilities.Tests/Monads/ResultTests.cs index cfc5f2ad..8e5a0a49 100644 --- a/Bearded.Utilities.Tests/Monads/ResultTests.cs +++ b/Bearded.Utilities.Tests/Monads/ResultTests.cs @@ -5,61 +5,39 @@ using Xunit; using Xunit.Sdk; -namespace Bearded.Utilities.Tests.Monads +namespace Bearded.Utilities.Tests.Monads; + +public sealed class ResultTests { - public sealed class ResultTests + public sealed class ResultOrDefault { - public sealed class ResultOrDefault + public sealed class WithEagerDefault { - public sealed class WithEagerDefault + [Fact] + public void ReturnsDefaultOnFailure() { - [Fact] - public void ReturnsDefaultOnFailure() - { - var result = Result.Failure("something went wrong"); - - result.ResultOrDefault(100).Should().Be(100); - } - - [Fact] - public void ReturnsResultOnSuccess() - { - var result = Result.Success(200); + var result = Result.Failure("something went wrong"); - result.ResultOrDefault(100).Should().Be(200); - } + result.ResultOrDefault(100).Should().Be(100); } - public sealed class WithLazyDefault + [Fact] + public void ReturnsResultOnSuccess() { - [Fact] - public void ReturnsDefaultOnFailure() - { - var result = Result.Failure("something went wrong"); - - result.ResultOrDefault(() => 100).Should().Be(100); - } - - [Fact] - public void ReturnsResultOnSuccess() - { - var result = Result.Success(200); + var result = Result.Success(200); - result.ResultOrDefault(() => 100).Should().Be(200); - } + result.ResultOrDefault(100).Should().Be(200); } } - public sealed class ResultOrThrow + public sealed class WithLazyDefault { [Fact] - public void TrowsOnFailure() + public void ReturnsDefaultOnFailure() { var result = Result.Failure("something went wrong"); - Action assertion = () => result.ResultOrThrow(_ => new ApplicationException()); - - assertion.Should().Throw(); + result.ResultOrDefault(() => 100).Should().Be(100); } [Fact] @@ -67,177 +45,198 @@ public void ReturnsResultOnSuccess() { var result = Result.Success(200); - result.ResultOrThrow(_ => new ApplicationException()).Should().Be(200); + result.ResultOrDefault(() => 100).Should().Be(200); } } + } - public sealed class AsMaybe + public sealed class ResultOrThrow + { + [Fact] + public void TrowsOnFailure() { - [Fact] - public void ReturnsNothingOnFailure() - { - var result = Result.Failure("something went wrong"); + var result = Result.Failure("something went wrong"); - result.AsMaybe().Should().BeNothing(); - } + Action assertion = () => result.ResultOrThrow(_ => new ApplicationException()); - [Fact] - public void ReturnsJustResultOnSuccess() - { - var result = Result.Success(200); + assertion.Should().Throw(); + } - result.AsMaybe().Should().BeJust(200); - } + [Fact] + public void ReturnsResultOnSuccess() + { + var result = Result.Success(200); + + result.ResultOrThrow(_ => new ApplicationException()).Should().Be(200); + } + } + + public sealed class AsMaybe + { + [Fact] + public void ReturnsNothingOnFailure() + { + var result = Result.Failure("something went wrong"); + + result.AsMaybe().Should().BeNothing(); + } + + [Fact] + public void ReturnsJustResultOnSuccess() + { + var result = Result.Success(200); + + result.AsMaybe().Should().BeJust(200); + } + } + + public sealed class Select + { + [Fact] + public void MapsFailureToFailure() + { + var result = Result.Failure("something went wrong"); + + result.Select(i => i * 2).Should().Be(Result.Failure("something went wrong")); + } + + [Fact] + public void MapsSuccessToSuccess() + { + var result = Result.Success(100); + + result.Select(i => i * 2).Should().Be(Result.Success(200)); + } + } + + public sealed class SelectMany + { + [Fact] + public void MapsFailureToFailure() + { + var result = Result.Failure("something went wrong"); + + result.SelectMany(i => Result.Success(i * 2)).Should().Be(Result.Failure("something went wrong")); + } + + [Fact] + public void MapsSuccessToSuccess() + { + var result = Result.Success(100); + + result.SelectMany(i => Result.Success(i * 2)).Should().Be(Result.Success(200)); + } + + [Fact] + public void MapsSuccessToFailure() + { + var result = Result.Success(100); + + result.SelectMany(i => Result.Failure("something went wrong")) + .Should().Be(Result.Failure("something went wrong")); } + } - public sealed class Select + public sealed class Match + { + public sealed class WithOneParameter { [Fact] - public void MapsFailureToFailure() + public void DoesNotCallOnSuccessWithFailure() { var result = Result.Failure("something went wrong"); - result.Select(i => i * 2).Should().Be(Result.Failure("something went wrong")); + result.Match(onSuccess: _ => throw new XunitException("Wrong method called")); } [Fact] - public void MapsSuccessToSuccess() + public void CallsOnSuccessWithResultOnSuccess() { var result = Result.Success(100); - result.Select(i => i * 2).Should().Be(Result.Success(200)); + var isCalled = false; + result.Match( + onSuccess: r => + { + r.Should().Be(100); + isCalled = true; + }); + + isCalled.Should().BeTrue("onSuccess should have been called"); } } - public sealed class SelectMany + public sealed class WithTwoParameters { [Fact] - public void MapsFailureToFailure() + public void CallsOnFailureWithErrorOnFailure() { var result = Result.Failure("something went wrong"); - result.SelectMany(i => Result.Success(i * 2)).Should().Be(Result.Failure("something went wrong")); - } - - [Fact] - public void MapsSuccessToSuccess() - { - var result = Result.Success(100); + var isCalled = false; + result.Match( + onSuccess: r => throw new XunitException("Wrong method called"), + onFailure: error => + { + error.Should().Be("something went wrong"); + isCalled = true; + }); - result.SelectMany(i => Result.Success(i * 2)).Should().Be(Result.Success(200)); + isCalled.Should().BeTrue("onFailure should have been called"); } [Fact] - public void MapsSuccessToFailure() + public void CallsOnSuccessWithResultOnSuccess() { var result = Result.Success(100); - result.SelectMany(i => Result.Failure("something went wrong")) - .Should().Be(Result.Failure("something went wrong")); + var isCalled = false; + result.Match( + onSuccess: r => + { + r.Should().Be(100); + isCalled = true; + }, + onFailure: _ => throw new XunitException("Wrong method called")); + + isCalled.Should().BeTrue("onSuccess should have been called"); } } - public sealed class Match + public sealed class Returning { - public sealed class WithOneParameter + [Fact] + public void CallsOnFailureWithErrorOnFailureAndReturnsItsValue() { - [Fact] - public void DoesNotCallOnSuccessWithFailure() - { - var result = Result.Failure("something went wrong"); - - result.Match(onSuccess: _ => throw new XunitException("Wrong method called")); - } - - [Fact] - public void CallsOnSuccessWithResultOnSuccess() - { - var result = Result.Success(100); - - var isCalled = false; - result.Match( - onSuccess: r => - { - r.Should().Be(100); - isCalled = true; - }); - - isCalled.Should().BeTrue("onSuccess should have been called"); - } - } + var result = Result.Failure("something went wrong"); + const string expectedResult = "expected result"; - public sealed class WithTwoParameters - { - [Fact] - public void CallsOnFailureWithErrorOnFailure() - { - var result = Result.Failure("something went wrong"); - - var isCalled = false; - result.Match( - onSuccess: r => throw new XunitException("Wrong method called"), - onFailure: error => - { - error.Should().Be("something went wrong"); - isCalled = true; - }); - - isCalled.Should().BeTrue("onFailure should have been called"); - } - - [Fact] - public void CallsOnSuccessWithResultOnSuccess() - { - var result = Result.Success(100); - - var isCalled = false; - result.Match( - onSuccess: r => - { - r.Should().Be(100); - isCalled = true; - }, - onFailure: _ => throw new XunitException("Wrong method called")); - - isCalled.Should().BeTrue("onSuccess should have been called"); - } + var actualResult = result.Match( + onSuccess: _ => throw new XunitException("Wrong method called"), + onFailure: error => + { + error.Should().Be("something went wrong"); + return expectedResult; + }); + + actualResult.Should().Be(expectedResult, "onFailure should have been called"); } - public sealed class Returning + [Fact] + public void CallsOnSuccessWithResultOnSuccessAndReturnsItsValue() { - [Fact] - public void CallsOnFailureWithErrorOnFailureAndReturnsItsValue() - { - var result = Result.Failure("something went wrong"); - const string expectedResult = "expected result"; - - var actualResult = result.Match( - onSuccess: _ => throw new XunitException("Wrong method called"), - onFailure: error => - { - error.Should().Be("something went wrong"); - return expectedResult; - }); - - actualResult.Should().Be(expectedResult, "onFailure should have been called"); - } - - [Fact] - public void CallsOnSuccessWithResultOnSuccessAndReturnsItsValue() - { - var result = Result.Success(100); - const string expectedReturn = "expected result"; - - var actualReturn = result.Match( - onSuccess: r => - { - r.Should().Be(100); - return expectedReturn; - }, - onFailure: _ => throw new XunitException("Wrong method called")); - - actualReturn.Should().Be(expectedReturn, "onSuccess should have been called"); - } + var result = Result.Success(100); + const string expectedReturn = "expected result"; + + var actualReturn = result.Match( + onSuccess: r => + { + r.Should().Be(100); + return expectedReturn; + }, + onFailure: _ => throw new XunitException("Wrong method called")); + + actualReturn.Should().Be(expectedReturn, "onSuccess should have been called"); } } } diff --git a/Bearded.Utilities.Tests/Noise/NoiseTests.cs b/Bearded.Utilities.Tests/Noise/NoiseTests.cs index 30af6ecb..12adb405 100644 --- a/Bearded.Utilities.Tests/Noise/NoiseTests.cs +++ b/Bearded.Utilities.Tests/Noise/NoiseTests.cs @@ -4,63 +4,62 @@ using FluentAssertions; using FsCheck.Xunit; -namespace Bearded.Utilities.Tests.Noise +namespace Bearded.Utilities.Tests.Noise; + +public abstract class NoiseTests { - public abstract class NoiseTests - { - protected abstract IProceduralTexture CreateProceduralTexture(int seed); + protected abstract IProceduralTexture CreateProceduralTexture(int seed); - [Property(Arbitrary = new[] { typeof(DoubleGenerators.UnitIntervalUpperBoundExclusive) })] - public void CannotGetValuesForNegativeXCoordinate(int seed, double x, double y) - { - var map = CreateProceduralTexture(seed); + [Property(Arbitrary = new[] { typeof(DoubleGenerators.UnitIntervalUpperBoundExclusive) })] + public void CannotGetValuesForNegativeXCoordinate(int seed, double x, double y) + { + var map = CreateProceduralTexture(seed); - Func getValueAtXMinusOne = () => map.ValueAt(x - 1.0, y); - getValueAtXMinusOne.Should().Throw(); - } + Func getValueAtXMinusOne = () => map.ValueAt(x - 1.0, y); + getValueAtXMinusOne.Should().Throw(); + } - [Property(Arbitrary = new[] { typeof(DoubleGenerators.UnitIntervalUpperBoundExclusive) })] - public void CannotGetValuesForXCoordinateLargerThanOne(int seed, double x, double y) - { - var map = CreateProceduralTexture(seed); + [Property(Arbitrary = new[] { typeof(DoubleGenerators.UnitIntervalUpperBoundExclusive) })] + public void CannotGetValuesForXCoordinateLargerThanOne(int seed, double x, double y) + { + var map = CreateProceduralTexture(seed); - Func getValueAtXPlusOne = () => map.ValueAt(x + 1.0, y); - getValueAtXPlusOne.Should().Throw(); - } + Func getValueAtXPlusOne = () => map.ValueAt(x + 1.0, y); + getValueAtXPlusOne.Should().Throw(); + } - [Property(Arbitrary = new[] { typeof(DoubleGenerators.UnitIntervalUpperBoundExclusive) })] - public void CannotGetValuesForNegativeYCoordinate(int seed, double x, double y) - { - var map = CreateProceduralTexture(seed); + [Property(Arbitrary = new[] { typeof(DoubleGenerators.UnitIntervalUpperBoundExclusive) })] + public void CannotGetValuesForNegativeYCoordinate(int seed, double x, double y) + { + var map = CreateProceduralTexture(seed); - Func getValueAtYMinusOne = () => map.ValueAt(x, y - 1.0); - getValueAtYMinusOne.Should().Throw(); - } + Func getValueAtYMinusOne = () => map.ValueAt(x, y - 1.0); + getValueAtYMinusOne.Should().Throw(); + } - [Property(Arbitrary = new[] { typeof(DoubleGenerators.UnitIntervalUpperBoundExclusive) })] - public void CannotGetValuesForYCoordinateLargerThanOne(int seed, double x, double y) - { - var map = CreateProceduralTexture(seed); + [Property(Arbitrary = new[] { typeof(DoubleGenerators.UnitIntervalUpperBoundExclusive) })] + public void CannotGetValuesForYCoordinateLargerThanOne(int seed, double x, double y) + { + var map = CreateProceduralTexture(seed); - Func getValueAtYPlusOne = () => map.ValueAt(x, y + 1.0); - getValueAtYPlusOne.Should().Throw(); - } + Func getValueAtYPlusOne = () => map.ValueAt(x, y + 1.0); + getValueAtYPlusOne.Should().Throw(); + } - [Property(Arbitrary = new[] { typeof(DoubleGenerators.UnitIntervalUpperBoundExclusive) })] - public void ReturnsValuesInUnitInterval(int seed, double x, double y) - { - var map = CreateProceduralTexture(seed); + [Property(Arbitrary = new[] { typeof(DoubleGenerators.UnitIntervalUpperBoundExclusive) })] + public void ReturnsValuesInUnitInterval(int seed, double x, double y) + { + var map = CreateProceduralTexture(seed); - map.ValueAt(x, y).Should().BeInRange(0, 1); - } + map.ValueAt(x, y).Should().BeInRange(0, 1); + } - [Property(Arbitrary = new[] { typeof(DoubleGenerators.UnitIntervalUpperBoundExclusive) })] - public void IsDeterministicWithSameSeed(int seed, double x, double y) - { - var map1 = CreateProceduralTexture(seed); - var map2 = CreateProceduralTexture(seed); + [Property(Arbitrary = new[] { typeof(DoubleGenerators.UnitIntervalUpperBoundExclusive) })] + public void IsDeterministicWithSameSeed(int seed, double x, double y) + { + var map1 = CreateProceduralTexture(seed); + var map2 = CreateProceduralTexture(seed); - map1.ValueAt(x, y).Should().BeApproximately(map2.ValueAt(x, y), double.Epsilon); - } + map1.ValueAt(x, y).Should().BeApproximately(map2.ValueAt(x, y), double.Epsilon); } } diff --git a/Bearded.Utilities.Tests/Noise/PerlinNoiseTests.cs b/Bearded.Utilities.Tests/Noise/PerlinNoiseTests.cs index b6b8ac5a..bae63d86 100644 --- a/Bearded.Utilities.Tests/Noise/PerlinNoiseTests.cs +++ b/Bearded.Utilities.Tests/Noise/PerlinNoiseTests.cs @@ -1,9 +1,8 @@ using Bearded.Utilities.Noise; -namespace Bearded.Utilities.Tests.Noise +namespace Bearded.Utilities.Tests.Noise; + +public sealed class PerlinNoiseTests : NoiseTests { - public sealed class PerlinNoiseTests : NoiseTests - { - protected override IProceduralTexture CreateProceduralTexture(int seed) => PerlinNoise.Generate(5, 5, new System.Random(seed)); - } + protected override IProceduralTexture CreateProceduralTexture(int seed) => PerlinNoise.Generate(5, 5, new System.Random(seed)); } diff --git a/Bearded.Utilities.Tests/Noise/UniformNoiseTests.cs b/Bearded.Utilities.Tests/Noise/UniformNoiseTests.cs index 707f7ca0..3ef0d994 100644 --- a/Bearded.Utilities.Tests/Noise/UniformNoiseTests.cs +++ b/Bearded.Utilities.Tests/Noise/UniformNoiseTests.cs @@ -1,9 +1,8 @@ using Bearded.Utilities.Noise; -namespace Bearded.Utilities.Tests.Noise +namespace Bearded.Utilities.Tests.Noise; + +public sealed class UniformNoiseTests : NoiseTests { - public sealed class UniformNoiseTests : NoiseTests - { - protected override IProceduralTexture CreateProceduralTexture(int seed) => UniformNoise.Generate(5, 5, new System.Random(seed)); - } + protected override IProceduralTexture CreateProceduralTexture(int seed) => UniformNoise.Generate(5, 5, new System.Random(seed)); } diff --git a/Bearded.Utilities.Tests/SpaceTime/GravitationalConstantTests.cs b/Bearded.Utilities.Tests/SpaceTime/GravitationalConstantTests.cs index c6eced99..efe086e8 100644 --- a/Bearded.Utilities.Tests/SpaceTime/GravitationalConstantTests.cs +++ b/Bearded.Utilities.Tests/SpaceTime/GravitationalConstantTests.cs @@ -6,139 +6,138 @@ using OpenTK.Mathematics; using Xunit; -namespace Bearded.Utilities.Tests.SpaceTime +namespace Bearded.Utilities.Tests.SpaceTime; + +public class GravitationalConstantTests { - public class GravitationalConstantTests + public class TheNumericValueProperty { - public class TheNumericValueProperty + [Property] + public void ReturnsTheValuePassedToTheConstructor(float value) { - [Property] - public void ReturnsTheValuePassedToTheConstructor(float value) - { - var g = new GravitationalConstant(value); + var g = new GravitationalConstant(value); - g.NumericValue.Should().Be(value); - } + g.NumericValue.Should().Be(value); } + } - public class TheZeroProperty + public class TheZeroProperty + { + [Fact] + public void ReturnsAZeroValue() { - [Fact] - public void ReturnsAZeroValue() - { - var zero = GravitationalConstant.Zero; + var zero = GravitationalConstant.Zero; - zero.NumericValue.Should().Be(0); - } + zero.NumericValue.Should().Be(0); } + } - public class TheOneProperty + public class TheOneProperty + { + [Fact] + public void ReturnsAZeroValue() { - [Fact] - public void ReturnsAZeroValue() - { - var one = GravitationalConstant.One; + var one = GravitationalConstant.One; - one.NumericValue.Should().Be(1); - } + one.NumericValue.Should().Be(1); } + } - public class TheAccelerationTowardsWithDifference2Method + public class TheAccelerationTowardsWithDifference2Method + { + [Property(Arbitrary = new []{typeof(LimitedRangeFloatGenerator)})] + public void CalculatesAccelerationCorrectly( + float gValue, float massValue, float differenceXValue, float differenceYValue) { - [Property(Arbitrary = new []{typeof(LimitedRangeFloatGenerator)})] - public void CalculatesAccelerationCorrectly( - float gValue, float massValue, float differenceXValue, float differenceYValue) - { - var g = new GravitationalConstant(gValue); - var mass = new Mass(massValue); - var difference = new Difference2(differenceXValue, differenceYValue); - var expectedAcceleration = gValue * massValue - / new Vector2(differenceXValue, differenceYValue).LengthSquared - * new Vector2(differenceXValue, differenceYValue).Normalized(); - - var acceleration = g.AccelerationTowards(mass, difference); - - acceleration.NumericValue.X.Should() - .BeApproximatelyOrBothNaNOrInfinity(expectedAcceleration.X); - acceleration.NumericValue.Y.Should() - .BeApproximatelyOrBothNaNOrInfinity(expectedAcceleration.Y); - } + var g = new GravitationalConstant(gValue); + var mass = new Mass(massValue); + var difference = new Difference2(differenceXValue, differenceYValue); + var expectedAcceleration = gValue * massValue + / new Vector2(differenceXValue, differenceYValue).LengthSquared + * new Vector2(differenceXValue, differenceYValue).Normalized(); + + var acceleration = g.AccelerationTowards(mass, difference); + + acceleration.NumericValue.X.Should() + .BeApproximatelyOrBothNaNOrInfinity(expectedAcceleration.X); + acceleration.NumericValue.Y.Should() + .BeApproximatelyOrBothNaNOrInfinity(expectedAcceleration.Y); } + } - public class TheAccelerationTowardsWithUnitMethod + public class TheAccelerationTowardsWithUnitMethod + { + [Property(Arbitrary = new []{typeof(LimitedRangeFloatGenerator)})] + public void CalculatesAccelerationCorrectly( + float gValue, float massValue, float differenceValue) { - [Property(Arbitrary = new []{typeof(LimitedRangeFloatGenerator)})] - public void CalculatesAccelerationCorrectly( - float gValue, float massValue, float differenceValue) - { - var g = new GravitationalConstant(gValue); - var mass = new Mass(massValue); - var difference = new Unit(differenceValue); - var expectedAcceleration = gValue * massValue - / differenceValue.Squared() - * Math.Sign(differenceValue); - - var acceleration = g.AccelerationTowards(mass, difference); - - acceleration.NumericValue.Should() - .BeApproximatelyOrBothNaNOrInfinity(expectedAcceleration); - } + var g = new GravitationalConstant(gValue); + var mass = new Mass(massValue); + var difference = new Unit(differenceValue); + var expectedAcceleration = gValue * massValue + / differenceValue.Squared() + * Math.Sign(differenceValue); + + var acceleration = g.AccelerationTowards(mass, difference); + + acceleration.NumericValue.Should() + .BeApproximatelyOrBothNaNOrInfinity(expectedAcceleration); } + } - public class TheAccelerationAtDistanceWithSquaredUnitMethod + public class TheAccelerationAtDistanceWithSquaredUnitMethod + { + [Property(Arbitrary = new []{typeof(LimitedRangeFloatGenerator)})] + public void CalculatesAccelerationCorrectly( + float gValue, float massValue, float distanceSquaredValueWithSign) { - [Property(Arbitrary = new []{typeof(LimitedRangeFloatGenerator)})] - public void CalculatesAccelerationCorrectly( - float gValue, float massValue, float distanceSquaredValueWithSign) - { - var g = new GravitationalConstant(gValue); - var mass = new Mass(massValue); - var distanceSquaredValue = Math.Abs(distanceSquaredValueWithSign); - var distanceSquared = new Squared(distanceSquaredValue); - var expectedAcceleration = gValue * massValue / distanceSquaredValue; - - var acceleration = g.AccelerationAtDistance(mass, distanceSquared); - - acceleration.NumericValue.Should() - .BeApproximatelyOrBothNaNOrInfinity(expectedAcceleration); - } + var g = new GravitationalConstant(gValue); + var mass = new Mass(massValue); + var distanceSquaredValue = Math.Abs(distanceSquaredValueWithSign); + var distanceSquared = new Squared(distanceSquaredValue); + var expectedAcceleration = gValue * massValue / distanceSquaredValue; + + var acceleration = g.AccelerationAtDistance(mass, distanceSquared); + + acceleration.NumericValue.Should() + .BeApproximatelyOrBothNaNOrInfinity(expectedAcceleration); } + } - public class TheAccelerationAtDistanceWithUnitMethod + public class TheAccelerationAtDistanceWithUnitMethod + { + [Property(Arbitrary = new []{typeof(LimitedRangeFloatGenerator)})] + public void CalculatesAccelerationCorrectly( + float gValue, float massValue, float distanceValue) { - [Property(Arbitrary = new []{typeof(LimitedRangeFloatGenerator)})] - public void CalculatesAccelerationCorrectly( - float gValue, float massValue, float distanceValue) - { - var g = new GravitationalConstant(gValue); - var mass = new Mass(massValue); - var distance = new Unit(distanceValue); - var expectedAcceleration = gValue * massValue / distanceValue.Squared(); - - var acceleration = g.AccelerationAtDistance(mass, distance); - - acceleration.NumericValue.Should() - .BeApproximatelyOrBothNaNOrInfinity(expectedAcceleration); - } + var g = new GravitationalConstant(gValue); + var mass = new Mass(massValue); + var distance = new Unit(distanceValue); + var expectedAcceleration = gValue * massValue / distanceValue.Squared(); + + var acceleration = g.AccelerationAtDistance(mass, distance); + + acceleration.NumericValue.Should() + .BeApproximatelyOrBothNaNOrInfinity(expectedAcceleration); } + } - public class TheAccelerationAtDistanceWithDifference2Method + public class TheAccelerationAtDistanceWithDifference2Method + { + [Property(Arbitrary = new []{typeof(LimitedRangeFloatGenerator)})] + public void CalculatesAccelerationCorrectly( + float gValue, float massValue, float differenceXValue, float differenceYValue) { - [Property(Arbitrary = new []{typeof(LimitedRangeFloatGenerator)})] - public void CalculatesAccelerationCorrectly( - float gValue, float massValue, float differenceXValue, float differenceYValue) - { - var g = new GravitationalConstant(gValue); - var mass = new Mass(massValue); - var difference = new Difference2(differenceXValue, differenceYValue); - var expectedAcceleration = gValue * massValue - / new Vector2(differenceXValue, differenceYValue).LengthSquared; - - var acceleration = g.AccelerationAtDistance(mass, difference); - - acceleration.NumericValue.Should() - .BeApproximatelyOrBothNaNOrInfinity(expectedAcceleration); - } + var g = new GravitationalConstant(gValue); + var mass = new Mass(massValue); + var difference = new Difference2(differenceXValue, differenceYValue); + var expectedAcceleration = gValue * massValue + / new Vector2(differenceXValue, differenceYValue).LengthSquared; + + var acceleration = g.AccelerationAtDistance(mass, difference); + + acceleration.NumericValue.Should() + .BeApproximatelyOrBothNaNOrInfinity(expectedAcceleration); } } } diff --git a/Bearded.Utilities.Tests/Tilemaps/ExtensionsTests.cs b/Bearded.Utilities.Tests/Tilemaps/ExtensionsTests.cs index 7e08faa6..321dc91e 100644 --- a/Bearded.Utilities.Tests/Tilemaps/ExtensionsTests.cs +++ b/Bearded.Utilities.Tests/Tilemaps/ExtensionsTests.cs @@ -8,17 +8,17 @@ using System.Linq; using Xunit; -namespace Bearded.Utilities.Tests.Tilemaps +namespace Bearded.Utilities.Tests.Tilemaps; + +public class ExtensionsTests { - public class ExtensionsTests + public class DirectionTests { - public class DirectionTests + [Fact] + public void DirectionsShouldContainAllDirections() { - [Fact] - public void DirectionsShouldContainAllDirections() + Extensions.Directions.Should().BeEquivalentTo(new[] { - Extensions.Directions.Should().BeEquivalentTo(new[] - { Direction.Right, Direction.UpRight, Direction.Up, @@ -28,205 +28,204 @@ public void DirectionsShouldContainAllDirections() Direction.Down, Direction.DownRight, }); - } - - [Theory] - [InlineData(Direction.Down, Direction.Up)] - [InlineData(Direction.DownLeft, Direction.UpRight)] - [InlineData(Direction.DownRight, Direction.UpLeft)] - [InlineData(Direction.Left, Direction.Right)] - [InlineData(Direction.Unknown, Direction.Unknown)] - public void OppositesShouldMatch(Direction direction, Direction expected) - { - direction.Opposite().Should().Be(expected); - expected.Opposite().Should().Be(direction); - } - - [Theory] - [InlineData(0, Direction.Right)] - [InlineData(45, Direction.UpRight)] - [InlineData(90, Direction.Up)] - [InlineData(135, Direction.UpLeft)] - [InlineData(180, Direction.Left)] - [InlineData(225, Direction.DownLeft)] - [InlineData(270, Direction.Down)] - [InlineData(315, Direction.DownRight)] - public void OrtogonalIsAsExpected(float degrees, Direction expected) - { - for (var delta = -22; delta < 22; delta++) - Direction2.FromDegrees(degrees + delta).Octagonal().Should().Be(expected); - } - - [Theory] - [InlineData(0, Direction.Right)] - [InlineData(90, Direction.Up)] - [InlineData(180, Direction.Left)] - [InlineData(270, Direction.Down)] - public void QuadrogonalIsAsExpected(float degrees, Direction expected) - { - for (var delta = -45; delta < 45; delta++) - Direction2.FromDegrees(degrees + delta).Quadrogonal().Should().Be(expected); - } - } - public class DirectionsTests - { - [Theory] - [InlineData(Directions.Right)] - [InlineData(Directions.UpRight)] - [InlineData(Directions.Up)] - [InlineData(Directions.UpLeft)] - [InlineData(Directions.Left)] - [InlineData(Directions.DownLeft)] - [InlineData(Directions.Down)] - [InlineData(Directions.DownRight)] - [InlineData(Directions.All)] - public void AnyIsTrueForAllButNone(Directions sut) - { - sut.Any().Should().BeTrue(); - } - - [Theory] - [InlineData(Directions.Right)] - [InlineData(Directions.UpRight)] - [InlineData(Directions.Up)] - [InlineData(Directions.UpLeft)] - [InlineData(Directions.Left)] - [InlineData(Directions.DownLeft)] - [InlineData(Directions.Down)] - [InlineData(Directions.DownRight)] - public void AnyMatchesForAll(Directions sut) - { - Directions.All.Any(sut).Should().BeTrue(); - } - - [Theory] - [InlineData(Directions.Right)] - [InlineData(Directions.UpRight)] - [InlineData(Directions.Up)] - [InlineData(Directions.UpLeft)] - [InlineData(Directions.Left)] - [InlineData(Directions.DownLeft)] - [InlineData(Directions.Down)] - [InlineData(Directions.DownRight)] - public void AllMatchesForAll(Directions sut) - { - Directions.All.All(sut).Should().BeTrue(); - } - - [Theory] - [InlineData(Directions.Right)] - [InlineData(Directions.UpRight)] - [InlineData(Directions.Up)] - [InlineData(Directions.UpLeft)] - [InlineData(Directions.Left)] - [InlineData(Directions.DownLeft)] - [InlineData(Directions.Down)] - [InlineData(Directions.DownRight)] - public void AllMatchesForSame(Directions sut) - { - sut.All(sut).Should().BeTrue(); - } - - [Theory] - [InlineData(Directions.Right)] - [InlineData(Directions.UpRight)] - [InlineData(Directions.Up)] - [InlineData(Directions.UpLeft)] - [InlineData(Directions.Left)] - [InlineData(Directions.DownLeft)] - [InlineData(Directions.Down)] - [InlineData(Directions.DownRight)] - public void AllDoesntMatchForNone(Directions sut) - { - Directions.None.All(sut).Should().BeFalse(); - } - - [Theory] - [InlineData(Directions.Right)] - [InlineData(Directions.UpRight)] - [InlineData(Directions.Up)] - [InlineData(Directions.UpLeft)] - [InlineData(Directions.Left)] - [InlineData(Directions.DownLeft)] - [InlineData(Directions.Down)] - [InlineData(Directions.DownRight)] - public void AnyDoesntMatchesForNone(Directions sut) - { - Directions.None.Any(sut).Should().BeFalse(); - } + } - [Property] - public void UnionMatchParts(Directions first, Directions second) - { - var union = Directions.All.Union(second); - (union & first).Should().Be(first); - (union & second).Should().Be(second); - } + [Theory] + [InlineData(Direction.Down, Direction.Up)] + [InlineData(Direction.DownLeft, Direction.UpRight)] + [InlineData(Direction.DownRight, Direction.UpLeft)] + [InlineData(Direction.Left, Direction.Right)] + [InlineData(Direction.Unknown, Direction.Unknown)] + public void OppositesShouldMatch(Direction direction, Direction expected) + { + direction.Opposite().Should().Be(expected); + expected.Opposite().Should().Be(direction); + } - [Property] - public void ExceptDoesntMatchSame(Directions sut) - { - var except = Directions.All.Except(sut); - (except & sut).Should().Be(Directions.None); - } + [Theory] + [InlineData(0, Direction.Right)] + [InlineData(45, Direction.UpRight)] + [InlineData(90, Direction.Up)] + [InlineData(135, Direction.UpLeft)] + [InlineData(180, Direction.Left)] + [InlineData(225, Direction.DownLeft)] + [InlineData(270, Direction.Down)] + [InlineData(315, Direction.DownRight)] + public void OrtogonalIsAsExpected(float degrees, Direction expected) + { + for (var delta = -22; delta < 22; delta++) + Direction2.FromDegrees(degrees + delta).Octagonal().Should().Be(expected); + } - [Fact] - public void AnyIsFalseOnNone() - { - Directions.None.Any().Should().BeFalse(); - } - - [Theory] - [InlineData(Directions.Right)] - [InlineData(Directions.UpRight)] - [InlineData(Directions.Up)] - [InlineData(Directions.UpLeft)] - [InlineData(Directions.Left)] - [InlineData(Directions.DownLeft)] - [InlineData(Directions.Down)] - [InlineData(Directions.DownRight)] - [InlineData(Directions.None)] - public void AllIsFalseForAllButAll(Directions sut) - { - sut.All().Should().BeFalse(); - } + [Theory] + [InlineData(0, Direction.Right)] + [InlineData(90, Direction.Up)] + [InlineData(180, Direction.Left)] + [InlineData(270, Direction.Down)] + public void QuadrogonalIsAsExpected(float degrees, Direction expected) + { + for (var delta = -45; delta < 45; delta++) + Direction2.FromDegrees(degrees + delta).Quadrogonal().Should().Be(expected); + } + } + public class DirectionsTests + { + [Theory] + [InlineData(Directions.Right)] + [InlineData(Directions.UpRight)] + [InlineData(Directions.Up)] + [InlineData(Directions.UpLeft)] + [InlineData(Directions.Left)] + [InlineData(Directions.DownLeft)] + [InlineData(Directions.Down)] + [InlineData(Directions.DownRight)] + [InlineData(Directions.All)] + public void AnyIsTrueForAllButNone(Directions sut) + { + sut.Any().Should().BeTrue(); + } - [Fact] - public void AllIsTrueOnAll() - { - Directions.All.All().Should().BeTrue(); - } + [Theory] + [InlineData(Directions.Right)] + [InlineData(Directions.UpRight)] + [InlineData(Directions.Up)] + [InlineData(Directions.UpLeft)] + [InlineData(Directions.Left)] + [InlineData(Directions.DownLeft)] + [InlineData(Directions.Down)] + [InlineData(Directions.DownRight)] + public void AnyMatchesForAll(Directions sut) + { + Directions.All.Any(sut).Should().BeTrue(); + } + [Theory] + [InlineData(Directions.Right)] + [InlineData(Directions.UpRight)] + [InlineData(Directions.Up)] + [InlineData(Directions.UpLeft)] + [InlineData(Directions.Left)] + [InlineData(Directions.DownLeft)] + [InlineData(Directions.Down)] + [InlineData(Directions.DownRight)] + public void AllMatchesForAll(Directions sut) + { + Directions.All.All(sut).Should().BeTrue(); } - public class DirectionAndDirectionsTests + + [Theory] + [InlineData(Directions.Right)] + [InlineData(Directions.UpRight)] + [InlineData(Directions.Up)] + [InlineData(Directions.UpLeft)] + [InlineData(Directions.Left)] + [InlineData(Directions.DownLeft)] + [InlineData(Directions.Down)] + [InlineData(Directions.DownRight)] + public void AllMatchesForSame(Directions sut) { - [Fact] - public void AllShouldEnumerateToAllDirections() - { - Directions.All.Enumerate().Should().BeEquivalentTo(Extensions.Directions); - } + sut.All(sut).Should().BeTrue(); + } - [Fact] - public void UnionShouldIncludeExpected() - { - var union = (Directions.Up | Directions.Down); - union.Includes(Direction.Up).Should().BeTrue(); - union.Includes(Direction.Down).Should().BeTrue(); - } + [Theory] + [InlineData(Directions.Right)] + [InlineData(Directions.UpRight)] + [InlineData(Directions.Up)] + [InlineData(Directions.UpLeft)] + [InlineData(Directions.Left)] + [InlineData(Directions.DownLeft)] + [InlineData(Directions.Down)] + [InlineData(Directions.DownRight)] + public void AllDoesntMatchForNone(Directions sut) + { + Directions.None.All(sut).Should().BeFalse(); + } - [Fact] - public void AndShouldReturnExpected() - { - Directions and = Directions.Up.And(Direction.Down); - (and & Directions.Up).Should().Be(Directions.Up); - (and & Directions.Down).Should().Be(Directions.Down); - } + [Theory] + [InlineData(Directions.Right)] + [InlineData(Directions.UpRight)] + [InlineData(Directions.Up)] + [InlineData(Directions.UpLeft)] + [InlineData(Directions.Left)] + [InlineData(Directions.DownLeft)] + [InlineData(Directions.Down)] + [InlineData(Directions.DownRight)] + public void AnyDoesntMatchesForNone(Directions sut) + { + Directions.None.Any(sut).Should().BeFalse(); + } - [Property] - public void ExceptDoesntMatchSame(Direction sut) - { - Directions.All.Except(sut).Enumerate().Should().NotContain(sut); - } + [Property] + public void UnionMatchParts(Directions first, Directions second) + { + var union = Directions.All.Union(second); + (union & first).Should().Be(first); + (union & second).Should().Be(second); + } + + [Property] + public void ExceptDoesntMatchSame(Directions sut) + { + var except = Directions.All.Except(sut); + (except & sut).Should().Be(Directions.None); + } + + [Fact] + public void AnyIsFalseOnNone() + { + Directions.None.Any().Should().BeFalse(); + } + + [Theory] + [InlineData(Directions.Right)] + [InlineData(Directions.UpRight)] + [InlineData(Directions.Up)] + [InlineData(Directions.UpLeft)] + [InlineData(Directions.Left)] + [InlineData(Directions.DownLeft)] + [InlineData(Directions.Down)] + [InlineData(Directions.DownRight)] + [InlineData(Directions.None)] + public void AllIsFalseForAllButAll(Directions sut) + { + sut.All().Should().BeFalse(); + } + + [Fact] + public void AllIsTrueOnAll() + { + Directions.All.All().Should().BeTrue(); + } + + } + public class DirectionAndDirectionsTests + { + [Fact] + public void AllShouldEnumerateToAllDirections() + { + Directions.All.Enumerate().Should().BeEquivalentTo(Extensions.Directions); + } + + [Fact] + public void UnionShouldIncludeExpected() + { + var union = (Directions.Up | Directions.Down); + union.Includes(Direction.Up).Should().BeTrue(); + union.Includes(Direction.Down).Should().BeTrue(); + } + + [Fact] + public void AndShouldReturnExpected() + { + Directions and = Directions.Up.And(Direction.Down); + (and & Directions.Up).Should().Be(Directions.Up); + (and & Directions.Down).Should().Be(Directions.Down); + } + + [Property] + public void ExceptDoesntMatchSame(Direction sut) + { + Directions.All.Except(sut).Enumerate().Should().NotContain(sut); } } -} \ No newline at end of file +} diff --git a/Bearded.Utilities.Tests/Tilemaps/RectangularTilemapTests.cs b/Bearded.Utilities.Tests/Tilemaps/RectangularTilemapTests.cs index 8529c9a6..275c94ab 100644 --- a/Bearded.Utilities.Tests/Tilemaps/RectangularTilemapTests.cs +++ b/Bearded.Utilities.Tests/Tilemaps/RectangularTilemapTests.cs @@ -5,103 +5,102 @@ using FluentAssertions; using Xunit; -namespace Bearded.Utilities.Tests.Tilemaps +namespace Bearded.Utilities.Tests.Tilemaps; + +public class RectangularTilemapTests { - public class RectangularTilemapTests - { - #region Tilemap + #region Tilemap - [Fact] - public void TestTilemapDimensions() - { - var tilemap = new Tilemap(42, 1337); + [Fact] + public void TestTilemapDimensions() + { + var tilemap = new Tilemap(42, 1337); - Assert.Equal(42, tilemap.Width); - Assert.Equal(1337, tilemap.Height); - Assert.Equal(42 * 1337, tilemap.Count); - } + Assert.Equal(42, tilemap.Width); + Assert.Equal(1337, tilemap.Height); + Assert.Equal(42 * 1337, tilemap.Count); + } - [Fact] - public void TestTilemapEnumeration() - { - var tilemap = new Tilemap(42, 1337); + [Fact] + public void TestTilemapEnumeration() + { + var tilemap = new Tilemap(42, 1337); - var tiles = tilemap.ToList(); + var tiles = tilemap.ToList(); - Assert.Equal(42 * 1337, tiles.Count); + Assert.Equal(42 * 1337, tiles.Count); - var tilesByHash = new HashSet>(); + var tilesByHash = new HashSet>(); - foreach (var tile in tiles) - { - Assert.True(tilesByHash.Add(tile)); - } - } - [Fact] - public void TestTilemapEnumerationAsEnumerable() + foreach (var tile in tiles) { - var tilemap = (IEnumerable)new Tilemap(42, 1337); + Assert.True(tilesByHash.Add(tile)); + } + } + [Fact] + public void TestTilemapEnumerationAsEnumerable() + { + var tilemap = (IEnumerable)new Tilemap(42, 1337); - var tilesByHash = new HashSet(); + var tilesByHash = new HashSet(); - foreach (var tile in tilemap) - { - Assert.True(tilesByHash.Add(tile)); - } + foreach (var tile in tilemap) + { + Assert.True(tilesByHash.Add(tile)); } + } - [Fact] - public void TestTilemapTileValidity() - { - var tilemap = new Tilemap(42, 1337); + [Fact] + public void TestTilemapTileValidity() + { + var tilemap = new Tilemap(42, 1337); - Assert.True(tilemap.IsValidTile(0, 0)); - Assert.True(tilemap.IsValidTile(13, 42)); - Assert.True(tilemap.IsValidTile(41, 1336)); + Assert.True(tilemap.IsValidTile(0, 0)); + Assert.True(tilemap.IsValidTile(13, 42)); + Assert.True(tilemap.IsValidTile(41, 1336)); - Assert.False(tilemap.IsValidTile(0, 1337)); - Assert.False(tilemap.IsValidTile(42, 0)); + Assert.False(tilemap.IsValidTile(0, 1337)); + Assert.False(tilemap.IsValidTile(42, 0)); - foreach (var tile in tilemap) - { - Assert.True(tilemap.IsValidTile(tile)); - } + foreach (var tile in tilemap) + { + Assert.True(tilemap.IsValidTile(tile)); } + } - [Fact] - public void TestTilemapIndexAccess() - { - var tilemap = new Tilemap(10,20); + [Fact] + public void TestTilemapIndexAccess() + { + var tilemap = new Tilemap(10,20); - for (int y = 0; y < tilemap.Height; y++) + for (int y = 0; y < tilemap.Height; y++) + { + for (int x = 0; x < tilemap.Width; x++) { - for (int x = 0; x < tilemap.Width; x++) - { - var expected = 1 + x * y; - tilemap[x, y] = expected; - tilemap[x, y].Should().Be(expected); - } + var expected = 1 + x * y; + tilemap[x, y] = expected; + tilemap[x, y].Should().Be(expected); } } + } - [Fact] - public void TestTilemapIndexAccessByTile() - { - var tilemap = new Tilemap(10, 20); + [Fact] + public void TestTilemapIndexAccessByTile() + { + var tilemap = new Tilemap(10, 20); - for (int y = 0; y < tilemap.Height; y++) + for (int y = 0; y < tilemap.Height; y++) + { + for (int x = 0; x < tilemap.Width; x++) { - for (int x = 0; x < tilemap.Width; x++) - { - var expected = 1 + x * y; - var tile = new Tile(tilemap,x, y); - tilemap[tile] = expected; - tilemap[tile].Should().Be(expected); - } + var expected = 1 + x * y; + var tile = new Tile(tilemap,x, y); + tilemap[tile] = expected; + tilemap[tile].Should().Be(expected); } } - - #endregion } -} \ No newline at end of file + + #endregion +} diff --git a/Bearded.Utilities.Tests/Tilemaps/TileTests.cs b/Bearded.Utilities.Tests/Tilemaps/TileTests.cs index 9309650a..4e26b2de 100644 --- a/Bearded.Utilities.Tests/Tilemaps/TileTests.cs +++ b/Bearded.Utilities.Tests/Tilemaps/TileTests.cs @@ -7,139 +7,138 @@ using System.Linq; using Xunit; -namespace Bearded.Utilities.Tests.Tilemaps +namespace Bearded.Utilities.Tests.Tilemaps; + +public class TileTests { - public class TileTests + [Fact] + public void CantCreateWithNullTilemap() { - [Fact] - public void CantCreateWithNullTilemap() - { - Action action = () => new Tile(null, 1, 1); - action.Should() - .Throw() - .WithMessage("*tilemap*"); - } + Action action = () => new Tile(null, 1, 1); + action.Should() + .Throw() + .WithMessage("*tilemap*"); + } - [Fact] - public void GetsValueFromTilemap() - { - var tilemap = new Tilemap(1, 1); - var value = 1; - tilemap[0, 0] = value; - var tile = new Tile(tilemap, 0, 0); - tile.Value.Should().Be(value); - } + [Fact] + public void GetsValueFromTilemap() + { + var tilemap = new Tilemap(1, 1); + var value = 1; + tilemap[0, 0] = value; + var tile = new Tile(tilemap, 0, 0); + tile.Value.Should().Be(value); + } - [Property(Arbitrary = new[] { typeof(IntGenerators.PositiveInt) })] - public void IsValidIsTheSameAsIsValidTileOnTilemap(int width, int length, int x, int y) - { - Tilemap tilemap = new Tilemap(width, length); - var tile = new Tile(tilemap, x, y); - tile.IsValid.Should().Be(tilemap.IsValidTile(x, y)); - } + [Property(Arbitrary = new[] { typeof(IntGenerators.PositiveInt) })] + public void IsValidIsTheSameAsIsValidTileOnTilemap(int width, int length, int x, int y) + { + Tilemap tilemap = new Tilemap(width, length); + var tile = new Tile(tilemap, x, y); + tile.IsValid.Should().Be(tilemap.IsValidTile(x, y)); + } - [Fact] - public void EmptyTileIsNotValid() - { - var tile = new Tile(); - tile.IsValid.Should().BeFalse(); - } + [Fact] + public void EmptyTileIsNotValid() + { + var tile = new Tile(); + tile.IsValid.Should().BeFalse(); + } - [Fact] - public void SingleTileHasNoValidNeighbours() - { - var tile = new Tile(new Tilemap(1, 1), 0, 0); - tile.ValidNeighbours.Should().BeEmpty(); - } + [Fact] + public void SingleTileHasNoValidNeighbours() + { + var tile = new Tile(new Tilemap(1, 1), 0, 0); + tile.ValidNeighbours.Should().BeEmpty(); + } - [Fact] - public void TilesOnSameCoordinatesAreEquale() - { - Tilemap tilemap = new Tilemap(1, 1); - var tile = new Tile(tilemap, 0, 0); - var tile2 = new Tile(tilemap, 0, 0); - (tile == tile2).Should().BeTrue(); - } - [Fact] - public void NullTileIsDifferent() - { - Tilemap tilemap = new Tilemap(1, 1); - var tile = new Tile(tilemap, 0, 0); - Tile? tile2 = null; - (tile != tile2).Should().BeTrue(); - } + [Fact] + public void TilesOnSameCoordinatesAreEquale() + { + Tilemap tilemap = new Tilemap(1, 1); + var tile = new Tile(tilemap, 0, 0); + var tile2 = new Tile(tilemap, 0, 0); + (tile == tile2).Should().BeTrue(); + } + [Fact] + public void NullTileIsDifferent() + { + Tilemap tilemap = new Tilemap(1, 1); + var tile = new Tile(tilemap, 0, 0); + Tile? tile2 = null; + (tile != tile2).Should().BeTrue(); + } - [Theory] - [InlineData(0, 0, 1, 3, 4)] - [InlineData(0, 1, 4, 5, 2, 0, 3)] - [InlineData(0, 2, 5, 1, 4)] - [InlineData(1, 0, 6, 7, 4, 1, 0)] - [InlineData(1, 1, 7, 8, 5, 2, 1, 0, 3, 6)] - [InlineData(1, 2, 8, 2, 1, 4, 7)] - [InlineData(2, 0, 7, 4, 3)] - [InlineData(2, 1, 8, 5, 4, 3, 6)] - [InlineData(2, 2, 5, 4, 7)] - public void ValidNeighboursAreAsExpected(int x, int y, params int[] values) + [Theory] + [InlineData(0, 0, 1, 3, 4)] + [InlineData(0, 1, 4, 5, 2, 0, 3)] + [InlineData(0, 2, 5, 1, 4)] + [InlineData(1, 0, 6, 7, 4, 1, 0)] + [InlineData(1, 1, 7, 8, 5, 2, 1, 0, 3, 6)] + [InlineData(1, 2, 8, 2, 1, 4, 7)] + [InlineData(2, 0, 7, 4, 3)] + [InlineData(2, 1, 8, 5, 4, 3, 6)] + [InlineData(2, 2, 5, 4, 7)] + public void ValidNeighboursAreAsExpected(int x, int y, params int[] values) + { + var tilemap = GetInitializedTilemap(); + var tile = new Tile(tilemap, x, y); + var list = tile.ValidNeighbours + .Select(v => v.Value).ToList(); + foreach (var v in values) { - var tilemap = GetInitializedTilemap(); - var tile = new Tile(tilemap, x, y); - var list = tile.ValidNeighbours - .Select(v => v.Value).ToList(); - foreach (var v in values) - { - list.Should().Contain(v); - } - list - .Should().HaveCount(values.Count()); + list.Should().Contain(v); } + list + .Should().HaveCount(values.Count()); + } - [Theory] - [InlineData(0, 0)] - [InlineData(0, 1)] - [InlineData(0, 2)] - [InlineData(1, 0)] - [InlineData(1, 1)] - [InlineData(1, 2)] - [InlineData(2, 0)] - [InlineData(2, 1)] - [InlineData(2, 2)] - public void PossibleNeighboursContainTileAsNeighbour(int x, int y) + [Theory] + [InlineData(0, 0)] + [InlineData(0, 1)] + [InlineData(0, 2)] + [InlineData(1, 0)] + [InlineData(1, 1)] + [InlineData(1, 2)] + [InlineData(2, 0)] + [InlineData(2, 1)] + [InlineData(2, 2)] + public void PossibleNeighboursContainTileAsNeighbour(int x, int y) + { + var tilemap = new Tilemap(3, 3); + var tile = new Tile(tilemap, x, y); + foreach (var neighbour in tile.PossibleNeighbours) { - var tilemap = new Tilemap(3, 3); - var tile = new Tile(tilemap, x, y); - foreach (var neighbour in tile.PossibleNeighbours) - { - neighbour.PossibleNeighbours.Contains(tile); - } - tile.PossibleNeighbours.Should().HaveCount(8); + neighbour.PossibleNeighbours.Contains(tile); } + tile.PossibleNeighbours.Should().HaveCount(8); + } - [Theory] - [InlineData(0, 2, Direction.UpLeft)] - [InlineData(1, 2, Direction.Up)] - [InlineData(2, 2, Direction.UpRight)] - [InlineData(0, 1, Direction.Left)] - [InlineData(2, 1, Direction.Right)] - [InlineData(0, 0, Direction.DownLeft)] - [InlineData(1, 0, Direction.Down)] - [InlineData(2, 0, Direction.DownRight)] - public void NeighbourReturnsExpectedTile(int x, int y, Direction direction) - { - var tilemap = new Tilemap(3, 3); - var tile = new Tile(tilemap, 1, 1); - var neighbour = tile.Neighbour(direction); - neighbour.X.Should().Be(x); - neighbour.Y.Should().Be(y); - } + [Theory] + [InlineData(0, 2, Direction.UpLeft)] + [InlineData(1, 2, Direction.Up)] + [InlineData(2, 2, Direction.UpRight)] + [InlineData(0, 1, Direction.Left)] + [InlineData(2, 1, Direction.Right)] + [InlineData(0, 0, Direction.DownLeft)] + [InlineData(1, 0, Direction.Down)] + [InlineData(2, 0, Direction.DownRight)] + public void NeighbourReturnsExpectedTile(int x, int y, Direction direction) + { + var tilemap = new Tilemap(3, 3); + var tile = new Tile(tilemap, 1, 1); + var neighbour = tile.Neighbour(direction); + neighbour.X.Should().Be(x); + neighbour.Y.Should().Be(y); + } - private static Tilemap GetInitializedTilemap() - { - var tilemap = new Tilemap(3, 3); - var i = 0; - for (int x = 0; x < tilemap.Width; x++) - for (int y = 0; y < tilemap.Height; y++) - tilemap[x, y] = i++; - return tilemap; - } + private static Tilemap GetInitializedTilemap() + { + var tilemap = new Tilemap(3, 3); + var i = 0; + for (int x = 0; x < tilemap.Width; x++) + for (int y = 0; y < tilemap.Height; y++) + tilemap[x, y] = i++; + return tilemap; } -} \ No newline at end of file +} diff --git a/Bearded.Utilities/Algorithms/BinPacking.Algorithm.cs b/Bearded.Utilities/Algorithms/BinPacking.Algorithm.cs index 33cf1387..ae657f73 100644 --- a/Bearded.Utilities/Algorithms/BinPacking.Algorithm.cs +++ b/Bearded.Utilities/Algorithms/BinPacking.Algorithm.cs @@ -2,186 +2,185 @@ using System.Collections.Generic; using System.Diagnostics; -namespace Bearded.Utilities.Algorithms +namespace Bearded.Utilities.Algorithms; + +public static partial class BinPacking { - public static partial class BinPacking + private class Tree { - private class Tree + private readonly IList> rectangles; + private Node root; + + public Tree(IList> rectangles) { - private readonly IList> rectangles; - private Node root; + this.rectangles = rectangles; + var first = rectangles[0]; + root = new Node(0, 0, first.Width, first.Height); + } - public Tree(IList> rectangles) - { - this.rectangles = rectangles; - var first = rectangles[0]; - root = new Node(0, 0, first.Width, first.Height); - } + public Result Fit() + { + var results = new List>(rectangles.Count); - public Result Fit() + foreach (var r in rectangles) { - var results = new List>(rectangles.Count); - - foreach (var r in rectangles) - { - var (x, y) = fitRectangleIntoTree(r.Width, r.Height); - results.Add(new PositionedRectangle(r, x, y)); - } + var (x, y) = fitRectangleIntoTree(r.Width, r.Height); + results.Add(new PositionedRectangle(r, x, y)); + } - var emptyPixels = root.CountEmptyPixels(); + var emptyPixels = root.CountEmptyPixels(); - return new Result(results.AsReadOnly(), root.W, root.H, emptyPixels); - } + return new Result(results.AsReadOnly(), root.W, root.H, emptyPixels); + } - // See http://codeincomplete.com/posts/2011/5/7/bin_packing/ for a full explanation of the algorithm - private (int x, int y) fitRectangleIntoTree(int width, int height) + // See http://codeincomplete.com/posts/2011/5/7/bin_packing/ for a full explanation of the algorithm + private (int x, int y) fitRectangleIntoTree(int width, int height) + { + var node = root.FindNodeWithSpaceFor(width, height); + if (node != null) { - var node = root.FindNodeWithSpaceFor(width, height); - if (node != null) - { - node.Split(width, height); - } - else - { - node = growNode(width, height); - } - - return (node.X, node.Y); + node.Split(width, height); } - - private Node growNode(int w, int h) + else { - var canGrowDown = w <= root.W; - var canGrowRight = h <= root.H; - - // We try to approximate a square which helps keep the tree relatively balanced - // instead of growing linearly into only one direction. - var shouldGrowRight = canGrowRight && root.H >= root.W + w; - var shouldGrowDown = canGrowDown && root.W >= root.H + h; - - if (shouldGrowRight) - return growRight(w, h); - if (shouldGrowDown) - return growDown(w, h); - if (canGrowRight) - return growRight(w, h); - if (canGrowDown) - return growDown(w, h); - - // Our own heuristics never violate the following constraint - // so this exception should never be thrown. - throw new InvalidOperationException( - "Was not able to grow bin packing node because we tried inserting a rectangle " + - "that was both wider and taller than the current packing. " + - "Rectangles should never be supplied in such an order. " + - "To guarantee this constraint, rectangles should be ordered so that each of them is larger " + - "in either width or height than all following ones." - ); + node = growNode(width, height); } - private Node growRight(int w, int h) - { - /* - Growing right creates a new node to the right of the current root - as well as a new root containing both. + return (node.X, node.Y); + } - 0,0-----+-----------+ - | old | extension | - | root | (width w) | - +------+-----------+ + private Node growNode(int w, int h) + { + var canGrowDown = w <= root.W; + var canGrowRight = h <= root.H; + + // We try to approximate a square which helps keep the tree relatively balanced + // instead of growing linearly into only one direction. + var shouldGrowRight = canGrowRight && root.H >= root.W + w; + var shouldGrowDown = canGrowDown && root.W >= root.H + h; + + if (shouldGrowRight) + return growRight(w, h); + if (shouldGrowDown) + return growDown(w, h); + if (canGrowRight) + return growRight(w, h); + if (canGrowDown) + return growDown(w, h); + + // Our own heuristics never violate the following constraint + // so this exception should never be thrown. + throw new InvalidOperationException( + "Was not able to grow bin packing node because we tried inserting a rectangle " + + "that was both wider and taller than the current packing. " + + "Rectangles should never be supplied in such an order. " + + "To guarantee this constraint, rectangles should be ordered so that each of them is larger " + + "in either width or height than all following ones." + ); + } - We then split that extension which inserts the (w, h) rectangle and - free up the remaining height. - */ + private Node growRight(int w, int h) + { + /* + Growing right creates a new node to the right of the current root + as well as a new root containing both. + + 0,0-----+-----------+ + | old | extension | + | root | (width w) | + +------+-----------+ + + We then split that extension which inserts the (w, h) rectangle and + free up the remaining height. + */ + + Debug.Assert(h <= root.H); + + var r = root; + var extension = new Node(r.W, 0, w, r.H); + extension.Split(w, h); + root = new Node(0, 0, r.W + w, r.H, (r, extension)); + return extension; + } + + private Node growDown(int w, int h) + { + /* + Growing right creates a new node to the bottom of the current root + as well as a new root containing both. + + 0,0-----------+ + | old root | + +------------+ + | extension | + | (height h) | + +------------+ + + We then split that extension which inserts the (w, h) rectangle and + free up the remaining width. + */ + + Debug.Assert(w <= root.W); + + var r = root; + var extension = new Node(0, r.H, r.W, h); + extension.Split(w, h); + root = new Node(0, 0, r.W, r.H + h, (extension, r)); + return extension; + } + + private class Node + { + private (Node Child1, Node Child2)? split; + + public int X { get; } + public int Y { get; } + public int W { get; } + public int H { get; } - Debug.Assert(h <= root.H); + public Node(int x, int y, int w, int h, (Node, Node)? split = null) + { + this.split = split; + X = x; + Y = y; + W = w; + H = h; + } - var r = root; - var extension = new Node(r.W, 0, w, r.H); - extension.Split(w, h); - root = new Node(0, 0, r.W + w, r.H, (r, extension)); - return extension; + public Node? FindNodeWithSpaceFor(int w, int h) + { + if (split is var (c1, c2)) + return c1.FindNodeWithSpaceFor(w, h) ?? c2.FindNodeWithSpaceFor(w, h); + if (w <= W && h <= H) + return this; + return null; } - private Node growDown(int w, int h) + public void Split(int w, int h) { /* - Growing right creates a new node to the bottom of the current root - as well as a new root containing both. - - 0,0-----------+ - | old root | - +------------+ - | extension | - | (height h) | - +------------+ - - We then split that extension which inserts the (w, h) rectangle and - free up the remaining width. + Splitting a node semantically means reserving a (w, h) area and + divvying up the remaining space as empty nodes as indicated in this diagram. + + X,Y-----+------+ + | w | | + |h |child2| + | | | + +------+------+ + | child1 | + +------+------+ */ - - Debug.Assert(w <= root.W); - - var r = root; - var extension = new Node(0, r.H, r.W, h); - extension.Split(w, h); - root = new Node(0, 0, r.W, r.H + h, (extension, r)); - return extension; + split = ( + new Node(X, Y + h, W, H - h), + new Node(X + w, Y, W - w, h) + ); } - private class Node + public int CountEmptyPixels() { - private (Node Child1, Node Child2)? split; - - public int X { get; } - public int Y { get; } - public int W { get; } - public int H { get; } - - public Node(int x, int y, int w, int h, (Node, Node)? split = null) - { - this.split = split; - X = x; - Y = y; - W = w; - H = h; - } - - public Node? FindNodeWithSpaceFor(int w, int h) - { - if (split is var (c1, c2)) - return c1.FindNodeWithSpaceFor(w, h) ?? c2.FindNodeWithSpaceFor(w, h); - if (w <= W && h <= H) - return this; - return null; - } - - public void Split(int w, int h) - { - /* - Splitting a node semantically means reserving a (w, h) area and - divvying up the remaining space as empty nodes as indicated in this diagram. - - X,Y-----+------+ - | w | | - |h |child2| - | | | - +------+------+ - | child1 | - +------+------+ - */ - split = ( - new Node(X, Y + h, W, H - h), - new Node(X + w, Y, W - w, h) - ); - } - - public int CountEmptyPixels() - { - if (split is var (down, right)) - return down.CountEmptyPixels() + right.CountEmptyPixels(); - return W * H; - } + if (split is var (down, right)) + return down.CountEmptyPixels() + right.CountEmptyPixels(); + return W * H; } } } diff --git a/Bearded.Utilities/Algorithms/BinPacking.BinPacking.Types.cs b/Bearded.Utilities/Algorithms/BinPacking.BinPacking.Types.cs index faee130c..21614fd3 100644 --- a/Bearded.Utilities/Algorithms/BinPacking.BinPacking.Types.cs +++ b/Bearded.Utilities/Algorithms/BinPacking.BinPacking.Types.cs @@ -1,105 +1,104 @@ using System.Collections.Generic; using System.Collections.ObjectModel; -namespace Bearded.Utilities.Algorithms +namespace Bearded.Utilities.Algorithms; + +public static partial class BinPacking { - public static partial class BinPacking + public interface IRectangle { - public interface IRectangle - { - public int Width { get; } - public int Height { get; } - } + public int Width { get; } + public int Height { get; } + } - /// - /// Input rectangle type for the packing algorithm. - /// - /// Type of custom user data associated with the rectangle. - public sealed class Rectangle : IRectangle - { - public T Value { get; } - public int Width { get; } - public int Height { get; } + /// + /// Input rectangle type for the packing algorithm. + /// + /// Type of custom user data associated with the rectangle. + public sealed class Rectangle : IRectangle + { + public T Value { get; } + public int Width { get; } + public int Height { get; } - public Rectangle(T value, int width, int height) - { - Value = value; - Width = width; - Height = height; - } + public Rectangle(T value, int width, int height) + { + Value = value; + Width = width; + Height = height; } + } - /// - /// Output rectangle type of the packing algorithm. - /// - /// Type of custom user data associated with the rectangle. - public sealed class PositionedRectangle - { - private readonly Rectangle rectangle; + /// + /// Output rectangle type of the packing algorithm. + /// + /// Type of custom user data associated with the rectangle. + public sealed class PositionedRectangle + { + private readonly Rectangle rectangle; - public int X { get; } - public int Y { get; } - public T Value => rectangle.Value; - public int Width => rectangle.Width; - public int Height => rectangle.Height; + public int X { get; } + public int Y { get; } + public T Value => rectangle.Value; + public int Width => rectangle.Width; + public int Height => rectangle.Height; - internal PositionedRectangle(Rectangle rectangle, int x, int y) - { - this.rectangle = rectangle; - X = x; - Y = y; - } + internal PositionedRectangle(Rectangle rectangle, int x, int y) + { + this.rectangle = rectangle; + X = x; + Y = y; } + } + /// + /// Result container of the rectangle packing algorithm. + /// + /// Type of custom user data associated with the rectangle. + public class Result + { /// - /// Result container of the rectangle packing algorithm. + /// The list of packed rectangles. /// - /// Type of custom user data associated with the rectangle. - public class Result - { - /// - /// The list of packed rectangles. - /// - public ReadOnlyCollection> Rectangles { get; } + public ReadOnlyCollection> Rectangles { get; } - /// - /// The width of the containing rectangle. - /// - public int Width { get; } + /// + /// The width of the containing rectangle. + /// + public int Width { get; } - /// - /// The height of the containing rectangle. - /// - public int Height { get; } + /// + /// The height of the containing rectangle. + /// + public int Height { get; } - /// - /// Number of pixels or grid cells of the containing rectangle wasted with this packing. - /// - public int EmptyPixels { get; } + /// + /// Number of pixels or grid cells of the containing rectangle wasted with this packing. + /// + public int EmptyPixels { get; } - /// - /// The total area of the containing rectangle (Width * Height). - /// - public int Area => Width * Height; + /// + /// The total area of the containing rectangle (Width * Height). + /// + public int Area => Width * Height; - /// - /// The fraction of pixels or grid cells of the containing rectangle filled by the packed rectangles. - /// Maximum is 1 for a perfect filling. - /// - public double Filled => 1 - EmptyPixels / (double) Area; + /// + /// The fraction of pixels or grid cells of the containing rectangle filled by the packed rectangles. + /// Maximum is 1 for a perfect filling. + /// + public double Filled => 1 - EmptyPixels / (double) Area; - internal static Result Empty { get; } = new Result( - new ReadOnlyCollection>(new List>()), - 0, 0, 0); + internal static Result Empty { get; } = new Result( + new ReadOnlyCollection>(new List>()), + 0, 0, 0); - internal Result(ReadOnlyCollection> rectangles, int width, int height, - int emptyPixels) - { - Rectangles = rectangles; - Width = width; - Height = height; - EmptyPixels = emptyPixels; - } + internal Result(ReadOnlyCollection> rectangles, int width, int height, + int emptyPixels) + { + Rectangles = rectangles; + Width = width; + Height = height; + EmptyPixels = emptyPixels; } } } diff --git a/Bearded.Utilities/Algorithms/BinPacking.cs b/Bearded.Utilities/Algorithms/BinPacking.cs index 1a6b91c2..97073b74 100644 --- a/Bearded.Utilities/Algorithms/BinPacking.cs +++ b/Bearded.Utilities/Algorithms/BinPacking.cs @@ -3,125 +3,124 @@ using System.Drawing; using System.Linq; -namespace Bearded.Utilities.Algorithms +namespace Bearded.Utilities.Algorithms; + +/// +/// This class contains the functionality to efficiently pack a list of rectangles into one larger rectangle. +/// It tries to pack them tightly and minimizes wasted space in the containing rectangle. +/// The result is not guaranteed to be optimal. +/// +/// The algorithm implemented is an almost 1:1 translation of the Binary Tree Bin Packing Algorithm by +/// Jake Gordon: http://codeincomplete.com/posts/2011/5/7/bin_packing/ +/// +public static partial class BinPacking { /// - /// This class contains the functionality to efficiently pack a list of rectangles into one larger rectangle. - /// It tries to pack them tightly and minimizes wasted space in the containing rectangle. - /// The result is not guaranteed to be optimal. - /// - /// The algorithm implemented is an almost 1:1 translation of the Binary Tree Bin Packing Algorithm by - /// Jake Gordon: http://codeincomplete.com/posts/2011/5/7/bin_packing/ + /// Packs a list of rectangles together trying to minimize the size of the containing rectangle. /// - public static partial class BinPacking + /// is null. + /// is null. + public static Result Pack(IEnumerable items, Func sizeSelector) { - /// - /// Packs a list of rectangles together trying to minimize the size of the containing rectangle. - /// - /// is null. - /// is null. - public static Result Pack(IEnumerable items, Func sizeSelector) - { - return validateArgumentsAndPack(items, sizeSelector, defaultHeuristic); - } + return validateArgumentsAndPack(items, sizeSelector, defaultHeuristic); + } - /// - /// Packs a list of rectangles together trying to minimize the size of the containing rectangle. - /// - /// is null. - public static Result Pack(IEnumerable> rectangles) - { - return validateArgumentsAndPack(rectangles, defaultHeuristic); - } + /// + /// Packs a list of rectangles together trying to minimize the size of the containing rectangle. + /// + /// is null. + public static Result Pack(IEnumerable> rectangles) + { + return validateArgumentsAndPack(rectangles, defaultHeuristic); + } - /// - /// Packs a list of rectangles together trying to minimize the size of the containing rectangle. - /// Unlike Pack(), this method tries several heuristics and returns the result with the densest packing. - /// - /// is null. - /// is null. - public static Result PackWithMultipleHeuristics(IEnumerable items, Func sizeSelector) - { - return validateArgumentsAndPack(items, sizeSelector, defaultHeuristic); - } + /// + /// Packs a list of rectangles together trying to minimize the size of the containing rectangle. + /// Unlike Pack(), this method tries several heuristics and returns the result with the densest packing. + /// + /// is null. + /// is null. + public static Result PackWithMultipleHeuristics(IEnumerable items, Func sizeSelector) + { + return validateArgumentsAndPack(items, sizeSelector, defaultHeuristic); + } - /// - /// Packs a list of rectangles together trying to minimize the size of the containing rectangle. - /// Unlike Pack(), this method tries several heuristics and returns the result with the densest packing. - /// - /// is null. - public static Result PackWithMultipleHeuristics(IEnumerable> rectangles) - { - return validateArgumentsAndPack(rectangles, allHeuristics); - } + /// + /// Packs a list of rectangles together trying to minimize the size of the containing rectangle. + /// Unlike Pack(), this method tries several heuristics and returns the result with the densest packing. + /// + /// is null. + public static Result PackWithMultipleHeuristics(IEnumerable> rectangles) + { + return validateArgumentsAndPack(rectangles, allHeuristics); + } - private static ICollection> defaultHeuristic { get; } - = new List> - { - (r1, r2) => (r2.Width * r2.Height).CompareTo(r1.Width * r1.Height) - }.AsReadOnly(); - private static ICollection> allHeuristics { get; } - = new List> - { - (r1, r2) => (r2.Width * r2.Height).CompareTo(r1.Width * r1.Height), - (r1, r2) => r2.Width.CompareTo(r1.Width), - (r1, r2) => r2.Height.CompareTo(r1.Height), - }.AsReadOnly(); - - private static Result validateArgumentsAndPack( - IEnumerable items, Func sizeSelector, ICollection> heuristics) + private static ICollection> defaultHeuristic { get; } + = new List> { - if (items == null) - throw new ArgumentNullException(nameof(items)); - if (sizeSelector == null) - throw new ArgumentNullException(nameof(sizeSelector)); - if (heuristics == null) - throw new ArgumentNullException(nameof(heuristics)); - if (heuristics.Count == 0) - throw new ArgumentException("Must provide at least one heuristic.", nameof(heuristics)); - - var rectangleList = items.Select(i => - { - var size = sizeSelector(i); - return new Rectangle(i, size.Width, size.Height); - }).ToList(); - - return packImplementation(rectangleList, heuristics); - } + (r1, r2) => (r2.Width * r2.Height).CompareTo(r1.Width * r1.Height) + }.AsReadOnly(); + private static ICollection> allHeuristics { get; } + = new List> + { + (r1, r2) => (r2.Width * r2.Height).CompareTo(r1.Width * r1.Height), + (r1, r2) => r2.Width.CompareTo(r1.Width), + (r1, r2) => r2.Height.CompareTo(r1.Height), + }.AsReadOnly(); - private static Result validateArgumentsAndPack( - IEnumerable> rectangles, ICollection> heuristics) + private static Result validateArgumentsAndPack( + IEnumerable items, Func sizeSelector, ICollection> heuristics) + { + if (items == null) + throw new ArgumentNullException(nameof(items)); + if (sizeSelector == null) + throw new ArgumentNullException(nameof(sizeSelector)); + if (heuristics == null) + throw new ArgumentNullException(nameof(heuristics)); + if (heuristics.Count == 0) + throw new ArgumentException("Must provide at least one heuristic.", nameof(heuristics)); + + var rectangleList = items.Select(i => { - if (rectangles == null) - throw new ArgumentNullException(nameof(rectangles)); - if (heuristics == null) - throw new ArgumentNullException(nameof(heuristics)); - if (heuristics.Count == 0) - throw new ArgumentException("Must provide at least one heuristic.", nameof(heuristics)); + var size = sizeSelector(i); + return new Rectangle(i, size.Width, size.Height); + }).ToList(); + + return packImplementation(rectangleList, heuristics); + } - var rectangleList = rectangles.ToList(); + private static Result validateArgumentsAndPack( + IEnumerable> rectangles, ICollection> heuristics) + { + if (rectangles == null) + throw new ArgumentNullException(nameof(rectangles)); + if (heuristics == null) + throw new ArgumentNullException(nameof(heuristics)); + if (heuristics.Count == 0) + throw new ArgumentException("Must provide at least one heuristic.", nameof(heuristics)); - return packImplementation(rectangleList, heuristics); - } + var rectangleList = rectangles.ToList(); - private static Result packImplementation( - List> rectangles, IEnumerable> heuristics) - { - if (rectangles.Count == 0) - return Result.Empty; + return packImplementation(rectangleList, heuristics); + } - Result bestResult = default!; + private static Result packImplementation( + List> rectangles, IEnumerable> heuristics) + { + if (rectangles.Count == 0) + return Result.Empty; - foreach (var heuristic in heuristics) - { - rectangles.Sort(heuristic); - var result = new Tree(rectangles).Fit(); + Result bestResult = default!; - if (bestResult == null || bestResult.Filled < result.Filled) - bestResult = result; - } + foreach (var heuristic in heuristics) + { + rectangles.Sort(heuristic); + var result = new Tree(rectangles).Fit(); - return bestResult; + if (bestResult == null || bestResult.Filled < result.Filled) + bestResult = result; } + + return bestResult; } } diff --git a/Bearded.Utilities/Algorithms/CoffmanGraham.cs b/Bearded.Utilities/Algorithms/CoffmanGraham.cs index b323f8ae..4bd76938 100644 --- a/Bearded.Utilities/Algorithms/CoffmanGraham.cs +++ b/Bearded.Utilities/Algorithms/CoffmanGraham.cs @@ -4,212 +4,212 @@ using System.Linq; using Bearded.Utilities.Graphs; -namespace Bearded.Utilities.Algorithms +namespace Bearded.Utilities.Algorithms; + +using Linq; +/// +/// This class contains logic to take a partially ordered set of elements and split it in a sequence of layers such +/// that the following holds: +/// +/// +/// +/// +/// If an element is smaller than another in the partial ordering, it will be on a lower level. +/// +/// +/// +/// Every layer contains at most a fixed number of W elements. +/// +/// +/// +/// +/// The input is given as a directed acyclic graph, such that there is an arrow from x to y if x is smaller than y. +/// +/// +/// +/// The algorithm itself takes O(n^2), but requires an order that is transitively reduced. If the provided graph is +/// not representing a transitively reduced ordering, the algorithm will create one. The runtime for the +/// transitive reduction of the directed acyclic graph is not included in the runtime, and is not known to be +/// possible in O(n^2). +/// +/// +public static class CoffmanGraham { - using Linq; - /// - /// This class contains logic to take a partially ordered set of elements and split it in a sequence of layers such - /// that the following holds: - /// - /// - /// - /// - /// If an element is smaller than another in the partial ordering, it will be on a lower level. - /// - /// - /// - /// Every layer contains at most a fixed number of W elements. - /// - /// - /// - /// - /// The input is given as a directed acyclic graph, such that there is an arrow from x to y if x is smaller than y. - /// - /// - /// - /// The algorithm itself takes O(n^2), but requires an order that is transitively reduced. If the provided graph is - /// not representing a transitively reduced ordering, the algorithm will create one. The runtime for the - /// transitive reduction of the directed acyclic graph is not included in the runtime, and is not known to be - /// possible in O(n^2). - /// - /// - public static class CoffmanGraham + public interface ISolver { - public interface ISolver + ImmutableList> Solve(IDirectedAcyclicGraph graph) + where T : IEquatable; + } + + private class ArbitraryGraphSolver : ISolver + { + private readonly ReducedGraphSolver internalSolver; + + internal ArbitraryGraphSolver(int maxLayerSize) { - ImmutableList> Solve(IDirectedAcyclicGraph graph) - where T : IEquatable; + internalSolver = new ReducedGraphSolver(maxLayerSize); } - private class ArbitraryGraphSolver : ISolver + public ImmutableList> Solve(IDirectedAcyclicGraph graph) + where T : IEquatable { - private readonly ReducedGraphSolver internalSolver; + var reducedGraph = DirectedAcyclicGraphTransitiveReducer.ReduceGraph(graph); + return internalSolver.Solve(reducedGraph); + } + } - internal ArbitraryGraphSolver(int maxLayerSize) - { - internalSolver = new ReducedGraphSolver(maxLayerSize); - } + private class ReducedGraphSolver : ISolver + { + private readonly int maxLayerSize; - public ImmutableList> Solve(IDirectedAcyclicGraph graph) - where T : IEquatable - { - var reducedGraph = DirectedAcyclicGraphTransitiveReducer.ReduceGraph(graph); - return internalSolver.Solve(reducedGraph); - } + internal ReducedGraphSolver(int maxLayerSize) + { + this.maxLayerSize = maxLayerSize; } - private class ReducedGraphSolver : ISolver + public ImmutableList> Solve(IDirectedAcyclicGraph graph) + where T : IEquatable { - private readonly int maxLayerSize; + if (graph.Count == 0) return ImmutableList>.Empty; - internal ReducedGraphSolver(int maxLayerSize) - { - this.maxLayerSize = maxLayerSize; - } + var ordering = createTopologicalOrdering(graph); + return createLayers(graph, ordering, maxLayerSize); + } - public ImmutableList> Solve(IDirectedAcyclicGraph graph) - where T : IEquatable - { - if (graph.Count == 0) return ImmutableList>.Empty; + // ReSharper disable once SuggestBaseTypeForParameter + private static IList createTopologicalOrdering(IDirectedAcyclicGraph graph) + where T : IEquatable + { + // The topological ordering from low to high. + var ordering = new List(graph.Count); - var ordering = createTopologicalOrdering(graph); - return createLayers(graph, ordering, maxLayerSize); - } + var elements = new HashSet(graph.Elements); + var orderedElementIndices = new Dictionary(); - // ReSharper disable once SuggestBaseTypeForParameter - private static IList createTopologicalOrdering(IDirectedAcyclicGraph graph) - where T : IEquatable + while (ordering.Count < graph.Count) { - // The topological ordering from low to high. - var ordering = new List(graph.Count); + // All the remaining elements which have all their predecessors added to the topological ordering + // already. This includes elements which have no predecessors at all. This is equivalent to the set + // of sources in a directed acyclic graph where all already elements and adjacent arrows have been + // removed. + var sources = elements.Where(allPredecessorsHaveBeenOrdered); + + // For each considered element, we look at the place of all their predecessors in the partial + // topological ordering we have constructed so far. + // * We first consider the most recently added predecessor of each element. That is, the predecessor + // of said element that is the highest in the current partial topological ordering. + // * Of all these predecessors, we pick the predecessor that is lowest in the ordering. If + // there is only one element with said predecessor, that element is added to the ordering next. + // * Ties are broken by looking at the next highest ordered predecessor of all tied elements. + // * If all predecessors of 2 or more elements are the same, we pick the element with the least + // predecessors. + // This selection process is implemented by representing the predecessors of an element as a + // decreasing number sequence of the predecessors' indices, and selecting the lowest of those in a + // reflected lexicographic ordering. + var next = sources.MinBy(createDecreasingNumberSequenceOfPredecessorIndices); + + orderedElementIndices.Add(next, ordering.Count); + ordering.Add(next); + elements.Remove(next); + } - var elements = new HashSet(graph.Elements); - var orderedElementIndices = new Dictionary(); + return ordering; - while (ordering.Count < graph.Count) - { - // All the remaining elements which have all their predecessors added to the topological ordering - // already. This includes elements which have no predecessors at all. This is equivalent to the set - // of sources in a directed acyclic graph where all already elements and adjacent arrows have been - // removed. - var sources = elements.Where(allPredecessorsHaveBeenOrdered); - - // For each considered element, we look at the place of all their predecessors in the partial - // topological ordering we have constructed so far. - // * We first consider the most recently added predecessor of each element. That is, the predecessor - // of said element that is the highest in the current partial topological ordering. - // * Of all these predecessors, we pick the predecessor that is lowest in the ordering. If - // there is only one element with said predecessor, that element is added to the ordering next. - // * Ties are broken by looking at the next highest ordered predecessor of all tied elements. - // * If all predecessors of 2 or more elements are the same, we pick the element with the least - // predecessors. - // This selection process is implemented by representing the predecessors of an element as a - // decreasing number sequence of the predecessors' indices, and selecting the lowest of those in a - // reflected lexicographic ordering. - var next = sources.MinBy(createDecreasingNumberSequenceOfPredecessorIndices); - - orderedElementIndices.Add(next, ordering.Count); - ordering.Add(next); - elements.Remove(next); - } + bool allPredecessorsHaveBeenOrdered(T e) => graph.GetDirectPredecessorsOf(e) + .All(n => orderedElementIndices.ContainsKey(n)); - return ordering; + DecreasingNumberSequence createDecreasingNumberSequenceOfPredecessorIndices(T e) + { + var predecessorIndices = graph.GetDirectPredecessorsOf(e).Select(n => orderedElementIndices[n]); + return DecreasingNumberSequence.FromUnsortedNumbers(predecessorIndices); + } + } - bool allPredecessorsHaveBeenOrdered(T e) => graph.GetDirectPredecessorsOf(e) - .All(n => orderedElementIndices.ContainsKey(n)); + private static ImmutableList> createLayers( + // ReSharper disable once SuggestBaseTypeForParameter + IDirectedAcyclicGraph graph, IList ordering, int maxLayerSize) + where T : IEquatable + { + var layersReversed = new List.Builder>(); + var elementToLayer = new Dictionary(); - DecreasingNumberSequence createDecreasingNumberSequenceOfPredecessorIndices(T e) + for (var i = ordering.Count - 1; i >= 0; i--) + { + // We fill in the layers from the last to the first (or bottom to top, if using the traditional + // representation where parents go above their children). For each element, we always choose the + // layer closest to the last (or lowest), such that all its successors are in a later (or lower) + // layer, and the layer has less than W elements. + // The list represents the layers in reverse order, meaning that the last layer of our result is the + // first element in the list. This allows us to use the automatic growing property of the list. + // Combining these two things, the following looks for the lowest integer k such that (1) each + // successor is in a set with an index lower lower than k, and (2) the set at index k has less than + // W elements. + + // Requirement (1) + var highestSuccessorLevel = graph.GetDirectSuccessorsOf(ordering[i]) + .Select(elmt => elementToLayer[elmt] + 1) + .Aggregate(0, Math.Max); + + // Requirement (2) + var candidateLayer = highestSuccessorLevel; + while ( + candidateLayer < layersReversed.Count && layersReversed[candidateLayer].Count == maxLayerSize) { - var predecessorIndices = graph.GetDirectPredecessorsOf(e).Select(n => orderedElementIndices[n]); - return DecreasingNumberSequence.FromUnsortedNumbers(predecessorIndices); + candidateLayer++; } - } - private static ImmutableList> createLayers( - // ReSharper disable once SuggestBaseTypeForParameter - IDirectedAcyclicGraph graph, IList ordering, int maxLayerSize) - where T : IEquatable - { - var layersReversed = new List.Builder>(); - var elementToLayer = new Dictionary(); - - for (var i = ordering.Count - 1; i >= 0; i--) + // Expand the list as needed + while (candidateLayer >= layersReversed.Count) { - // We fill in the layers from the last to the first (or bottom to top, if using the traditional - // representation where parents go above their children). For each element, we always choose the - // layer closest to the last (or lowest), such that all its successors are in a later (or lower) - // layer, and the layer has less than W elements. - // The list represents the layers in reverse order, meaning that the last layer of our result is the - // first element in the list. This allows us to use the automatic growing property of the list. - // Combining these two things, the following looks for the lowest integer k such that (1) each - // successor is in a set with an index lower lower than k, and (2) the set at index k has less than - // W elements. - - // Requirement (1) - var highestSuccessorLevel = graph.GetDirectSuccessorsOf(ordering[i]) - .Select(elmt => elementToLayer[elmt] + 1) - .Aggregate(0, Math.Max); - - // Requirement (2) - var candidateLayer = highestSuccessorLevel; - while ( - candidateLayer < layersReversed.Count && layersReversed[candidateLayer].Count == maxLayerSize) - { - candidateLayer++; - } - - // Expand the list as needed - while (candidateLayer >= layersReversed.Count) - { - layersReversed.Add(ImmutableHashSet.CreateBuilder()); - } - - // Add the element to the layer - layersReversed[candidateLayer].Add(ordering[i]); - elementToLayer.Add(ordering[i], candidateLayer); + layersReversed.Add(ImmutableHashSet.CreateBuilder()); } - return ImmutableList.CreateRange(layersReversed.Select(b => b.ToImmutable()).Reverse()); + // Add the element to the layer + layersReversed[candidateLayer].Add(ordering[i]); + elementToLayer.Add(ordering[i], candidateLayer); } + + return ImmutableList.CreateRange(layersReversed.Select(b => b.ToImmutable()).Reverse()); } + } - public static ISolver SolverForArbitraryGraphs(int maxLayerSize) => new ArbitraryGraphSolver(maxLayerSize); + public static ISolver SolverForArbitraryGraphs(int maxLayerSize) => new ArbitraryGraphSolver(maxLayerSize); - public static ISolver SolverForReducedGraphs(int maxLayerSize) => new ReducedGraphSolver(maxLayerSize); + public static ISolver SolverForReducedGraphs(int maxLayerSize) => new ReducedGraphSolver(maxLayerSize); - private struct DecreasingNumberSequence : IComparable, IComparable - { - private readonly ImmutableList numbers; + private struct DecreasingNumberSequence : IComparable, IComparable + { + private readonly ImmutableList numbers; - private DecreasingNumberSequence(ImmutableList numbers) - { - this.numbers = numbers; - } + private DecreasingNumberSequence(ImmutableList numbers) + { + this.numbers = numbers; + } - public int CompareTo(object obj) => CompareTo((DecreasingNumberSequence) obj); + public int CompareTo(object obj) => CompareTo((DecreasingNumberSequence) obj); - public int CompareTo(DecreasingNumberSequence other) + public int CompareTo(DecreasingNumberSequence other) + { + // The comparison between decreasing number sequences is implemented as a reflected lexicographic order. + // That is, a number sequence n_0...n_i is considered smaller than a sequence m_0...m_j if either: + // * there exists a s >=0 such that n_k = m_k for 0 <= k < s and n_s < m_s, or + // * n_k = m_k for 0 <= k <= min(i, j) and i < j. + for (var i = 0; i < Math.Min(numbers.Count, other.numbers.Count); i++) { - // The comparison between decreasing number sequences is implemented as a reflected lexicographic order. - // That is, a number sequence n_0...n_i is considered smaller than a sequence m_0...m_j if either: - // * there exists a s >=0 such that n_k = m_k for 0 <= k < s and n_s < m_s, or - // * n_k = m_k for 0 <= k <= min(i, j) and i < j. - for (var i = 0; i < Math.Min(numbers.Count, other.numbers.Count); i++) + if (numbers[i] != other.numbers[i]) { - if (numbers[i] != other.numbers[i]) - { - return numbers[i].CompareTo(other.numbers[i]); - } + return numbers[i].CompareTo(other.numbers[i]); } - - return numbers.Count - other.numbers.Count; } - public static DecreasingNumberSequence FromSortedNumbers(IEnumerable numbers) - => new DecreasingNumberSequence(numbers.ToImmutableList()); - - public static DecreasingNumberSequence FromUnsortedNumbers(IEnumerable numbers) - => new DecreasingNumberSequence(numbers.OrderByDescending(a => a).ToImmutableList()); + return numbers.Count - other.numbers.Count; } + + public static DecreasingNumberSequence FromSortedNumbers(IEnumerable numbers) + => new DecreasingNumberSequence(numbers.ToImmutableList()); + + public static DecreasingNumberSequence FromUnsortedNumbers(IEnumerable numbers) + => new DecreasingNumberSequence(numbers.OrderByDescending(a => a).ToImmutableList()); } } + diff --git a/Bearded.Utilities/Algorithms/HungarianAlgorithm.cs b/Bearded.Utilities/Algorithms/HungarianAlgorithm.cs index 797bdca6..ff86af51 100644 --- a/Bearded.Utilities/Algorithms/HungarianAlgorithm.cs +++ b/Bearded.Utilities/Algorithms/HungarianAlgorithm.cs @@ -2,419 +2,418 @@ using System.Linq; using OpenTK.Mathematics; -namespace Bearded.Utilities.Algorithms +namespace Bearded.Utilities.Algorithms; + +/// +/// The Hungarian algorithm is a cubic algorithm that solves the assignment problem. +/// The algorithm calculates a maximum matching between workers and jobs, while minimising the total cost. +/// +/// The original implementation was by Kevin L. Stern. +public sealed class HungarianAlgorithm { + #region Public static interface /// - /// The Hungarian algorithm is a cubic algorithm that solves the assignment problem. - /// The algorithm calculates a maximum matching between workers and jobs, while minimising the total cost. + /// Evaluates a minimal cost matching using the given cost matrix. /// - /// The original implementation was by Kevin L. Stern. - public sealed class HungarianAlgorithm + /// The cost matrix, where matrix[i, j] holds the cost of assigning worker i to job j, + /// for all i, j. Needs to be square. + /// The minimal cost matching based on the specified cost matrix. + public static int[] Run(float[,] costMatrix) { - #region Public static interface - /// - /// Evaluates a minimal cost matching using the given cost matrix. - /// - /// The cost matrix, where matrix[i, j] holds the cost of assigning worker i to job j, - /// for all i, j. Needs to be square. - /// The minimal cost matching based on the specified cost matrix. - public static int[] Run(float[,] costMatrix) - { - var instance = new HungarianAlgorithm(costMatrix); - return instance.execute(); - } + var instance = new HungarianAlgorithm(costMatrix); + return instance.execute(); + } - /// - /// Evaluates a minimal cost matching using the given metric. - /// - /// The type representing the workers. - /// The type representing the jobs. - /// The elements representing the workers. - /// The elements representing the jobs. - /// A function that calculates the cost of assigning a job to a worker. - /// The minimal cost matching based on the specified metric. - public static int[] Run(TWorker[] workers, TJob[] jobs, Func getCost) - { - var costMatrix = new float[workers.Length, jobs.Length]; - for (var t = 0; t < jobs.Length; t++) - for (var s = 0; s < workers.Length; s++) - costMatrix[s, t] = getCost(workers[s], jobs[t]); + /// + /// Evaluates a minimal cost matching using the given metric. + /// + /// The type representing the workers. + /// The type representing the jobs. + /// The elements representing the workers. + /// The elements representing the jobs. + /// A function that calculates the cost of assigning a job to a worker. + /// The minimal cost matching based on the specified metric. + public static int[] Run(TWorker[] workers, TJob[] jobs, Func getCost) + { + var costMatrix = new float[workers.Length, jobs.Length]; + for (var t = 0; t < jobs.Length; t++) + for (var s = 0; s < workers.Length; s++) + costMatrix[s, t] = getCost(workers[s], jobs[t]); - return Run(costMatrix); - } + return Run(costMatrix); + } - /// - /// Evaluates a minimal cost matching on two-dimensional vectors using the least-squares metric. - /// - /// The source vectors. - /// The destination vectors. - /// The minimal cost matching based on the least-squares metric. - public static int[] Run(Vector2[] from, Vector2[] to) - { - return Run(from, to, (u, v) => (v - u).LengthSquared); - } + /// + /// Evaluates a minimal cost matching on two-dimensional vectors using the least-squares metric. + /// + /// The source vectors. + /// The destination vectors. + /// The minimal cost matching based on the least-squares metric. + public static int[] Run(Vector2[] from, Vector2[] to) + { + return Run(from, to, (u, v) => (v - u).LengthSquared); + } - /// - /// Evaluates a minimal cost matching on two-dimensional vectors using the least-squares metric. - /// - /// The source vectors. - /// The destination vectors. - /// The minimal cost matching based on the least-squares metric. - public static int[] Run(Vector3[] from, Vector3[] to) + /// + /// Evaluates a minimal cost matching on two-dimensional vectors using the least-squares metric. + /// + /// The source vectors. + /// The destination vectors. + /// The minimal cost matching based on the least-squares metric. + public static int[] Run(Vector3[] from, Vector3[] to) + { + return Run(from, to, (u, v) => (v - u).LengthSquared); + } + #endregion + + #region Implementation + private readonly float[,] costMatrix; + private readonly int dimension; + private readonly float[] sourceLabels; + private readonly float[] targetLabels; + private readonly int[] minSlackTargetBySource; + private readonly float[] minSlackValueBySource; + private readonly int[] sourceMatches; + private readonly int[] targetMatches; + private readonly int[] parentSourceByCommittedTarget; + private readonly bool[] matchedSources; + + private HungarianAlgorithm(float[,] costMatrix) + { + if (costMatrix.GetLength(0) != costMatrix.GetLength(1)) + throw new ArgumentException("Hungarian algorithm requires a square cost matrix.", nameof(costMatrix)); + if (costMatrix.Cast().Any(f => float.IsInfinity(f) || float.IsNaN(f))) + throw new ArgumentException("Received an infinite or NaN cost in the cost matrix.", nameof(costMatrix)); + this.costMatrix = costMatrix; + dimension = costMatrix.GetLength(0); + sourceLabels = new float[dimension]; + targetLabels = new float[dimension]; + minSlackTargetBySource = new int[dimension]; + minSlackValueBySource = new float[dimension]; + matchedSources = new bool[dimension]; + parentSourceByCommittedTarget = new int[dimension]; + sourceMatches = new int[dimension]; + targetMatches = new int[dimension]; + + for (var i = 0; i < dimension; i++) { - return Run(from, to, (u, v) => (v - u).LengthSquared); + sourceMatches[i] = -1; + targetMatches[i] = -1; } - #endregion - - #region Implementation - private readonly float[,] costMatrix; - private readonly int dimension; - private readonly float[] sourceLabels; - private readonly float[] targetLabels; - private readonly int[] minSlackTargetBySource; - private readonly float[] minSlackValueBySource; - private readonly int[] sourceMatches; - private readonly int[] targetMatches; - private readonly int[] parentSourceByCommittedTarget; - private readonly bool[] matchedSources; - - private HungarianAlgorithm(float[,] costMatrix) + } + + private int[] execute() + { + /* + * Heuristics to improve performance: Reduce rows and columns by their + * smallest element, compute an initial non-zero dual feasible solution and + * create a greedy matching from workers to jobs of the cost matrix. + **/ + reduce(); + computeInitialFeasibleSolution(); + greedyMatch(); + + var t = firstUnmatchedSource(); + while (t < dimension) { - if (costMatrix.GetLength(0) != costMatrix.GetLength(1)) - throw new ArgumentException("Hungarian algorithm requires a square cost matrix.", nameof(costMatrix)); - if (costMatrix.Cast().Any(f => float.IsInfinity(f) || float.IsNaN(f))) - throw new ArgumentException("Received an infinite or NaN cost in the cost matrix.", nameof(costMatrix)); - this.costMatrix = costMatrix; - dimension = costMatrix.GetLength(0); - sourceLabels = new float[dimension]; - targetLabels = new float[dimension]; - minSlackTargetBySource = new int[dimension]; - minSlackValueBySource = new float[dimension]; - matchedSources = new bool[dimension]; - parentSourceByCommittedTarget = new int[dimension]; - sourceMatches = new int[dimension]; - targetMatches = new int[dimension]; - - for (var i = 0; i < dimension; i++) - { - sourceMatches[i] = -1; - targetMatches[i] = -1; - } + initializePhase(t); + executePhase(); + t = firstUnmatchedSource(); } - private int[] execute() - { - /* - * Heuristics to improve performance: Reduce rows and columns by their - * smallest element, compute an initial non-zero dual feasible solution and - * create a greedy matching from workers to jobs of the cost matrix. - **/ - reduce(); - computeInitialFeasibleSolution(); - greedyMatch(); - - var t = firstUnmatchedSource(); - while (t < dimension) - { - initializePhase(t); - executePhase(); - t = firstUnmatchedSource(); - } + var result = new int[dimension]; + Array.Copy(sourceMatches, result, dimension); + for (var i = 0; i < result.Length; i++) + if (result[i] >= dimension) + result[i] = -1; - var result = new int[dimension]; - Array.Copy(sourceMatches, result, dimension); - for (var i = 0; i < result.Length; i++) - if (result[i] >= dimension) - result[i] = -1; + return result; + } - return result; - } + /// + /// Reduces the cost matrix by subtracting the smallest element of each row from + /// all elements of the row as well as the smallest element of each column from + /// all elements of the column. + /// Note that an optimal assignment for a reduced cost matrix is optimal for the + /// original cost matrix. + /// + private void reduce() + { + reduceRows(); + reduceColumns(); + } - /// - /// Reduces the cost matrix by subtracting the smallest element of each row from - /// all elements of the row as well as the smallest element of each column from - /// all elements of the column. - /// Note that an optimal assignment for a reduced cost matrix is optimal for the - /// original cost matrix. - /// - private void reduce() + private void reduceRows() + { + for (var s = 0; s < dimension; s++) { - reduceRows(); - reduceColumns(); + var min = minimumInRow(s); + subsctractValueFromRow(s, min); } + } - private void reduceRows() + private float minimumInRow(int s) + { + var min = float.PositiveInfinity; + for (var t = 0; t < dimension; t++) { - for (var s = 0; s < dimension; s++) + if (costMatrix[s, t] < min) { - var min = minimumInRow(s); - subsctractValueFromRow(s, min); + min = costMatrix[s, t]; } } + return min; + } - private float minimumInRow(int s) + private void subsctractValueFromRow(int s, float value) + { + for (var t = 0; t < dimension; t++) { - var min = float.PositiveInfinity; - for (var t = 0; t < dimension; t++) - { - if (costMatrix[s, t] < min) - { - min = costMatrix[s, t]; - } - } - return min; + costMatrix[s, t] -= value; } + } - private void subsctractValueFromRow(int s, float value) - { - for (var t = 0; t < dimension; t++) - { - costMatrix[s, t] -= value; - } - } + private void reduceColumns() + { + var mins = findColumnMinimums(); + substractValuesFromColumns(mins); + } - private void reduceColumns() + private float[] findColumnMinimums() + { + var mins = new float[dimension]; + for (var t = 0; t < dimension; t++) { - var mins = findColumnMinimums(); - substractValuesFromColumns(mins); + mins[t] = float.PositiveInfinity; } - - private float[] findColumnMinimums() + for (var s = 0; s < dimension; s++) { - var mins = new float[dimension]; for (var t = 0; t < dimension; t++) { - mins[t] = float.PositiveInfinity; - } - for (var s = 0; s < dimension; s++) - { - for (var t = 0; t < dimension; t++) + if (costMatrix[s, t] < mins[t]) { - if (costMatrix[s, t] < mins[t]) - { - mins[t] = costMatrix[s, t]; - } + mins[t] = costMatrix[s, t]; } } - return mins; } + return mins; + } - private void substractValuesFromColumns(float[] values) + private void substractValuesFromColumns(float[] values) + { + for (var s = 0; s < dimension; s++) { - for (var s = 0; s < dimension; s++) + for (var t = 0; t < dimension; t++) { - for (var t = 0; t < dimension; t++) - { - costMatrix[s, t] -= values[t]; - } + costMatrix[s, t] -= values[t]; } } + } - /// - /// Compute an initial feasible solution by assigning zero labels to the workers and by assigning to each job a - /// label equal to the minimum cost among its incident edges. - /// - private void computeInitialFeasibleSolution() - { - for (var t = 0; t < dimension; t++) - targetLabels[t] = float.PositiveInfinity; + /// + /// Compute an initial feasible solution by assigning zero labels to the workers and by assigning to each job a + /// label equal to the minimum cost among its incident edges. + /// + private void computeInitialFeasibleSolution() + { + for (var t = 0; t < dimension; t++) + targetLabels[t] = float.PositiveInfinity; - for (var s = 0; s < dimension; s++) - for (var t = 0; t < dimension; t++) - if (costMatrix[s, t] < targetLabels[t]) - targetLabels[t] = costMatrix[s, t]; - } + for (var s = 0; s < dimension; s++) + for (var t = 0; t < dimension; t++) + if (costMatrix[s, t] < targetLabels[t]) + targetLabels[t] = costMatrix[s, t]; + } - /// - /// Greedily find a matching to start the algorithm with. - /// - private void greedyMatch() + /// + /// Greedily find a matching to start the algorithm with. + /// + private void greedyMatch() + { + for (var s = 0; s < dimension; s++) { - for (var s = 0; s < dimension; s++) + for (var t = 0; t < dimension; t++) { - for (var t = 0; t < dimension; t++) + if (sourceMatches[s] == -1 && targetMatches[t] == -1 + // ReSharper disable once CompareOfFloatsByEqualityOperator + && costMatrix[s, t] - sourceLabels[s] - targetLabels[t] == 0) { - if (sourceMatches[s] == -1 && targetMatches[t] == -1 - // ReSharper disable once CompareOfFloatsByEqualityOperator - && costMatrix[s, t] - sourceLabels[s] - targetLabels[t] == 0) - { - match(s, t); - } + match(s, t); } } } + } - /// - /// Initializes the next phase of the algorithm by clearing the committed - /// workers and jobs sets and by initializing the slack arrays to the values - /// corresponding to the specified root worker. - /// - /// The worker at which to root the next phase. - private void initializePhase(int s) + /// + /// Initializes the next phase of the algorithm by clearing the committed + /// workers and jobs sets and by initializing the slack arrays to the values + /// corresponding to the specified root worker. + /// + /// The worker at which to root the next phase. + private void initializePhase(int s) + { + for (var i = 0; i < matchedSources.Length; i++) { - for (var i = 0; i < matchedSources.Length; i++) - { - matchedSources[i] = false; - parentSourceByCommittedTarget[i] = -1; - } - - matchedSources[s] = true; - for (var t = 0; t < dimension; t++) - { - minSlackValueBySource[t] = costMatrix[s, t] - sourceLabels[s] - - targetLabels[t]; - minSlackTargetBySource[t] = s; - } + matchedSources[i] = false; + parentSourceByCommittedTarget[i] = -1; } - /// - /// Execute a single phase of the algorithm. A phase of the Hungarian algorithm - /// consists of building a set of committed workers and a set of committed jobs - /// from a root unmatched worker by following alternating unmatched/matched - /// zero-slack edges. If an unmatched job is encountered, then an augmenting - /// path has been found and the matching is grown. If the connected zero-slack - /// edges have been exhausted, the labels of committed workers are increased by - /// the minimum slack among committed workers and non-committed jobs to create - /// more zero-slack edges (the labels of committed jobs are simultaneously - /// decreased by the same amount in order to maintain a feasible labeling). - /// - /// - /// The runtime of a single phase of the algorithm is O(n^2), where n is the - /// dimension of the internal square cost matrix, since each edge is visited at - /// most once and since increasing the labeling is accomplished in time O(n) by - /// maintaining the minimum slack values among non-committed jobs. When a phase - /// completes, the matching will have increased in size. - /// - private void executePhase() + matchedSources[s] = true; + for (var t = 0; t < dimension; t++) { - while (true) - { - var (minSlackSource, minSlackDest, minSlackValue) = findMinSlack(); + minSlackValueBySource[t] = costMatrix[s, t] - sourceLabels[s] + - targetLabels[t]; + minSlackTargetBySource[t] = s; + } + } - if (minSlackValue > 0) - updateLabeling(minSlackValue); + /// + /// Execute a single phase of the algorithm. A phase of the Hungarian algorithm + /// consists of building a set of committed workers and a set of committed jobs + /// from a root unmatched worker by following alternating unmatched/matched + /// zero-slack edges. If an unmatched job is encountered, then an augmenting + /// path has been found and the matching is grown. If the connected zero-slack + /// edges have been exhausted, the labels of committed workers are increased by + /// the minimum slack among committed workers and non-committed jobs to create + /// more zero-slack edges (the labels of committed jobs are simultaneously + /// decreased by the same amount in order to maintain a feasible labeling). + /// + /// + /// The runtime of a single phase of the algorithm is O(n^2), where n is the + /// dimension of the internal square cost matrix, since each edge is visited at + /// most once and since increasing the labeling is accomplished in time O(n) by + /// maintaining the minimum slack values among non-committed jobs. When a phase + /// completes, the matching will have increased in size. + /// + private void executePhase() + { + while (true) + { + var (minSlackSource, minSlackDest, minSlackValue) = findMinSlack(); - parentSourceByCommittedTarget[minSlackDest] = minSlackSource; - if (targetMatches[minSlackDest] == -1) - { - // An augmenting path has been found. - growMatching(minSlackDest); - return; - } + if (minSlackValue > 0) + updateLabeling(minSlackValue); - // Update slack values since we increased the size of the committed workers set. - updateSlackValues(minSlackDest); + parentSourceByCommittedTarget[minSlackDest] = minSlackSource; + if (targetMatches[minSlackDest] == -1) + { + // An augmenting path has been found. + growMatching(minSlackDest); + return; } + + // Update slack values since we increased the size of the committed workers set. + updateSlackValues(minSlackDest); } + } - private (int source, int destination, float value) findMinSlack() - { - var minSlackDest = -1; - var minSlackSource = -1; - var minSlackValue = float.PositiveInfinity; + private (int source, int destination, float value) findMinSlack() + { + var minSlackDest = -1; + var minSlackSource = -1; + var minSlackValue = float.PositiveInfinity; - for (var t = 0; t < dimension; t++) - { - if (parentSourceByCommittedTarget[t] != -1) continue; - if (minSlackValueBySource[t] >= minSlackValue) continue; + for (var t = 0; t < dimension; t++) + { + if (parentSourceByCommittedTarget[t] != -1) continue; + if (minSlackValueBySource[t] >= minSlackValue) continue; - minSlackValue = minSlackValueBySource[t]; - minSlackSource = minSlackTargetBySource[t]; - minSlackDest = t; - } - return (minSlackSource, minSlackDest, minSlackValue); + minSlackValue = minSlackValueBySource[t]; + minSlackSource = minSlackTargetBySource[t]; + minSlackDest = t; } + return (minSlackSource, minSlackDest, minSlackValue); + } - private void growMatching(int minSlackDest) + private void growMatching(int minSlackDest) + { + var committedJob = minSlackDest; + var parentWorker = parentSourceByCommittedTarget[committedJob]; + while (true) { - var committedJob = minSlackDest; - var parentWorker = parentSourceByCommittedTarget[committedJob]; - while (true) + var temp = sourceMatches[parentWorker]; + match(parentWorker, committedJob); + committedJob = temp; + if (committedJob == -1) { - var temp = sourceMatches[parentWorker]; - match(parentWorker, committedJob); - committedJob = temp; - if (committedJob == -1) - { - break; - } - parentWorker = parentSourceByCommittedTarget[committedJob]; + break; } + parentWorker = parentSourceByCommittedTarget[committedJob]; } + } - private void updateSlackValues(int minSlackDest) + private void updateSlackValues(int minSlackDest) + { + var worker = targetMatches[minSlackDest]; + matchedSources[worker] = true; + for (var j = 0; j < dimension; j++) { - var worker = targetMatches[minSlackDest]; - matchedSources[worker] = true; - for (var j = 0; j < dimension; j++) - { - if (parentSourceByCommittedTarget[j] != -1) continue; + if (parentSourceByCommittedTarget[j] != -1) continue; - var slack = costMatrix[worker, j] - sourceLabels[worker] - targetLabels[j]; + var slack = costMatrix[worker, j] - sourceLabels[worker] - targetLabels[j]; - if (minSlackValueBySource[j] <= slack) continue; + if (minSlackValueBySource[j] <= slack) continue; - minSlackValueBySource[j] = slack; - minSlackTargetBySource[j] = worker; - } + minSlackValueBySource[j] = slack; + minSlackTargetBySource[j] = worker; } + } - /// - /// Returns the first unmatched source (or dim if none). - /// - /// - private int firstUnmatchedSource() + /// + /// Returns the first unmatched source (or dim if none). + /// + /// + private int firstUnmatchedSource() + { + int s; + for (s = 0; s < dimension; s++) { - int s; - for (s = 0; s < dimension; s++) + if (sourceMatches[s] == -1) { - if (sourceMatches[s] == -1) - { - break; - } + break; } - return s; } + return s; + } - /// - /// Matches source s to destination t. - /// - /// - /// - private void match(int s, int t) + /// + /// Matches source s to destination t. + /// + /// + /// + private void match(int s, int t) + { + sourceMatches[s] = t; + targetMatches[t] = s; + } + + /// + /// Updates labels with the specified slack by adding the slack value for + /// committed workers and by subtracting the slack value for committed jobs. In + /// addition, update the minimum slack values appropriately. + /// + /// + private void updateLabeling(float slack) + { + for (var s = 0; s < dimension; s++) { - sourceMatches[s] = t; - targetMatches[t] = s; + if (matchedSources[s]) + { + sourceLabels[s] += slack; + } } - /// - /// Updates labels with the specified slack by adding the slack value for - /// committed workers and by subtracting the slack value for committed jobs. In - /// addition, update the minimum slack values appropriately. - /// - /// - private void updateLabeling(float slack) + for (var t = 0; t < dimension; t++) { - for (var s = 0; s < dimension; s++) + if (parentSourceByCommittedTarget[t] != -1) { - if (matchedSources[s]) - { - sourceLabels[s] += slack; - } + targetLabels[t] -= slack; } - - for (var t = 0; t < dimension; t++) + else { - if (parentSourceByCommittedTarget[t] != -1) - { - targetLabels[t] -= slack; - } - else - { - minSlackValueBySource[t] -= slack; - } + minSlackValueBySource[t] -= slack; } } - #endregion } + #endregion } diff --git a/Bearded.Utilities/Collections/DeletableObjectList.cs b/Bearded.Utilities/Collections/DeletableObjectList.cs index ba864ea7..24f4a6b6 100644 --- a/Bearded.Utilities/Collections/DeletableObjectList.cs +++ b/Bearded.Utilities/Collections/DeletableObjectList.cs @@ -1,180 +1,179 @@ using System; using System.Collections.Generic; -namespace Bearded.Utilities.Collections +namespace Bearded.Utilities.Collections; + +/// +/// This class represents a list that automatically removes items that have been deleted as specified by the IDeletable interface. +/// It uses a List as backing data structure. +/// The backing data structure is automatically compacted after too many deletions, and when there are no active enumerators. +/// Enumeration of the list skips all deleted items, and items can be deleted while enumerating. +/// Apart from manual removal and forced compaction all operations of this class are armourtised constant time. +/// This list is not threadsafe. +/// +public sealed class DeletableObjectList : IEnumerable + where T : class, IDeletable { - /// - /// This class represents a list that automatically removes items that have been deleted as specified by the IDeletable interface. - /// It uses a List as backing data structure. - /// The backing data structure is automatically compacted after too many deletions, and when there are no active enumerators. - /// Enumeration of the list skips all deleted items, and items can be deleted while enumerating. - /// Apart from manual removal and forced compaction all operations of this class are armourtised constant time. - /// This list is not threadsafe. - /// - public sealed class DeletableObjectList : IEnumerable - where T : class, IDeletable - { - #region Fields and Properties + #region Fields and Properties - private readonly List list; + private readonly List list; - private int enumerators; - private int count; - private float emptyFraction => (float)(list.Count - count) / count; + private int enumerators; + private int count; + private float emptyFraction => (float)(list.Count - count) / count; - /// - /// Gets or sets the maximum fraction of deleted objects this list can contain before it compacts the backing data structure. - /// The lower the value, the more aggressively and more often is the list compacted. - /// A value of 0 compacts as often as possible. - /// - public float MaxEmptyFraction { get; set; } + /// + /// Gets or sets the maximum fraction of deleted objects this list can contain before it compacts the backing data structure. + /// The lower the value, the more aggressively and more often is the list compacted. + /// A value of 0 compacts as often as possible. + /// + public float MaxEmptyFraction { get; set; } - /// - /// Gets an approximation of the number of items in the list. - /// The value is an inclusive upper bound to the actual number of items. - /// - public int ApproximateCount => count; + /// + /// Gets an approximation of the number of items in the list. + /// The value is an inclusive upper bound to the actual number of items. + /// + public int ApproximateCount => count; - #endregion + #endregion - #region Constructor + #region Constructor - public DeletableObjectList() - : this(4) - { - } - - public DeletableObjectList(int capacity) - { - list = new List(capacity); - MaxEmptyFraction = 0.2f; - } + public DeletableObjectList() + : this(4) + { + } - #endregion + public DeletableObjectList(int capacity) + { + list = new List(capacity); + MaxEmptyFraction = 0.2f; + } - #region Methods + #endregion - /// - /// Adds an item to this deletable object list. The item cannot be null. - /// - /// The item to add. - public void Add(T item) - { - if (item == null) - throw new ArgumentNullException(nameof(item)); + #region Methods - list.Add(item); - count++; - } + /// + /// Adds an item to this deletable object list. The item cannot be null. + /// + /// The item to add. + public void Add(T item) + { + if (item == null) + throw new ArgumentNullException(nameof(item)); - /// - /// Tries removing a given item from the list. - /// This performs a linear search and therefor runs in O(n) time, where n the number of items in the list, - /// assuming frequent enumeration or forced compaction. - /// This method does not have to be called for items with Deleted set to true. Those items will be removed automatically. - /// - /// The item to remove. - /// True if the item was found and removed, false otherwise. - public bool Remove(T item) - { - var i = list.IndexOf(item); - if (i == -1) - return false; - ClearBackingArrayIndex(i); - considerCompacting(); - return true; - } + list.Add(item); + count++; + } - internal void ClearBackingArrayIndex(int i) - { - list[i] = null; - count--; + /// + /// Tries removing a given item from the list. + /// This performs a linear search and therefor runs in O(n) time, where n the number of items in the list, + /// assuming frequent enumeration or forced compaction. + /// This method does not have to be called for items with Deleted set to true. Those items will be removed automatically. + /// + /// The item to remove. + /// True if the item was found and removed, false otherwise. + public bool Remove(T item) + { + var i = list.IndexOf(item); + if (i == -1) + return false; + ClearBackingArrayIndex(i); + considerCompacting(); + return true; + } - if (count == 0) - list.Clear(); - } + internal void ClearBackingArrayIndex(int i) + { + list[i] = null; + count--; - /// - /// Clears the list of all items. - /// - public void Clear() - { + if (count == 0) list.Clear(); - count = 0; - } - - #region Enumeration + } - /// - System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() => GetEnumerator(); + /// + /// Clears the list of all items. + /// + public void Clear() + { + list.Clear(); + count = 0; + } - /// - public IEnumerator GetEnumerator() => new DeletableObjectListEnumerator(this, list); + #region Enumeration - internal void RegisterEnumerator() - { - enumerators++; - } + /// + System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() => GetEnumerator(); - internal void UnregisterEnumerator() - { - enumerators--; - considerCompacting(); - } + /// + public IEnumerator GetEnumerator() => new DeletableObjectListEnumerator(this, list); - #endregion + internal void RegisterEnumerator() + { + enumerators++; + } - #region Compaction and Trimming + internal void UnregisterEnumerator() + { + enumerators--; + considerCompacting(); + } - /// - /// Force the list to compact its backing data structure for optimal enumeration performance. - /// It further trims it to use minimal memory. - /// This operation takes O(n) time, where n the number of items in the list, - /// assuming frequent enumeration or forced compaction. - /// - public void TrimExcess() - { - ForceCompact(); - list.TrimExcess(); - } + #endregion - /// - /// Force the list to compact its backing data structure for optimal enumeration performance. - /// This operation takes O(n) time, where n the number of items in the list, - /// assuming frequent enumeration or forced compaction. - /// - public void ForceCompact() - { - if (enumerators != 0) - { - throw new InvalidOperationException( - "Cannot compact list while enumerating. " + - "If you were not enumerating, check that your enumerators are disposed of correctly."); - } + #region Compaction and Trimming - compact(); - } + /// + /// Force the list to compact its backing data structure for optimal enumeration performance. + /// It further trims it to use minimal memory. + /// This operation takes O(n) time, where n the number of items in the list, + /// assuming frequent enumeration or forced compaction. + /// + public void TrimExcess() + { + ForceCompact(); + list.TrimExcess(); + } - private void considerCompacting() + /// + /// Force the list to compact its backing data structure for optimal enumeration performance. + /// This operation takes O(n) time, where n the number of items in the list, + /// assuming frequent enumeration or forced compaction. + /// + public void ForceCompact() + { + if (enumerators != 0) { - if (enumerators == 0 && - count > 0 && - emptyFraction > MaxEmptyFraction) - { - compact(); - } + throw new InvalidOperationException( + "Cannot compact list while enumerating. " + + "If you were not enumerating, check that your enumerators are disposed of correctly."); } - private void compact() + compact(); + } + + private void considerCompacting() + { + if (enumerators == 0 && + count > 0 && + emptyFraction > MaxEmptyFraction) { - list.RemoveAll(t => t == null || t.Deleted); - count = list.Count; + compact(); } + } - #endregion + private void compact() + { + list.RemoveAll(t => t == null || t.Deleted); + count = list.Count; + } - #endregion + #endregion + + #endregion - } } diff --git a/Bearded.Utilities/Collections/DeletableObjectListEnumerator.cs b/Bearded.Utilities/Collections/DeletableObjectListEnumerator.cs index 072e242f..7cc6d918 100644 --- a/Bearded.Utilities/Collections/DeletableObjectListEnumerator.cs +++ b/Bearded.Utilities/Collections/DeletableObjectListEnumerator.cs @@ -1,61 +1,60 @@ using System.Collections.Generic; -namespace Bearded.Utilities.Collections +namespace Bearded.Utilities.Collections; + +/// +/// Enumerator for deletable object list. +/// Kept internal to hide implementation. +/// +internal class DeletableObjectListEnumerator : IEnumerator + where T : class, IDeletable { - /// - /// Enumerator for deletable object list. - /// Kept internal to hide implementation. - /// - internal class DeletableObjectListEnumerator : IEnumerator - where T : class, IDeletable - { - private readonly DeletableObjectList deletableObjectList; - private readonly List list; - private int i; + private readonly DeletableObjectList deletableObjectList; + private readonly List list; + private int i; - object System.Collections.IEnumerator.Current => Current; - public T Current { get; private set; } + object System.Collections.IEnumerator.Current => Current; + public T Current { get; private set; } - public DeletableObjectListEnumerator(DeletableObjectList deletableObjectList, List list) - { - this.deletableObjectList = deletableObjectList; - this.list = list; + public DeletableObjectListEnumerator(DeletableObjectList deletableObjectList, List list) + { + this.deletableObjectList = deletableObjectList; + this.list = list; - deletableObjectList.RegisterEnumerator(); - } + deletableObjectList.RegisterEnumerator(); + } - public void Dispose() - { - deletableObjectList.UnregisterEnumerator(); - } + public void Dispose() + { + deletableObjectList.UnregisterEnumerator(); + } - public bool MoveNext() + public bool MoveNext() + { + while (list.Count > i) { - while (list.Count > i) + var item = list[i]; + if (item != null) { - var item = list[i]; - if (item != null) + if (item.Deleted) { - if (item.Deleted) - { - deletableObjectList.ClearBackingArrayIndex(i); - } - else - { - Current = item; - i++; - return true; - } + deletableObjectList.ClearBackingArrayIndex(i); + } + else + { + Current = item; + i++; + return true; } - - i++; } - return false; - } - public void Reset() - { - i = 0; + i++; } + return false; + } + + public void Reset() + { + i = 0; } } diff --git a/Bearded.Utilities/Collections/IDeletable.cs b/Bearded.Utilities/Collections/IDeletable.cs index d2a62467..edda9c93 100644 --- a/Bearded.Utilities/Collections/IDeletable.cs +++ b/Bearded.Utilities/Collections/IDeletable.cs @@ -1,14 +1,13 @@ -namespace Bearded.Utilities.Collections +namespace Bearded.Utilities.Collections; + +/// +/// An interface for objects that can be deleted, used for automatic deletion from DeletableObjectList. +/// +public interface IDeletable { /// - /// An interface for objects that can be deleted, used for automatic deletion from DeletableObjectList. + /// Whether the object was deleted. This will cause it to be removed from all deletable object lists it is contained in. + /// Once this returns true, it should never return false afterwards. Otherwise integrity of lists can not be guarnateed. /// - public interface IDeletable - { - /// - /// Whether the object was deleted. This will cause it to be removed from all deletable object lists it is contained in. - /// Once this returns true, it should never return false afterwards. Otherwise integrity of lists can not be guarnateed. - /// - bool Deleted { get; } - } + bool Deleted { get; } } diff --git a/Bearded.Utilities/Collections/IIdable.cs b/Bearded.Utilities/Collections/IIdable.cs index 288e278e..72d00362 100644 --- a/Bearded.Utilities/Collections/IIdable.cs +++ b/Bearded.Utilities/Collections/IIdable.cs @@ -1,8 +1,7 @@  -namespace Bearded.Utilities.Collections +namespace Bearded.Utilities.Collections; + +public interface IIdable { - public interface IIdable - { - Id Id { get; } - } + Id Id { get; } } diff --git a/Bearded.Utilities/Collections/IdDictionary.cs b/Bearded.Utilities/Collections/IdDictionary.cs index 2a7c6e57..d33aa30d 100644 --- a/Bearded.Utilities/Collections/IdDictionary.cs +++ b/Bearded.Utilities/Collections/IdDictionary.cs @@ -1,12 +1,11 @@ using System.Collections.Generic; -namespace Bearded.Utilities.Collections +namespace Bearded.Utilities.Collections; + +public class IdDictionary : Dictionary, T> + where T : IIdable { - public class IdDictionary : Dictionary, T> - where T : IIdable - { - public void Add(T item) => Add(item.Id, item); + public void Add(T item) => Add(item.Id, item); - public void Remove(T item) => Remove(item.Id); - } + public void Remove(T item) => Remove(item.Id); } diff --git a/Bearded.Utilities/Collections/MutableLinkedList.cs b/Bearded.Utilities/Collections/MutableLinkedList.cs index 410b1f3c..e7c80293 100644 --- a/Bearded.Utilities/Collections/MutableLinkedList.cs +++ b/Bearded.Utilities/Collections/MutableLinkedList.cs @@ -1,191 +1,190 @@ using System; using System.Collections.Generic; -namespace Bearded.Utilities.Collections +namespace Bearded.Utilities.Collections; + +/// +/// A generic linked list that can be modified while it is being enumerated. +/// Unless otherwise specified, all operations on this list, including removal by node, run in constant time. +/// This list is not threadsafe. +/// +public sealed class MutableLinkedList : IEnumerable + where T : class { - /// - /// A generic linked list that can be modified while it is being enumerated. - /// Unless otherwise specified, all operations on this list, including removal by node, run in constant time. - /// This list is not threadsafe. - /// - public sealed class MutableLinkedList : IEnumerable - where T : class - { - #region Fields and Properties + #region Fields and Properties - private readonly LinkedList> enumerators = - new LinkedList>(); + private readonly LinkedList> enumerators = + new LinkedList>(); - public MutableLinkedListNode First { get; private set; } + public MutableLinkedListNode First { get; private set; } - public MutableLinkedListNode Last { get; private set; } + public MutableLinkedListNode Last { get; private set; } - public int Count { get; private set; } + public int Count { get; private set; } - #endregion + #endregion - #region Methods + #region Methods - #region Add() + #region Add() - /// - /// Adds an item to the linked list. The node used to store the item is returned. - /// - public MutableLinkedListNode Add(T item) - { - var node = MutableLinkedListNode.For(item); - Add(node); - return node; - } + /// + /// Adds an item to the linked list. The node used to store the item is returned. + /// + public MutableLinkedListNode Add(T item) + { + var node = MutableLinkedListNode.For(item); + Add(node); + return node; + } - /// - /// Adds a linked list node to this list. - /// If the node is already in another list, an exception is thrown. - /// - public void Add(MutableLinkedListNode node) - { - if (node.List != null) - throw new ArgumentException("Node cannot already be in a list when adding it to one.", nameof(node)); + /// + /// Adds a linked list node to this list. + /// If the node is already in another list, an exception is thrown. + /// + public void Add(MutableLinkedListNode node) + { + if (node.List != null) + throw new ArgumentException("Node cannot already be in a list when adding it to one.", nameof(node)); - node.List = this; + node.List = this; - if (Count == 0) - { - First = node; - } - else - { - node.Prev = Last; - Last.Next = node; - } + if (Count == 0) + { + First = node; + } + else + { + node.Prev = Last; + Last.Next = node; + } - Last = node; + Last = node; - Count++; - } + Count++; + } - #endregion + #endregion - #region Remove() + #region Remove() - /// - /// Tries to return the first occurrence of an item from the list. - /// Returns true if it found and removed the item or false if the item is not in the list. - /// - /// This method takes O(Count) time and its use is discouraged. - public bool Remove(T item) + /// + /// Tries to return the first occurrence of an item from the list. + /// Returns true if it found and removed the item or false if the item is not in the list. + /// + /// This method takes O(Count) time and its use is discouraged. + public bool Remove(T item) + { + var node = First; + while (node != null) { - var node = First; - while (node != null) + if (node.Value == item) { - if (node.Value == item) - { - Remove(node); - return true; - } - node = node.Next; + Remove(node); + return true; } - return false; + node = node.Next; } + return false; + } - /// - /// Removes the specified node from the list. - /// Throws an exception if the given node is not in the list. - /// - public void Remove(MutableLinkedListNode node) - { - if (node.List != this) - throw new ArgumentException("Node must be in list to be removed."); + /// + /// Removes the specified node from the list. + /// Throws an exception if the given node is not in the list. + /// + public void Remove(MutableLinkedListNode node) + { + if (node.List != this) + throw new ArgumentException("Node must be in list to be removed."); - foreach (var enumerator in enumerators) - enumerator.OnObjectRemove(node); + foreach (var enumerator in enumerators) + enumerator.OnObjectRemove(node); - if (node == Last) - Last = node.Prev; - if (node == First) - First = node.Next; + if (node == Last) + Last = node.Prev; + if (node == First) + First = node.Next; - Count--; + Count--; - if (node.Next != null) - { - node.Next.Prev = node.Prev; - } - if (node.Prev != null) - { - node.Prev.Next = node.Next; - } - node.Next = null; - node.Prev = null; - node.List = null; + if (node.Next != null) + { + node.Next.Prev = node.Prev; } - - #endregion - - #region Insertion - - /// - /// Adds a given linked list node before another one already in this list. - /// This will throw an exception if the node is already in another list, of if the node to add before is null, or not in this list. - /// - public void AddBefore(MutableLinkedListNode newNode, MutableLinkedListNode beforeThis) + if (node.Prev != null) { - if (beforeThis.List != this) - throw new ArgumentException("The object to insert before must be in the same list."); - Add(newNode); - InsertBefore(newNode, beforeThis); + node.Prev.Next = node.Next; } + node.Next = null; + node.Prev = null; + node.List = null; + } - /// - /// Inserts a node before another node in the list. - /// The given nodes must both be in the list, and the node to insert must be the last one in it. - /// Otherwise an exception is thrown. - /// - public void InsertBefore(MutableLinkedListNode node, MutableLinkedListNode beforeThis) - { - if (node.List != this) - throw new ArgumentException("Object must already be in list before inserting."); - if (beforeThis.List != this) - throw new ArgumentException("The object to insert before must be in the same list."); - if (node != Last) - throw new ArgumentException("Inserted object must be last object in list."); - if (node == beforeThis) - throw new ArgumentException("Cannot insert object before itself."); + #endregion - Last = node.Prev; - if (beforeThis == First) - First = node; + #region Insertion - node.Prev.Next = null; - node.Prev = beforeThis.Prev; - beforeThis.Prev = node; - node.Next = beforeThis; + /// + /// Adds a given linked list node before another one already in this list. + /// This will throw an exception if the node is already in another list, of if the node to add before is null, or not in this list. + /// + public void AddBefore(MutableLinkedListNode newNode, MutableLinkedListNode beforeThis) + { + if (beforeThis.List != this) + throw new ArgumentException("The object to insert before must be in the same list."); + Add(newNode); + InsertBefore(newNode, beforeThis); + } - if (node.Prev != null) - node.Prev.Next = node; - } + /// + /// Inserts a node before another node in the list. + /// The given nodes must both be in the list, and the node to insert must be the last one in it. + /// Otherwise an exception is thrown. + /// + public void InsertBefore(MutableLinkedListNode node, MutableLinkedListNode beforeThis) + { + if (node.List != this) + throw new ArgumentException("Object must already be in list before inserting."); + if (beforeThis.List != this) + throw new ArgumentException("The object to insert before must be in the same list."); + if (node != Last) + throw new ArgumentException("Inserted object must be last object in list."); + if (node == beforeThis) + throw new ArgumentException("Cannot insert object before itself."); + + Last = node.Prev; + if (beforeThis == First) + First = node; + + node.Prev.Next = null; + node.Prev = beforeThis.Prev; + beforeThis.Prev = node; + node.Next = beforeThis; + + if (node.Prev != null) + node.Prev.Next = node; + } - #endregion + #endregion - #region Enumerating + #region Enumerating - public IEnumerator GetEnumerator() - { - var e = new MutableLinkedListEnumerator(this); - enumerators.AddFirst(e); - e.SetNode(enumerators.First); - return e; - } + public IEnumerator GetEnumerator() + { + var e = new MutableLinkedListEnumerator(this); + enumerators.AddFirst(e); + e.SetNode(enumerators.First); + return e; + } - internal void ForgetEnumerator(LinkedListNode> node) - => enumerators.Remove(node); + internal void ForgetEnumerator(LinkedListNode> node) + => enumerators.Remove(node); - System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() - => GetEnumerator(); + System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() + => GetEnumerator(); - #endregion + #endregion - #endregion + #endregion - } } diff --git a/Bearded.Utilities/Collections/MutableLinkedListEnumerator.cs b/Bearded.Utilities/Collections/MutableLinkedListEnumerator.cs index a7bd3f67..a6bc1ee2 100644 --- a/Bearded.Utilities/Collections/MutableLinkedListEnumerator.cs +++ b/Bearded.Utilities/Collections/MutableLinkedListEnumerator.cs @@ -1,96 +1,95 @@ using System.Collections.Generic; -namespace Bearded.Utilities.Collections +namespace Bearded.Utilities.Collections; + +/// +/// Enumerator for mutable linked lists. +/// Kept internal to hide implementation. +/// +internal sealed class MutableLinkedListEnumerator : IEnumerator + where T : class { - /// - /// Enumerator for mutable linked lists. - /// Kept internal to hide implementation. - /// - internal sealed class MutableLinkedListEnumerator : IEnumerator - where T : class - { - private readonly MutableLinkedList list; + private readonly MutableLinkedList list; - private bool initialised; - private bool currentWasDeleted; - private bool done; + private bool initialised; + private bool currentWasDeleted; + private bool done; - public MutableLinkedListEnumerator(MutableLinkedList list) - { - this.list = list; - } + public MutableLinkedListEnumerator(MutableLinkedList list) + { + this.list = list; + } - private LinkedListNode> node; - public void SetNode(LinkedListNode> node) - { - if (this.node == null) - this.node = node; - } + private LinkedListNode> node; + public void SetNode(LinkedListNode> node) + { + if (this.node == null) + this.node = node; + } - public T Current - { - get { return current.Value; } - } + public T Current + { + get { return current.Value; } + } - private MutableLinkedListNode current; + private MutableLinkedListNode current; - public void OnObjectRemove(MutableLinkedListNode obj) - { - if (obj != current) - return; + public void OnObjectRemove(MutableLinkedListNode obj) + { + if (obj != current) + return; - currentWasDeleted = true; - current = obj.Next; - if (current == null) - done = true; - } + currentWasDeleted = true; + current = obj.Next; + if (current == null) + done = true; + } - public void Dispose() - { - list.ForgetEnumerator(node); - } + public void Dispose() + { + list.ForgetEnumerator(node); + } - public bool MoveNext() + public bool MoveNext() + { + if (done) + return false; + if (!initialised) { - if (done) + if (list.Count == 0) return false; - if (!initialised) + initialised = true; + current = list.First; + } + else + { + if (currentWasDeleted) { - if (list.Count == 0) - return false; - initialised = true; - current = list.First; + currentWasDeleted = false; + return true; } - else + current = current.Next; + if (current == null) { - if (currentWasDeleted) - { - currentWasDeleted = false; - return true; - } - current = current.Next; - if (current == null) - { - done = true; - return false; - } + done = true; + return false; } - return true; - } - - public void Reset() - { - current = null; - done = false; - initialised = false; - currentWasDeleted = false; } + return true; + } - object System.Collections.IEnumerator.Current - { - get { return Current; } - } + public void Reset() + { + current = null; + done = false; + initialised = false; + currentWasDeleted = false; + } + object System.Collections.IEnumerator.Current + { + get { return Current; } } + } diff --git a/Bearded.Utilities/Collections/MutableLinkedListItem.cs b/Bearded.Utilities/Collections/MutableLinkedListItem.cs index 248c0509..9090a71b 100644 --- a/Bearded.Utilities/Collections/MutableLinkedListItem.cs +++ b/Bearded.Utilities/Collections/MutableLinkedListItem.cs @@ -1,17 +1,15 @@ -namespace Bearded.Utilities.Collections -{ +namespace Bearded.Utilities.Collections; - /// - /// This type can be used to have a class be both the value as well as the node in a linked list to prevent unneeded allocations. - /// Usage: - /// MyClass : MutableLinkedListItem<MyClass> - /// MutableLinkedList<MyClass> - /// Make sure you add this as node and not as item to the linked list to prevent creation of the node wrapper. - /// - /// The type of the inheriting class. - public abstract class MutableLinkedListItem : MutableLinkedListNode - where TMe : MutableLinkedListNode - { +/// +/// This type can be used to have a class be both the value as well as the node in a linked list to prevent unneeded allocations. +/// Usage: +/// MyClass : MutableLinkedListItem<MyClass> +/// MutableLinkedList<MyClass> +/// Make sure you add this as node and not as item to the linked list to prevent creation of the node wrapper. +/// +/// The type of the inheriting class. +public abstract class MutableLinkedListItem : MutableLinkedListNode + where TMe : MutableLinkedListNode +{ - } } diff --git a/Bearded.Utilities/Collections/MutableLinkedListNode.cs b/Bearded.Utilities/Collections/MutableLinkedListNode.cs index 3cbb16f2..91908e2e 100644 --- a/Bearded.Utilities/Collections/MutableLinkedListNode.cs +++ b/Bearded.Utilities/Collections/MutableLinkedListNode.cs @@ -1,106 +1,105 @@ -namespace Bearded.Utilities.Collections +namespace Bearded.Utilities.Collections; + +/// +/// Static class to initialise new mutable linked list nodes. +/// +public static class MutableLinkedListNode { /// - /// Static class to initialise new mutable linked list nodes. + /// Returns a new mutable linked list node for the given value. /// - public static class MutableLinkedListNode + public static MutableLinkedListNode For(T value) + where T : class { - /// - /// Returns a new mutable linked list node for the given value. - /// - public static MutableLinkedListNode For(T value) - where T : class - { - return new MutableLinkedListNode(value); - } + return new MutableLinkedListNode(value); } +} + +/// +/// A node for mutable linked lists. +/// +public class MutableLinkedListNode + where T : class +{ + + #region Fields and Properties + + private readonly T value; /// - /// A node for mutable linked lists. + /// The value stored in the node. /// - public class MutableLinkedListNode - where T : class + public T Value { get { return value; } } + + // Next, Prev and List are internally writeable to + // simplify addition, removal and insertion code. + // Do not mess with them! + internal MutableLinkedListNode Next { get; set; } + internal MutableLinkedListNode Prev { get; set; } + + /// + /// The list the node is part of. + /// + public MutableLinkedList List { get; internal set; } + + internal bool ChangingList { get; private set; } + + #endregion + + #region Constructors + + internal MutableLinkedListNode(T value) { + this.value = value; + } + + internal MutableLinkedListNode() + { + value = this as T; + } + + #endregion - #region Fields and Properties - - private readonly T value; - - /// - /// The value stored in the node. - /// - public T Value { get { return value; } } - - // Next, Prev and List are internally writeable to - // simplify addition, removal and insertion code. - // Do not mess with them! - internal MutableLinkedListNode Next { get; set; } - internal MutableLinkedListNode Prev { get; set; } - - /// - /// The list the node is part of. - /// - public MutableLinkedList List { get; internal set; } - - internal bool ChangingList { get; private set; } - - #endregion - - #region Constructors - - internal MutableLinkedListNode(T value) - { - this.value = value; - } - - internal MutableLinkedListNode() - { - value = this as T; - } - - #endregion - - #region Methods - - /// - /// Adds this node to a given list. - /// - /// The list to add the node to. - public void AddToList(MutableLinkedList list) - { - list.Add(this); - } - - /// - /// Adds this node to a given list before another node. - /// - /// The list to add the node to. - /// The node to add this before. - public void AddToListBefore(MutableLinkedList list, MutableLinkedListNode beforeThis) - { - list.AddBefore(this, beforeThis); - } - - /// - /// Insert this node before another on. - /// Both nodes must be in the same list, and this node must be the last on in the list. - /// - /// The node to add this before. - public void InsertBefore(MutableLinkedListNode beforeThis) - { - List.InsertBefore(this, beforeThis); - } - - /// - /// Removes this node from the list it is in. - /// - public void RemoveFromList() - { - List.Remove(this); - } - - #endregion + #region Methods + /// + /// Adds this node to a given list. + /// + /// The list to add the node to. + public void AddToList(MutableLinkedList list) + { + list.Add(this); + } + + /// + /// Adds this node to a given list before another node. + /// + /// The list to add the node to. + /// The node to add this before. + public void AddToListBefore(MutableLinkedList list, MutableLinkedListNode beforeThis) + { + list.AddBefore(this, beforeThis); + } + + /// + /// Insert this node before another on. + /// Both nodes must be in the same list, and this node must be the last on in the list. + /// + /// The node to add this before. + public void InsertBefore(MutableLinkedListNode beforeThis) + { + List.InsertBefore(this, beforeThis); + } + + /// + /// Removes this node from the list it is in. + /// + public void RemoveFromList() + { + List.Remove(this); } + + #endregion + } diff --git a/Bearded.Utilities/Collections/PrefixTrie.cs b/Bearded.Utilities/Collections/PrefixTrie.cs index 5bc3771e..13f8b381 100644 --- a/Bearded.Utilities/Collections/PrefixTrie.cs +++ b/Bearded.Utilities/Collections/PrefixTrie.cs @@ -5,242 +5,241 @@ using System.Text; using Bearded.Utilities.Linq; -namespace Bearded.Utilities.Collections +namespace Bearded.Utilities.Collections; + +public sealed class PrefixTrie : ICollection { - public sealed class PrefixTrie : ICollection + private struct Node { - private struct Node - { - private readonly Dictionary? values; + private readonly Dictionary? values; - public string Key { get; } + public string Key { get; } - #region creating + #region creating - // expects and requires sorted list of unique strings - public Node(List values) - : this(values, 0, 0, values.Count) - { - - } + // expects and requires sorted list of unique strings + public Node(List values) + : this(values, 0, 0, values.Count) + { - private Node(List values, int index, int iMin, int iMax) - : this() - { - var s = values[iMin]; + } - if (s.Length == index) - { - Key = s; - iMin++; - } + private Node(List values, int index, int iMin, int iMax) + : this() + { + var s = values[iMin]; - this.values = makeChildren(values, index, iMin, iMax); + if (s.Length == index) + { + Key = s; + iMin++; } - private static Dictionary? makeChildren(List values, int index, int iMin, int iMax) - { - if (iMin >= iMax) - return null; + this.values = makeChildren(values, index, iMin, iMax); + } - var dict = new Dictionary(); + private static Dictionary? makeChildren(List values, int index, int iMin, int iMax) + { + if (iMin >= iMax) + return null; - var index2 = index + 1; + var dict = new Dictionary(); - var c = values[iMin][index]; - for (var i = iMin + 1; i < iMax; i++) - { - var c2 = values[i][index]; + var index2 = index + 1; - if (c2 == c) - continue; + var c = values[iMin][index]; + for (var i = iMin + 1; i < iMax; i++) + { + var c2 = values[i][index]; - dict.Add(c, new Node(values, index2, iMin, i)); - iMin = i; - c = c2; - } - dict.Add(c, new Node(values, index2, iMin, iMax)); + if (c2 == c) + continue; - return dict; + dict.Add(c, new Node(values, index2, iMin, i)); + iMin = i; + c = c2; } + dict.Add(c, new Node(values, index2, iMin, iMax)); + + return dict; + } - #endregion + #endregion - #region accessing + #region accessing - public IEnumerable AllKeys + public IEnumerable AllKeys + { + get { - get + var stack = new Stack(); + stack.Push(this); + do { - var stack = new Stack(); - stack.Push(this); - do - { - var n = stack.Pop(); - if (n.Key != null) - yield return n.Key; - if (n.values == null) continue; - foreach (var n2 in n.values.Values) - stack.Push(n2); - } while (stack.Count > 0); - } + var n = stack.Pop(); + if (n.Key != null) + yield return n.Key; + if (n.values == null) continue; + foreach (var n2 in n.values.Values) + stack.Push(n2); + } while (stack.Count > 0); } + } - public string LongestPrefixAddition + public string LongestPrefixAddition + { + get { - get + var builder = new StringBuilder(); + var n = this; + while (n.values?.Count == 1) { - var builder = new StringBuilder(); - var n = this; - while (n.values?.Count == 1) - { - var pair = n.values.First(); - builder.Append(pair.Key); - n = pair.Value; - } - return builder.ToString(); + var pair = n.values.First(); + builder.Append(pair.Key); + n = pair.Value; } + return builder.ToString(); } + } - public Node? this[char character] + public Node? this[char character] + { + get { - get - { - if (values == null) - return null; - if (values.TryGetValue(character, out var node)) - return node; + if (values == null) return null; - } + if (values.TryGetValue(character, out var node)) + return node; + return null; } - - #endregion } - private readonly Node root; + #endregion + } - public int Count { get; } + private readonly Node root; - public bool IsReadOnly => true; + public int Count { get; } - /// - /// Initialises a new prefix trie from a sequence of strings. - /// Duplicate and null strings will be ignored. - /// - /// is null. - public PrefixTrie(IEnumerable values) - { - if (values == null) - throw new ArgumentNullException(nameof(values)); + public bool IsReadOnly => true; - var valueList = values.NotNull().Distinct() - .OrderBy(s => s).ToList(); + /// + /// Initialises a new prefix trie from a sequence of strings. + /// Duplicate and null strings will be ignored. + /// + /// is null. + public PrefixTrie(IEnumerable values) + { + if (values == null) + throw new ArgumentNullException(nameof(values)); - Count = valueList.Count; + var valueList = values.NotNull().Distinct() + .OrderBy(s => s).ToList(); - if (valueList.Count == 0) - return; + Count = valueList.Count; - root = new Node(valueList); - } + if (valueList.Count == 0) + return; - private Node? getNode(string s) - { - if (Count == 0) - return null; + root = new Node(valueList); + } - if (s == string.Empty) - return root; + private Node? getNode(string s) + { + if (Count == 0) + return null; - var node = root; - foreach (var t in s) - { - var next = node[t]; - if (!next.HasValue) - return null; - node = next.Value; - } - return node; - } + if (s == string.Empty) + return root; - public bool Contains(string s) + var node = root; + foreach (var t in s) { - if (s == null) - throw new ArgumentNullException(nameof(s)); - - return getNode(s)?.Key != null; + var next = node[t]; + if (!next.HasValue) + return null; + node = next.Value; } + return node; + } - /// - /// Returns all contained strings with a given prefix. - /// - /// Empty sequence if prefix is not contained in tree. - /// is null. - public IEnumerable AllKeys(string prefix) - { - if (prefix == null) - throw new ArgumentNullException(nameof(prefix)); + public bool Contains(string s) + { + if (s == null) + throw new ArgumentNullException(nameof(s)); - return getNode(prefix)?.AllKeys ?? Enumerable.Empty(); - } + return getNode(s)?.Key != null; + } - /// - /// Returns the maximum prefix that prefixes the same set of strings are the given one. - /// - /// Null if prefix is not contained in tree. - /// is null. - public string? ExtendPrefix(string prefix) - { - if (prefix == null) - throw new ArgumentNullException(nameof(prefix)); + /// + /// Returns all contained strings with a given prefix. + /// + /// Empty sequence if prefix is not contained in tree. + /// is null. + public IEnumerable AllKeys(string prefix) + { + if (prefix == null) + throw new ArgumentNullException(nameof(prefix)); - var node = getNode(prefix); - return node.HasValue ? prefix + node.Value.LongestPrefixAddition : null; - } + return getNode(prefix)?.AllKeys ?? Enumerable.Empty(); + } - public IEnumerator GetEnumerator() - { - if (Count == 0) - yield break; + /// + /// Returns the maximum prefix that prefixes the same set of strings are the given one. + /// + /// Null if prefix is not contained in tree. + /// is null. + public string? ExtendPrefix(string prefix) + { + if (prefix == null) + throw new ArgumentNullException(nameof(prefix)); - foreach (var key in root.AllKeys) - yield return key; - } + var node = getNode(prefix); + return node.HasValue ? prefix + node.Value.LongestPrefixAddition : null; + } - IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); + public IEnumerator GetEnumerator() + { + if (Count == 0) + yield break; - public void CopyTo(string[] array, int arrayIndex) + foreach (var key in root.AllKeys) + yield return key; + } + + IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); + + public void CopyTo(string[] array, int arrayIndex) + { + if (array == null) + throw new ArgumentNullException(nameof(array)); + if (arrayIndex < 0) + throw new ArgumentOutOfRangeException(nameof(arrayIndex)); + if (arrayIndex + Count > array.Length) + throw new ArgumentException("The array does not have enough available space."); + + if (Count == 0) + return; + + var i = arrayIndex; + foreach (var key in root.AllKeys) { - if (array == null) - throw new ArgumentNullException(nameof(array)); - if (arrayIndex < 0) - throw new ArgumentOutOfRangeException(nameof(arrayIndex)); - if (arrayIndex + Count > array.Length) - throw new ArgumentException("The array does not have enough available space."); - - if (Count == 0) - return; - - var i = arrayIndex; - foreach (var key in root.AllKeys) - { - array[i++] = key; - } + array[i++] = key; } + } - /// - /// Not implemented. Will throw . - /// - public void Add(string item) => throw new NotSupportedException(); + /// + /// Not implemented. Will throw . + /// + public void Add(string item) => throw new NotSupportedException(); - /// - /// Not implemented. Will throw . - /// - public bool Remove(string item) => throw new NotSupportedException(); + /// + /// Not implemented. Will throw . + /// + public bool Remove(string item) => throw new NotSupportedException(); - /// - /// Not implemented. Will throw . - /// - public void Clear() => throw new NotSupportedException(); - } + /// + /// Not implemented. Will throw . + /// + public void Clear() => throw new NotSupportedException(); } diff --git a/Bearded.Utilities/Collections/PriorityQueue.cs b/Bearded.Utilities/Collections/PriorityQueue.cs index fbfb17e2..3c3fad72 100644 --- a/Bearded.Utilities/Collections/PriorityQueue.cs +++ b/Bearded.Utilities/Collections/PriorityQueue.cs @@ -2,99 +2,98 @@ using System.Collections.Generic; using System.Linq; -namespace Bearded.Utilities.Collections +namespace Bearded.Utilities.Collections; + +/// +/// A priority queue that is implemented with a binary min-heap. +/// +/// +/// +public sealed class PriorityQueue : StaticPriorityQueue where TPriority : IComparable { + private readonly Dictionary valueDict = new Dictionary(); + /// - /// A priority queue that is implemented with a binary min-heap. + /// Creates a new instance of a priority queue. /// - /// - /// - public sealed class PriorityQueue : StaticPriorityQueue where TPriority : IComparable - { - private readonly Dictionary valueDict = new Dictionary(); - - /// - /// Creates a new instance of a priority queue. - /// - public PriorityQueue() { } + public PriorityQueue() { } - /// - /// Creates a new instance of a priority queue with the specified initial capacity. - /// - /// Initial capacity of the priority queue. - public PriorityQueue(int capacity) - : base(capacity) { } + /// + /// Creates a new instance of a priority queue with the specified initial capacity. + /// + /// Initial capacity of the priority queue. + public PriorityQueue(int capacity) + : base(capacity) { } - /// - /// Creates a new instance of a priority queue with the specified initial data. - /// - /// Initial data to fill the priority queue with. - public PriorityQueue(IEnumerable> data) - { - this.data = data.ToArray(); - Count = this.data.Length; - for (int i = 0; i < this.data.Length; i++) - valueDict.Add(this.data[i].Value, i); - for (int i = this.data.Length / 2 - 1; i >= 0; i--) - cascadeDown(i); - } + /// + /// Creates a new instance of a priority queue with the specified initial data. + /// + /// Initial data to fill the priority queue with. + public PriorityQueue(IEnumerable> data) + { + this.data = data.ToArray(); + Count = this.data.Length; + for (int i = 0; i < this.data.Length; i++) + valueDict.Add(this.data[i].Value, i); + for (int i = this.data.Length / 2 - 1; i >= 0; i--) + cascadeDown(i); + } - /// - /// Decreases the priority of the specified element [O(log n)]. - /// - /// The element that should change. - /// The new priority of the element. - public void DecreasePriority(TValue value, TPriority newPriority) - { - int i = valueDict[value]; - if (data[i].Key.CompareTo(newPriority) < 0) - throw new InvalidOperationException("Can not increase the priority."); - data[i] = new KeyValuePair(newPriority, value); - cascadeUp(i); - } + /// + /// Decreases the priority of the specified element [O(log n)]. + /// + /// The element that should change. + /// The new priority of the element. + public void DecreasePriority(TValue value, TPriority newPriority) + { + int i = valueDict[value]; + if (data[i].Key.CompareTo(newPriority) < 0) + throw new InvalidOperationException("Can not increase the priority."); + data[i] = new KeyValuePair(newPriority, value); + cascadeUp(i); + } - /// - /// Empties the priority queue. - /// - public override void Clear() - { - base.Clear(); - valueDict.Clear(); - } + /// + /// Empties the priority queue. + /// + public override void Clear() + { + base.Clear(); + valueDict.Clear(); + } - /// - /// Adds a new element to the end of the tree. - /// - /// The priority of the new element. - /// The element itself. - protected override void add(TPriority priority, TValue value) - { - valueDict.Add(value, Count); - base.add(priority, value); - } + /// + /// Adds a new element to the end of the tree. + /// + /// The priority of the new element. + /// The element itself. + protected override void add(TPriority priority, TValue value) + { + valueDict.Add(value, Count); + base.add(priority, value); + } - /// - /// Swaps two elements in the tree. - /// - /// The index of the first element. - /// The index of the second element. - protected override void swap(int i1, int i2) - { - valueDict[data[i1].Value] = i2; - valueDict[data[i2].Value] = i1; + /// + /// Swaps two elements in the tree. + /// + /// The index of the first element. + /// The index of the second element. + protected override void swap(int i1, int i2) + { + valueDict[data[i1].Value] = i2; + valueDict[data[i2].Value] = i1; - base.swap(i1, i2); - } + base.swap(i1, i2); + } - /// - /// Removes an element from the tree, resetting its value to the default state. - /// - /// The index of the element to be removed. - protected override void reset(int i) - { - valueDict.Remove(data[i].Value); + /// + /// Removes an element from the tree, resetting its value to the default state. + /// + /// The index of the element to be removed. + protected override void reset(int i) + { + valueDict.Remove(data[i].Value); - base.reset(i); - } + base.reset(i); } } diff --git a/Bearded.Utilities/Collections/StaticPriorityQueue.cs b/Bearded.Utilities/Collections/StaticPriorityQueue.cs index b8a8da91..7b76e05c 100644 --- a/Bearded.Utilities/Collections/StaticPriorityQueue.cs +++ b/Bearded.Utilities/Collections/StaticPriorityQueue.cs @@ -3,218 +3,217 @@ using System.Collections.Generic; using System.Linq; -namespace Bearded.Utilities.Collections +namespace Bearded.Utilities.Collections; + +/// +/// A priority queue that is implemented with a binary min-heap and does not support the updating of priorities. +/// +/// +/// +public class StaticPriorityQueue + : IEnumerable> + where TPriority : IComparable { /// - /// A priority queue that is implemented with a binary min-heap and does not support the updating of priorities. + /// Array-representation of the entire heap. /// - /// - /// - public class StaticPriorityQueue - : IEnumerable> - where TPriority : IComparable - { - /// - /// Array-representation of the entire heap. - /// - protected KeyValuePair[] data; + protected KeyValuePair[] data; // ReSharper disable once MemberCanBeProtected.Global - /// - /// The amount of elements the tree contains. - /// - public int Count { get; protected set; } + /// + /// The amount of elements the tree contains. + /// + public int Count { get; protected set; } // ReSharper disable once MemberCanBeProtected.Global - /// - /// Creates a new instance of a static priority queue. - /// - public StaticPriorityQueue() - : this(1) { } - - /// - /// Creates a new instance of a static priority queue with the specified initial capacity. - /// - /// Initial capacity of the priority queue. - public StaticPriorityQueue(int capacity) - { - data = new KeyValuePair[capacity]; - } + /// + /// Creates a new instance of a static priority queue. + /// + public StaticPriorityQueue() + : this(1) { } - /// - /// Creates a new instance of a static priority queue containing the initial data. - /// - /// Initial data to fill the queue with. - public StaticPriorityQueue(IEnumerable> data) - { - this.data = data.ToArray(); - Count = this.data.Length; - for (int i = this.data.Length / 2 - 1; i >= 0; i--) - cascadeDown(i); - } + /// + /// Creates a new instance of a static priority queue with the specified initial capacity. + /// + /// Initial capacity of the priority queue. + public StaticPriorityQueue(int capacity) + { + data = new KeyValuePair[capacity]; + } - /// - /// Adds a new element to the tree [O(log n)]. - /// - /// The priority of the new element. - /// The element itself. - public void Enqueue(TPriority priority, TValue value) - { - if (Count == data.Length) - increaseCapacity(); + /// + /// Creates a new instance of a static priority queue containing the initial data. + /// + /// Initial data to fill the queue with. + public StaticPriorityQueue(IEnumerable> data) + { + this.data = data.ToArray(); + Count = this.data.Length; + for (int i = this.data.Length / 2 - 1; i >= 0; i--) + cascadeDown(i); + } - add(priority, value); - cascadeUp(Count); - Count++; - } + /// + /// Adds a new element to the tree [O(log n)]. + /// + /// The priority of the new element. + /// The element itself. + public void Enqueue(TPriority priority, TValue value) + { + if (Count == data.Length) + increaseCapacity(); - /// - /// Returns the element with the lowest priority from the tree without removing it [O(1)]. - /// - /// The element with the lowest priority. - public KeyValuePair Peek() - { - if (Count == 0) - throw new InvalidOperationException(); + add(priority, value); + cascadeUp(Count); + Count++; + } - return data[0]; - } + /// + /// Returns the element with the lowest priority from the tree without removing it [O(1)]. + /// + /// The element with the lowest priority. + public KeyValuePair Peek() + { + if (Count == 0) + throw new InvalidOperationException(); - /// - /// Dequeues the element with the lowest priority from the tree [O(log n)]. - /// - /// The element with the lowest priority. - public KeyValuePair Dequeue() - { - if (Count == 0) - throw new InvalidOperationException(); + return data[0]; + } - var oldRoot = data[0]; - swap(0, Count - 1); - reset(Count - 1); - Count--; - cascadeDown(0); + /// + /// Dequeues the element with the lowest priority from the tree [O(log n)]. + /// + /// The element with the lowest priority. + public KeyValuePair Dequeue() + { + if (Count == 0) + throw new InvalidOperationException(); - return oldRoot; - } + var oldRoot = data[0]; + swap(0, Count - 1); + reset(Count - 1); + Count--; + cascadeDown(0); - /// - /// Empties the priority queue. - /// - public virtual void Clear() - { - data = new KeyValuePair[data.Length]; - Count = 0; - } + return oldRoot; + } - private void increaseCapacity() - { - var newData = new KeyValuePair[2 * data.Length + 1]; - Array.Copy(data, newData, data.Length); - data = newData; - } + /// + /// Empties the priority queue. + /// + public virtual void Clear() + { + data = new KeyValuePair[data.Length]; + Count = 0; + } + + private void increaseCapacity() + { + var newData = new KeyValuePair[2 * data.Length + 1]; + Array.Copy(data, newData, data.Length); + data = newData; + } - /// - /// Cascades the changes upwards in the tree starting from the specified index [O(log n)]. - /// - /// The index at which the heap property might be violated. - protected void cascadeUp(int i) + /// + /// Cascades the changes upwards in the tree starting from the specified index [O(log n)]. + /// + /// The index at which the heap property might be violated. + protected void cascadeUp(int i) + { + while (i > 0) { - while (i > 0) - { - var parent = getParent(i); + var parent = getParent(i); - if (data[i].Key.CompareTo(data[parent].Key) < 0) - swap(i, parent); - else return; + if (data[i].Key.CompareTo(data[parent].Key) < 0) + swap(i, parent); + else return; - i = parent; - } + i = parent; } + } - /// - /// Cascades the changes downwards in the tree starting from the specified index [O(log n)]. - /// - /// The index at which the heap proprty might be violated. - protected void cascadeDown(int i) + /// + /// Cascades the changes downwards in the tree starting from the specified index [O(log n)]. + /// + /// The index at which the heap proprty might be violated. + protected void cascadeDown(int i) + { + while (true) { - while (true) - { - var left = getLeftChild(i); - var right = getRightChild(i); - var smallest = i; - - if (left < Count && data[left].Key.CompareTo(data[smallest].Key) < 0) - smallest = left; - if (right < Count && data[right].Key.CompareTo(data[smallest].Key) < 0) - smallest = right; - - if (smallest == i) - return; - - swap(i, smallest); - i = smallest; - } - } + var left = getLeftChild(i); + var right = getRightChild(i); + var smallest = i; - #region Index operations - /// - /// Adds a new element to the end of the tree. - /// - /// The priority of the new element. - /// The element itself. - protected virtual void add(TPriority priority, TValue value) - { - data[Count] = new KeyValuePair(priority, value); - } + if (left < Count && data[left].Key.CompareTo(data[smallest].Key) < 0) + smallest = left; + if (right < Count && data[right].Key.CompareTo(data[smallest].Key) < 0) + smallest = right; - /// - /// Swaps two elements in the tree. - /// - /// The index of the first element. - /// The index of the second element. - protected virtual void swap(int i1, int i2) - { - var oldFirst = data[i1]; - data[i1] = data[i2]; - data[i2] = oldFirst; - } + if (smallest == i) + return; - /// - /// Removes an element from the tree, resetting its value to the default state. - /// - /// The index of the element to be removed. - protected virtual void reset(int i) - { - data[i] = default; + swap(i, smallest); + i = smallest; } - #endregion + } - #region Tree traversal - private static int getParent(int i) - { - return (i - 1) / 2; - } + #region Index operations + /// + /// Adds a new element to the end of the tree. + /// + /// The priority of the new element. + /// The element itself. + protected virtual void add(TPriority priority, TValue value) + { + data[Count] = new KeyValuePair(priority, value); + } - private static int getLeftChild(int i) - { - return 2 * i + 1; - } + /// + /// Swaps two elements in the tree. + /// + /// The index of the first element. + /// The index of the second element. + protected virtual void swap(int i1, int i2) + { + var oldFirst = data[i1]; + data[i1] = data[i2]; + data[i2] = oldFirst; + } - private static int getRightChild(int i) - { - return 2 * i + 2; - } - #endregion + /// + /// Removes an element from the tree, resetting its value to the default state. + /// + /// The index of the element to be removed. + protected virtual void reset(int i) + { + data[i] = default; + } + #endregion + + #region Tree traversal + private static int getParent(int i) + { + return (i - 1) / 2; + } + + private static int getLeftChild(int i) + { + return 2 * i + 1; + } - public IEnumerator> GetEnumerator() + private static int getRightChild(int i) + { + return 2 * i + 2; + } + #endregion + + public IEnumerator> GetEnumerator() + { + for (var i = 0; i < Count; i++) { - for (var i = 0; i < Count; i++) - { - yield return data[i]; - } + yield return data[i]; } - - IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); } + + IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); } diff --git a/Bearded.Utilities/Core/AsyncAtomicUpdating.cs b/Bearded.Utilities/Core/AsyncAtomicUpdating.cs index 739d5d08..1783b957 100644 --- a/Bearded.Utilities/Core/AsyncAtomicUpdating.cs +++ b/Bearded.Utilities/Core/AsyncAtomicUpdating.cs @@ -1,40 +1,39 @@ -namespace Bearded.Utilities +namespace Bearded.Utilities; + +public sealed class AsyncAtomicUpdating { - public sealed class AsyncAtomicUpdating - { - private readonly object mutex = new object(); + private readonly object mutex = new object(); - public T Current { get; private set; } - public T Previous { get; private set; } - private T lastRecorded; + public T Current { get; private set; } + public T Previous { get; private set; } + private T lastRecorded; - public AsyncAtomicUpdating(T initialState) - { - Current = initialState; - Previous = initialState; - lastRecorded = initialState; - } + public AsyncAtomicUpdating(T initialState) + { + Current = initialState; + Previous = initialState; + lastRecorded = initialState; + } - public void SetLastKnownState(T state) + public void SetLastKnownState(T state) + { + lock (mutex) { - lock (mutex) - { - lastRecorded = state; - } + lastRecorded = state; } + } - public void Update() + public void Update() + { + lock (mutex) { - lock (mutex) - { - UpdateTo(lastRecorded); - } + UpdateTo(lastRecorded); } + } - public void UpdateTo(T state) - { - Previous = Current; - Current = state; - } + public void UpdateTo(T state) + { + Previous = Current; + Current = state; } } diff --git a/Bearded.Utilities/Core/Box.cs b/Bearded.Utilities/Core/Box.cs index 491594dc..e0ad7825 100644 --- a/Bearded.Utilities/Core/Box.cs +++ b/Bearded.Utilities/Core/Box.cs @@ -1,12 +1,11 @@ -namespace Bearded.Utilities +namespace Bearded.Utilities; + +public sealed class Box where T : struct { - public sealed class Box where T : struct - { - public T Value { get; } + public T Value { get; } - public Box(T value) - { - Value = value; - } + public Box(T value) + { + Value = value; } } diff --git a/Bearded.Utilities/Core/Do.cs b/Bearded.Utilities/Core/Do.cs index 98e935b3..35dd1a61 100644 --- a/Bearded.Utilities/Core/Do.cs +++ b/Bearded.Utilities/Core/Do.cs @@ -1,9 +1,8 @@ -namespace Bearded.Utilities +namespace Bearded.Utilities; + +public static class Do { - public static class Do - { - public static Box Box(T value) - where T : struct - => new Box(value); - } + public static Box Box(T value) + where T : struct + => new Box(value); } diff --git a/Bearded.Utilities/Core/Environment.cs b/Bearded.Utilities/Core/Environment.cs index e005a738..eddec0c0 100644 --- a/Bearded.Utilities/Core/Environment.cs +++ b/Bearded.Utilities/Core/Environment.cs @@ -1,97 +1,96 @@ using System; using System.IO; -namespace Bearded.Utilities +namespace Bearded.Utilities; + +public enum Platform { - public enum Platform - { - Windows, - Linux, - OSX - } + Windows, + Linux, + OSX +} - public static class Environment - { - #region Current platform - private static Platform? currentPlatform; +public static class Environment +{ + #region Current platform + private static Platform? currentPlatform; - private static Platform detectPlatform() + private static Platform detectPlatform() + { + switch (System.Environment.OSVersion.Platform) { - switch (System.Environment.OSVersion.Platform) - { - case PlatformID.Unix: - // Well, there are chances MacOSX is reported as Unix instead of MacOSX. - // Instead of platform check, we'll do a feature checks (Mac specific root folders) - if (Directory.Exists("/Applications") - && Directory.Exists("/System") - && Directory.Exists("/Users") - && Directory.Exists("/Volumes")) - return Platform.OSX; - else - return Platform.Linux; - - case PlatformID.MacOSX: + case PlatformID.Unix: + // Well, there are chances MacOSX is reported as Unix instead of MacOSX. + // Instead of platform check, we'll do a feature checks (Mac specific root folders) + if (Directory.Exists("/Applications") + && Directory.Exists("/System") + && Directory.Exists("/Users") + && Directory.Exists("/Volumes")) return Platform.OSX; + else + return Platform.Linux; - default: - return Platform.Windows; - } + case PlatformID.MacOSX: + return Platform.OSX; + + default: + return Platform.Windows; } + } - public static Platform CurrentPlatform => currentPlatform ?? (currentPlatform = detectPlatform()).Value; + public static Platform CurrentPlatform => currentPlatform ?? (currentPlatform = detectPlatform()).Value; - #endregion + #endregion - #region User settings directory - private static string userSettingsDirectory; + #region User settings directory + private static string userSettingsDirectory; - private static string buildUserSettingsDirectory() + private static string buildUserSettingsDirectory() + { + switch (CurrentPlatform) { - switch (CurrentPlatform) - { - case Platform.Windows: - return System.Environment.GetFolderPath(System.Environment.SpecialFolder.ApplicationData); + case Platform.Windows: + return System.Environment.GetFolderPath(System.Environment.SpecialFolder.ApplicationData); - case Platform.OSX: - var osxHomeDir = System.Environment.GetFolderPath(System.Environment.SpecialFolder.Personal); + case Platform.OSX: + var osxHomeDir = System.Environment.GetFolderPath(System.Environment.SpecialFolder.Personal); - if (string.IsNullOrEmpty(osxHomeDir)) - osxHomeDir = System.Environment.GetEnvironmentVariable("HOME"); + if (string.IsNullOrEmpty(osxHomeDir)) + osxHomeDir = System.Environment.GetEnvironmentVariable("HOME"); - return string.IsNullOrEmpty(osxHomeDir) ? "." : Path.Combine(osxHomeDir, "Library", "Application Support"); + return string.IsNullOrEmpty(osxHomeDir) ? "." : Path.Combine(osxHomeDir, "Library", "Application Support"); - case Platform.Linux: - var linuxConfigDir = System.Environment.GetEnvironmentVariable("XDG_CONFIG_HOME"); + case Platform.Linux: + var linuxConfigDir = System.Environment.GetEnvironmentVariable("XDG_CONFIG_HOME"); - if (!string.IsNullOrEmpty(linuxConfigDir)) - return linuxConfigDir; + if (!string.IsNullOrEmpty(linuxConfigDir)) + return linuxConfigDir; - var linuxHomeDir = System.Environment.GetEnvironmentVariable("HOME"); - // ReSharper disable once AssignNullToNotNullAttribute - return string.IsNullOrEmpty(linuxConfigDir) ? "." : Path.Combine(linuxHomeDir, ".config"); - default: - throw new InvalidOperationException("Encountered unknown platform."); - } + var linuxHomeDir = System.Environment.GetEnvironmentVariable("HOME"); + // ReSharper disable once AssignNullToNotNullAttribute + return string.IsNullOrEmpty(linuxConfigDir) ? "." : Path.Combine(linuxHomeDir, ".config"); + default: + throw new InvalidOperationException("Encountered unknown platform."); } + } - /// - /// Gets the default user settings directory for the current platform. - /// For Windows: %appdata% - /// For OSX: ~/Library/Application Support - /// For Linux: ~/.config - /// - public static string UserSettingsDirectory => userSettingsDirectory ?? (userSettingsDirectory = buildUserSettingsDirectory()); - - /// - /// Gets the default user setting directory for a given game name. - /// For Windows: %appdata%\[gamename] - /// For OSX: ~/Library/Application Support/[gamename] - /// For Linux: ~/.config/[gamename] - /// - public static string UserSettingsDirectoryFor(string gameName) - { - return Path.Combine(UserSettingsDirectory, gameName); - } - #endregion + /// + /// Gets the default user settings directory for the current platform. + /// For Windows: %appdata% + /// For OSX: ~/Library/Application Support + /// For Linux: ~/.config + /// + public static string UserSettingsDirectory => userSettingsDirectory ?? (userSettingsDirectory = buildUserSettingsDirectory()); + + /// + /// Gets the default user setting directory for a given game name. + /// For Windows: %appdata%\[gamename] + /// For OSX: ~/Library/Application Support/[gamename] + /// For Linux: ~/.config/[gamename] + /// + public static string UserSettingsDirectoryFor(string gameName) + { + return Path.Combine(UserSettingsDirectory, gameName); } + #endregion } diff --git a/Bearded.Utilities/Core/EventHandlers.cs b/Bearded.Utilities/Core/EventHandlers.cs index dd89a6dc..58487428 100644 --- a/Bearded.Utilities/Core/EventHandlers.cs +++ b/Bearded.Utilities/Core/EventHandlers.cs @@ -1,9 +1,8 @@ -namespace Bearded.Utilities -{ - public delegate void VoidEventHandler(); +namespace Bearded.Utilities; - public delegate void GenericEventHandler(T t); - public delegate void GenericEventHandler(T1 t1, T2 t2); - public delegate void GenericEventHandler(T1 t1, T2 t2, T3 t3); - public delegate void GenericEventHandler(T1 t1, T2 t2, T3 t3, T4 t4); -} +public delegate void VoidEventHandler(); + +public delegate void GenericEventHandler(T t); +public delegate void GenericEventHandler(T1 t1, T2 t2); +public delegate void GenericEventHandler(T1 t1, T2 t2, T3 t3); +public delegate void GenericEventHandler(T1 t1, T2 t2, T3 t3, T4 t4); \ No newline at end of file diff --git a/Bearded.Utilities/Core/Id.cs b/Bearded.Utilities/Core/Id.cs index 3412eec3..81229f87 100644 --- a/Bearded.Utilities/Core/Id.cs +++ b/Bearded.Utilities/Core/Id.cs @@ -1,26 +1,25 @@ using System; using System.Runtime.InteropServices; -namespace Bearded.Utilities +namespace Bearded.Utilities; + +[StructLayout(LayoutKind.Sequential, Pack = 1)] +public readonly struct Id : IEquatable> { - [StructLayout(LayoutKind.Sequential, Pack = 1)] - public readonly struct Id : IEquatable> - { - public int Value { get; } + public int Value { get; } - public Id(int value) - { - Value = value; - } + public Id(int value) + { + Value = value; + } - public bool IsValid => Value != 0; - public static Id Invalid => new Id(0); + public bool IsValid => Value != 0; + public static Id Invalid => new Id(0); - public override string ToString() => $"{typeof(T).Name}:{Value}"; - public override int GetHashCode() => Value.GetHashCode(); - public bool Equals(Id other) => Value.Equals(other.Value); - public override bool Equals(object? obj) => obj is Id id && Equals(id); - public static bool operator ==(Id left, Id right) => left.Equals(right); - public static bool operator !=(Id left, Id right) => !(left == right); - } + public override string ToString() => $"{typeof(T).Name}:{Value}"; + public override int GetHashCode() => Value.GetHashCode(); + public bool Equals(Id other) => Value.Equals(other.Value); + public override bool Equals(object? obj) => obj is Id id && Equals(id); + public static bool operator ==(Id left, Id right) => left.Equals(right); + public static bool operator !=(Id left, Id right) => !(left == right); } diff --git a/Bearded.Utilities/Core/IdManager.cs b/Bearded.Utilities/Core/IdManager.cs index 7f217a0e..36495e2e 100644 --- a/Bearded.Utilities/Core/IdManager.cs +++ b/Bearded.Utilities/Core/IdManager.cs @@ -1,18 +1,17 @@ using System; using System.Collections.Generic; -namespace Bearded.Utilities +namespace Bearded.Utilities; + +public sealed class IdManager { - public sealed class IdManager - { - private readonly Dictionary lastIds = new Dictionary(); + private readonly Dictionary lastIds = new Dictionary(); - public Id GetNext() - { - var type = typeof(T); - var id = lastIds.TryGetValue(type, out var lastId) ? lastId + 1 : 1; - lastIds[type] = id; - return new Id(id); - } + public Id GetNext() + { + var type = typeof(T); + var id = lastIds.TryGetValue(type, out var lastId) ? lastId + 1 : 1; + lastIds[type] = id; + return new Id(id); } } diff --git a/Bearded.Utilities/Core/MathConstants.cs b/Bearded.Utilities/Core/MathConstants.cs index 575b011e..316030ea 100644 --- a/Bearded.Utilities/Core/MathConstants.cs +++ b/Bearded.Utilities/Core/MathConstants.cs @@ -1,42 +1,41 @@ using System; -namespace Bearded.Utilities +namespace Bearded.Utilities; + +public static class MathConstants { - public static class MathConstants - { - /// - /// Represents the square root of 2 (1.414213f). - /// - public const float Sqrt2 = 1.414213f; - - /// - /// Represents the square root of 3 (1.732051f). - /// - public const float Sqrt3 = 1.732051f; - - /// - /// Represents the value of pi (3.14159274). - /// - public const float Pi = MathF.PI; - - /// - /// Represents the value of pi divided by two (1.57079637). - /// - public const float PiOver2 = Pi / 2.0f; - - /// - /// Represents the value of pi divided by four (0.7853982). - /// - public const float PiOver4 = Pi / 4.0f; - - /// - /// Represents the value of pi times two (6.28318548). - /// - public const float TwoPi = 2 * Pi; - - /// - /// Represents the value of tau (6.28318548). - /// - public const float Tau = 2 * Pi; - } + /// + /// Represents the square root of 2 (1.414213f). + /// + public const float Sqrt2 = 1.414213f; + + /// + /// Represents the square root of 3 (1.732051f). + /// + public const float Sqrt3 = 1.732051f; + + /// + /// Represents the value of pi (3.14159274). + /// + public const float Pi = MathF.PI; + + /// + /// Represents the value of pi divided by two (1.57079637). + /// + public const float PiOver2 = Pi / 2.0f; + + /// + /// Represents the value of pi divided by four (0.7853982). + /// + public const float PiOver4 = Pi / 4.0f; + + /// + /// Represents the value of pi times two (6.28318548). + /// + public const float TwoPi = 2 * Pi; + + /// + /// Represents the value of tau (6.28318548). + /// + public const float Tau = 2 * Pi; } diff --git a/Bearded.Utilities/Core/MathExtensions.cs b/Bearded.Utilities/Core/MathExtensions.cs index d6ea0c1a..b6d6a51a 100644 --- a/Bearded.Utilities/Core/MathExtensions.cs +++ b/Bearded.Utilities/Core/MathExtensions.cs @@ -1,306 +1,305 @@ using System; using OpenTK.Mathematics; -namespace Bearded.Utilities +namespace Bearded.Utilities; + +public static class MathExtensions { - public static class MathExtensions + #region Clamped + /// + /// Clamps the value to a specified range. + /// + public static int Clamped(this int value, int min, int max) + { + if (value <= min) + return min; + if (value >= max) + return max; + return value; + } + + /// + /// Clamps the value to a specified range. + /// + public static float Clamped(this float value, float min, float max) + { + if (value <= min) + return min; + if (value >= max) + return max; + return value; + } + + /// + /// Clamps the value to a specified range. + /// + public static double Clamped(this double value, double min, double max) + { + if (value <= min) + return min; + if (value >= max) + return max; + return value; + } + #endregion + + #region Modulo + /// + /// Gives the number projected to Zn. + /// + public static int ModuloN(this int a, int n) => ((a % n) + n) % n; + + #endregion + + #region Powers + /// + /// Squares an integer. + /// + public static int Squared(this int i) => i * i; + + /// + /// Squares a float. + /// + public static float Squared(this float f) => f * f; + + /// + /// Squares a double. + /// + public static double Squared(this double d) => d * d; + + + /// + /// Cubes an integer. + /// + public static int Cubed(this int i) => i * i * i; + + /// + /// Cubes a float. + /// + public static float Cubed(this float f) => f * f * f; + + /// + /// Cubes a double. + /// + public static double Cubed(this double d) => d * d * d; + + + /// + /// Returns the square root of the specified number. + /// + public static float Sqrted(this float f) => (float)Math.Sqrt(f); + + /// + /// Returns the square root of the specified number. + /// + public static double Sqrted(this double d) => Math.Sqrt(d); + + + /// + /// Returns a specified number raised to the specified power. + /// + public static float Powed(this float b, float power) => (float)Math.Pow(b, power); + + /// + /// Returns a specified number raised to the specified power. + /// + public static double Powed(this double b, double power) => Math.Pow(b, power); + + #endregion + + #region Trigonomitry + + #region Float + /// + /// Returns the cosine of the specified angle. + /// + public static float Cos(this float f) => (float)Math.Cos(f); + + /// + /// Returns the sine of the specified angle. + /// + public static float Sin(this float f) => (float)Math.Sin(f); + + /// + /// Returns the tangent of the specified angle. + /// + public static float Tan(this float f) => (float)Math.Tan(f); + + /// + /// Returns the angle whose cosine is the specified number. + /// + public static float Acos(this float f) => (float)Math.Acos(f); + + /// + /// Returns the angle whose sine is the specified number. + /// + public static float Asin(this float f) => (float)Math.Asin(f); + + /// + /// Returns the angle whose tangent is the specified number. + /// + public static float Atan(this float f) => (float)Math.Atan(f); + + #endregion + + #region Double + /// + /// Returns the cosine of the specified angle. + /// + public static double Cos(this double d) => Math.Cos(d); + + /// + /// Returns the sine of the specified angle. + /// + public static double Sin(this double d) => Math.Sin(d); + + /// + /// Returns the tangent of the specified angle. + /// + public static double Tan(this double d) => Math.Tan(d); + + /// + /// Returns the angle whose cosine is the specified number. + /// + public static double Acos(this double d) => Math.Acos(d); + + /// + /// Returns the angle whose sine is the specified number. + /// + public static double Asin(this double d) => Math.Asin(d); + + /// + /// Returns the angle whose tangent is the specified number. + /// + public static double Atan(this double d) => Math.Atan(d); + + #endregion + + #endregion + + #region Rounding + + /// + /// Returns the lowest integral number higher than or equal to the specified number. + /// + public static int CeiledToInt(float f) => (int)Math.Ceiling(f); + + /// + /// Returns the lowest integral number higher than or equal to the specified number. + /// + public static int CeiledToInt(double d) => (int)Math.Ceiling(d); + + + /// + /// Returns the highest integral number lower than or equal to the specified number. + /// + public static int FlooredToInt(float f) => (int)Math.Floor(f); + + /// + /// Returns the highest integral number lower than or equal to the specified number. + /// + public static int FlooredToInt(double d) => (int)Math.Floor(d); + + + /// + /// Returns the integral number closest to the specified number. + /// + public static int RoundedToInt(float f) => (int)Math.Round(f); + + /// + /// Returns the integral number closest to the specified number. + /// + public static int RoundedToInt(double d) => (int)Math.Round(d); + + #endregion + + #region NaN sanity checks + + public static void ThrowIfNaN(this double d) => d.ThrowIfNaN("Double is NaN while it is not allowed to."); + public static void ThrowIfNaN(this float f) => f.ThrowIfNaN("Double is NaN while it is not allowed to."); + public static void ThrowIfNaN(this Vector2 v) => v.ThrowIfNaN("Double is NaN while it is not allowed to."); + public static void ThrowIfNaN(this Vector3 v) => v.ThrowIfNaN("Double is NaN while it is not allowed to."); + public static void ThrowIfNaN(this Vector4 v) => v.ThrowIfNaN("Double is NaN while it is not allowed to."); + + public static void ThrowIfNaN(this double d, string exceptionString) + { + if (double.IsNaN(d)) + throw new ArithmeticException(exceptionString); + } + + public static void ThrowIfNaN(this float f, string exceptionString) { - #region Clamped - /// - /// Clamps the value to a specified range. - /// - public static int Clamped(this int value, int min, int max) - { - if (value <= min) - return min; - if (value >= max) - return max; - return value; - } - - /// - /// Clamps the value to a specified range. - /// - public static float Clamped(this float value, float min, float max) - { - if (value <= min) - return min; - if (value >= max) - return max; - return value; - } - - /// - /// Clamps the value to a specified range. - /// - public static double Clamped(this double value, double min, double max) - { - if (value <= min) - return min; - if (value >= max) - return max; - return value; - } - #endregion - - #region Modulo - /// - /// Gives the number projected to Zn. - /// - public static int ModuloN(this int a, int n) => ((a % n) + n) % n; - - #endregion - - #region Powers - /// - /// Squares an integer. - /// - public static int Squared(this int i) => i * i; - - /// - /// Squares a float. - /// - public static float Squared(this float f) => f * f; - - /// - /// Squares a double. - /// - public static double Squared(this double d) => d * d; - - - /// - /// Cubes an integer. - /// - public static int Cubed(this int i) => i * i * i; - - /// - /// Cubes a float. - /// - public static float Cubed(this float f) => f * f * f; - - /// - /// Cubes a double. - /// - public static double Cubed(this double d) => d * d * d; - - - /// - /// Returns the square root of the specified number. - /// - public static float Sqrted(this float f) => (float)Math.Sqrt(f); - - /// - /// Returns the square root of the specified number. - /// - public static double Sqrted(this double d) => Math.Sqrt(d); - - - /// - /// Returns a specified number raised to the specified power. - /// - public static float Powed(this float b, float power) => (float)Math.Pow(b, power); - - /// - /// Returns a specified number raised to the specified power. - /// - public static double Powed(this double b, double power) => Math.Pow(b, power); - - #endregion - - #region Trigonomitry - - #region Float - /// - /// Returns the cosine of the specified angle. - /// - public static float Cos(this float f) => (float)Math.Cos(f); - - /// - /// Returns the sine of the specified angle. - /// - public static float Sin(this float f) => (float)Math.Sin(f); - - /// - /// Returns the tangent of the specified angle. - /// - public static float Tan(this float f) => (float)Math.Tan(f); - - /// - /// Returns the angle whose cosine is the specified number. - /// - public static float Acos(this float f) => (float)Math.Acos(f); - - /// - /// Returns the angle whose sine is the specified number. - /// - public static float Asin(this float f) => (float)Math.Asin(f); - - /// - /// Returns the angle whose tangent is the specified number. - /// - public static float Atan(this float f) => (float)Math.Atan(f); - - #endregion - - #region Double - /// - /// Returns the cosine of the specified angle. - /// - public static double Cos(this double d) => Math.Cos(d); - - /// - /// Returns the sine of the specified angle. - /// - public static double Sin(this double d) => Math.Sin(d); - - /// - /// Returns the tangent of the specified angle. - /// - public static double Tan(this double d) => Math.Tan(d); - - /// - /// Returns the angle whose cosine is the specified number. - /// - public static double Acos(this double d) => Math.Acos(d); - - /// - /// Returns the angle whose sine is the specified number. - /// - public static double Asin(this double d) => Math.Asin(d); - - /// - /// Returns the angle whose tangent is the specified number. - /// - public static double Atan(this double d) => Math.Atan(d); - - #endregion - - #endregion - - #region Rounding - - /// - /// Returns the lowest integral number higher than or equal to the specified number. - /// - public static int CeiledToInt(float f) => (int)Math.Ceiling(f); - - /// - /// Returns the lowest integral number higher than or equal to the specified number. - /// - public static int CeiledToInt(double d) => (int)Math.Ceiling(d); - - - /// - /// Returns the highest integral number lower than or equal to the specified number. - /// - public static int FlooredToInt(float f) => (int)Math.Floor(f); - - /// - /// Returns the highest integral number lower than or equal to the specified number. - /// - public static int FlooredToInt(double d) => (int)Math.Floor(d); - - - /// - /// Returns the integral number closest to the specified number. - /// - public static int RoundedToInt(float f) => (int)Math.Round(f); - - /// - /// Returns the integral number closest to the specified number. - /// - public static int RoundedToInt(double d) => (int)Math.Round(d); - - #endregion - - #region NaN sanity checks - - public static void ThrowIfNaN(this double d) => d.ThrowIfNaN("Double is NaN while it is not allowed to."); - public static void ThrowIfNaN(this float f) => f.ThrowIfNaN("Double is NaN while it is not allowed to."); - public static void ThrowIfNaN(this Vector2 v) => v.ThrowIfNaN("Double is NaN while it is not allowed to."); - public static void ThrowIfNaN(this Vector3 v) => v.ThrowIfNaN("Double is NaN while it is not allowed to."); - public static void ThrowIfNaN(this Vector4 v) => v.ThrowIfNaN("Double is NaN while it is not allowed to."); - - public static void ThrowIfNaN(this double d, string exceptionString) - { - if (double.IsNaN(d)) - throw new ArithmeticException(exceptionString); - } - - public static void ThrowIfNaN(this float f, string exceptionString) - { - if (float.IsNaN(f)) - throw new ArithmeticException(exceptionString); - } - - public static void ThrowIfNaN(this Vector2 vector, string exceptionString) - { - if (vector.IsNaN()) - throw new ArithmeticException(exceptionString); - } - - public static void ThrowIfNaN(this Vector3 vector, string exceptionString) - { - if (vector.IsNaN()) - throw new ArithmeticException(exceptionString); - } - - public static void ThrowIfNaN(this Vector4 vector, string exceptionString) - { - if (vector.IsNaN()) - throw new ArithmeticException(exceptionString); - } - - /// - /// Checks whether any of the vector components is NaN. - /// - public static bool IsNaN(this Vector2 vector) => float.IsNaN(vector.X) || float.IsNaN(vector.Y); - - /// - /// Checks whether any of the vector components is NaN. - /// - public static bool IsNaN(this Vector3 vector) => float.IsNaN(vector.X) || float.IsNaN(vector.Y) || float.IsNaN(vector.Z); - - /// - /// Checks whether any of the vector components is NaN. - /// - public static bool IsNaN(this Vector4 vector) => float.IsNaN(vector.X) || float.IsNaN(vector.Y) || float.IsNaN(vector.Z) || float.IsNaN(vector.W); - - #endregion - - #region Vector - public static Vector3 WithZ(this Vector2 xy) => new Vector3(xy.X, xy.Y, 0); - public static Vector3 WithZ(this Vector2 xy, float z) => new Vector3(xy.X, xy.Y, z); - public static Vector4 WithW(this Vector3 xyz, float w) => new Vector4(xyz, w); - public static Vector4 WithZw(this Vector2 xy, float z, float w) => new Vector4(xy.X, xy.Y, z, w); - - /// - /// Normalizes a vector. If all components of the vector are zero, no exception is thrown. Instead the zero vector is returned. - /// - public static Vector2 NormalizedSafe(this Vector2 vector) - { - var lSqrd = vector.LengthSquared; - - return lSqrd == 0 ? new Vector2() : vector / lSqrd.Sqrted(); - } - /// - /// Normalizes a vector. If all components of the vector are zero, no exception is thrown. Instead the zero vector is returned. - /// - public static Vector3 NormalizedSafe(this Vector3 vector) - { - var lSqrd = vector.LengthSquared; - - return lSqrd == 0 ? new Vector3() : vector / lSqrd.Sqrted(); - } - /// - /// Normalizes a vector. If all components of the vector are zero, no exception is thrown. Instead the zero vector is returned. - /// - public static Vector4 NormalizedSafe(this Vector4 vector) - { - var lSqrd = vector.LengthSquared; - - return lSqrd == 0 ? new Vector4() : vector / lSqrd.Sqrted(); - } - #endregion + if (float.IsNaN(f)) + throw new ArithmeticException(exceptionString); + } + + public static void ThrowIfNaN(this Vector2 vector, string exceptionString) + { + if (vector.IsNaN()) + throw new ArithmeticException(exceptionString); + } + + public static void ThrowIfNaN(this Vector3 vector, string exceptionString) + { + if (vector.IsNaN()) + throw new ArithmeticException(exceptionString); + } + + public static void ThrowIfNaN(this Vector4 vector, string exceptionString) + { + if (vector.IsNaN()) + throw new ArithmeticException(exceptionString); + } + + /// + /// Checks whether any of the vector components is NaN. + /// + public static bool IsNaN(this Vector2 vector) => float.IsNaN(vector.X) || float.IsNaN(vector.Y); + + /// + /// Checks whether any of the vector components is NaN. + /// + public static bool IsNaN(this Vector3 vector) => float.IsNaN(vector.X) || float.IsNaN(vector.Y) || float.IsNaN(vector.Z); + + /// + /// Checks whether any of the vector components is NaN. + /// + public static bool IsNaN(this Vector4 vector) => float.IsNaN(vector.X) || float.IsNaN(vector.Y) || float.IsNaN(vector.Z) || float.IsNaN(vector.W); + + #endregion + + #region Vector + public static Vector3 WithZ(this Vector2 xy) => new Vector3(xy.X, xy.Y, 0); + public static Vector3 WithZ(this Vector2 xy, float z) => new Vector3(xy.X, xy.Y, z); + public static Vector4 WithW(this Vector3 xyz, float w) => new Vector4(xyz, w); + public static Vector4 WithZw(this Vector2 xy, float z, float w) => new Vector4(xy.X, xy.Y, z, w); + + /// + /// Normalizes a vector. If all components of the vector are zero, no exception is thrown. Instead the zero vector is returned. + /// + public static Vector2 NormalizedSafe(this Vector2 vector) + { + var lSqrd = vector.LengthSquared; + + return lSqrd == 0 ? new Vector2() : vector / lSqrd.Sqrted(); + } + /// + /// Normalizes a vector. If all components of the vector are zero, no exception is thrown. Instead the zero vector is returned. + /// + public static Vector3 NormalizedSafe(this Vector3 vector) + { + var lSqrd = vector.LengthSquared; + + return lSqrd == 0 ? new Vector3() : vector / lSqrd.Sqrted(); + } + /// + /// Normalizes a vector. If all components of the vector are zero, no exception is thrown. Instead the zero vector is returned. + /// + public static Vector4 NormalizedSafe(this Vector4 vector) + { + var lSqrd = vector.LengthSquared; + + return lSqrd == 0 ? new Vector4() : vector / lSqrd.Sqrted(); } + #endregion } diff --git a/Bearded.Utilities/Core/Maybe.cs b/Bearded.Utilities/Core/Maybe.cs index 53de3736..b22871da 100644 --- a/Bearded.Utilities/Core/Maybe.cs +++ b/Bearded.Utilities/Core/Maybe.cs @@ -3,91 +3,90 @@ using System.Runtime.CompilerServices; using Bearded.Utilities.Monads; -namespace Bearded.Utilities +namespace Bearded.Utilities; + +public readonly struct Maybe : IEquatable> { - public readonly struct Maybe : IEquatable> + private readonly bool hasValue; + private readonly T value; + + private Maybe(T value) { - private readonly bool hasValue; - private readonly T value; + hasValue = true; + this.value = value; + } - private Maybe(T value) - { - hasValue = true; - this.value = value; - } + public static Maybe Nothing => default; - public static Maybe Nothing => default; + internal static Maybe Just(T value) => new Maybe(value); - internal static Maybe Just(T value) => new Maybe(value); + public T ValueOrDefault(T @default) => hasValue ? value : @default; - public T ValueOrDefault(T @default) => hasValue ? value : @default; + public T ValueOrDefault(Func defaultProvider) => hasValue ? value : defaultProvider(); - public T ValueOrDefault(Func defaultProvider) => hasValue ? value : defaultProvider(); + public Result ValueOrFailure(TError error) => + hasValue ? (Result) Result.Success(value) : Result.Failure(error); - public Result ValueOrFailure(TError error) => - hasValue ? (Result) Result.Success(value) : Result.Failure(error); + public Result ValueOrFailure(Func errorProvider) => + hasValue ? (Result) Result.Success(value) : Result.Failure(errorProvider()); - public Result ValueOrFailure(Func errorProvider) => - hasValue ? (Result) Result.Success(value) : Result.Failure(errorProvider()); + public Maybe Select(Func selector) => + hasValue ? Maybe.Just(selector(value)) : Maybe.Nothing; - public Maybe Select(Func selector) => - hasValue ? Maybe.Just(selector(value)) : Maybe.Nothing; + public Maybe SelectMany(Func> selector) => + hasValue ? selector(value) : Maybe.Nothing; - public Maybe SelectMany(Func> selector) => - hasValue ? selector(value) : Maybe.Nothing; + public Maybe Where(Func predicate) => hasValue && predicate(value) ? this : Nothing; - public Maybe Where(Func predicate) => hasValue && predicate(value) ? this : Nothing; + public void Match(Action onValue) + { + if (hasValue) + onValue(value); + } - public void Match(Action onValue) + public void Match(Action onValue, Action onNothing) + { + if (hasValue) { - if (hasValue) - onValue(value); + onValue(value); } - - public void Match(Action onValue, Action onNothing) + else { - if (hasValue) - { - onValue(value); - } - else - { - onNothing(); - } + onNothing(); } + } - public TResult Match(Func onValue, Func onNothing) - { - return hasValue ? onValue(value) : onNothing(); - } + public TResult Match(Func onValue, Func onNothing) + { + return hasValue ? onValue(value) : onNothing(); + } - public bool Equals(Maybe other) => - hasValue == other.hasValue && EqualityComparer.Default.Equals(value, other.value); + public bool Equals(Maybe other) => + hasValue == other.hasValue && EqualityComparer.Default.Equals(value, other.value); - public override bool Equals(object? obj) => obj is Maybe other && Equals(other); + public override bool Equals(object? obj) => obj is Maybe other && Equals(other); - public override int GetHashCode() => hasValue ? EqualityComparer.Default.GetHashCode(value) : 0; + public override int GetHashCode() => hasValue ? EqualityComparer.Default.GetHashCode(value) : 0; - public override string ToString() => hasValue ? $"just {value}" : "nothing"; + public override string ToString() => hasValue ? $"just {value}" : "nothing"; - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static implicit operator Maybe(NothingMaybe _) => Nothing; - } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static implicit operator Maybe(NothingMaybe _) => Nothing; +} - public static class Maybe - { - public static Maybe FromNullable(T? value) where T : class => - value == null ? Nothing : Maybe.Just(value); +public static class Maybe +{ + public static Maybe FromNullable(T? value) where T : class => + value == null ? Nothing : Maybe.Just(value); - public static Maybe FromNullable(T? value) where T : struct => - value.HasValue ? Maybe.Just(value.Value) : Nothing; + public static Maybe FromNullable(T? value) where T : struct => + value.HasValue ? Maybe.Just(value.Value) : Nothing; - public static Maybe Just(T value) => Maybe.Just(value); + public static Maybe Just(T value) => Maybe.Just(value); - public static NothingMaybe Nothing => default; - } + public static NothingMaybe Nothing => default; +} - public readonly struct NothingMaybe - { - } +public readonly struct NothingMaybe +{ } diff --git a/Bearded.Utilities/Core/MoreMath.cs b/Bearded.Utilities/Core/MoreMath.cs index de65b10c..f8fe98e6 100644 --- a/Bearded.Utilities/Core/MoreMath.cs +++ b/Bearded.Utilities/Core/MoreMath.cs @@ -1,45 +1,44 @@ using System; -namespace Bearded.Utilities +namespace Bearded.Utilities; + +public static class MoreMath { - public static class MoreMath - { - private const double degreesToRadians = Math.PI / 180; - private const double radiansToDegrees = 180 / Math.PI; - - /// - /// Returns the lowest integral number higher than or equal to the specified number. - /// - public static int CeilToInt(double d) => (int)Math.Ceiling(d); - - /// - /// Returns the highest integral number lower than or equal to the specified number. - /// - public static int FloorToInt(double d) => (int)Math.Floor(d); - - /// - /// Returns the integral number closest to the specified number. - /// - public static int RoundToInt(double d) => (int)Math.Round(d); - - /// - /// Converts an angle in radians to degrees. - /// - public static float RadiansToDegrees(float radians) => radians * (float) radiansToDegrees; - - /// - /// Converts an angle in radians to degrees. - /// - public static double RadiansToDegrees(double radians) => radians * radiansToDegrees; - - /// - /// Converts an angle in degrees to radians. - /// - public static float DegreesToRadians(float degrees) => degrees * (float) degreesToRadians; - - /// - /// Converts an angle in degrees to radians. - /// - public static double DegreesToRadians(double degrees) => degrees * degreesToRadians; - } + private const double degreesToRadians = Math.PI / 180; + private const double radiansToDegrees = 180 / Math.PI; + + /// + /// Returns the lowest integral number higher than or equal to the specified number. + /// + public static int CeilToInt(double d) => (int)Math.Ceiling(d); + + /// + /// Returns the highest integral number lower than or equal to the specified number. + /// + public static int FloorToInt(double d) => (int)Math.Floor(d); + + /// + /// Returns the integral number closest to the specified number. + /// + public static int RoundToInt(double d) => (int)Math.Round(d); + + /// + /// Converts an angle in radians to degrees. + /// + public static float RadiansToDegrees(float radians) => radians * (float) radiansToDegrees; + + /// + /// Converts an angle in radians to degrees. + /// + public static double RadiansToDegrees(double radians) => radians * radiansToDegrees; + + /// + /// Converts an angle in degrees to radians. + /// + public static float DegreesToRadians(float degrees) => degrees * (float) degreesToRadians; + + /// + /// Converts an angle in degrees to radians. + /// + public static double DegreesToRadians(double degrees) => degrees * degreesToRadians; } diff --git a/Bearded.Utilities/Core/RandomExtensions.cs b/Bearded.Utilities/Core/RandomExtensions.cs index 1831a2c2..662291ce 100644 --- a/Bearded.Utilities/Core/RandomExtensions.cs +++ b/Bearded.Utilities/Core/RandomExtensions.cs @@ -1,149 +1,148 @@ using System; -namespace Bearded.Utilities +namespace Bearded.Utilities; + +/// +/// This class adds a variety of extension methods for the Random class to expand its functionality. +/// Note that several of these methods are slightly biased for the sake of performance. +/// +public static class RandomExtensions { + #region NextLong() + + /// + /// Returns random (biased) long integer. + /// + public static long NextLong(this Random random) => random.NextLong(0, long.MaxValue); + /// - /// This class adds a variety of extension methods for the Random class to expand its functionality. - /// Note that several of these methods are slightly biased for the sake of performance. + /// Returns random (biased) long integer in the interval [0, upper bound[ /// - public static class RandomExtensions + public static long NextLong(this Random random, long max) => random.NextLong(0, max); + + /// + /// Returns random (biased) long integer in the interval [lower bound, upper bound[ + /// + public static long NextLong(this Random random, long min, long max) { - #region NextLong() - - /// - /// Returns random (biased) long integer. - /// - public static long NextLong(this Random random) => random.NextLong(0, long.MaxValue); - - /// - /// Returns random (biased) long integer in the interval [0, upper bound[ - /// - public static long NextLong(this Random random, long max) => random.NextLong(0, max); - - /// - /// Returns random (biased) long integer in the interval [lower bound, upper bound[ - /// - public static long NextLong(this Random random, long min, long max) - { - if (min == max) - return min; - - if (min > max) - throw new ArgumentException("Maximum must be larger or equal to minimum bound."); - - // "awmygawd this is so biased" - Tom Rijnbeek - var buf = new byte[8]; - random.NextBytes(buf); - var longRand = BitConverter.ToInt64(buf, 0); - - return Math.Abs(longRand % (max - min)) + min; - } - - #endregion - - #region NextDouble() - - /// - /// Returns a random double in the interval [0, upper bound[. - /// - public static double NextDouble(this Random random, double max) => random.NextDouble() * max; - - /// - /// Returns a random double in the interval [lower bound, upper bound[. - /// - public static double NextDouble(this Random random, double min, double max) - => random.NextDouble() * (max - min) + min; - - #endregion - - #region NormalDouble() - - /// - /// Generates a random double using the standard normal distribution. - /// - public static double NormalDouble(this Random random) - { - // Box-Muller - var u1 = random.NextDouble(); - var u2 = random.NextDouble(); - return Math.Sqrt(-2 * Math.Log(u1)) * Math.Cos(2 * Math.PI * u2); - } - - /// - /// Generates a random double using the normal distribution with the given mean and deviation. - /// - public static double NormalDouble(this Random random, double mean, double deviation) - => mean + deviation * random.NormalDouble(); - - #endregion - - #region NextFloat() - - /// - /// Returns random float in the interval [0, 1[. - /// - public static float NextFloat(this Random random) => (float)random.NextDouble(); - - /// - /// Returns a random float in the interval [0, upper bound[. - /// - public static float NextFloat(this Random random, float max) - => (float)(random.NextDouble() * max); - - /// - /// Returns a random float in the interval [lower bound, upper bound[. - /// - public static float NextFloat(this Random random, float min, float max) - => (float)(random.NextDouble() * (max - min) + min); - - #endregion - - #region NormalFloat() - - /// - /// Generates a random float using the standard normal distribution. - /// - public static float NormalFloat(this Random random) - => (float)random.NormalDouble(); - - /// - /// Generates a random float using the normal distribution with the given mean and deviation. - /// - public static float NormalFloat(this Random random, float mean, float deviation) - => mean + (float)(deviation * random.NormalDouble()); - - #endregion - - #region Various - - /// - /// Returns -1 or 1 randomly. - /// - public static int NextSign(this Random random) => 2 * random.Next(2) - 1; - - /// - /// Returns true half the time, false otherwise. - /// - public static bool NextBool(this Random random) - => random.NextDouble() < 0.5; - - /// - /// Returns true with the given probability, and false otherwise. - /// - public static bool NextBool(this Random random, double probability) - => random.NextDouble() < probability; - - /// - /// Returns an integer with a given expected value. Will always return either the floor or ceil of the given value. - /// - public static int Discretise(this Random random, float value) - { - var i = (int)value; - var rest = value - i; - return random.NextBool(rest) ? i + 1 : i; - } - - #endregion + if (min == max) + return min; + + if (min > max) + throw new ArgumentException("Maximum must be larger or equal to minimum bound."); + // "awmygawd this is so biased" - Tom Rijnbeek + var buf = new byte[8]; + random.NextBytes(buf); + var longRand = BitConverter.ToInt64(buf, 0); + + return Math.Abs(longRand % (max - min)) + min; } + + #endregion + + #region NextDouble() + + /// + /// Returns a random double in the interval [0, upper bound[. + /// + public static double NextDouble(this Random random, double max) => random.NextDouble() * max; + + /// + /// Returns a random double in the interval [lower bound, upper bound[. + /// + public static double NextDouble(this Random random, double min, double max) + => random.NextDouble() * (max - min) + min; + + #endregion + + #region NormalDouble() + + /// + /// Generates a random double using the standard normal distribution. + /// + public static double NormalDouble(this Random random) + { + // Box-Muller + var u1 = random.NextDouble(); + var u2 = random.NextDouble(); + return Math.Sqrt(-2 * Math.Log(u1)) * Math.Cos(2 * Math.PI * u2); + } + + /// + /// Generates a random double using the normal distribution with the given mean and deviation. + /// + public static double NormalDouble(this Random random, double mean, double deviation) + => mean + deviation * random.NormalDouble(); + + #endregion + + #region NextFloat() + + /// + /// Returns random float in the interval [0, 1[. + /// + public static float NextFloat(this Random random) => (float)random.NextDouble(); + + /// + /// Returns a random float in the interval [0, upper bound[. + /// + public static float NextFloat(this Random random, float max) + => (float)(random.NextDouble() * max); + + /// + /// Returns a random float in the interval [lower bound, upper bound[. + /// + public static float NextFloat(this Random random, float min, float max) + => (float)(random.NextDouble() * (max - min) + min); + + #endregion + + #region NormalFloat() + + /// + /// Generates a random float using the standard normal distribution. + /// + public static float NormalFloat(this Random random) + => (float)random.NormalDouble(); + + /// + /// Generates a random float using the normal distribution with the given mean and deviation. + /// + public static float NormalFloat(this Random random, float mean, float deviation) + => mean + (float)(deviation * random.NormalDouble()); + + #endregion + + #region Various + + /// + /// Returns -1 or 1 randomly. + /// + public static int NextSign(this Random random) => 2 * random.Next(2) - 1; + + /// + /// Returns true half the time, false otherwise. + /// + public static bool NextBool(this Random random) + => random.NextDouble() < 0.5; + + /// + /// Returns true with the given probability, and false otherwise. + /// + public static bool NextBool(this Random random, double probability) + => random.NextDouble() < probability; + + /// + /// Returns an integer with a given expected value. Will always return either the floor or ceil of the given value. + /// + public static int Discretise(this Random random, float value) + { + var i = (int)value; + var rest = value - i; + return random.NextBool(rest) ? i + 1 : i; + } + + #endregion + } diff --git a/Bearded.Utilities/Core/ResettableLazy.cs b/Bearded.Utilities/Core/ResettableLazy.cs index 40583c93..e15c29c0 100644 --- a/Bearded.Utilities/Core/ResettableLazy.cs +++ b/Bearded.Utilities/Core/ResettableLazy.cs @@ -1,49 +1,48 @@ using System; -namespace Bearded.Utilities +namespace Bearded.Utilities; + +/// +/// Factory class for creating ResettableLazy instances. +/// +public static class ResettableLazy { - /// - /// Factory class for creating ResettableLazy instances. - /// - public static class ResettableLazy + public static ResettableLazy From(Func factory) { - public static ResettableLazy From(Func factory) - { - return new ResettableLazy(factory); - } + return new ResettableLazy(factory); } +} - /// - /// This class represents a lazily initialized value. It can be reset to call the initialization again afterwards. - /// It is not thread safe. - /// - public sealed class ResettableLazy - { - private readonly Func factory; +/// +/// This class represents a lazily initialized value. It can be reset to call the initialization again afterwards. +/// It is not thread safe. +/// +public sealed class ResettableLazy +{ + private readonly Func factory; - private bool hasValue; - private T value = default!; + private bool hasValue; + private T value = default!; - public T Value => ensureValue(); + public T Value => ensureValue(); - public ResettableLazy(Func factory) - { - this.factory = factory; - } + public ResettableLazy(Func factory) + { + this.factory = factory; + } - public void Reset() - { - hasValue = false; - } + public void Reset() + { + hasValue = false; + } - private T ensureValue() + private T ensureValue() + { + if (!hasValue) { - if (!hasValue) - { - value = factory(); - hasValue = true; - } - return value; + value = factory(); + hasValue = true; } + return value; } } diff --git a/Bearded.Utilities/Core/Singleton.cs b/Bearded.Utilities/Core/Singleton.cs index aefa6f0b..832ec751 100644 --- a/Bearded.Utilities/Core/Singleton.cs +++ b/Bearded.Utilities/Core/Singleton.cs @@ -1,17 +1,16 @@ using System; -namespace Bearded.Utilities +namespace Bearded.Utilities; + +public abstract class Singleton where TSelf : Singleton { - public abstract class Singleton where TSelf : Singleton - { - public static TSelf Instance { get; private set; } + public static TSelf Instance { get; private set; } - protected Singleton() - { - if (Instance != null) - throw new InvalidOperationException("A singleton can only be instantiated once."); + protected Singleton() + { + if (Instance != null) + throw new InvalidOperationException("A singleton can only be instantiated once."); - Instance = (TSelf)this; - } + Instance = (TSelf)this; } } diff --git a/Bearded.Utilities/Core/StaticRandom.cs b/Bearded.Utilities/Core/StaticRandom.cs index 98f6d04c..9c48643f 100644 --- a/Bearded.Utilities/Core/StaticRandom.cs +++ b/Bearded.Utilities/Core/StaticRandom.cs @@ -1,161 +1,160 @@ using System; -namespace Bearded.Utilities +namespace Bearded.Utilities; + +/// +/// This static class offers a variety of pseudo random methods. +/// The class is threadsafe and uses a different internal random object for each thread. +/// Note that several of the methods are slightly biased for the sake of performance. +/// +/// The actual implementations of the custom random methods can be found in RandomExtensions. +public static class StaticRandom { + #region Threadsafe random + [ThreadStatic] + private static Random random; + + /// + /// The thread safe instance of Random used by StaticRandom + /// + // is internal for use as default random in Linq.Extensions + internal static Random Random => random ?? (random = new Random()); + /// - /// This static class offers a variety of pseudo random methods. - /// The class is threadsafe and uses a different internal random object for each thread. - /// Note that several of the methods are slightly biased for the sake of performance. + /// Overrides the Random object for the calling thread by one with the given seed. /// - /// The actual implementations of the custom random methods can be found in RandomExtensions. - public static class StaticRandom + public static void SeedWith(int seed) { - #region Threadsafe random - [ThreadStatic] - private static Random random; - - /// - /// The thread safe instance of Random used by StaticRandom - /// - // is internal for use as default random in Linq.Extensions - internal static Random Random => random ?? (random = new Random()); + random = new Random(seed); + } - /// - /// Overrides the Random object for the calling thread by one with the given seed. - /// - public static void SeedWith(int seed) - { - random = new Random(seed); - } + #endregion - #endregion - - #region Int() + #region Int() - /// - /// Returns a random integer. - /// - public static int Int() => Random.Next(); + /// + /// Returns a random integer. + /// + public static int Int() => Random.Next(); - /// - /// Returns a (biased) random integer in the interval [0, upper bound[. - /// - public static int Int(int max) => Random.Next(max); + /// + /// Returns a (biased) random integer in the interval [0, upper bound[. + /// + public static int Int(int max) => Random.Next(max); - /// - /// Returns random (biased) integer in the interval [lower bound, upper bound[. - /// - public static int Int(int min, int max) => Random.Next(min, max); + /// + /// Returns random (biased) integer in the interval [lower bound, upper bound[. + /// + public static int Int(int min, int max) => Random.Next(min, max); - #endregion + #endregion - #region Long() + #region Long() - /// - /// Returns random (biased) long integer. - /// - public static long Long() => Random.NextLong(); + /// + /// Returns random (biased) long integer. + /// + public static long Long() => Random.NextLong(); - /// - /// Returns random (biased) long integer in the interval [0, upper bound[. - /// - public static long Long(long max) => Random.NextLong(max); + /// + /// Returns random (biased) long integer in the interval [0, upper bound[. + /// + public static long Long(long max) => Random.NextLong(max); - /// - /// Returns random (biased) long integer in the interval [lower bound, upper bound[. - /// - public static long Long(long min, long max) => Random.NextLong(min, max); + /// + /// Returns random (biased) long integer in the interval [lower bound, upper bound[. + /// + public static long Long(long min, long max) => Random.NextLong(min, max); - #endregion + #endregion - #region Double() + #region Double() - /// - /// Returns a random double in the interval [0, 1[. - /// - public static double Double() => Random.NextDouble(); + /// + /// Returns a random double in the interval [0, 1[. + /// + public static double Double() => Random.NextDouble(); - /// - /// Returns a random double in the interval [0, upper bound[. - /// - public static double Double(double max) => Random.NextDouble(max); + /// + /// Returns a random double in the interval [0, upper bound[. + /// + public static double Double(double max) => Random.NextDouble(max); - /// - /// Returns a random double in the interval [lower bound, upper bound[. - /// - public static double Double(double min, double max) => Random.NextDouble(min, max); + /// + /// Returns a random double in the interval [lower bound, upper bound[. + /// + public static double Double(double min, double max) => Random.NextDouble(min, max); - #endregion + #endregion - #region NormalDouble() + #region NormalDouble() - /// - /// Generates a random double using the standard normal distribution. - /// - public static double NormalDouble() => Random.NormalDouble(); + /// + /// Generates a random double using the standard normal distribution. + /// + public static double NormalDouble() => Random.NormalDouble(); - /// - /// Generates a random double using the normal distribution with the given mean and deviation. - /// - public static double NormalDouble(double mean, double deviation) => Random.NormalDouble(mean, deviation); + /// + /// Generates a random double using the normal distribution with the given mean and deviation. + /// + public static double NormalDouble(double mean, double deviation) => Random.NormalDouble(mean, deviation); - #endregion + #endregion - #region Float() - /// - /// Returns random float in the interval [0, 1[. - /// - public static float Float() => Random.NextFloat(); + #region Float() + /// + /// Returns random float in the interval [0, 1[. + /// + public static float Float() => Random.NextFloat(); - /// - /// Returns a random float in the interval [0, upper bound[. - /// - public static float Float(float max) => Random.NextFloat(max); + /// + /// Returns a random float in the interval [0, upper bound[. + /// + public static float Float(float max) => Random.NextFloat(max); - /// - /// Returns a random float in the interval [lower bound, upper bound[. - /// - public static float Float(float min, float max) => Random.NextFloat(min, max); + /// + /// Returns a random float in the interval [lower bound, upper bound[. + /// + public static float Float(float min, float max) => Random.NextFloat(min, max); - #endregion + #endregion - #region NormalFloat() + #region NormalFloat() - /// - /// Generates a random float using the standard normal distribution. - /// - public static float NormalFloat() => Random.NormalFloat(); + /// + /// Generates a random float using the standard normal distribution. + /// + public static float NormalFloat() => Random.NormalFloat(); - /// - /// Generates a random float using the normal distribution with the given mean and deviation. - /// - public static float NormalFloat(float mean, float deviation) => Random.NormalFloat(mean, deviation); + /// + /// Generates a random float using the normal distribution with the given mean and deviation. + /// + public static float NormalFloat(float mean, float deviation) => Random.NormalFloat(mean, deviation); - #endregion + #endregion - #region Various + #region Various - /// - /// Returns -1 or 1 randomly. - /// - public static int Sign() => Random.NextSign(); + /// + /// Returns -1 or 1 randomly. + /// + public static int Sign() => Random.NextSign(); - /// - /// Returns true half the time, false otherwise. - /// - public static bool Bool() => Random.NextBool(); + /// + /// Returns true half the time, false otherwise. + /// + public static bool Bool() => Random.NextBool(); - /// - /// Returns true with the given probability, and false otherwise. - /// - public static bool Bool(double probability) => Random.NextBool(probability); + /// + /// Returns true with the given probability, and false otherwise. + /// + public static bool Bool(double probability) => Random.NextBool(probability); - /// - /// Returns an integer with a given expected value. Will always return either the floor or ceil of the given value. - /// - public static int Discretise(float value) => Random.Discretise(value); + /// + /// Returns an integer with a given expected value. Will always return either the floor or ceil of the given value. + /// + public static int Discretise(float value) => Random.Discretise(value); - #endregion - } + #endregion } diff --git a/Bearded.Utilities/Core/Void.cs b/Bearded.Utilities/Core/Void.cs index 682ce829..8bc51ca6 100644 --- a/Bearded.Utilities/Core/Void.cs +++ b/Bearded.Utilities/Core/Void.cs @@ -1,23 +1,22 @@ using System; -namespace Bearded.Utilities +namespace Bearded.Utilities; + +public readonly struct Void : IComparable, IEquatable { - public readonly struct Void : IComparable, IEquatable - { - // Behold its power http://xkcd.com/1486/ + // Behold its power http://xkcd.com/1486/ - public int CompareTo(Void _) => 0; - public bool Equals(Void _) => true; - public override bool Equals(object? obj) => obj is Void; - public override int GetHashCode() => 0; + public int CompareTo(Void _) => 0; + public bool Equals(Void _) => true; + public override bool Equals(object? obj) => obj is Void; + public override int GetHashCode() => 0; - // ReSharper disable UnusedParameter.Global - public static bool operator ==(Void _, Void __) => true; - public static bool operator !=(Void _, Void __) => false; - public static bool operator >=(Void _, Void __) => true; - public static bool operator <=(Void _, Void __) => true; - public static bool operator >(Void _, Void __) => false; - public static bool operator <(Void _, Void __) => false; - // ReSharper restore UnusedParameter.Global - } + // ReSharper disable UnusedParameter.Global + public static bool operator ==(Void _, Void __) => true; + public static bool operator !=(Void _, Void __) => false; + public static bool operator >=(Void _, Void __) => true; + public static bool operator <=(Void _, Void __) => true; + public static bool operator >(Void _, Void __) => false; + public static bool operator <(Void _, Void __) => false; + // ReSharper restore UnusedParameter.Global } diff --git a/Bearded.Utilities/Core/interpolation/IInterpolationMethod1d.cs b/Bearded.Utilities/Core/interpolation/IInterpolationMethod1d.cs index cf9b4936..9a0512a7 100644 --- a/Bearded.Utilities/Core/interpolation/IInterpolationMethod1d.cs +++ b/Bearded.Utilities/Core/interpolation/IInterpolationMethod1d.cs @@ -1,7 +1,6 @@ -namespace Bearded.Utilities +namespace Bearded.Utilities; + +public interface IInterpolationMethod1d { - public interface IInterpolationMethod1d - { - public double Interpolate(double from, double to, double t); - } + public double Interpolate(double from, double to, double t); } diff --git a/Bearded.Utilities/Core/interpolation/IInterpolationMethod2d.cs b/Bearded.Utilities/Core/interpolation/IInterpolationMethod2d.cs index 294ca2ad..8c285e07 100644 --- a/Bearded.Utilities/Core/interpolation/IInterpolationMethod2d.cs +++ b/Bearded.Utilities/Core/interpolation/IInterpolationMethod2d.cs @@ -1,7 +1,6 @@ -namespace Bearded.Utilities +namespace Bearded.Utilities; + +public interface IInterpolationMethod2d { - public interface IInterpolationMethod2d - { - public double Interpolate(double value00, double value10, double value01, double value11, double u, double v); - } + public double Interpolate(double value00, double value10, double value01, double value11, double u, double v); } diff --git a/Bearded.Utilities/Core/interpolation/Interpolate.cs b/Bearded.Utilities/Core/interpolation/Interpolate.cs index 0683e8e5..2a3f8999 100644 --- a/Bearded.Utilities/Core/interpolation/Interpolate.cs +++ b/Bearded.Utilities/Core/interpolation/Interpolate.cs @@ -1,220 +1,219 @@ using OpenTK.Mathematics; -namespace Bearded.Utilities +namespace Bearded.Utilities; + +public static class Interpolate { - public static class Interpolate + #region Bezier 2D + /// + /// Performs a first order Bezier curve interpolation. + /// + public static Vector2 Bezier(Vector2 p0, Vector2 p1, float t) { - #region Bezier 2D - /// - /// Performs a first order Bezier curve interpolation. - /// - public static Vector2 Bezier(Vector2 p0, Vector2 p1, float t) - { - if (t <= 0) - return p0; - if (t >= 1) - return p1; + if (t <= 0) + return p0; + if (t >= 1) + return p1; - return bezier(p0, p1, t); - } + return bezier(p0, p1, t); + } - private static Vector2 bezier(Vector2 p0, Vector2 p1, float t) - => p0 + (p1 - p0) * t; + private static Vector2 bezier(Vector2 p0, Vector2 p1, float t) + => p0 + (p1 - p0) * t; - /// - /// Performs a second order Bezier curve interpolation. - /// - public static Vector2 Bezier(Vector2 p0, Vector2 p1, Vector2 p2, float t) - { - if (t <= 0) - return p0; - if (t >= 1) - return p2; + /// + /// Performs a second order Bezier curve interpolation. + /// + public static Vector2 Bezier(Vector2 p0, Vector2 p1, Vector2 p2, float t) + { + if (t <= 0) + return p0; + if (t >= 1) + return p2; - return bezier(p0, p1, p2, t); - } + return bezier(p0, p1, p2, t); + } - private static Vector2 bezier(Vector2 p0, Vector2 p1, Vector2 p2, float t) - => bezier(bezier(p0, p1, t), bezier(p1, p2, t), t); + private static Vector2 bezier(Vector2 p0, Vector2 p1, Vector2 p2, float t) + => bezier(bezier(p0, p1, t), bezier(p1, p2, t), t); - /// - /// Performs a third order Bezier curve interpolation. - /// - public static Vector2 Bezier(Vector2 p0, Vector2 p1, Vector2 p2, Vector2 p3, float t) - { - if (t <= 0) - return p0; - if (t >= 1) - return p3; + /// + /// Performs a third order Bezier curve interpolation. + /// + public static Vector2 Bezier(Vector2 p0, Vector2 p1, Vector2 p2, Vector2 p3, float t) + { + if (t <= 0) + return p0; + if (t >= 1) + return p3; - return bezier(p0, p1, p2, p3, t); - } + return bezier(p0, p1, p2, p3, t); + } - private static Vector2 bezier(Vector2 p0, Vector2 p1, Vector2 p2, Vector2 p3, float t) - => bezier(bezier(p0, p1, p2, t), bezier(p1, p2, p3, t), t); + private static Vector2 bezier(Vector2 p0, Vector2 p1, Vector2 p2, Vector2 p3, float t) + => bezier(bezier(p0, p1, p2, t), bezier(p1, p2, p3, t), t); - #endregion + #endregion - #region Bezier 3D - /// - /// Performs a first order Bezier curve interpolation. - /// - public static Vector3 Bezier(Vector3 p0, Vector3 p1, float t) - { - if (t <= 0) - return p0; - if (t >= 1) - return p1; + #region Bezier 3D + /// + /// Performs a first order Bezier curve interpolation. + /// + public static Vector3 Bezier(Vector3 p0, Vector3 p1, float t) + { + if (t <= 0) + return p0; + if (t >= 1) + return p1; - return bezier(p0, p1, t); - } + return bezier(p0, p1, t); + } - private static Vector3 bezier(Vector3 p0, Vector3 p1, float t) - => p0 + (p1 - p0) * t; + private static Vector3 bezier(Vector3 p0, Vector3 p1, float t) + => p0 + (p1 - p0) * t; - /// - /// Performs a second order Bezier curve interpolation. - /// - public static Vector3 Bezier(Vector3 p0, Vector3 p1, Vector3 p2, float t) - { - if (t <= 0) - return p0; - if (t >= 1) - return p2; + /// + /// Performs a second order Bezier curve interpolation. + /// + public static Vector3 Bezier(Vector3 p0, Vector3 p1, Vector3 p2, float t) + { + if (t <= 0) + return p0; + if (t >= 1) + return p2; - return bezier(p0, p1, p2, t); - } + return bezier(p0, p1, p2, t); + } - private static Vector3 bezier(Vector3 p0, Vector3 p1, Vector3 p2, float t) - => bezier(bezier(p0, p1, t), bezier(p1, p2, t), t); + private static Vector3 bezier(Vector3 p0, Vector3 p1, Vector3 p2, float t) + => bezier(bezier(p0, p1, t), bezier(p1, p2, t), t); - /// - /// Performs a third order Bezier curve interpolation. - /// - public static Vector3 Bezier(Vector3 p0, Vector3 p1, Vector3 p2, Vector3 p3, float t) - { - if (t <= 0) - return p0; - if (t >= 1) - return p3; + /// + /// Performs a third order Bezier curve interpolation. + /// + public static Vector3 Bezier(Vector3 p0, Vector3 p1, Vector3 p2, Vector3 p3, float t) + { + if (t <= 0) + return p0; + if (t >= 1) + return p3; - return bezier(p0, p1, p2, p3, t); - } + return bezier(p0, p1, p2, p3, t); + } - private static Vector3 bezier(Vector3 p0, Vector3 p1, Vector3 p2, Vector3 p3, float t) - => bezier(bezier(p0, p1, p2, t), bezier(p1, p2, p3, t), t); + private static Vector3 bezier(Vector3 p0, Vector3 p1, Vector3 p2, Vector3 p3, float t) + => bezier(bezier(p0, p1, p2, t), bezier(p1, p2, p3, t), t); - #endregion + #endregion - #region Smooth - /// - /// Performs a clamped Hermite spline interpolation. - /// - public static float Hermite(float from, float fromTangent, float to, float toTangent, float t) - { - if (t <= 0) - return from; - if (t >= 1) - return to; + #region Smooth + /// + /// Performs a clamped Hermite spline interpolation. + /// + public static float Hermite(float from, float fromTangent, float to, float toTangent, float t) + { + if (t <= 0) + return from; + if (t >= 1) + return to; - var d = from - to; + var d = from - to; - return (( - (2 * d + fromTangent + toTangent) * t - - 3 * d - 2 * fromTangent - toTangent) * t + + return (( + (2 * d + fromTangent + toTangent) * t + - 3 * d - 2 * fromTangent - toTangent) * t + fromTangent) * t + - from; - } + from; + } - /// - /// Performs a cubic interpolation between two values. - /// - public static float SmoothStep(float from, float to, float t) - { - if (t <= 0) - return from; - if (t >= 1) - return to; + /// + /// Performs a cubic interpolation between two values. + /// + public static float SmoothStep(float from, float to, float t) + { + if (t <= 0) + return from; + if (t >= 1) + return to; - return (2 * t - 3) * t * t * (from - to) + from; - } - #endregion + return (2 * t - 3) * t * t * (from - to) + from; + } + #endregion - #region Linear - /// - /// Performs a linear interpolation between two values. - /// - public static float Lerp(float from, float to, float t) - { - if (t <= 0) - return from; - if (t >= 1) - return to; + #region Linear + /// + /// Performs a linear interpolation between two values. + /// + public static float Lerp(float from, float to, float t) + { + if (t <= 0) + return from; + if (t >= 1) + return to; - return from + (to - from) * t; - } + return from + (to - from) * t; + } + + /// + /// Performs a bilinear interpolation between four values. + /// + public static float BiLerp(float value00, float value10, float value01, float value11, float u, float v) + { + float first, second; - /// - /// Performs a bilinear interpolation between four values. - /// - public static float BiLerp(float value00, float value10, float value01, float value11, float u, float v) + if (u <= 0) + { + first = value00; + second = value01; + } + else if (u >= 1) { - float first, second; - - if (u <= 0) - { - first = value00; - second = value01; - } - else if (u >= 1) - { - first = value10; - second = value11; - } - else - { - first = value00 + (value10 - value00) * u; - second = value01 + (value11 - value01) * u; - } - - if (v <= 0) - return first; - if (v >= 1) - return second; - - return first + (second - first) * v; + first = value10; + second = value11; } + else + { + first = value00 + (value10 - value00) * u; + second = value01 + (value11 - value01) * u; + } + + if (v <= 0) + return first; + if (v >= 1) + return second; + + return first + (second - first) * v; + } + + /// + /// Performs a bilinear interpolation between four values. + /// + public static Vector2 BiLerp(Vector2 value00, Vector2 value10, Vector2 value01, Vector2 value11, float u, float v) + { + Vector2 first, second; - /// - /// Performs a bilinear interpolation between four values. - /// - public static Vector2 BiLerp(Vector2 value00, Vector2 value10, Vector2 value01, Vector2 value11, float u, float v) + if (u <= 0) { - Vector2 first, second; - - if (u <= 0) - { - first = value00; - second = value01; - } - else if (u >= 1) - { - first = value10; - second = value11; - } - else - { - first = value00 + (value10 - value00) * u; - second = value01 + (value11 - value01) * u; - } - - if (v <= 0) - return first; - if (v >= 1) - return second; - - return first + (second - first) * v; + first = value00; + second = value01; } - #endregion + else if (u >= 1) + { + first = value10; + second = value11; + } + else + { + first = value00 + (value10 - value00) * u; + second = value01 + (value11 - value01) * u; + } + + if (v <= 0) + return first; + if (v >= 1) + return second; + + return first + (second - first) * v; } + #endregion } diff --git a/Bearded.Utilities/Core/interpolation/Interpolation1d.cs b/Bearded.Utilities/Core/interpolation/Interpolation1d.cs index 52c8187b..03dec35d 100644 --- a/Bearded.Utilities/Core/interpolation/Interpolation1d.cs +++ b/Bearded.Utilities/Core/interpolation/Interpolation1d.cs @@ -1,40 +1,39 @@ using System; -namespace Bearded.Utilities +namespace Bearded.Utilities; + +public static class Interpolation1d { - public static class Interpolation1d - { - public static IInterpolationMethod1d Nearest { get; } = new NearestInterpolation(); - public static IInterpolationMethod1d Linear { get; } = new LinearInterpolation(); - public static IInterpolationMethod1d SmoothStep { get; } = new SmoothStepInterpolation(); + public static IInterpolationMethod1d Nearest { get; } = new NearestInterpolation(); + public static IInterpolationMethod1d Linear { get; } = new LinearInterpolation(); + public static IInterpolationMethod1d SmoothStep { get; } = new SmoothStepInterpolation(); - private sealed class NearestInterpolation : IInterpolationMethod1d + private sealed class NearestInterpolation : IInterpolationMethod1d + { + public double Interpolate(double from, double to, double t) { - public double Interpolate(double from, double to, double t) - { - return t < 0.5 ? from : to; - } + return t < 0.5 ? from : to; } + } - private sealed class LinearInterpolation : IInterpolationMethod1d + private sealed class LinearInterpolation : IInterpolationMethod1d + { + public double Interpolate(double from, double to, double t) { - public double Interpolate(double from, double to, double t) - { - var clampedT = Math.Max(0, Math.Min(t, 1)); - return from * (1 - clampedT) + to * clampedT; - } + var clampedT = Math.Max(0, Math.Min(t, 1)); + return from * (1 - clampedT) + to * clampedT; } + } - private sealed class SmoothStepInterpolation : IInterpolationMethod1d + private sealed class SmoothStepInterpolation : IInterpolationMethod1d + { + public double Interpolate(double from, double to, double t) { - public double Interpolate(double from, double to, double t) - { - var clampedT = Math.Max(0, Math.Min(t, 1)); - var smoothT = smoothStep(clampedT); - return from * (1 - smoothT) + to * smoothT; - } - - private static double smoothStep(double t) => t * t * (3 - 2 * t); + var clampedT = Math.Max(0, Math.Min(t, 1)); + var smoothT = smoothStep(clampedT); + return from * (1 - smoothT) + to * smoothT; } + + private static double smoothStep(double t) => t * t * (3 - 2 * t); } } diff --git a/Bearded.Utilities/Core/interpolation/Interpolation2d.cs b/Bearded.Utilities/Core/interpolation/Interpolation2d.cs index 25c9a5a9..49600bdf 100644 --- a/Bearded.Utilities/Core/interpolation/Interpolation2d.cs +++ b/Bearded.Utilities/Core/interpolation/Interpolation2d.cs @@ -1,26 +1,25 @@ -namespace Bearded.Utilities +namespace Bearded.Utilities; + +public static class Interpolation2d { - public static class Interpolation2d + public static IInterpolationMethod2d Nearest { get; } = new ComposedInterpolation(Interpolation1d.Nearest); + public static IInterpolationMethod2d BiLinear { get; } = new ComposedInterpolation(Interpolation1d.Linear); + + private sealed class ComposedInterpolation : IInterpolationMethod2d { - public static IInterpolationMethod2d Nearest { get; } = new ComposedInterpolation(Interpolation1d.Nearest); - public static IInterpolationMethod2d BiLinear { get; } = new ComposedInterpolation(Interpolation1d.Linear); + private readonly IInterpolationMethod1d inner; - private sealed class ComposedInterpolation : IInterpolationMethod2d + public ComposedInterpolation(IInterpolationMethod1d inner) { - private readonly IInterpolationMethod1d inner; - - public ComposedInterpolation(IInterpolationMethod1d inner) - { - this.inner = inner; - } + this.inner = inner; + } - public double Interpolate( - double value00, double value10, double value01, double value11, double u, double v) - { - var valueU0 = inner.Interpolate(value00, value10, u); - var valueU1 = inner.Interpolate(value01, value11, u); - return inner.Interpolate(valueU0, valueU1, v); - } + public double Interpolate( + double value00, double value10, double value01, double value11, double u, double v) + { + var valueU0 = inner.Interpolate(value00, value10, u); + var valueU1 = inner.Interpolate(value01, value11, u); + return inner.Interpolate(valueU0, valueU1, v); } } } diff --git a/Bearded.Utilities/Geometry/Angle.cs b/Bearded.Utilities/Geometry/Angle.cs index be730747..b47ed65d 100644 --- a/Bearded.Utilities/Geometry/Angle.cs +++ b/Bearded.Utilities/Geometry/Angle.cs @@ -2,380 +2,379 @@ using System.Globalization; using OpenTK.Mathematics; -namespace Bearded.Utilities.Geometry +namespace Bearded.Utilities.Geometry; + +/// +/// A typesafe representation of a signed angle. +/// +public readonly struct Angle : IEquatable, IFormattable { + private readonly float radians; + + #region Constructing + + private Angle(float radians) + { + this.radians = radians; + } + + /// + /// Initialises an angle from a relative angle value in radians. + /// + public static Angle FromRadians(float radians) + { + return new Angle(radians); + } + + /// + /// Initialises an angle from a relative angle value in degrees. + /// + public static Angle FromDegrees(float degrees) + { + return new Angle(MoreMath.DegreesToRadians(degrees)); + } + + /// + /// Initialises an angle as the signed difference between two directional unit vectors in the 2D plane. + /// If the vectors are not unit length the result is undefined. + /// + public static Angle Between(Vector2 from, Vector2 to) + { + float perpDot = from.Y * to.X - from.X * to.Y; + + return FromRadians(MathF.Atan2(perpDot, Vector2.Dot(from, to))); + } + + /// + /// Initialises an angle as the signed difference between two directions in the 2D plane. + /// The returned value is the smallest possible angle from one direction to the other. + /// + public static Angle Between(Direction2 from, Direction2 to) + { + return to - from; + } + + /// + /// Initialises an angle as the signed difference between two directions in the 2D plane. + /// The returned value is the smallest positive angle from one direction to the other. + /// + public static Angle BetweenPositive(Direction2 from, Direction2 to) + { + var a = Between(from, to); + if (a.radians < 0) + a += MathConstants.TwoPi.Radians(); + return a; + } + + /// + /// Initialises an angle as the signed difference between two directions in the 2D plane. + /// The returned value is the smallest negative angle from one direction to the other. + /// + public static Angle BetweenNegative(Direction2 from, Direction2 to) + { + var a = Between(from, to); + if (a.radians > 0) + a -= MathConstants.TwoPi.Radians(); + return a; + } + + #endregion + + #region Static Fields + + /// + /// The default zero angle. + /// + public static readonly Angle Zero = new Angle(0); + + #endregion + + #region Properties + + /// + /// Gets the value of the angle in radians. + /// + public float Radians => radians; + + /// + /// Gets the value of the angle in degrees. + /// + public float Degrees => MoreMath.RadiansToDegrees(radians); + + /// + /// Gets a 2x2 rotation matrix that rotates vectors by this angle. + /// + public Matrix2 Transformation => Matrix2.CreateRotation(radians); + + /// + /// Gets the magnitude (absolute value) of the angle in radians. + /// + public float MagnitudeInRadians => Math.Abs(radians); + + /// + /// Gets the magnitude (absolute value) of the angle in degrees. + /// + public float MagnitudeInDegrees => Math.Abs(Degrees); + + #endregion + + #region Methods + + #region Arithmetic + + /// + /// Returns the Sine of the angle. + /// + public float Sin() + { + return MathF.Sin(radians); + } + /// + /// Returns the Cosine of the angle. + /// + public float Cos() + { + return MathF.Cos(radians); + } + /// + /// Returns the Tangent of the angle. + /// + public float Tan() + { + return MathF.Tan(radians); + } + /// + /// Returns the Sign of the angle. + /// + public int Sign() + { + return Math.Sign(radians); + } + /// + /// Returns the absolute value of the angle. + /// + public Angle Abs() + { + return new Angle(Math.Abs(radians)); + } + /// + /// Returns a new Angle with |value| == 1 radians and the same sign as this angle. + /// Returns a new Angle with value 0 if the angle is zero. + /// + public Angle Normalized() + { + return new Angle(Math.Sign(radians)); + } + /// + /// Clamps this angle between a minimum and a maximum angle. + /// + public Angle Clamped(Angle min, Angle max) + { + return Clamp(this, min, max); + } + + #region Statics + + /// + /// Returns the larger of two angles. + /// + public static Angle Max(Angle a1, Angle a2) + { + return a1 > a2 ? a1 : a2; + } + + /// + /// Returns the smaller of two angles. + /// + public static Angle Min(Angle a1, Angle a2) + { + return a1 < a2 ? a1 : a2; + } + + /// + /// Clamps one angle between a minimum and a maximum angle. + /// + public static Angle Clamp(Angle a, Angle min, Angle max) + { + return a < max ? a > min ? a : min : max; + } + + #endregion + + #endregion + + #endregion + + #region Operators + + #region Arithmetic + + /// + /// Adds two angles. + /// + public static Angle operator +(Angle angle1, Angle angle2) + { + return new Angle(angle1.radians + angle2.radians); + } + /// - /// A typesafe representation of a signed angle. - /// - public readonly struct Angle : IEquatable, IFormattable - { - private readonly float radians; - - #region Constructing - - private Angle(float radians) - { - this.radians = radians; - } - - /// - /// Initialises an angle from a relative angle value in radians. - /// - public static Angle FromRadians(float radians) - { - return new Angle(radians); - } - - /// - /// Initialises an angle from a relative angle value in degrees. - /// - public static Angle FromDegrees(float degrees) - { - return new Angle(MoreMath.DegreesToRadians(degrees)); - } - - /// - /// Initialises an angle as the signed difference between two directional unit vectors in the 2D plane. - /// If the vectors are not unit length the result is undefined. - /// - public static Angle Between(Vector2 from, Vector2 to) - { - float perpDot = from.Y * to.X - from.X * to.Y; - - return FromRadians(MathF.Atan2(perpDot, Vector2.Dot(from, to))); - } - - /// - /// Initialises an angle as the signed difference between two directions in the 2D plane. - /// The returned value is the smallest possible angle from one direction to the other. - /// - public static Angle Between(Direction2 from, Direction2 to) - { - return to - from; - } - - /// - /// Initialises an angle as the signed difference between two directions in the 2D plane. - /// The returned value is the smallest positive angle from one direction to the other. - /// - public static Angle BetweenPositive(Direction2 from, Direction2 to) - { - var a = Between(from, to); - if (a.radians < 0) - a += MathConstants.TwoPi.Radians(); - return a; - } - - /// - /// Initialises an angle as the signed difference between two directions in the 2D plane. - /// The returned value is the smallest negative angle from one direction to the other. - /// - public static Angle BetweenNegative(Direction2 from, Direction2 to) - { - var a = Between(from, to); - if (a.radians > 0) - a -= MathConstants.TwoPi.Radians(); - return a; - } - - #endregion - - #region Static Fields - - /// - /// The default zero angle. - /// - public static readonly Angle Zero = new Angle(0); - - #endregion - - #region Properties - - /// - /// Gets the value of the angle in radians. - /// - public float Radians => radians; - - /// - /// Gets the value of the angle in degrees. - /// - public float Degrees => MoreMath.RadiansToDegrees(radians); - - /// - /// Gets a 2x2 rotation matrix that rotates vectors by this angle. - /// - public Matrix2 Transformation => Matrix2.CreateRotation(radians); - - /// - /// Gets the magnitude (absolute value) of the angle in radians. - /// - public float MagnitudeInRadians => Math.Abs(radians); - - /// - /// Gets the magnitude (absolute value) of the angle in degrees. - /// - public float MagnitudeInDegrees => Math.Abs(Degrees); - - #endregion - - #region Methods - - #region Arithmetic - - /// - /// Returns the Sine of the angle. - /// - public float Sin() - { - return MathF.Sin(radians); - } - /// - /// Returns the Cosine of the angle. - /// - public float Cos() - { - return MathF.Cos(radians); - } - /// - /// Returns the Tangent of the angle. - /// - public float Tan() - { - return MathF.Tan(radians); - } - /// - /// Returns the Sign of the angle. - /// - public int Sign() - { - return Math.Sign(radians); - } - /// - /// Returns the absolute value of the angle. - /// - public Angle Abs() - { - return new Angle(Math.Abs(radians)); - } - /// - /// Returns a new Angle with |value| == 1 radians and the same sign as this angle. - /// Returns a new Angle with value 0 if the angle is zero. - /// - public Angle Normalized() - { - return new Angle(Math.Sign(radians)); - } - /// - /// Clamps this angle between a minimum and a maximum angle. - /// - public Angle Clamped(Angle min, Angle max) - { - return Clamp(this, min, max); - } - - #region Statics - - /// - /// Returns the larger of two angles. - /// - public static Angle Max(Angle a1, Angle a2) - { - return a1 > a2 ? a1 : a2; - } - - /// - /// Returns the smaller of two angles. - /// - public static Angle Min(Angle a1, Angle a2) - { - return a1 < a2 ? a1 : a2; - } - - /// - /// Clamps one angle between a minimum and a maximum angle. - /// - public static Angle Clamp(Angle a, Angle min, Angle max) - { - return a < max ? a > min ? a : min : max; - } - - #endregion - - #endregion - - #endregion - - #region Operators - - #region Arithmetic - - /// - /// Adds two angles. - /// - public static Angle operator +(Angle angle1, Angle angle2) - { - return new Angle(angle1.radians + angle2.radians); - } - - /// - /// Substracts an angle from another. - /// - public static Angle operator -(Angle angle1, Angle angle2) - { - return new Angle(angle1.radians - angle2.radians); - } - - /// - /// Inverts an angle. - /// - public static Angle operator -(Angle angle) - { - return new Angle(-angle.radians); - } - - /// - /// Multiplies an angle with a scalar. - /// - public static Angle operator *(Angle angle, float scalar) - { - return new Angle(angle.radians * scalar); - } - - /// - /// Multiplies an angle with a scalar. - /// - public static Angle operator *(float scalar, Angle angle) - { - return new Angle(angle.radians * scalar); - } - - /// - /// Divides an angle by an inverse scalar. - /// - public static Angle operator /(Angle angle, float invScalar) - { - return new Angle(angle.radians / invScalar); - } - - /// - /// Linearly interpolates between two angles. - /// - public static Angle Lerp(Angle angle0, Angle angle1, float t) - { - return angle0 + (angle1 - angle0) * t; - } - - #endregion - - #region Boolean - - /// - /// Indicates whether the current object is equal to another object of the same type. - /// - /// An object to compare with this object. - /// - /// true if the current object is equal to the parameter; otherwise, false. - /// - public bool Equals(Angle other) - { - return radians == other.radians; - } - - /// - /// Determines whether the specified , is equal to this instance. - /// - /// The to compare with this instance. - /// - /// true if the specified is equal to this instance; otherwise, false. - /// - public override bool Equals(object? obj) - { - return obj is Angle angle && Equals(angle); - } - - /// - /// Returns a hash code for this instance. - /// - /// - /// A hash code for this instance, suitable for use in hashing algorithms and data structures like a hash table. - /// - public override int GetHashCode() - { - return radians.GetHashCode(); - } - - /// - /// Checks two angles for equality. - /// - public static bool operator ==(Angle x, Angle y) - { - return x.Equals(y); - } - /// - /// Checks two angles for inequality. - /// - public static bool operator !=(Angle x, Angle y) - { - return !(x == y); - } - - /// - /// Checks whether one angle is smaller than another. - /// - public static bool operator <(Angle x, Angle y) - { - return x.radians < y.radians; - } - /// - /// Checks whether one angle is greater than another. - /// - public static bool operator >(Angle x, Angle y) - { - return x.radians > y.radians; - } - - /// - /// Checks whether one angle is smaller or equal to another. - /// - public static bool operator <=(Angle x, Angle y) - { - return x.radians <= y.radians; - } - /// - /// Checks whether one angle is greater or equal to another. - /// - public static bool operator >=(Angle x, Angle y) - { - return x.radians >= y.radians; - } - - #endregion - - #region String - - public override string ToString() => ToString(null, CultureInfo.CurrentCulture); - - public string ToString(string? format, IFormatProvider? formatProvider) - => $"{Degrees.ToString(format, formatProvider)}°"; - - public string ToString(string? format) - => ToString(format, CultureInfo.CurrentCulture); - - #endregion - - #region Casts - - /// - /// Casts an angle to a direction in the 2D plane. - /// This is the same as Direction.Zero + angle. - /// - public static explicit operator Direction2(Angle angle) - { - return Direction2.FromRadians(angle.radians); - } - - #endregion - - #endregion + /// Substracts an angle from another. + /// + public static Angle operator -(Angle angle1, Angle angle2) + { + return new Angle(angle1.radians - angle2.radians); } + + /// + /// Inverts an angle. + /// + public static Angle operator -(Angle angle) + { + return new Angle(-angle.radians); + } + + /// + /// Multiplies an angle with a scalar. + /// + public static Angle operator *(Angle angle, float scalar) + { + return new Angle(angle.radians * scalar); + } + + /// + /// Multiplies an angle with a scalar. + /// + public static Angle operator *(float scalar, Angle angle) + { + return new Angle(angle.radians * scalar); + } + + /// + /// Divides an angle by an inverse scalar. + /// + public static Angle operator /(Angle angle, float invScalar) + { + return new Angle(angle.radians / invScalar); + } + + /// + /// Linearly interpolates between two angles. + /// + public static Angle Lerp(Angle angle0, Angle angle1, float t) + { + return angle0 + (angle1 - angle0) * t; + } + + #endregion + + #region Boolean + + /// + /// Indicates whether the current object is equal to another object of the same type. + /// + /// An object to compare with this object. + /// + /// true if the current object is equal to the parameter; otherwise, false. + /// + public bool Equals(Angle other) + { + return radians == other.radians; + } + + /// + /// Determines whether the specified , is equal to this instance. + /// + /// The to compare with this instance. + /// + /// true if the specified is equal to this instance; otherwise, false. + /// + public override bool Equals(object? obj) + { + return obj is Angle angle && Equals(angle); + } + + /// + /// Returns a hash code for this instance. + /// + /// + /// A hash code for this instance, suitable for use in hashing algorithms and data structures like a hash table. + /// + public override int GetHashCode() + { + return radians.GetHashCode(); + } + + /// + /// Checks two angles for equality. + /// + public static bool operator ==(Angle x, Angle y) + { + return x.Equals(y); + } + /// + /// Checks two angles for inequality. + /// + public static bool operator !=(Angle x, Angle y) + { + return !(x == y); + } + + /// + /// Checks whether one angle is smaller than another. + /// + public static bool operator <(Angle x, Angle y) + { + return x.radians < y.radians; + } + /// + /// Checks whether one angle is greater than another. + /// + public static bool operator >(Angle x, Angle y) + { + return x.radians > y.radians; + } + + /// + /// Checks whether one angle is smaller or equal to another. + /// + public static bool operator <=(Angle x, Angle y) + { + return x.radians <= y.radians; + } + /// + /// Checks whether one angle is greater or equal to another. + /// + public static bool operator >=(Angle x, Angle y) + { + return x.radians >= y.radians; + } + + #endregion + + #region String + + public override string ToString() => ToString(null, CultureInfo.CurrentCulture); + + public string ToString(string? format, IFormatProvider? formatProvider) + => $"{Degrees.ToString(format, formatProvider)}°"; + + public string ToString(string? format) + => ToString(format, CultureInfo.CurrentCulture); + + #endregion + + #region Casts + + /// + /// Casts an angle to a direction in the 2D plane. + /// This is the same as Direction.Zero + angle. + /// + public static explicit operator Direction2(Angle angle) + { + return Direction2.FromRadians(angle.radians); + } + + #endregion + + #endregion } diff --git a/Bearded.Utilities/Geometry/Bivector2.cs b/Bearded.Utilities/Geometry/Bivector2.cs index 1a968a4c..1af83e52 100644 --- a/Bearded.Utilities/Geometry/Bivector2.cs +++ b/Bearded.Utilities/Geometry/Bivector2.cs @@ -1,43 +1,42 @@ using System; using OpenTK.Mathematics; -namespace Bearded.Utilities.Geometry +namespace Bearded.Utilities.Geometry; + +/// +/// Represents a bivector in two-dimensional Euclidean space. +/// +public readonly struct Bivector2 : IEquatable { - /// - /// Represents a bivector in two-dimensional Euclidean space. - /// - public readonly struct Bivector2 : IEquatable - { - public float Magnitude { get; } + public float Magnitude { get; } - public static readonly Bivector2 Zero = new Bivector2(0); + public static readonly Bivector2 Zero = new Bivector2(0); - public static readonly Bivector2 Unit = new Bivector2(1); + public static readonly Bivector2 Unit = new Bivector2(1); - public static Bivector2 Wedge(Vector2 left, Vector2 right) => - new Bivector2(left.X * right.Y - left.Y * right.X); + public static Bivector2 Wedge(Vector2 left, Vector2 right) => + new Bivector2(left.X * right.Y - left.Y * right.X); - public Bivector2(float magnitude) - { - Magnitude = magnitude; - } + public Bivector2(float magnitude) + { + Magnitude = magnitude; + } - public static Bivector2 operator +(Bivector2 left, Bivector2 right) => - new Bivector2(left.Magnitude + right.Magnitude); + public static Bivector2 operator +(Bivector2 left, Bivector2 right) => + new Bivector2(left.Magnitude + right.Magnitude); - public static Bivector2 operator -(Bivector2 left, Bivector2 right) => - new Bivector2(left.Magnitude - right.Magnitude); + public static Bivector2 operator -(Bivector2 left, Bivector2 right) => + new Bivector2(left.Magnitude - right.Magnitude); - public static Bivector2 operator -(Bivector2 bivector) => new Bivector2(-bivector.Magnitude); + public static Bivector2 operator -(Bivector2 bivector) => new Bivector2(-bivector.Magnitude); - public bool Equals(Bivector2 other) => Magnitude.Equals(other.Magnitude); + public bool Equals(Bivector2 other) => Magnitude.Equals(other.Magnitude); - public override bool Equals(object? obj) => obj is Bivector2 other && Equals(other); + public override bool Equals(object? obj) => obj is Bivector2 other && Equals(other); - public override int GetHashCode() => Magnitude.GetHashCode(); + public override int GetHashCode() => Magnitude.GetHashCode(); - public static bool operator ==(Bivector2 left, Bivector2 right) => left.Equals(right); + public static bool operator ==(Bivector2 left, Bivector2 right) => left.Equals(right); - public static bool operator !=(Bivector2 left, Bivector2 right) => !left.Equals(right); - } + public static bool operator !=(Bivector2 left, Bivector2 right) => !left.Equals(right); } diff --git a/Bearded.Utilities/Geometry/Bivector3.cs b/Bearded.Utilities/Geometry/Bivector3.cs index 3206f970..3d337525 100644 --- a/Bearded.Utilities/Geometry/Bivector3.cs +++ b/Bearded.Utilities/Geometry/Bivector3.cs @@ -1,55 +1,54 @@ using System; using OpenTK.Mathematics; -namespace Bearded.Utilities.Geometry +namespace Bearded.Utilities.Geometry; + +/// +/// Represents a bivector in three-dimensional Euclidean space. +/// +public readonly struct Bivector3 : IEquatable { - /// - /// Represents a bivector in three-dimensional Euclidean space. - /// - public readonly struct Bivector3 : IEquatable - { - public float Xy { get; } - public float Yz { get; } - public float Xz { get; } + public float Xy { get; } + public float Yz { get; } + public float Xz { get; } - public static readonly Bivector3 Zero = new Bivector3(0, 0, 0); + public static readonly Bivector3 Zero = new Bivector3(0, 0, 0); - public static readonly Bivector3 UnitXy = new Bivector3(1, 0, 0); + public static readonly Bivector3 UnitXy = new Bivector3(1, 0, 0); - public static readonly Bivector3 UnitYz = new Bivector3(0, 1, 0); + public static readonly Bivector3 UnitYz = new Bivector3(0, 1, 0); - public static readonly Bivector3 UnitXz = new Bivector3(0, 0, 1); + public static readonly Bivector3 UnitXz = new Bivector3(0, 0, 1); - public static Bivector3 Wedge(Vector3 left, Vector3 right) => - new Bivector3( - left.X * right.Y - right.X * left.Y, - left.Y * right.Z - right.Y * left.Z, - left.X * right.Z - right.X * left.Z); + public static Bivector3 Wedge(Vector3 left, Vector3 right) => + new Bivector3( + left.X * right.Y - right.X * left.Y, + left.Y * right.Z - right.Y * left.Z, + left.X * right.Z - right.X * left.Z); - public Bivector3(float xy, float yz, float xz) - { - Xy = xy; - Yz = yz; - Xz = xz; - } + public Bivector3(float xy, float yz, float xz) + { + Xy = xy; + Yz = yz; + Xz = xz; + } - public static Bivector3 operator +(Bivector3 left, Bivector3 right) => - new Bivector3(left.Xy + right.Xy, left.Yz + right.Yz, left.Xz + right.Xz); + public static Bivector3 operator +(Bivector3 left, Bivector3 right) => + new Bivector3(left.Xy + right.Xy, left.Yz + right.Yz, left.Xz + right.Xz); - public static Bivector3 operator -(Bivector3 left, Bivector3 right) => - new Bivector3(left.Xy - right.Xy, left.Yz - right.Yz, left.Xz - right.Xz); + public static Bivector3 operator -(Bivector3 left, Bivector3 right) => + new Bivector3(left.Xy - right.Xy, left.Yz - right.Yz, left.Xz - right.Xz); - public static Bivector3 operator -(Bivector3 bivector) => - new Bivector3(-bivector.Xy, -bivector.Yz, -bivector.Xz); + public static Bivector3 operator -(Bivector3 bivector) => + new Bivector3(-bivector.Xy, -bivector.Yz, -bivector.Xz); - public bool Equals(Bivector3 other) => Xy.Equals(other.Xy) && Yz.Equals(other.Yz) && Xz.Equals(other.Xz); + public bool Equals(Bivector3 other) => Xy.Equals(other.Xy) && Yz.Equals(other.Yz) && Xz.Equals(other.Xz); - public override bool Equals(object? obj) => obj is Bivector3 other && Equals(other); + public override bool Equals(object? obj) => obj is Bivector3 other && Equals(other); - public override int GetHashCode() => HashCode.Combine(Xy, Yz, Xz); + public override int GetHashCode() => HashCode.Combine(Xy, Yz, Xz); - public static bool operator ==(Bivector3 left, Bivector3 right) => left.Equals(right); + public static bool operator ==(Bivector3 left, Bivector3 right) => left.Equals(right); - public static bool operator !=(Bivector3 left, Bivector3 right) => !left.Equals(right); - } + public static bool operator !=(Bivector3 left, Bivector3 right) => !left.Equals(right); } diff --git a/Bearded.Utilities/Geometry/CircularArc2.cs b/Bearded.Utilities/Geometry/CircularArc2.cs index dd386d72..57d3cf28 100644 --- a/Bearded.Utilities/Geometry/CircularArc2.cs +++ b/Bearded.Utilities/Geometry/CircularArc2.cs @@ -1,127 +1,126 @@ using System; using OpenTK.Mathematics; -namespace Bearded.Utilities.Geometry +namespace Bearded.Utilities.Geometry; + +/// +/// Represents an arc of a circle between a pair of distinct points. +/// +/// +/// +/// +/// If the start and end directions are equal, the short arc is the zero-length arc in this point, and the long arc +/// is the full circle arc traversing the circle in positive direction (i.e. Angle will be 2π). +/// +/// +/// If the start and end directions are opposites, the short arc is the arc traversing the circle in negative +/// direction (i.e. Angle will be -π), and the long arc is the arc traversing the circle in the positive direction +/// (i.e. Angle will be π). +/// +/// +/// +public readonly struct CircularArc2 : IEquatable { + public Vector2 Center { get; } + public float Radius { get; } + public Direction2 Start { get; } + public Direction2 End { get; } + public bool IsShortArc { get; } + + public bool IsLongArc => !IsShortArc; + public Angle Angle => IsShortArc ? End - Start : oppositeAngle(End - Start); + public Vector2 StartPoint => Center + Radius * Start.Vector; + public Vector2 EndPoint => Center + Radius * End.Vector; + + public float ArcLength => Angle.Radians * Radius; + + /// + /// Returns the major arc if this arc is the minor arc, and returns the major arc if this is the minor arc. + /// + public CircularArc2 Opposite => new CircularArc2(Center, Radius, Start, End, !IsShortArc); + /// - /// Represents an arc of a circle between a pair of distinct points. + /// Returns the same arc with the start and end directions swapped. /// - /// - /// - /// - /// If the start and end directions are equal, the short arc is the zero-length arc in this point, and the long arc - /// is the full circle arc traversing the circle in positive direction (i.e. Angle will be 2π). - /// - /// - /// If the start and end directions are opposites, the short arc is the arc traversing the circle in negative - /// direction (i.e. Angle will be -π), and the long arc is the arc traversing the circle in the positive direction - /// (i.e. Angle will be π). - /// - /// - /// - public readonly struct CircularArc2 : IEquatable + public CircularArc2 Reversed => new CircularArc2(Center, Radius, End, Start, IsShortArc); + + /// + /// Constructs the minor arc in the circle with specified center and radius between the given directions. + /// + public static CircularArc2 ShortArcBetweenDirections( + Vector2 center, float radius, Direction2 start, Direction2 end) { - public Vector2 Center { get; } - public float Radius { get; } - public Direction2 Start { get; } - public Direction2 End { get; } - public bool IsShortArc { get; } - - public bool IsLongArc => !IsShortArc; - public Angle Angle => IsShortArc ? End - Start : oppositeAngle(End - Start); - public Vector2 StartPoint => Center + Radius * Start.Vector; - public Vector2 EndPoint => Center + Radius * End.Vector; - - public float ArcLength => Angle.Radians * Radius; - - /// - /// Returns the major arc if this arc is the minor arc, and returns the major arc if this is the minor arc. - /// - public CircularArc2 Opposite => new CircularArc2(Center, Radius, Start, End, !IsShortArc); - - /// - /// Returns the same arc with the start and end directions swapped. - /// - public CircularArc2 Reversed => new CircularArc2(Center, Radius, End, Start, IsShortArc); - - /// - /// Constructs the minor arc in the circle with specified center and radius between the given directions. - /// - public static CircularArc2 ShortArcBetweenDirections( - Vector2 center, float radius, Direction2 start, Direction2 end) - { - return new CircularArc2(center, radius, start, end, true); - } + return new CircularArc2(center, radius, start, end, true); + } - /// - /// Constructs the major arc in the circle with specified center and radius between the given directions. - /// - public static CircularArc2 LongArcBetweenDirections( - Vector2 center, float radius, Direction2 start, Direction2 end) - { - return new CircularArc2(center, radius, start, end, false); - } + /// + /// Constructs the major arc in the circle with specified center and radius between the given directions. + /// + public static CircularArc2 LongArcBetweenDirections( + Vector2 center, float radius, Direction2 start, Direction2 end) + { + return new CircularArc2(center, radius, start, end, false); + } - /// - /// Constructs the arc in the circle with specified center and radius starting in the given direction, - /// subtending the given angle. - /// - public static CircularArc2 FromStartAndAngle( - Vector2 center, float radius, Direction2 start, Angle angle) + /// + /// Constructs the arc in the circle with specified center and radius starting in the given direction, + /// subtending the given angle. + /// + public static CircularArc2 FromStartAndAngle( + Vector2 center, float radius, Direction2 start, Angle angle) + { + var radians = angle.Radians; + if (radians < -MathConstants.TwoPi || radians >= MathConstants.TwoPi) { - var radians = angle.Radians; - if (radians < -MathConstants.TwoPi || radians >= MathConstants.TwoPi) - { - throw new ArgumentException( - "Angle must be between -360° inclusive and +360° exclusive.", nameof(angle)); - } - - // When the start and end are opposite each other, the short arc goes in the negative direction (i.e. the - // angle is -π), so conversely -π should be considered a short arc. The long arc goes in the positive - // direction (i.e. the angle is -π), so conversely -π should be considered a long arc. - var isShortArc = radians >= -MathConstants.Pi && radians < MathConstants.Pi; - return new CircularArc2(center, radius, start, start + angle, isShortArc); + throw new ArgumentException( + "Angle must be between -360° inclusive and +360° exclusive.", nameof(angle)); } - private CircularArc2(Vector2 center, float radius, Direction2 start, Direction2 end, bool isShortArc) - { - Center = center; - Radius = radius; - Start = start; - End = end; - IsShortArc = isShortArc; - } + // When the start and end are opposite each other, the short arc goes in the negative direction (i.e. the + // angle is -π), so conversely -π should be considered a short arc. The long arc goes in the positive + // direction (i.e. the angle is -π), so conversely -π should be considered a long arc. + var isShortArc = radians >= -MathConstants.Pi && radians < MathConstants.Pi; + return new CircularArc2(center, radius, start, start + angle, isShortArc); + } - public bool Equals(CircularArc2 other) => - Center.Equals(other.Center) - && Radius.Equals(other.Radius) - && Start.Equals(other.Start) - && End.Equals(other.End) - && IsShortArc.Equals(other.IsShortArc); + private CircularArc2(Vector2 center, float radius, Direction2 start, Direction2 end, bool isShortArc) + { + Center = center; + Radius = radius; + Start = start; + End = end; + IsShortArc = isShortArc; + } - public override bool Equals(object? obj) => obj is CircularArc2 other && Equals(other); + public bool Equals(CircularArc2 other) => + Center.Equals(other.Center) + && Radius.Equals(other.Radius) + && Start.Equals(other.Start) + && End.Equals(other.End) + && IsShortArc.Equals(other.IsShortArc); - public override int GetHashCode() => HashCode.Combine(Center, Radius, Start, End, IsShortArc); + public override bool Equals(object? obj) => obj is CircularArc2 other && Equals(other); - public static bool operator ==(CircularArc2 left, CircularArc2 right) => left.Equals(right); - public static bool operator !=(CircularArc2 left, CircularArc2 right) => !left.Equals(right); + public override int GetHashCode() => HashCode.Combine(Center, Radius, Start, End, IsShortArc); - public override string ToString() => $"{Center} × {Radius}; {arcString} {Start}-{End}"; + public static bool operator ==(CircularArc2 left, CircularArc2 right) => left.Equals(right); + public static bool operator !=(CircularArc2 left, CircularArc2 right) => !left.Equals(right); - private string arcString => IsShortArc ? "short arc" : "long arc"; + public override string ToString() => $"{Center} × {Radius}; {arcString} {Start}-{End}"; - private static Angle oppositeAngle(Angle original) - { - var originalMagnitude = original.Abs(); - var oppositeMagnitude = MathConstants.TwoPi.Radians() - originalMagnitude; + private string arcString => IsShortArc ? "short arc" : "long arc"; - var originalDirection = original.Sign(); - // If the sign is 0, start == end. In this case, the opposite angle needs to be the full arc in positive - // direction. - if (originalDirection == 0) originalDirection = -1; - var oppositeDirection = -originalDirection; + private static Angle oppositeAngle(Angle original) + { + var originalMagnitude = original.Abs(); + var oppositeMagnitude = MathConstants.TwoPi.Radians() - originalMagnitude; - return oppositeDirection * oppositeMagnitude; - } + var originalDirection = original.Sign(); + // If the sign is 0, start == end. In this case, the opposite angle needs to be the full arc in positive + // direction. + if (originalDirection == 0) originalDirection = -1; + var oppositeDirection = -originalDirection; + + return oppositeDirection * oppositeMagnitude; } } diff --git a/Bearded.Utilities/Geometry/Direction2.cs b/Bearded.Utilities/Geometry/Direction2.cs index 4e782483..d55e45a5 100644 --- a/Bearded.Utilities/Geometry/Direction2.cs +++ b/Bearded.Utilities/Geometry/Direction2.cs @@ -2,269 +2,268 @@ using System.Globalization; using OpenTK.Mathematics; -namespace Bearded.Utilities.Geometry +namespace Bearded.Utilities.Geometry; + +/// +/// A typesafe representation of a direction in two dimensional space. +/// +public readonly struct Direction2 : IEquatable, IFormattable { - /// - /// A typesafe representation of a direction in two dimensional space. - /// - public readonly struct Direction2 : IEquatable, IFormattable - { - private const long numSteps = (1L << 32); + private const long numSteps = (1L << 32); - private const float fromRadians = numSteps / MathConstants.TwoPi; - private const float toRadians = MathConstants.TwoPi / numSteps; + private const float fromRadians = numSteps / MathConstants.TwoPi; + private const float toRadians = MathConstants.TwoPi / numSteps; - private const float fromDegrees = numSteps / 360f; - private const float toDegrees = 360f / numSteps; + private const float fromDegrees = numSteps / 360f; + private const float toDegrees = 360f / numSteps; - private readonly uint data; + private readonly uint data; - #region Constructing + #region Constructing - private Direction2(uint data) - { - this.data = data; - } + private Direction2(uint data) + { + this.data = data; + } - /// - /// Initialises a direction from an absolute angle value in radians. - /// - public static Direction2 FromRadians(float radians) - { - return new Direction2((uint)(radians * fromRadians)); - } + /// + /// Initialises a direction from an absolute angle value in radians. + /// + public static Direction2 FromRadians(float radians) + { + return new Direction2((uint)(radians * fromRadians)); + } - /// - /// Initialises a direction from an absolute angle value in degrees. - /// - public static Direction2 FromDegrees(float degrees) - { - return new Direction2((uint)(degrees * fromDegrees)); - } + /// + /// Initialises a direction from an absolute angle value in degrees. + /// + public static Direction2 FromDegrees(float degrees) + { + return new Direction2((uint)(degrees * fromDegrees)); + } - /// - /// Initialises a direction along a vector. - /// - public static Direction2 Of(Vector2 vector) - { - return FromRadians(MathF.Atan2(vector.Y, vector.X)); - } + /// + /// Initialises a direction along a vector. + /// + public static Direction2 Of(Vector2 vector) + { + return FromRadians(MathF.Atan2(vector.Y, vector.X)); + } - /// - /// Initialises the direction between two points. - /// - /// The base point. - /// The point the directions "points" towards. - public static Direction2 Between(Vector2 from, Vector2 to) - { - return Of(to - from); - } + /// + /// Initialises the direction between two points. + /// + /// The base point. + /// The point the directions "points" towards. + public static Direction2 Between(Vector2 from, Vector2 to) + { + return Of(to - from); + } - #endregion + #endregion - #region Static Fields + #region Static Fields - /// - /// Default base direction (along positive X axis). - /// - public static readonly Direction2 Zero = new Direction2(0); + /// + /// Default base direction (along positive X axis). + /// + public static readonly Direction2 Zero = new Direction2(0); - #endregion + #endregion - #region Properties + #region Properties - /// - /// Gets the absolute angle of the direction in radians between 0 and 2pi. - /// - public float Radians { get { return data * toRadians; } } + /// + /// Gets the absolute angle of the direction in radians between 0 and 2pi. + /// + public float Radians { get { return data * toRadians; } } - /// - /// Gets the absolute angle of the direction in degrees between 0 and 360. - /// - public float Degrees { get { return data * toDegrees; } } + /// + /// Gets the absolute angle of the direction in degrees between 0 and 360. + /// + public float Degrees { get { return data * toDegrees; } } - /// - /// Gets the absolute angle of the direction in radians between -pi and pi. - /// - public float RadiansSigned { get { return (int)data * toRadians; } } + /// + /// Gets the absolute angle of the direction in radians between -pi and pi. + /// + public float RadiansSigned { get { return (int)data * toRadians; } } - /// - /// Gets the absolute angle of the direction in degrees between -180 and 180. - /// - public float DegreesSigned { get { return (int)data * toDegrees; } } + /// + /// Gets the absolute angle of the direction in degrees between -180 and 180. + /// + public float DegreesSigned { get { return (int)data * toDegrees; } } - /// - /// Gets the unit vector pointing in this direction. - /// - public Vector2 Vector + /// + /// Gets the unit vector pointing in this direction. + /// + public Vector2 Vector + { + get { - get - { - var radians = Radians; - return new Vector2(MathF.Cos(radians), MathF.Sin(radians)); - } + var radians = Radians; + return new Vector2(MathF.Cos(radians), MathF.Sin(radians)); } + } - #endregion + #endregion - #region Methods + #region Methods - /// - /// Returns this direction turnen towards a goal direction with a given maximum step length in radians. - /// This will never overshoot the goal. - /// - /// The goal direction. - /// The maximum step length in radians. Negative values will return the original direction. - public Direction2 TurnedTowards(Direction2 goal, float maxStepInRadians) - { - if (maxStepInRadians <= 0) - return this; + /// + /// Returns this direction turnen towards a goal direction with a given maximum step length in radians. + /// This will never overshoot the goal. + /// + /// The goal direction. + /// The maximum step length in radians. Negative values will return the original direction. + public Direction2 TurnedTowards(Direction2 goal, float maxStepInRadians) + { + if (maxStepInRadians <= 0) + return this; - var step = maxStepInRadians.Radians(); + var step = maxStepInRadians.Radians(); - var thisToGoal = goal - this; + var thisToGoal = goal - this; - if (step > thisToGoal.Abs()) - return goal; + if (step > thisToGoal.Abs()) + return goal; - step *= thisToGoal.Sign(); + step *= thisToGoal.Sign(); - return this + step; - } + return this + step; + } - #region Statics + #region Statics - /// - /// Linearly interpolates between two directions. - /// This always interpolates along the shorter arc. - /// - /// The first direction (at p == 0). - /// The second direction (at p == 1). - /// The parameter. - public static Direction2 Lerp(Direction2 d0, Direction2 d1, float p) - { - return d0 + p * (d1 - d0); - } + /// + /// Linearly interpolates between two directions. + /// This always interpolates along the shorter arc. + /// + /// The first direction (at p == 0). + /// The second direction (at p == 1). + /// The parameter. + public static Direction2 Lerp(Direction2 d0, Direction2 d1, float p) + { + return d0 + p * (d1 - d0); + } - #endregion + #endregion - #endregion + #endregion - #region Operators + #region Operators - #region Arithmetic + #region Arithmetic - /// - /// Adds an angle to a direction. - /// - public static Direction2 operator +(Direction2 direction, Angle angle) - { - return new Direction2((uint)(direction.data + angle.Radians * fromRadians)); - } + /// + /// Adds an angle to a direction. + /// + public static Direction2 operator +(Direction2 direction, Angle angle) + { + return new Direction2((uint)(direction.data + angle.Radians * fromRadians)); + } - /// - /// Substracts an angle from a direction. - /// - public static Direction2 operator -(Direction2 direction, Angle angle) - { - return new Direction2((uint)(direction.data - angle.Radians * fromRadians)); - } + /// + /// Substracts an angle from a direction. + /// + public static Direction2 operator -(Direction2 direction, Angle angle) + { + return new Direction2((uint)(direction.data - angle.Radians * fromRadians)); + } - /// - /// Gets the signed difference between two directions. - /// Always returns the angle of the shorter arc. - /// - public static Angle operator -(Direction2 direction1, Direction2 direction2) - { - return Angle.FromRadians(((int)direction1.data - (int)direction2.data) * toRadians); - } + /// + /// Gets the signed difference between two directions. + /// Always returns the angle of the shorter arc. + /// + public static Angle operator -(Direction2 direction1, Direction2 direction2) + { + return Angle.FromRadians(((int)direction1.data - (int)direction2.data) * toRadians); + } - /// - /// Gets the inverse direction to a direction. - /// - public static Direction2 operator -(Direction2 direction) - { - return new Direction2(direction.data + (uint.MaxValue / 2 + 1)); - } + /// + /// Gets the inverse direction to a direction. + /// + public static Direction2 operator -(Direction2 direction) + { + return new Direction2(direction.data + (uint.MaxValue / 2 + 1)); + } - #endregion + #endregion - #region Boolean + #region Boolean - /// - /// Indicates whether the current object is equal to another object of the same type. - /// - /// An object to compare with this object. - /// - /// true if the current object is equal to the parameter; otherwise, false. - /// - public bool Equals(Direction2 other) - { - return data == other.data; - } + /// + /// Indicates whether the current object is equal to another object of the same type. + /// + /// An object to compare with this object. + /// + /// true if the current object is equal to the parameter; otherwise, false. + /// + public bool Equals(Direction2 other) + { + return data == other.data; + } - /// - /// Determines whether the specified , is equal to this instance. - /// - /// The to compare with this instance. - /// - /// true if the specified is equal to this instance; otherwise, false. - /// - public override bool Equals(object? obj) - { - return obj is Direction2 direction2 && Equals(direction2); - } + /// + /// Determines whether the specified , is equal to this instance. + /// + /// The to compare with this instance. + /// + /// true if the specified is equal to this instance; otherwise, false. + /// + public override bool Equals(object? obj) + { + return obj is Direction2 direction2 && Equals(direction2); + } - /// - /// Returns a hash code for this instance. - /// - /// - /// A hash code for this instance, suitable for use in hashing algorithms and data structures like a hash table. - /// - public override int GetHashCode() - { - return data.GetHashCode(); - } + /// + /// Returns a hash code for this instance. + /// + /// + /// A hash code for this instance, suitable for use in hashing algorithms and data structures like a hash table. + /// + public override int GetHashCode() + { + return data.GetHashCode(); + } - /// - /// Checks two directions for equality. - /// - public static bool operator ==(Direction2 x, Direction2 y) - { - return x.Equals(y); - } + /// + /// Checks two directions for equality. + /// + public static bool operator ==(Direction2 x, Direction2 y) + { + return x.Equals(y); + } - /// - /// Checks two directions for inequality. - /// - public static bool operator !=(Direction2 x, Direction2 y) - { - return !(x == y); - } + /// + /// Checks two directions for inequality. + /// + public static bool operator !=(Direction2 x, Direction2 y) + { + return !(x == y); + } - #endregion + #endregion - #region tostring + #region tostring - public override string ToString() => ToString(null, CultureInfo.CurrentCulture); + public override string ToString() => ToString(null, CultureInfo.CurrentCulture); - public string ToString(string? format, IFormatProvider? formatProvider) - => $"{Radians.ToString(format, formatProvider)} rad"; + public string ToString(string? format, IFormatProvider? formatProvider) + => $"{Radians.ToString(format, formatProvider)} rad"; - public string ToString(string? format) - => ToString(format, CultureInfo.CurrentCulture); + public string ToString(string? format) + => ToString(format, CultureInfo.CurrentCulture); - #endregion + #endregion - #region Casts + #region Casts - #endregion + #endregion - #endregion + #endregion - } } diff --git a/Bearded.Utilities/Geometry/GeometryExtensions.cs b/Bearded.Utilities/Geometry/GeometryExtensions.cs index 21cd84f8..2d6af312 100644 --- a/Bearded.Utilities/Geometry/GeometryExtensions.cs +++ b/Bearded.Utilities/Geometry/GeometryExtensions.cs @@ -1,20 +1,19 @@ -namespace Bearded.Utilities.Geometry +namespace Bearded.Utilities.Geometry; + +public static class GeometryExtensions { - public static class GeometryExtensions - { - /// - /// Converts floating point value into a type safe angle representation in radians. - /// - public static Angle Radians(this float value) => Angle.FromRadians(value); + /// + /// Converts floating point value into a type safe angle representation in radians. + /// + public static Angle Radians(this float value) => Angle.FromRadians(value); - /// - /// Converts floating point value into a type safe angle representation in degrees. - /// - public static Angle Degrees(this float value) => Angle.FromDegrees(value); + /// + /// Converts floating point value into a type safe angle representation in degrees. + /// + public static Angle Degrees(this float value) => Angle.FromDegrees(value); - /// - /// Converts an integer value into a type safe angle representation in degrees. - /// - public static Angle Degrees(this int value) => Angle.FromDegrees(value); - } + /// + /// Converts an integer value into a type safe angle representation in degrees. + /// + public static Angle Degrees(this int value) => Angle.FromDegrees(value); } diff --git a/Bearded.Utilities/Geometry/Interval.cs b/Bearded.Utilities/Geometry/Interval.cs index d846fb17..9650583d 100644 --- a/Bearded.Utilities/Geometry/Interval.cs +++ b/Bearded.Utilities/Geometry/Interval.cs @@ -1,20 +1,19 @@ -namespace Bearded.Utilities.Geometry +namespace Bearded.Utilities.Geometry; + +/// +/// A collection of interval helper methods. +/// +public static class Interval { /// - /// A collection of interval helper methods. + /// Checks if two closed intervals overlap. /// - public static class Interval - { - /// - /// Checks if two closed intervals overlap. - /// - /// The (inclusive) lower bound of the first interval. - /// The (inclusive) upper bound of the first interval. - /// The (inclusive) lower bound of the second interval. - /// The (inclusive) upper bound of the second interval. - /// - public static bool DoOverlap(double amin, double amax, double bmin, double bmax) - // Negation of bmin > amax || amin > bmax - => bmin <= amax && amin <= bmax; - } + /// The (inclusive) lower bound of the first interval. + /// The (inclusive) upper bound of the first interval. + /// The (inclusive) lower bound of the second interval. + /// The (inclusive) upper bound of the second interval. + /// + public static bool DoOverlap(double amin, double amax, double bmin, double bmax) + // Negation of bmin > amax || amin > bmax + => bmin <= amax && amin <= bmax; } diff --git a/Bearded.Utilities/Geometry/PolarPosition.cs b/Bearded.Utilities/Geometry/PolarPosition.cs index 198882f4..0d20b2aa 100644 --- a/Bearded.Utilities/Geometry/PolarPosition.cs +++ b/Bearded.Utilities/Geometry/PolarPosition.cs @@ -2,138 +2,137 @@ using System.Globalization; using OpenTK.Mathematics; -namespace Bearded.Utilities.Geometry +namespace Bearded.Utilities.Geometry; + +/// +/// Represents a position in two-dimensional space using polar coordinates. +/// +public readonly struct PolarPosition : IEquatable, IFormattable { + #region Properties + /// + /// Distance from the origin. + /// + public float R { get; } + /// - /// Represents a position in two-dimensional space using polar coordinates. + /// Direction of the vector originating from the origin pointing towards the point. /// - public readonly struct PolarPosition : IEquatable, IFormattable + public Direction2 Angle { get; } + + #endregion + + /// + /// Creates a new polar position. + /// + /// The distance of the point to the origin. + /// The direction of the vector originating in the origin pointing towards the point. + public PolarPosition(float r, Direction2 angle) { - #region Properties - /// - /// Distance from the origin. - /// - public float R { get; } - - /// - /// Direction of the vector originating from the origin pointing towards the point. - /// - public Direction2 Angle { get; } - - #endregion - - /// - /// Creates a new polar position. - /// - /// The distance of the point to the origin. - /// The direction of the vector originating in the origin pointing towards the point. - public PolarPosition(float r, Direction2 angle) - { - if (r < 0) - throw new ArgumentException("The radius has to be non-negative."); + if (r < 0) + throw new ArgumentException("The radius has to be non-negative."); - R = r; - Angle = angle; - } + R = r; + Angle = angle; + } - #region Conversion methods - /// - /// Converts the polar coordinates into Euclidean coordinates. - /// - /// Vector corresponding to the vector originating in the origin pointing towards this point. - public Vector2 ToVector2() - { - return Angle.Vector * R; - } - #endregion - - #region Static creators - /// - /// Converts an Euclidean position into polar coordinates. - /// - /// The Euclidean representation of the point. - /// The polar representation of the specified point. - public static PolarPosition FromVector2(Vector2 position) - { - return new PolarPosition(position.Length, Direction2.Of(position)); - } - #endregion - - #region IEquatable implementation - /// - /// Indicates whether the current object is equal to another object of the same type. - /// - /// - /// true if the current object is equal to the parameter; otherwise, false. - /// - /// An object to compare with this object. - public bool Equals(PolarPosition other) - { - // ReSharper disable CompareOfFloatsByEqualityOperator - return R == other.R && (R == 0 || Angle == other.Angle); - // ReSharper restore CompareOfFloatsByEqualityOperator - } - #endregion - - #region Object overrides - /// - /// Indicates whether this instance and a specified object are equal. - /// - /// - /// true if and this instance are the same type and represent the same value; otherwise, false. - /// - /// Another object to compare to. 2 - public override bool Equals(object? obj) => base.Equals(obj); - - /// - /// Returns the hash code for this instance. - /// - /// - /// A 32-bit signed integer that is the hash code for this instance. - /// - /// 2 - public override int GetHashCode() - { - // ReSharper disable once CompareOfFloatsByEqualityOperator - if (R == 0) - return 0; - - unchecked - { - return (R.GetHashCode() * 397) ^ Angle.GetHashCode(); - } - } + #region Conversion methods + /// + /// Converts the polar coordinates into Euclidean coordinates. + /// + /// Vector corresponding to the vector originating in the origin pointing towards this point. + public Vector2 ToVector2() + { + return Angle.Vector * R; + } + #endregion - public override string ToString() => ToString(null, CultureInfo.CurrentCulture); + #region Static creators + /// + /// Converts an Euclidean position into polar coordinates. + /// + /// The Euclidean representation of the point. + /// The polar representation of the specified point. + public static PolarPosition FromVector2(Vector2 position) + { + return new PolarPosition(position.Length, Direction2.Of(position)); + } + #endregion - public string ToString(string? format, IFormatProvider? formatProvider) - => $"{R.ToString(format, formatProvider)} ∠{Angle.ToString(format, formatProvider)}"; + #region IEquatable implementation + /// + /// Indicates whether the current object is equal to another object of the same type. + /// + /// + /// true if the current object is equal to the parameter; otherwise, false. + /// + /// An object to compare with this object. + public bool Equals(PolarPosition other) + { + // ReSharper disable CompareOfFloatsByEqualityOperator + return R == other.R && (R == 0 || Angle == other.Angle); + // ReSharper restore CompareOfFloatsByEqualityOperator + } + #endregion - public string ToString(string? format) - => ToString(format, CultureInfo.CurrentCulture); + #region Object overrides + /// + /// Indicates whether this instance and a specified object are equal. + /// + /// + /// true if and this instance are the same type and represent the same value; otherwise, false. + /// + /// Another object to compare to. 2 + public override bool Equals(object? obj) => base.Equals(obj); - #endregion + /// + /// Returns the hash code for this instance. + /// + /// + /// A 32-bit signed integer that is the hash code for this instance. + /// + /// 2 + public override int GetHashCode() + { + // ReSharper disable once CompareOfFloatsByEqualityOperator + if (R == 0) + return 0; - #region Operator - #region Equality - /// - /// Checks two polar positions for equality. - /// - /// True if the polar positions are equal, false otherwise. - public static bool operator ==(PolarPosition p1, PolarPosition p2) + unchecked { - return p1.Equals(p2); + return (R.GetHashCode() * 397) ^ Angle.GetHashCode(); } + } - /// - /// Checks two polar positions for inequality. - /// - /// True if the polar positions are not equal, false otherwise. - public static bool operator !=(PolarPosition p1, PolarPosition p2) - { - return !(p1 == p2); - } - #endregion - #endregion + public override string ToString() => ToString(null, CultureInfo.CurrentCulture); + + public string ToString(string? format, IFormatProvider? formatProvider) + => $"{R.ToString(format, formatProvider)} ∠{Angle.ToString(format, formatProvider)}"; + + public string ToString(string? format) + => ToString(format, CultureInfo.CurrentCulture); + + #endregion + + #region Operator + #region Equality + /// + /// Checks two polar positions for equality. + /// + /// True if the polar positions are equal, false otherwise. + public static bool operator ==(PolarPosition p1, PolarPosition p2) + { + return p1.Equals(p2); + } + + /// + /// Checks two polar positions for inequality. + /// + /// True if the polar positions are not equal, false otherwise. + public static bool operator !=(PolarPosition p1, PolarPosition p2) + { + return !(p1 == p2); } + #endregion + #endregion } diff --git a/Bearded.Utilities/Geometry/Rectangle.cs b/Bearded.Utilities/Geometry/Rectangle.cs index 3b50e4fc..dac1b032 100644 --- a/Bearded.Utilities/Geometry/Rectangle.cs +++ b/Bearded.Utilities/Geometry/Rectangle.cs @@ -2,94 +2,93 @@ using System.Globalization; using OpenTK.Mathematics; -namespace Bearded.Utilities.Geometry +namespace Bearded.Utilities.Geometry; + +/// +/// Represents an axis-aligned rectangle. +/// All properties assume the x axis pointing right and the y axis pointing down. +/// Negative width or height rectangles are not allowed, and the constructor will throw with those values. +/// +public readonly struct Rectangle : IEquatable, IFormattable { - /// - /// Represents an axis-aligned rectangle. - /// All properties assume the x axis pointing right and the y axis pointing down. - /// Negative width or height rectangles are not allowed, and the constructor will throw with those values. - /// - public readonly struct Rectangle : IEquatable, IFormattable - { - public float Left { get; } - public float Top { get; } - public float Width { get; } - public float Height { get; } + public float Left { get; } + public float Top { get; } + public float Width { get; } + public float Height { get; } - public float Right => Left + Width; - public float Bottom => Top + Height; + public float Right => Left + Width; + public float Bottom => Top + Height; - public Vector2 TopLeft => new Vector2(Left, Top); - public Vector2 TopRight => new Vector2(Right, Top); - public Vector2 BottomLeft => new Vector2(Left, Bottom); - public Vector2 BottomRight => new Vector2(Right, Bottom); + public Vector2 TopLeft => new Vector2(Left, Top); + public Vector2 TopRight => new Vector2(Right, Top); + public Vector2 BottomLeft => new Vector2(Left, Bottom); + public Vector2 BottomRight => new Vector2(Right, Bottom); - public Vector2 Center => new Vector2(Left + Width * 0.5f, Top + Height * 0.5f); + public Vector2 Center => new Vector2(Left + Width * 0.5f, Top + Height * 0.5f); - public float Area => Width * Height; + public float Area => Width * Height; - public Rectangle(float x, float y, float width, float height) - { - if (width < 0 || height < 0) - throw new ArgumentException("Width and height of the rectangle have to be non-negative."); + public Rectangle(float x, float y, float width, float height) + { + if (width < 0 || height < 0) + throw new ArgumentException("Width and height of the rectangle have to be non-negative."); - Left = x; - Top = y; - Width = width; - Height = height; - } + Left = x; + Top = y; + Width = width; + Height = height; + } - public Rectangle(Vector2 xy, float width, float height) - : this(xy.X, xy.Y, width, height) { } + public Rectangle(Vector2 xy, float width, float height) + : this(xy.X, xy.Y, width, height) { } - public bool Contains(float x, float y) - => Left <= x && x <= Right && Top <= y && y <= Bottom; + public bool Contains(float x, float y) + => Left <= x && x <= Right && Top <= y && y <= Bottom; - public bool Contains(Vector2 xy) => Contains(xy.X, xy.Y); + public bool Contains(Vector2 xy) => Contains(xy.X, xy.Y); - public bool Contains(Rectangle other) - => Left <= other.Left && other.Right <= Right - && Top <= other.Top && other.Bottom <= Bottom; + public bool Contains(Rectangle other) + => Left <= other.Left && other.Right <= Right + && Top <= other.Top && other.Bottom <= Bottom; - public bool Intersects(Rectangle other) - => !(other.Left > Right || other.Right < Left - || other.Top > Bottom || other.Bottom < Top); + public bool Intersects(Rectangle other) + => !(other.Left > Right || other.Right < Left + || other.Top > Bottom || other.Bottom < Top); - public static Rectangle WithCorners(Vector2 upperLeft, Vector2 bottomRight) - => new Rectangle(upperLeft.X, upperLeft.Y, bottomRight.X - upperLeft.X, bottomRight.Y - upperLeft.Y); + public static Rectangle WithCorners(Vector2 upperLeft, Vector2 bottomRight) + => new Rectangle(upperLeft.X, upperLeft.Y, bottomRight.X - upperLeft.X, bottomRight.Y - upperLeft.Y); - public static Rectangle WithSides(float top, float right, float bottom, float left) - => new Rectangle(top, left, bottom - top, right - left); + public static Rectangle WithSides(float top, float right, float bottom, float left) + => new Rectangle(top, left, bottom - top, right - left); - public bool Equals(Rectangle other) - // ReSharper disable CompareOfFloatsByEqualityOperator - => Left == other.Left && Right == other.Right && Width == other.Width && Height == other.Height; - // ReSharper restore CompareOfFloatsByEqualityOperator + public bool Equals(Rectangle other) + // ReSharper disable CompareOfFloatsByEqualityOperator + => Left == other.Left && Right == other.Right && Width == other.Width && Height == other.Height; + // ReSharper restore CompareOfFloatsByEqualityOperator - public override bool Equals(object? obj) => obj is Rectangle rectangle && Equals(rectangle); + public override bool Equals(object? obj) => obj is Rectangle rectangle && Equals(rectangle); - public override int GetHashCode() + public override int GetHashCode() + { + unchecked { - unchecked - { - var hashCode = Left.GetHashCode(); - hashCode = (hashCode * 397) ^ Top.GetHashCode(); - hashCode = (hashCode * 397) ^ Width.GetHashCode(); - hashCode = (hashCode * 397) ^ Height.GetHashCode(); - return hashCode; - } + var hashCode = Left.GetHashCode(); + hashCode = (hashCode * 397) ^ Top.GetHashCode(); + hashCode = (hashCode * 397) ^ Width.GetHashCode(); + hashCode = (hashCode * 397) ^ Height.GetHashCode(); + return hashCode; } + } - public override string ToString() => ToString(null, CultureInfo.CurrentCulture); + public override string ToString() => ToString(null, CultureInfo.CurrentCulture); - public string ToString(string? format, IFormatProvider? formatProvider) - => $"[{Left.ToString(format, formatProvider)}, {Top.ToString(format, formatProvider)} x " + - $"[{Right.ToString(format, formatProvider)}, {Bottom.ToString(format, formatProvider)}"; + public string ToString(string? format, IFormatProvider? formatProvider) + => $"[{Left.ToString(format, formatProvider)}, {Top.ToString(format, formatProvider)} x " + + $"[{Right.ToString(format, formatProvider)}, {Bottom.ToString(format, formatProvider)}"; - public string ToString(string? format) - => ToString(format, CultureInfo.CurrentCulture); + public string ToString(string? format) + => ToString(format, CultureInfo.CurrentCulture); - public static bool operator ==(Rectangle left, Rectangle right) => left.Equals(right); - public static bool operator !=(Rectangle left, Rectangle right) => !left.Equals(right); - } + public static bool operator ==(Rectangle left, Rectangle right) => left.Equals(right); + public static bool operator !=(Rectangle left, Rectangle right) => !left.Equals(right); } diff --git a/Bearded.Utilities/Geometry/arcs/Arc.cs b/Bearded.Utilities/Geometry/arcs/Arc.cs index 4dbaeb95..41fb187f 100644 --- a/Bearded.Utilities/Geometry/arcs/Arc.cs +++ b/Bearded.Utilities/Geometry/arcs/Arc.cs @@ -1,135 +1,134 @@ using System; -namespace Bearded.Utilities.Geometry +namespace Bearded.Utilities.Geometry; + +/// +/// Represents a generic arc. +/// The class handles calculation of the approximate length and is able to normalise against the tangent length. +/// +/// The coordinate type. +public abstract class Arc { + #region Fields + private bool initialized; + private readonly float[] segmentLengths; + private float length; + #endregion + + #region Properties /// - /// Represents a generic arc. - /// The class handles calculation of the approximate length and is able to normalise against the tangent length. + /// The (approximate) length of the arc. /// - /// The coordinate type. - public abstract class Arc + public float Length { - #region Fields - private bool initialized; - private readonly float[] segmentLengths; - private float length; - #endregion - - #region Properties - /// - /// The (approximate) length of the arc. - /// - public float Length - { - get - { - ensureInitialized(); - return length; - } - } - #endregion - - #region Initialization - /// - /// Initializes the arc. - /// - /// The amount of linear segments the arc is split in. A larger amount of segments results in higher precision for length and remapping. - protected Arc(int segments = 100) + get { - segmentLengths = new float[segments + 1]; + ensureInitialized(); + return length; } + } + #endregion - // This class is lazy as our children might need to set fields - // in their constructor before being able to execute the - // getPointAt and getDistanceBetween methods correctly. - private void ensureInitialized() - { - if (initialized) - return; - - segmentLengths[0] = 0; - T prev = getPointAt(0); - - float totalLength = 0; - float step = 1.0f / (segmentLengths.Length - 1); - - for (int i = 1; i < segmentLengths.Length; i++) - { - float t = i * step; - T curr = getPointAt(t); - totalLength += getDistanceBetween(prev, curr); - segmentLengths[i] = totalLength; - prev = curr; - } - - length = totalLength; - initialized = true; - } - #endregion - - #region Get methods - /// - /// Calculates the position of the point on the arc at parameter t. - /// - /// The arc parameter (should be between 0 and 1). - /// The coordinates of the point on the arc at the specified parameter. - protected abstract T getPointAt(float t); - - /// - /// Calculates the distance between a set of coordinates. - /// - /// The first coordinate. - /// The second coordinate. - /// The distance between the specified coordinates. - protected abstract float getDistanceBetween(T p1, T p2); - - /// - /// Gets the point at the specified parameter of the arc taking tangent speed into account. - /// - /// The arc parameter (should be between 0 and 1). - /// The coordinates of the point on the arc at the specified parameter. - public T GetPointAt(float t) - { - return getPointAt(t); - } + #region Initialization + /// + /// Initializes the arc. + /// + /// The amount of linear segments the arc is split in. A larger amount of segments results in higher precision for length and remapping. + protected Arc(int segments = 100) + { + segmentLengths = new float[segments + 1]; + } - /// - /// Gets the point at the specified parameter of the arc normalised against tangent speed. - /// - /// The arc parameter (should be between 0 and 1). - /// The coordinates of the point on the arc at the specified parameter. - public T GetPointAtNormalised(float t) - { - ensureInitialized(); - return getPointAt(mapDistance(t.Clamped(0, 1) * Length)); - } - #endregion + // This class is lazy as our children might need to set fields + // in their constructor before being able to execute the + // getPointAt and getDistanceBetween methods correctly. + private void ensureInitialized() + { + if (initialized) + return; - #region Distance mapping - private float mapDistance(float distance) - { - if (distance <= 0) - return 0; - if (distance >= Length) - return 1; - - int i = findSegmentLengthIndex(distance); - float step = 1.0f / segmentLengths.Length; - if (segmentLengths[i] == distance) - return i * step; - if (i == 0) - return 0; - - float over = distance - segmentLengths[i - 1]; - float ratio = over / (segmentLengths[i] - segmentLengths[i - 1]); - return (i + ratio) * step; - } + segmentLengths[0] = 0; + T prev = getPointAt(0); + + float totalLength = 0; + float step = 1.0f / (segmentLengths.Length - 1); - private int findSegmentLengthIndex(float distance) + for (int i = 1; i < segmentLengths.Length; i++) { - int index = Array.BinarySearch(segmentLengths, distance); - return index < 0 ? ~index : 0; + float t = i * step; + T curr = getPointAt(t); + totalLength += getDistanceBetween(prev, curr); + segmentLengths[i] = totalLength; + prev = curr; } - #endregion + + length = totalLength; + initialized = true; + } + #endregion + + #region Get methods + /// + /// Calculates the position of the point on the arc at parameter t. + /// + /// The arc parameter (should be between 0 and 1). + /// The coordinates of the point on the arc at the specified parameter. + protected abstract T getPointAt(float t); + + /// + /// Calculates the distance between a set of coordinates. + /// + /// The first coordinate. + /// The second coordinate. + /// The distance between the specified coordinates. + protected abstract float getDistanceBetween(T p1, T p2); + + /// + /// Gets the point at the specified parameter of the arc taking tangent speed into account. + /// + /// The arc parameter (should be between 0 and 1). + /// The coordinates of the point on the arc at the specified parameter. + public T GetPointAt(float t) + { + return getPointAt(t); + } + + /// + /// Gets the point at the specified parameter of the arc normalised against tangent speed. + /// + /// The arc parameter (should be between 0 and 1). + /// The coordinates of the point on the arc at the specified parameter. + public T GetPointAtNormalised(float t) + { + ensureInitialized(); + return getPointAt(mapDistance(t.Clamped(0, 1) * Length)); + } + #endregion + + #region Distance mapping + private float mapDistance(float distance) + { + if (distance <= 0) + return 0; + if (distance >= Length) + return 1; + + int i = findSegmentLengthIndex(distance); + float step = 1.0f / segmentLengths.Length; + if (segmentLengths[i] == distance) + return i * step; + if (i == 0) + return 0; + + float over = distance - segmentLengths[i - 1]; + float ratio = over / (segmentLengths[i] - segmentLengths[i - 1]); + return (i + ratio) * step; + } + + private int findSegmentLengthIndex(float distance) + { + int index = Array.BinarySearch(segmentLengths, distance); + return index < 0 ? ~index : 0; } + #endregion } diff --git a/Bearded.Utilities/Geometry/arcs/Arc2.cs b/Bearded.Utilities/Geometry/arcs/Arc2.cs index 190fb972..266d47f7 100644 --- a/Bearded.Utilities/Geometry/arcs/Arc2.cs +++ b/Bearded.Utilities/Geometry/arcs/Arc2.cs @@ -1,28 +1,27 @@ using OpenTK.Mathematics; -namespace Bearded.Utilities.Geometry +namespace Bearded.Utilities.Geometry; + +/// +/// Represents an arc in two-dimensional Euclidean space. +/// +public abstract class Arc2 : Arc { /// - /// Represents an arc in two-dimensional Euclidean space. + /// Initializes the arc. /// - public abstract class Arc2 : Arc - { - /// - /// Initializes the arc. - /// - /// The amount of linear segments the arc is split in. A larger amount of segments results in higher precision for length and remapping. - protected Arc2(int segments = 100) - : base(segments) { } + /// The amount of linear segments the arc is split in. A larger amount of segments results in higher precision for length and remapping. + protected Arc2(int segments = 100) + : base(segments) { } - /// - /// Calculates the distance between two points using the Euclidean metric. - /// - /// The first coordinate. - /// The second coordinate. - /// The Euclidean distance between the specified points. - protected override float getDistanceBetween(Vector2 p1, Vector2 p2) - { - return (p2 - p1).Length; - } + /// + /// Calculates the distance between two points using the Euclidean metric. + /// + /// The first coordinate. + /// The second coordinate. + /// The Euclidean distance between the specified points. + protected override float getDistanceBetween(Vector2 p1, Vector2 p2) + { + return (p2 - p1).Length; } } diff --git a/Bearded.Utilities/Geometry/arcs/Arc3.cs b/Bearded.Utilities/Geometry/arcs/Arc3.cs index 8eba3859..dce2fe45 100644 --- a/Bearded.Utilities/Geometry/arcs/Arc3.cs +++ b/Bearded.Utilities/Geometry/arcs/Arc3.cs @@ -1,28 +1,27 @@ using OpenTK.Mathematics; -namespace Bearded.Utilities.Geometry +namespace Bearded.Utilities.Geometry; + +/// +/// Represents an arc in three-dimensional Euclidean space. +/// +public abstract class Arc3 : Arc { /// - /// Represents an arc in three-dimensional Euclidean space. + /// Initializes the arc. /// - public abstract class Arc3 : Arc - { - /// - /// Initializes the arc. - /// - /// The amount of linear segments the arc is split in. A larger amount of segments results in higher precision for length and remapping. - protected Arc3(int segments = 100) - : base(segments) { } + /// The amount of linear segments the arc is split in. A larger amount of segments results in higher precision for length and remapping. + protected Arc3(int segments = 100) + : base(segments) { } - /// - /// Calculates the distance between two points using the Euclidean metric. - /// - /// The first coordinate. - /// The second coordinate. - /// The Euclidean distance between the specified points. - protected override float getDistanceBetween(Vector3 p1, Vector3 p2) - { - return (p2 - p1).Length; - } + /// + /// Calculates the distance between two points using the Euclidean metric. + /// + /// The first coordinate. + /// The second coordinate. + /// The Euclidean distance between the specified points. + protected override float getDistanceBetween(Vector3 p1, Vector3 p2) + { + return (p2 - p1).Length; } } diff --git a/Bearded.Utilities/Geometry/arcs/Bezier2nd2.cs b/Bearded.Utilities/Geometry/arcs/Bezier2nd2.cs index f2934981..75e062ad 100644 --- a/Bearded.Utilities/Geometry/arcs/Bezier2nd2.cs +++ b/Bearded.Utilities/Geometry/arcs/Bezier2nd2.cs @@ -1,44 +1,43 @@ using OpenTK.Mathematics; -namespace Bearded.Utilities.Geometry +namespace Bearded.Utilities.Geometry; + +/// +/// Represents a quadratic Bezier curve in two-dimensional space. +/// +// ReSharper disable once InconsistentNaming +public sealed class Bezier2nd2 : Arc2 { + private readonly Vector2 p0; + private readonly Vector2 p1; + private readonly Vector2 p2; + /// - /// Represents a quadratic Bezier curve in two-dimensional space. + /// Initializes the Bezier curve with 100 segments. /// - // ReSharper disable once InconsistentNaming - public sealed class Bezier2nd2 : Arc2 + public Bezier2nd2(Vector2 p0, Vector2 p1, Vector2 p2) + : this(p0, p1, p2, 100) { - private readonly Vector2 p0; - private readonly Vector2 p1; - private readonly Vector2 p2; - - /// - /// Initializes the Bezier curve with 100 segments. - /// - public Bezier2nd2(Vector2 p0, Vector2 p1, Vector2 p2) - : this(p0, p1, p2, 100) - { - } + } - /// - /// Initializes the Bezier curve. - /// - public Bezier2nd2(Vector2 p0, Vector2 p1, Vector2 p2, int segments) - : base(segments) - { - this.p0 = p0; - this.p1 = p1; - this.p2 = p2; - } + /// + /// Initializes the Bezier curve. + /// + public Bezier2nd2(Vector2 p0, Vector2 p1, Vector2 p2, int segments) + : base(segments) + { + this.p0 = p0; + this.p1 = p1; + this.p2 = p2; + } - /// - /// Calculates the point on the Bezier curve at parameter t. - /// - /// The arc parameter t. - /// The Euclidean coordinates of the point on the curve at parameter t. - protected override Vector2 getPointAt(float t) - { - return Interpolate.Bezier(p0, p1, p2, t); - } + /// + /// Calculates the point on the Bezier curve at parameter t. + /// + /// The arc parameter t. + /// The Euclidean coordinates of the point on the curve at parameter t. + protected override Vector2 getPointAt(float t) + { + return Interpolate.Bezier(p0, p1, p2, t); } } diff --git a/Bearded.Utilities/Geometry/arcs/Bezier2nd3.cs b/Bearded.Utilities/Geometry/arcs/Bezier2nd3.cs index 12a27e76..11d1965e 100644 --- a/Bearded.Utilities/Geometry/arcs/Bezier2nd3.cs +++ b/Bearded.Utilities/Geometry/arcs/Bezier2nd3.cs @@ -1,44 +1,43 @@ using OpenTK.Mathematics; -namespace Bearded.Utilities.Geometry +namespace Bearded.Utilities.Geometry; + +/// +/// Represents a quadratic Bezier curve in three-dimensional space. +/// +// ReSharper disable once InconsistentNaming +public sealed class Bezier2nd3 : Arc3 { + private readonly Vector3 p0; + private readonly Vector3 p1; + private readonly Vector3 p2; + /// - /// Represents a quadratic Bezier curve in three-dimensional space. + /// Initializes the Bezier curve with 100 segments. /// - // ReSharper disable once InconsistentNaming - public sealed class Bezier2nd3 : Arc3 + public Bezier2nd3(Vector3 p0, Vector3 p1, Vector3 p2) + : this(p0, p1, p2, 100) { - private readonly Vector3 p0; - private readonly Vector3 p1; - private readonly Vector3 p2; - - /// - /// Initializes the Bezier curve with 100 segments. - /// - public Bezier2nd3(Vector3 p0, Vector3 p1, Vector3 p2) - : this(p0, p1, p2, 100) - { - } + } - /// - /// Initializes the Bezier curve. - /// - public Bezier2nd3(Vector3 p0, Vector3 p1, Vector3 p2, int segments) - : base(segments) - { - this.p0 = p0; - this.p1 = p1; - this.p2 = p2; - } + /// + /// Initializes the Bezier curve. + /// + public Bezier2nd3(Vector3 p0, Vector3 p1, Vector3 p2, int segments) + : base(segments) + { + this.p0 = p0; + this.p1 = p1; + this.p2 = p2; + } - /// - /// Calculates the point on the Bezier curve at parameter t. - /// - /// The arc parameter t. - /// The Euclidean coordinates of the point on the curve at parameter t. - protected override Vector3 getPointAt(float t) - { - return Interpolate.Bezier(p0, p1, p2, t); - } + /// + /// Calculates the point on the Bezier curve at parameter t. + /// + /// The arc parameter t. + /// The Euclidean coordinates of the point on the curve at parameter t. + protected override Vector3 getPointAt(float t) + { + return Interpolate.Bezier(p0, p1, p2, t); } } diff --git a/Bearded.Utilities/Geometry/arcs/Bezier3rd2.cs b/Bearded.Utilities/Geometry/arcs/Bezier3rd2.cs index ab335a43..7e9b5fc0 100644 --- a/Bearded.Utilities/Geometry/arcs/Bezier3rd2.cs +++ b/Bearded.Utilities/Geometry/arcs/Bezier3rd2.cs @@ -1,46 +1,45 @@ using OpenTK.Mathematics; -namespace Bearded.Utilities.Geometry +namespace Bearded.Utilities.Geometry; + +/// +/// Represents a cubic Bezier curve in two-dimensional space. +/// +// ReSharper disable once InconsistentNaming +public sealed class Bezier3rd2 : Arc2 { + private readonly Vector2 p0; + private readonly Vector2 p1; + private readonly Vector2 p2; + private readonly Vector2 p3; + /// - /// Represents a cubic Bezier curve in two-dimensional space. + /// Initializes the Bezier curve with 100 segments. /// - // ReSharper disable once InconsistentNaming - public sealed class Bezier3rd2 : Arc2 + public Bezier3rd2(Vector2 p0, Vector2 p1, Vector2 p2, Vector2 p3) + : this(p0, p1, p2, p3, 100) { - private readonly Vector2 p0; - private readonly Vector2 p1; - private readonly Vector2 p2; - private readonly Vector2 p3; - - /// - /// Initializes the Bezier curve with 100 segments. - /// - public Bezier3rd2(Vector2 p0, Vector2 p1, Vector2 p2, Vector2 p3) - : this(p0, p1, p2, p3, 100) - { - } + } - /// - /// Initializes the Bezier curve. - /// - public Bezier3rd2(Vector2 p0, Vector2 p1, Vector2 p2, Vector2 p3, int segments) - : base(segments) - { - this.p0 = p0; - this.p1 = p1; - this.p2 = p2; - this.p3 = p3; - } + /// + /// Initializes the Bezier curve. + /// + public Bezier3rd2(Vector2 p0, Vector2 p1, Vector2 p2, Vector2 p3, int segments) + : base(segments) + { + this.p0 = p0; + this.p1 = p1; + this.p2 = p2; + this.p3 = p3; + } - /// - /// Calculates the point on the Bezier curve at parameter t. - /// - /// The arc parameter t. - /// The Euclidean coordinates of the point on the curve at parameter t. - protected override Vector2 getPointAt(float t) - { - return Interpolate.Bezier(p0, p1, p2, p3, t); - } + /// + /// Calculates the point on the Bezier curve at parameter t. + /// + /// The arc parameter t. + /// The Euclidean coordinates of the point on the curve at parameter t. + protected override Vector2 getPointAt(float t) + { + return Interpolate.Bezier(p0, p1, p2, p3, t); } } diff --git a/Bearded.Utilities/Geometry/arcs/Bezier3rd3.cs b/Bearded.Utilities/Geometry/arcs/Bezier3rd3.cs index 61d75a54..cb632ad9 100644 --- a/Bearded.Utilities/Geometry/arcs/Bezier3rd3.cs +++ b/Bearded.Utilities/Geometry/arcs/Bezier3rd3.cs @@ -1,46 +1,45 @@ using OpenTK.Mathematics; -namespace Bearded.Utilities.Geometry +namespace Bearded.Utilities.Geometry; + +/// +/// Represents a cubic Bezier curve in three-dimensional space. +/// +// ReSharper disable once InconsistentNaming +public sealed class Bezier3rd3 : Arc3 { + private readonly Vector3 p0; + private readonly Vector3 p1; + private readonly Vector3 p2; + private readonly Vector3 p3; + /// - /// Represents a cubic Bezier curve in three-dimensional space. + /// Initializes the Bezier curve with 100 segments. /// - // ReSharper disable once InconsistentNaming - public sealed class Bezier3rd3 : Arc3 + public Bezier3rd3(Vector3 p0, Vector3 p1, Vector3 p2, Vector3 p3) + : this(p0, p1, p2, p3, 100) { - private readonly Vector3 p0; - private readonly Vector3 p1; - private readonly Vector3 p2; - private readonly Vector3 p3; - - /// - /// Initializes the Bezier curve with 100 segments. - /// - public Bezier3rd3(Vector3 p0, Vector3 p1, Vector3 p2, Vector3 p3) - : this(p0, p1, p2, p3, 100) - { - } + } - /// - /// Initializes the Bezier curve. - /// - public Bezier3rd3(Vector3 p0, Vector3 p1, Vector3 p2, Vector3 p3, int segments) - : base(segments) - { - this.p0 = p0; - this.p1 = p1; - this.p2 = p2; - this.p3 = p3; - } + /// + /// Initializes the Bezier curve. + /// + public Bezier3rd3(Vector3 p0, Vector3 p1, Vector3 p2, Vector3 p3, int segments) + : base(segments) + { + this.p0 = p0; + this.p1 = p1; + this.p2 = p2; + this.p3 = p3; + } - /// - /// Calculates the point on the Bezier curve at parameter t. - /// - /// The arc parameter t. - /// The Euclidean coordinates of the point on the curve at parameter t. - protected override Vector3 getPointAt(float t) - { - return Interpolate.Bezier(p0, p1, p2, p3, t); - } + /// + /// Calculates the point on the Bezier curve at parameter t. + /// + /// The arc parameter t. + /// The Euclidean coordinates of the point on the curve at parameter t. + protected override Vector3 getPointAt(float t) + { + return Interpolate.Bezier(p0, p1, p2, p3, t); } } diff --git a/Bearded.Utilities/Graphs/AdjacencyListDirectedAcyclicGraph.cs b/Bearded.Utilities/Graphs/AdjacencyListDirectedAcyclicGraph.cs index a100a353..d4ea5d96 100644 --- a/Bearded.Utilities/Graphs/AdjacencyListDirectedAcyclicGraph.cs +++ b/Bearded.Utilities/Graphs/AdjacencyListDirectedAcyclicGraph.cs @@ -1,18 +1,17 @@ using System; using System.Collections.Immutable; -namespace Bearded.Utilities.Graphs +namespace Bearded.Utilities.Graphs; + +sealed class AdjacencyListDirectedAcyclicGraph : AdjacencyListDirectedGraph, IDirectedAcyclicGraph + where T : IEquatable { - sealed class AdjacencyListDirectedAcyclicGraph : AdjacencyListDirectedGraph, IDirectedAcyclicGraph - where T : IEquatable - { - internal AdjacencyListDirectedAcyclicGraph( - ImmutableList elements, - ImmutableDictionary> directSuccessors, - ImmutableDictionary> directPredecessors) - : base( - elements, - directSuccessors, - directPredecessors) { } - } + internal AdjacencyListDirectedAcyclicGraph( + ImmutableList elements, + ImmutableDictionary> directSuccessors, + ImmutableDictionary> directPredecessors) + : base( + elements, + directSuccessors, + directPredecessors) { } } diff --git a/Bearded.Utilities/Graphs/AdjacencyListDirectedGraph.cs b/Bearded.Utilities/Graphs/AdjacencyListDirectedGraph.cs index b634e1bc..df961a8b 100644 --- a/Bearded.Utilities/Graphs/AdjacencyListDirectedGraph.cs +++ b/Bearded.Utilities/Graphs/AdjacencyListDirectedGraph.cs @@ -2,34 +2,33 @@ using System.Collections.Generic; using System.Collections.Immutable; -namespace Bearded.Utilities.Graphs -{ - class AdjacencyListDirectedGraph : IDirectedGraph where T : IEquatable - { - private readonly ImmutableList elements; - private readonly ImmutableDictionary> directSuccessors; - private readonly ImmutableDictionary> directPredecessors; +namespace Bearded.Utilities.Graphs; - public IEnumerable Elements => elements; - public int Count => elements.Count; +class AdjacencyListDirectedGraph : IDirectedGraph where T : IEquatable +{ + private readonly ImmutableList elements; + private readonly ImmutableDictionary> directSuccessors; + private readonly ImmutableDictionary> directPredecessors; - internal AdjacencyListDirectedGraph( - ImmutableList elements, - ImmutableDictionary> directSuccessors, - ImmutableDictionary> directPredecessors) - { - this.elements = elements; - this.directSuccessors = directSuccessors; - this.directPredecessors = directPredecessors; - } + public IEnumerable Elements => elements; + public int Count => elements.Count; - public IEnumerable GetDirectSuccessorsOf(T element) => - directSuccessors.TryGetValue(element, out var successor) - ? successor - : throw new ArgumentOutOfRangeException(nameof(element), "Element not found in graph."); - public IEnumerable GetDirectPredecessorsOf(T element) => - directPredecessors.TryGetValue(element, out var predecessor) - ? predecessor - : throw new ArgumentOutOfRangeException(nameof(element), "Element not found in graph."); + internal AdjacencyListDirectedGraph( + ImmutableList elements, + ImmutableDictionary> directSuccessors, + ImmutableDictionary> directPredecessors) + { + this.elements = elements; + this.directSuccessors = directSuccessors; + this.directPredecessors = directPredecessors; } + + public IEnumerable GetDirectSuccessorsOf(T element) => + directSuccessors.TryGetValue(element, out var successor) + ? successor + : throw new ArgumentOutOfRangeException(nameof(element), "Element not found in graph."); + public IEnumerable GetDirectPredecessorsOf(T element) => + directPredecessors.TryGetValue(element, out var predecessor) + ? predecessor + : throw new ArgumentOutOfRangeException(nameof(element), "Element not found in graph."); } diff --git a/Bearded.Utilities/Graphs/DirectedAcyclicGraphTransitiveReducer.cs b/Bearded.Utilities/Graphs/DirectedAcyclicGraphTransitiveReducer.cs index a6dd3972..6e0ac84f 100644 --- a/Bearded.Utilities/Graphs/DirectedAcyclicGraphTransitiveReducer.cs +++ b/Bearded.Utilities/Graphs/DirectedAcyclicGraphTransitiveReducer.cs @@ -2,74 +2,73 @@ using System.Collections.Generic; using System.Linq; -namespace Bearded.Utilities.Graphs +namespace Bearded.Utilities.Graphs; + +public static class DirectedAcyclicGraphTransitiveReducer where T : IEquatable { - public static class DirectedAcyclicGraphTransitiveReducer where T : IEquatable + public static IDirectedAcyclicGraph ReduceGraph(IDirectedAcyclicGraph graph) { - public static IDirectedAcyclicGraph ReduceGraph(IDirectedAcyclicGraph graph) + var elementsEnumerable = graph.Elements; + var elements = elementsEnumerable as IList ?? elementsEnumerable.ToList(); + var graphBuilder = DirectedGraphBuilder.NewBuilder(); + + foreach (var element in elements) { - var elementsEnumerable = graph.Elements; - var elements = elementsEnumerable as IList ?? elementsEnumerable.ToList(); - var graphBuilder = DirectedGraphBuilder.NewBuilder(); + graphBuilder.AddElement(element); + } - foreach (var element in elements) + foreach (var element in elements) + { + foreach (var child in getOnlyChildrenWithoutIndirectPath(graph, element)) { - graphBuilder.AddElement(element); + graphBuilder.AddArrow(element, child); } + } - foreach (var element in elements) - { - foreach (var child in getOnlyChildrenWithoutIndirectPath(graph, element)) - { - graphBuilder.AddArrow(element, child); - } - } + return graphBuilder.CreateAcyclicGraphUnsafe(); + } - return graphBuilder.CreateAcyclicGraphUnsafe(); - } + private static IEnumerable getOnlyChildrenWithoutIndirectPath(IDirectedAcyclicGraph graph, T element) + { + var childrenEnumerable = graph.GetDirectSuccessorsOf(element); + var children = childrenEnumerable as IList + ?? childrenEnumerable.ToList(); + var reducedEdges = new HashSet(children); - private static IEnumerable getOnlyChildrenWithoutIndirectPath(IDirectedAcyclicGraph graph, T element) + foreach (var child in children) { - var childrenEnumerable = graph.GetDirectSuccessorsOf(element); - var children = childrenEnumerable as IList - ?? childrenEnumerable.ToList(); - var reducedEdges = new HashSet(children); - - foreach (var child in children) + foreach (var descendant in getAllSuccessorsRecursively(graph, child)) { - foreach (var descendant in getAllSuccessorsRecursively(graph, child)) + if (descendant.Equals(child)) { - if (descendant.Equals(child)) - { - continue; - } - - // Every arrow to a successor of our direct child is already reachable. - reducedEdges.Remove(descendant); + continue; } - } - return reducedEdges; + // Every arrow to a successor of our direct child is already reachable. + reducedEdges.Remove(descendant); + } } - // ReSharper disable once SuggestBaseTypeForParameter - private static IEnumerable getAllSuccessorsRecursively(IDirectedAcyclicGraph graph, T start) + return reducedEdges; + } + + // ReSharper disable once SuggestBaseTypeForParameter + private static IEnumerable getAllSuccessorsRecursively(IDirectedAcyclicGraph graph, T start) + { + yield return start; + foreach (var child in graph.GetDirectSuccessorsOf(start)) { - yield return start; - foreach (var child in graph.GetDirectSuccessorsOf(start)) + foreach (var descendant in getAllSuccessorsRecursively(graph, child)) { - foreach (var descendant in getAllSuccessorsRecursively(graph, child)) - { - yield return descendant; - } + yield return descendant; } } } +} - public static class DirectedAcyclicGraphTransitiveReducer - { - public static IDirectedAcyclicGraph ReduceGraph(IDirectedAcyclicGraph graph) - where T : IEquatable - => DirectedAcyclicGraphTransitiveReducer.ReduceGraph(graph); - } +public static class DirectedAcyclicGraphTransitiveReducer +{ + public static IDirectedAcyclicGraph ReduceGraph(IDirectedAcyclicGraph graph) + where T : IEquatable + => DirectedAcyclicGraphTransitiveReducer.ReduceGraph(graph); } diff --git a/Bearded.Utilities/Graphs/DirectedGraphBuilder.cs b/Bearded.Utilities/Graphs/DirectedGraphBuilder.cs index cc6e61ad..42f9f22a 100644 --- a/Bearded.Utilities/Graphs/DirectedGraphBuilder.cs +++ b/Bearded.Utilities/Graphs/DirectedGraphBuilder.cs @@ -3,149 +3,148 @@ using System.Collections.Immutable; using System.Linq; -namespace Bearded.Utilities.Graphs +namespace Bearded.Utilities.Graphs; + +public sealed class DirectedGraphBuilder where T : IEquatable { - public sealed class DirectedGraphBuilder where T : IEquatable + private readonly HashSet elements = new HashSet(); + private readonly HashSet sources = new HashSet(); + private readonly Dictionary> directSuccessors = new Dictionary>(); + private readonly Dictionary> directPredecessors = new Dictionary>(); + + public static DirectedGraphBuilder NewBuilder() { - private readonly HashSet elements = new HashSet(); - private readonly HashSet sources = new HashSet(); - private readonly Dictionary> directSuccessors = new Dictionary>(); - private readonly Dictionary> directPredecessors = new Dictionary>(); + return new DirectedGraphBuilder(); + } - public static DirectedGraphBuilder NewBuilder() - { - return new DirectedGraphBuilder(); - } + public static DirectedGraphBuilder FromExistingGraph(IDirectedGraph graph) + { + var builder = new DirectedGraphBuilder(); - public static DirectedGraphBuilder FromExistingGraph(IDirectedGraph graph) + foreach (var element in graph.Elements) { - var builder = new DirectedGraphBuilder(); + builder.elements.Add(element); - foreach (var element in graph.Elements) - { - builder.elements.Add(element); - - var successors = new HashSet(graph.GetDirectSuccessorsOf(element)); - var predecessors = new HashSet(graph.GetDirectPredecessorsOf(element)); + var successors = new HashSet(graph.GetDirectSuccessorsOf(element)); + var predecessors = new HashSet(graph.GetDirectPredecessorsOf(element)); - builder.directSuccessors.Add(element, successors); - builder.directPredecessors.Add(element, predecessors); + builder.directSuccessors.Add(element, successors); + builder.directPredecessors.Add(element, predecessors); - if (predecessors.Count == 0) - { - builder.sources.Add(element); - } + if (predecessors.Count == 0) + { + builder.sources.Add(element); } - - return builder; } - public static IDirectedAcyclicGraph EmptyGraph() - { - return new DirectedGraphBuilder().CreateAcyclicGraphUnsafe(); - } + return builder; + } - private DirectedGraphBuilder() { } + public static IDirectedAcyclicGraph EmptyGraph() + { + return new DirectedGraphBuilder().CreateAcyclicGraphUnsafe(); + } - public DirectedGraphBuilder AddElement(T element) - { - if (element == null) throw new ArgumentNullException(nameof(element)); - if (!elements.Add(element)) throw new ArgumentException("Element already in graph.", nameof(element)); + private DirectedGraphBuilder() { } - sources.Add(element); - directSuccessors.Add(element, new HashSet()); - directPredecessors.Add(element, new HashSet()); + public DirectedGraphBuilder AddElement(T element) + { + if (element == null) throw new ArgumentNullException(nameof(element)); + if (!elements.Add(element)) throw new ArgumentException("Element already in graph.", nameof(element)); - return this; - } + sources.Add(element); + directSuccessors.Add(element, new HashSet()); + directPredecessors.Add(element, new HashSet()); - public DirectedGraphBuilder AddArrow(T from, T to) - { - if (from == null) throw new ArgumentNullException(nameof(from)); - if (to == null) throw new ArgumentNullException(nameof(to)); - if (!elements.Contains(from)) throw new ArgumentException("Element not in graph.", nameof(from)); - if (!elements.Contains(to)) throw new ArgumentException("Element not in graph.", nameof(to)); - if (directSuccessors[from].Contains(to)) throw new ArgumentException("Arrow already in graph."); + return this; + } - directSuccessors[from].Add(to); - directPredecessors[to].Add(from); + public DirectedGraphBuilder AddArrow(T from, T to) + { + if (from == null) throw new ArgumentNullException(nameof(from)); + if (to == null) throw new ArgumentNullException(nameof(to)); + if (!elements.Contains(from)) throw new ArgumentException("Element not in graph.", nameof(from)); + if (!elements.Contains(to)) throw new ArgumentException("Element not in graph.", nameof(to)); + if (directSuccessors[from].Contains(to)) throw new ArgumentException("Arrow already in graph."); - sources.Remove(to); + directSuccessors[from].Add(to); + directPredecessors[to].Add(from); - return this; - } + sources.Remove(to); - /// - /// Creates an immutable directed graph instance. - /// - public IDirectedGraph CreateGraph() - { - return new AdjacencyListDirectedGraph( - ImmutableList.CreateRange(elements), - directSuccessors.ToImmutableDictionary( - pair => pair.Key, - pair => ImmutableList.CreateRange(pair.Value)), - directPredecessors.ToImmutableDictionary( - pair => pair.Key, - pair => ImmutableList.CreateRange(pair.Value))); - } + return this; + } - /// - /// Creates an immutable directed acyclic graph instance. - /// - /// It will verify that no cycles exist. - /// - public IDirectedAcyclicGraph CreateAcyclicGraph() - { - if (isGraphCyclic()) - { - throw new InvalidOperationException("Cannot create an directed acyclic graph with cycles."); - } + /// + /// Creates an immutable directed graph instance. + /// + public IDirectedGraph CreateGraph() + { + return new AdjacencyListDirectedGraph( + ImmutableList.CreateRange(elements), + directSuccessors.ToImmutableDictionary( + pair => pair.Key, + pair => ImmutableList.CreateRange(pair.Value)), + directPredecessors.ToImmutableDictionary( + pair => pair.Key, + pair => ImmutableList.CreateRange(pair.Value))); + } - return CreateAcyclicGraphUnsafe(); + /// + /// Creates an immutable directed acyclic graph instance. + /// + /// It will verify that no cycles exist. + /// + public IDirectedAcyclicGraph CreateAcyclicGraph() + { + if (isGraphCyclic()) + { + throw new InvalidOperationException("Cannot create an directed acyclic graph with cycles."); } - private bool isGraphCyclic() - { - var visited = new HashSet(); - var currentPath = new HashSet(); + return CreateAcyclicGraphUnsafe(); + } - return elements.Any(leadsBackToCurrentPath); + private bool isGraphCyclic() + { + var visited = new HashSet(); + var currentPath = new HashSet(); + + return elements.Any(leadsBackToCurrentPath); - bool leadsBackToCurrentPath(T element) + bool leadsBackToCurrentPath(T element) + { + if (visited.Contains(element)) { - if (visited.Contains(element)) - { - return currentPath.Contains(element); - } + return currentPath.Contains(element); + } - visited.Add(element); - currentPath.Add(element); + visited.Add(element); + currentPath.Add(element); - if (directSuccessors[element].Any(leadsBackToCurrentPath)) - { - return true; - } + if (directSuccessors[element].Any(leadsBackToCurrentPath)) + { + return true; + } - currentPath.Remove(element); + currentPath.Remove(element); - return false; - } + return false; } + } - /// - /// Creates an immutable directed acyclic graph instance without verifying that there are no cycles. - /// - public IDirectedAcyclicGraph CreateAcyclicGraphUnsafe() - { - return new AdjacencyListDirectedAcyclicGraph( - ImmutableList.CreateRange(elements), - directSuccessors.ToImmutableDictionary( - pair => pair.Key, - pair => ImmutableList.CreateRange(pair.Value)), - directPredecessors.ToImmutableDictionary( - pair => pair.Key, - pair => ImmutableList.CreateRange(pair.Value))); - } + /// + /// Creates an immutable directed acyclic graph instance without verifying that there are no cycles. + /// + public IDirectedAcyclicGraph CreateAcyclicGraphUnsafe() + { + return new AdjacencyListDirectedAcyclicGraph( + ImmutableList.CreateRange(elements), + directSuccessors.ToImmutableDictionary( + pair => pair.Key, + pair => ImmutableList.CreateRange(pair.Value)), + directPredecessors.ToImmutableDictionary( + pair => pair.Key, + pair => ImmutableList.CreateRange(pair.Value))); } } diff --git a/Bearded.Utilities/Graphs/IDirectedAcyclicGraph.cs b/Bearded.Utilities/Graphs/IDirectedAcyclicGraph.cs index 75a10140..607dda63 100644 --- a/Bearded.Utilities/Graphs/IDirectedAcyclicGraph.cs +++ b/Bearded.Utilities/Graphs/IDirectedAcyclicGraph.cs @@ -1,6 +1,5 @@ using System; -namespace Bearded.Utilities.Graphs -{ - public interface IDirectedAcyclicGraph : IDirectedGraph where T : IEquatable { } -} +namespace Bearded.Utilities.Graphs; + +public interface IDirectedAcyclicGraph : IDirectedGraph where T : IEquatable { } diff --git a/Bearded.Utilities/Graphs/IDirectedGraph.cs b/Bearded.Utilities/Graphs/IDirectedGraph.cs index 6663f48f..6fd5cb91 100644 --- a/Bearded.Utilities/Graphs/IDirectedGraph.cs +++ b/Bearded.Utilities/Graphs/IDirectedGraph.cs @@ -1,13 +1,12 @@ using System; using System.Collections.Generic; -namespace Bearded.Utilities.Graphs +namespace Bearded.Utilities.Graphs; + +public interface IDirectedGraph where T : IEquatable { - public interface IDirectedGraph where T : IEquatable - { - IEnumerable Elements { get; } - int Count { get; } - IEnumerable GetDirectSuccessorsOf(T element); - IEnumerable GetDirectPredecessorsOf(T element); - } + IEnumerable Elements { get; } + int Count { get; } + IEnumerable GetDirectSuccessorsOf(T element); + IEnumerable GetDirectPredecessorsOf(T element); } diff --git a/Bearded.Utilities/IO/FileModifiedWatcher.cs b/Bearded.Utilities/IO/FileModifiedWatcher.cs index 12546de3..8f5fba4e 100644 --- a/Bearded.Utilities/IO/FileModifiedWatcher.cs +++ b/Bearded.Utilities/IO/FileModifiedWatcher.cs @@ -1,78 +1,77 @@ using System; using System.IO; -namespace Bearded.Utilities.IO +namespace Bearded.Utilities.IO; + +/// +/// This class can be used to watch a file for modifications by checking its last write time stamp. +/// +public class FileModifiedWatcher { + private DateTime? lastModified; + /// - /// This class can be used to watch a file for modifications by checking its last write time stamp. + /// Gets the path of the file watched. /// - public class FileModifiedWatcher - { - private DateTime? lastModified; + public string Path { get; } - /// - /// Gets the path of the file watched. - /// - public string Path { get; } + /// + /// Gets the filename, without directories, of the file watched. + /// + public string FileName { get; } - /// - /// Gets the filename, without directories, of the file watched. - /// - public string FileName { get; } + /// + /// Creates a new watching the specified file. + /// + /// The file to watch. Must be valid path. + /// Otherwise, behaviour is undefined and exceptions may be thrown. + public FileModifiedWatcher(string path) + { + Path = path; + FileName = System.IO.Path.GetFileName(path); - /// - /// Creates a new watching the specified file. - /// - /// The file to watch. Must be valid path. - /// Otherwise, behaviour is undefined and exceptions may be thrown. - public FileModifiedWatcher(string path) - { - Path = path; - FileName = System.IO.Path.GetFileName(path); + Reset(); + } - Reset(); - } + private DateTime? getLastWriteTime() + { + return File.Exists(Path) + ? (DateTime?)File.GetLastWriteTime(Path) + : null; + } - private DateTime? getLastWriteTime() - { - return File.Exists(Path) - ? (DateTime?)File.GetLastWriteTime(Path) - : null; - } + /// + /// Resets this watcher to ignore all past modifications to file. + /// + public void Reset() + { + lastModified = getLastWriteTime(); + } - /// - /// Resets this watcher to ignore all past modifications to file. - /// - public void Reset() - { - lastModified = getLastWriteTime(); - } + /// + /// Checks whether the file was changed since the last reset of the watcher, and then resets the watcher. + /// + /// True, if the file has a different last-write time stamp than when the watcher was reset last. + /// True if the file was created or deleted since the last reset. + /// False otherwise. + public bool WasModified() => WasModified(true); - /// - /// Checks whether the file was changed since the last reset of the watcher, and then resets the watcher. - /// - /// True, if the file has a different last-write time stamp than when the watcher was reset last. - /// True if the file was created or deleted since the last reset. - /// False otherwise. - public bool WasModified() => WasModified(true); + /// + /// Checks whether the file was changed since the last reset of the watcher. + /// + /// Whether to reset the watcher after checking for changes. + /// True, if the file has a different last-write time stamp than when the watcher was reset last. + /// True if the file was created or deleted since the last reset. + /// False otherwise. + public bool WasModified(bool resetModified) + { + var modified = getLastWriteTime(); - /// - /// Checks whether the file was changed since the last reset of the watcher. - /// - /// Whether to reset the watcher after checking for changes. - /// True, if the file has a different last-write time stamp than when the watcher was reset last. - /// True if the file was created or deleted since the last reset. - /// False otherwise. - public bool WasModified(bool resetModified) - { - var modified = getLastWriteTime(); - - if (Nullable.Equals(modified, lastModified)) - return false; + if (Nullable.Equals(modified, lastModified)) + return false; - if (resetModified) - lastModified = modified; - return true; - } + if (resetModified) + lastModified = modified; + return true; } } diff --git a/Bearded.Utilities/IO/Logger.cs b/Bearded.Utilities/IO/Logger.cs index 2dddad32..fe032de3 100644 --- a/Bearded.Utilities/IO/Logger.cs +++ b/Bearded.Utilities/IO/Logger.cs @@ -3,328 +3,327 @@ using System.Collections.ObjectModel; using System.Linq; -namespace Bearded.Utilities.IO -{ - public delegate void LogEvent(Logger.Entry entry); +namespace Bearded.Utilities.IO; - /// - /// Automatic class to improve logging by keeping track of logged history, added events, and adding semantics to log messages. - /// Unless otherwise mentioned, the members of this class are thread-safe. - /// - public class Logger +public delegate void LogEvent(Logger.Entry entry); + +/// +/// Automatic class to improve logging by keeping track of logged history, added events, and adding semantics to log messages. +/// Unless otherwise mentioned, the members of this class are thread-safe. +/// +public class Logger +{ + #region Writers + public sealed class Writer { - #region Writers - public sealed class Writer - { - private readonly Logger logger; - private readonly Severity severity; + private readonly Logger logger; + private readonly Severity severity; - internal Writer(Logger logger, Severity severity) - { - this.logger = logger; - this.severity = severity; - } + internal Writer(Logger logger, Severity severity) + { + this.logger = logger; + this.severity = severity; + } - public void Log(string text) { - logger.AddEntry(new Entry(text, severity)); - } + public void Log(string text) { + logger.AddEntry(new Entry(text, severity)); + } - public void Log(T obj) { - logger.AddEntry(new Entry(obj?.ToString() ?? "null", severity)); - } + public void Log(T obj) { + logger.AddEntry(new Entry(obj?.ToString() ?? "null", severity)); + } - public void Log(string text, params object[] parameters) { - Log(string.Format(text, parameters)); - } + public void Log(string text, params object[] parameters) { + Log(string.Format(text, parameters)); } + } + + public Writer? Fatal { get; } + public Writer? Error { get; } + public Writer? Warning { get; } + public Writer? Info { get; } + public Writer? Debug { get; } + public Writer? Trace { get; } + #endregion - public Writer? Fatal { get; } - public Writer? Error { get; } - public Writer? Warning { get; } - public Writer? Info { get; } - public Writer? Debug { get; } - public Writer? Trace { get; } - #endregion + #region types + + public enum Severity + { + /// + /// Used for any error that forces an application shutdown. + /// + Fatal = 0, + /// + /// Used for errors that are fatal to the operation, but leave the application alive. + /// + Error = 1, + /// + /// Used for anything that can cause application oddities. + /// + Warning = 2, + /// + /// Used for generally useful information to log. + /// + Info = 3, + /// + /// Used for information that is diagnostically helpful to people more than just developers. + /// + Debug = 4, + /// + /// Used for tracing purposes only. + /// + Trace = 5, + } + + public readonly struct Entry : IEquatable + { + public string Text { get; } - #region types + public Severity Severity { get; } - public enum Severity + public DateTime Time { get; } + + public Entry(string text) + : this(text, Severity.Debug, DateTime.Now) { - /// - /// Used for any error that forces an application shutdown. - /// - Fatal = 0, - /// - /// Used for errors that are fatal to the operation, but leave the application alive. - /// - Error = 1, - /// - /// Used for anything that can cause application oddities. - /// - Warning = 2, - /// - /// Used for generally useful information to log. - /// - Info = 3, - /// - /// Used for information that is diagnostically helpful to people more than just developers. - /// - Debug = 4, - /// - /// Used for tracing purposes only. - /// - Trace = 5, } - public readonly struct Entry : IEquatable + public Entry(string text, Severity severity) + : this(text, severity, DateTime.Now) { - public string Text { get; } - - public Severity Severity { get; } - - public DateTime Time { get; } + } - public Entry(string text) - : this(text, Severity.Debug, DateTime.Now) - { - } + public Entry(string text, Severity severity, DateTime time) + { + Text = text; + Severity = severity; + Time = time; + } - public Entry(string text, Severity severity) - : this(text, severity, DateTime.Now) - { - } + internal void WriteToConsole() + { + ConsoleColor rgb; - public Entry(string text, Severity severity, DateTime time) + switch (Severity) { - Text = text; - Severity = severity; - Time = time; + case Severity.Fatal: + case Severity.Error: + rgb = ConsoleColor.Red; + break; + case Severity.Warning: + rgb = ConsoleColor.Yellow; + break; + case Severity.Info: + rgb = ConsoleColor.White; + break; + case Severity.Debug: + rgb = ConsoleColor.Green; + break; + case Severity.Trace: + rgb = ConsoleColor.Blue; + break; + default: + throw new ArgumentOutOfRangeException(); } - internal void WriteToConsole() - { - ConsoleColor rgb; - - switch (Severity) - { - case Severity.Fatal: - case Severity.Error: - rgb = ConsoleColor.Red; - break; - case Severity.Warning: - rgb = ConsoleColor.Yellow; - break; - case Severity.Info: - rgb = ConsoleColor.White; - break; - case Severity.Debug: - rgb = ConsoleColor.Green; - break; - case Severity.Trace: - rgb = ConsoleColor.Blue; - break; - default: - throw new ArgumentOutOfRangeException(); - } - - Console.ForegroundColor = rgb; - Console.WriteLine(Text); - Console.ResetColor(); - } + Console.ForegroundColor = rgb; + Console.WriteLine(Text); + Console.ResetColor(); + } - public bool Equals(Entry other) - => string.Equals(Text, other.Text) && Severity == other.Severity && Time.Equals(other.Time); + public bool Equals(Entry other) + => string.Equals(Text, other.Text) && Severity == other.Severity && Time.Equals(other.Time); - public override bool Equals(object? obj) => obj is Entry entry && Equals(entry); + public override bool Equals(object? obj) => obj is Entry entry && Equals(entry); - public override int GetHashCode() + public override int GetHashCode() + { + unchecked { - unchecked - { - var hashCode = Text?.GetHashCode() ?? 0; - hashCode = (hashCode * 397) ^ (int)Severity; - hashCode = (hashCode * 397) ^ Time.GetHashCode(); - return hashCode; - } + var hashCode = Text?.GetHashCode() ?? 0; + hashCode = (hashCode * 397) ^ (int)Severity; + hashCode = (hashCode * 397) ^ Time.GetHashCode(); + return hashCode; } + } - public static bool operator ==(Entry left, Entry right) - => left.Equals(right); + public static bool operator ==(Entry left, Entry right) + => left.Equals(right); - public static bool operator !=(Entry left, Entry right) - => !left.Equals(right); - } - #endregion + public static bool operator !=(Entry left, Entry right) + => !left.Equals(right); + } + #endregion - #region fields and constructor + #region fields and constructor - private readonly List lines = new List(); + private readonly List lines = new List(); - /// - /// Gets the stored history of events. - /// Iterating this collection might throw exceptions if new entries are locked in the mean time, wether by the calling or by other threads. - /// For thread-safe iteration, use GetSafeRecentEntries. - /// - public ReadOnlyCollection RecentEntries { get; } + /// + /// Gets the stored history of events. + /// Iterating this collection might throw exceptions if new entries are locked in the mean time, wether by the calling or by other threads. + /// For thread-safe iteration, use GetSafeRecentEntries. + /// + public ReadOnlyCollection RecentEntries { get; } - /// - /// Gets a copy of the recent entry history. - /// This method allocates significant memory and should not be used unless necessary. - /// - public ReadOnlyCollection GetSafeRecentEntries() + /// + /// Gets a copy of the recent entry history. + /// This method allocates significant memory and should not be used unless necessary. + /// + public ReadOnlyCollection GetSafeRecentEntries() + { + List copy; + lock (lines) { - List copy; - lock (lines) - { - copy = lines.ToList(); - } - return copy.AsReadOnly(); + copy = lines.ToList(); } + return copy.AsReadOnly(); + } - /// - /// Gets a copy of the recent entry history with a minimum severity. - /// This method allocates significant memory and should not be used unless necessary. - /// - public ReadOnlyCollection GetSafeRecentEntriesWithSeverity(Severity severity) + /// + /// Gets a copy of the recent entry history with a minimum severity. + /// This method allocates significant memory and should not be used unless necessary. + /// + public ReadOnlyCollection GetSafeRecentEntriesWithSeverity(Severity severity) + { + List copy; + lock (lines) { - List copy; - lock (lines) - { - copy = lines.Where(entry => entry.Severity <= severity).ToList(); - } - return copy.AsReadOnly(); + copy = lines.Where(entry => entry.Severity <= severity).ToList(); } + return copy.AsReadOnly(); + } - /// - /// Adds all recent entries to a provided collection. - /// - public void CopyRecentEntries(ICollection collection) + /// + /// Adds all recent entries to a provided collection. + /// + public void CopyRecentEntries(ICollection collection) + { + lock (lines) { - lock (lines) - { - if (collection is List list) - ensureListCapacity(list, list.Count + lines.Count); + if (collection is List list) + ensureListCapacity(list, list.Count + lines.Count); - foreach (var entry in lines) - collection.Add(entry); - } + foreach (var entry in lines) + collection.Add(entry); } + } - /// - /// Adds all recent entries that are at least of the given severity to a provided collection. - /// - public void CopyRecentEntriesWithSeverity(Severity severity, ICollection collection) + /// + /// Adds all recent entries that are at least of the given severity to a provided collection. + /// + public void CopyRecentEntriesWithSeverity(Severity severity, ICollection collection) + { + lock (lines) { - lock (lines) - { - if (severity == Severity.Trace && collection is List list) - ensureListCapacity(list, list.Count + lines.Count); + if (severity == Severity.Trace && collection is List list) + ensureListCapacity(list, list.Count + lines.Count); - foreach (var entry in lines.Where(entry => entry.Severity <= severity)) - collection.Add(entry); - } + foreach (var entry in lines.Where(entry => entry.Severity <= severity)) + collection.Add(entry); } + } - private static void ensureListCapacity(List list, int neededCapacity) - { - if (list.Capacity < neededCapacity) - list.Capacity = Math.Max(list.Capacity * 2, neededCapacity); - } + private static void ensureListCapacity(List list, int neededCapacity) + { + if (list.Capacity < neededCapacity) + list.Capacity = Math.Max(list.Capacity * 2, neededCapacity); + } - /// - /// If RaiseEvents is true, this event is raised every time an event is added to the log. - /// - public event LogEvent Logged; + /// + /// If RaiseEvents is true, this event is raised every time an event is added to the log. + /// + public event LogEvent Logged; - #region settings + #region settings - /// - /// If this is true, all added events are automatically written to the standard output. - /// Default is true. - /// - public bool MirrorToConsole { get; set; } = true; + /// + /// If this is true, all added events are automatically written to the standard output. + /// Default is true. + /// + public bool MirrorToConsole { get; set; } = true; - /// - /// If this is true, the Logged event is raised for every added entry. - /// Default is true. - /// - public bool RaiseEvents { get; set; } = true; + /// + /// If this is true, the Logged event is raised for every added entry. + /// Default is true. + /// + public bool RaiseEvents { get; set; } = true; - /// - /// If this is true, the logged history will be pruned regularly as controlled by MaxHistoryLength and PrunedLength. - /// Default is true. - /// - public bool AllowPruning { get; set; } = true; + /// + /// If this is true, the logged history will be pruned regularly as controlled by MaxHistoryLength and PrunedLength. + /// Default is true. + /// + public bool AllowPruning { get; set; } = true; - /// - /// If AllowPruning is true, when the log reaches this length, it is pruned by deleting older entries until it is PrunedLength long. - /// Default is 10,000. - /// - public int MaxHistoryLength { get; set; } = 10000; + /// + /// If AllowPruning is true, when the log reaches this length, it is pruned by deleting older entries until it is PrunedLength long. + /// Default is 10,000. + /// + public int MaxHistoryLength { get; set; } = 10000; - /// - /// This is the length the list is pruned to everytime it reaches MaxHistoryLength. - /// Default is 5,000. - /// - public int PrunedLength { get; set; } = 5000; + /// + /// This is the length the list is pruned to everytime it reaches MaxHistoryLength. + /// Default is 5,000. + /// + public int PrunedLength { get; set; } = 5000; - public Logger() : this(new HashSet(Enum.GetValues(typeof(Severity)).Cast())) { } + public Logger() : this(new HashSet(Enum.GetValues(typeof(Severity)).Cast())) { } - public Logger(ICollection enabledSeverities) - { - Fatal = createWriterIfSeverityEnabled(enabledSeverities, Severity.Fatal); - Error = createWriterIfSeverityEnabled(enabledSeverities, Severity.Error); - Warning = createWriterIfSeverityEnabled(enabledSeverities, Severity.Warning); - Info = createWriterIfSeverityEnabled(enabledSeverities, Severity.Info); - Debug = createWriterIfSeverityEnabled(enabledSeverities, Severity.Debug); - Trace = createWriterIfSeverityEnabled(enabledSeverities, Severity.Trace); - - RecentEntries = lines.AsReadOnly(); - } + public Logger(ICollection enabledSeverities) + { + Fatal = createWriterIfSeverityEnabled(enabledSeverities, Severity.Fatal); + Error = createWriterIfSeverityEnabled(enabledSeverities, Severity.Error); + Warning = createWriterIfSeverityEnabled(enabledSeverities, Severity.Warning); + Info = createWriterIfSeverityEnabled(enabledSeverities, Severity.Info); + Debug = createWriterIfSeverityEnabled(enabledSeverities, Severity.Debug); + Trace = createWriterIfSeverityEnabled(enabledSeverities, Severity.Trace); + + RecentEntries = lines.AsReadOnly(); + } - private Writer? createWriterIfSeverityEnabled(ICollection enabledSeverities, Severity severity) - => enabledSeverities.Contains(severity) ? new Writer(this, severity) : null; + private Writer? createWriterIfSeverityEnabled(ICollection enabledSeverities, Severity severity) + => enabledSeverities.Contains(severity) ? new Writer(this, severity) : null; - #endregion + #endregion - #endregion + #endregion - #region log methods + #region log methods - private void addEntrySafe(Entry entry) + private void addEntrySafe(Entry entry) + { + lock (lines) { - lock (lines) - { - lines.Add(entry); + lines.Add(entry); - if (MirrorToConsole) - entry.WriteToConsole(); + if (MirrorToConsole) + entry.WriteToConsole(); - pruneIfNecessary(); - } + pruneIfNecessary(); } + } - private void pruneIfNecessary() - { - if (!AllowPruning) - return; - - if (lines.Count <= MaxHistoryLength) - return; + private void pruneIfNecessary() + { + if (!AllowPruning) + return; - var toRemove = (lines.Count - PrunedLength).Clamped(0, lines.Count); - lines.RemoveRange(0, toRemove); - } + if (lines.Count <= MaxHistoryLength) + return; - public void AddEntry(Entry entry) - { - addEntrySafe(entry); + var toRemove = (lines.Count - PrunedLength).Clamped(0, lines.Count); + lines.RemoveRange(0, toRemove); + } - // event is raised here, since if it was called inside lock, it could result in deadlock - if (RaiseEvents) - Logged?.Invoke(entry); - } + public void AddEntry(Entry entry) + { + addEntrySafe(entry); - #endregion + // event is raised here, since if it was called inside lock, it could result in deadlock + if (RaiseEvents) + Logged?.Invoke(entry); } + + #endregion } diff --git a/Bearded.Utilities/Input/Actions/DigitalAction.cs b/Bearded.Utilities/Input/Actions/DigitalAction.cs index 44444a0f..c74618f6 100644 --- a/Bearded.Utilities/Input/Actions/DigitalAction.cs +++ b/Bearded.Utilities/Input/Actions/DigitalAction.cs @@ -1,16 +1,15 @@ -namespace Bearded.Utilities.Input.Actions +namespace Bearded.Utilities.Input.Actions; + +abstract class DigitalAction : IAction { - abstract class DigitalAction : IAction - { - public abstract bool Hit { get; } - public abstract bool Active { get; } - public abstract bool Released { get; } + public abstract bool Hit { get; } + public abstract bool Active { get; } + public abstract bool Released { get; } - public bool IsAnalog => false; - public float AnalogAmount => Active ? 1 : 0; + public bool IsAnalog => false; + public float AnalogAmount => Active ? 1 : 0; - public override bool Equals(object? obj) => Equals(obj as IAction); - public bool Equals(IAction other) => other is DigitalAction && this.IsSameAs(other); - public override int GetHashCode() => ToString()?.GetHashCode() ?? 0; - } + public override bool Equals(object? obj) => Equals(obj as IAction); + public bool Equals(IAction other) => other is DigitalAction && this.IsSameAs(other); + public override int GetHashCode() => ToString()?.GetHashCode() ?? 0; } diff --git a/Bearded.Utilities/Input/Actions/DummyAction.cs b/Bearded.Utilities/Input/Actions/DummyAction.cs index e2a6a4f6..2f9142b8 100644 --- a/Bearded.Utilities/Input/Actions/DummyAction.cs +++ b/Bearded.Utilities/Input/Actions/DummyAction.cs @@ -1,24 +1,23 @@ -namespace Bearded.Utilities.Input.Actions +namespace Bearded.Utilities.Input.Actions; + +sealed class DummyAction : IAction { - sealed class DummyAction : IAction - { - private readonly string name; + private readonly string name; - public DummyAction(string name) - { - this.name = name; - } + public DummyAction(string name) + { + this.name = name; + } - public bool Hit => false; - public bool Active => false; - public bool Released => false; - public bool IsAnalog => false; - public float AnalogAmount => 0; + public bool Hit => false; + public bool Active => false; + public bool Released => false; + public bool IsAnalog => false; + public float AnalogAmount => 0; - public override string ToString() => name; + public override string ToString() => name; - public override bool Equals(object? obj) => Equals(obj as IAction); - public bool Equals(IAction other) => other is DummyAction && this.IsSameAs(other); - public override int GetHashCode() => ToString().GetHashCode(); - } + public override bool Equals(object? obj) => Equals(obj as IAction); + public bool Equals(IAction other) => other is DummyAction && this.IsSameAs(other); + public override int GetHashCode() => ToString().GetHashCode(); } diff --git a/Bearded.Utilities/Input/Actions/GamePadAction.cs b/Bearded.Utilities/Input/Actions/GamePadAction.cs index b66c2edb..e1135eb8 100644 --- a/Bearded.Utilities/Input/Actions/GamePadAction.cs +++ b/Bearded.Utilities/Input/Actions/GamePadAction.cs @@ -166,3 +166,4 @@ // } // } // } + diff --git a/Bearded.Utilities/Input/Actions/KeyboardAction.cs b/Bearded.Utilities/Input/Actions/KeyboardAction.cs index e5ddc355..da622ccd 100644 --- a/Bearded.Utilities/Input/Actions/KeyboardAction.cs +++ b/Bearded.Utilities/Input/Actions/KeyboardAction.cs @@ -1,22 +1,21 @@ using OpenTK.Windowing.GraphicsLibraryFramework; -namespace Bearded.Utilities.Input.Actions +namespace Bearded.Utilities.Input.Actions; + +sealed class KeyboardAction : DigitalAction { - sealed class KeyboardAction : DigitalAction - { - private readonly InputManager inputManager; - private readonly Keys key; + private readonly InputManager inputManager; + private readonly Keys key; - public KeyboardAction(InputManager inputManager, Keys key) - { - this.inputManager = inputManager; - this.key = key; - } + public KeyboardAction(InputManager inputManager, Keys key) + { + this.inputManager = inputManager; + this.key = key; + } - public override bool Hit => inputManager.IsKeyHit(key); - public override bool Active => inputManager.IsKeyPressed(key); - public override bool Released => inputManager.IsKeyReleased(key); + public override bool Hit => inputManager.IsKeyHit(key); + public override bool Active => inputManager.IsKeyPressed(key); + public override bool Released => inputManager.IsKeyReleased(key); - public override string ToString() => "keyboard:" + key; - } + public override string ToString() => "keyboard:" + key; } diff --git a/Bearded.Utilities/Input/Actions/LambdaAction.cs b/Bearded.Utilities/Input/Actions/LambdaAction.cs index bb3e164f..bd5c9fdf 100644 --- a/Bearded.Utilities/Input/Actions/LambdaAction.cs +++ b/Bearded.Utilities/Input/Actions/LambdaAction.cs @@ -2,33 +2,32 @@ using System.Collections.Generic; using System.Linq; -namespace Bearded.Utilities.Input.Actions +namespace Bearded.Utilities.Input.Actions; + +public static class DeferredAction { - public static class DeferredAction - { - public static IAction From(Func actionSelector) => new LambdaAction(actionSelector); + public static IAction From(Func actionSelector) => new LambdaAction(actionSelector); - public static IAction Any(IEnumerable> actionSelectors) => InputAction.AnyOf(actionSelectors.Select(From)); + public static IAction Any(IEnumerable> actionSelectors) => InputAction.AnyOf(actionSelectors.Select(From)); - public static IAction Any(params Func[] actionSelectors) => Any((IEnumerable>)actionSelectors); + public static IAction Any(params Func[] actionSelectors) => Any((IEnumerable>)actionSelectors); - private sealed class LambdaAction : IAction - { - private readonly Func actionSelector; - private IAction action => actionSelector(); + private sealed class LambdaAction : IAction + { + private readonly Func actionSelector; + private IAction action => actionSelector(); - public LambdaAction(Func actionSelector) - { - this.actionSelector = actionSelector; - } + public LambdaAction(Func actionSelector) + { + this.actionSelector = actionSelector; + } - public bool Hit => action.Hit; - public bool Active => action.Active; - public bool Released => action.Released; - public bool IsAnalog => action.IsAnalog; - public float AnalogAmount => action.AnalogAmount; + public bool Hit => action.Hit; + public bool Active => action.Active; + public bool Released => action.Released; + public bool IsAnalog => action.IsAnalog; + public float AnalogAmount => action.AnalogAmount; - public bool Equals(IAction other) => this.IsSameAs(other); - } + public bool Equals(IAction other) => this.IsSameAs(other); } } diff --git a/Bearded.Utilities/Input/Actions/MouseAction.cs b/Bearded.Utilities/Input/Actions/MouseAction.cs index d71fd18f..083d78a2 100644 --- a/Bearded.Utilities/Input/Actions/MouseAction.cs +++ b/Bearded.Utilities/Input/Actions/MouseAction.cs @@ -1,22 +1,21 @@ using OpenTK.Windowing.GraphicsLibraryFramework; -namespace Bearded.Utilities.Input.Actions +namespace Bearded.Utilities.Input.Actions; + +sealed class MouseButtonAction : DigitalAction { - sealed class MouseButtonAction : DigitalAction - { - private readonly InputManager inputManager; - private readonly MouseButton button; + private readonly InputManager inputManager; + private readonly MouseButton button; - public MouseButtonAction(InputManager inputManager, MouseButton button) - { - this.inputManager = inputManager; - this.button = button; - } + public MouseButtonAction(InputManager inputManager, MouseButton button) + { + this.inputManager = inputManager; + this.button = button; + } - public override bool Hit => inputManager.IsMouseButtonHit(button); - public override bool Active => inputManager.IsMouseButtonPressed(button); - public override bool Released => inputManager.IsMouseButtonReleased(button); + public override bool Hit => inputManager.IsMouseButtonHit(button); + public override bool Active => inputManager.IsMouseButtonPressed(button); + public override bool Released => inputManager.IsMouseButtonReleased(button); - public override string ToString() => "mouse:" + button; - } + public override string ToString() => "mouse:" + button; } diff --git a/Bearded.Utilities/Input/GamePadStateManager.cs b/Bearded.Utilities/Input/GamePadStateManager.cs index 34405c8a..191901ee 100644 --- a/Bearded.Utilities/Input/GamePadStateManager.cs +++ b/Bearded.Utilities/Input/GamePadStateManager.cs @@ -1,33 +1,32 @@ -namespace Bearded.Utilities.Input +namespace Bearded.Utilities.Input; + +public class GamePadStateManager { - public class GamePadStateManager - { - public void ProcessEventsAsync() {} - public void Update(bool windowIsActive) {} + public void ProcessEventsAsync() {} + public void Update(bool windowIsActive) {} - // public int Id { get; } - // - // public AsyncAtomicUpdating State { get; } = new AsyncAtomicUpdating(); - // - // public static GamePadStateManager ForId(int id) => new GamePadStateManager(id); - // - // private GamePadStateManager(int id) - // { - // Id = id; - // } - // - // public void ProcessEventsAsync() => State.SetLastKnownState(GamePad.GetState(Id)); - // - // public void Update(bool windowIsActive) - // { - // if (windowIsActive) - // { - // State.Update(); - // } - // else - // { - // State.UpdateToDefault(); - // } - // } - } + // public int Id { get; } + // + // public AsyncAtomicUpdating State { get; } = new AsyncAtomicUpdating(); + // + // public static GamePadStateManager ForId(int id) => new GamePadStateManager(id); + // + // private GamePadStateManager(int id) + // { + // Id = id; + // } + // + // public void ProcessEventsAsync() => State.SetLastKnownState(GamePad.GetState(Id)); + // + // public void Update(bool windowIsActive) + // { + // if (windowIsActive) + // { + // State.Update(); + // } + // else + // { + // State.UpdateToDefault(); + // } + // } } diff --git a/Bearded.Utilities/Input/IAction.cs b/Bearded.Utilities/Input/IAction.cs index afb2b849..69fdb3f7 100644 --- a/Bearded.Utilities/Input/IAction.cs +++ b/Bearded.Utilities/Input/IAction.cs @@ -1,14 +1,13 @@ using System; -namespace Bearded.Utilities.Input +namespace Bearded.Utilities.Input; + +public interface IAction : IEquatable { - public interface IAction : IEquatable - { - bool Hit { get; } - bool Active { get; } - bool Released { get; } + bool Hit { get; } + bool Active { get; } + bool Released { get; } - bool IsAnalog { get; } - float AnalogAmount { get; } - } + bool IsAnalog { get; } + float AnalogAmount { get; } } diff --git a/Bearded.Utilities/Input/InputAction.cs b/Bearded.Utilities/Input/InputAction.cs index 4380197b..a5d2d9e4 100644 --- a/Bearded.Utilities/Input/InputAction.cs +++ b/Bearded.Utilities/Input/InputAction.cs @@ -5,132 +5,131 @@ using Bearded.Utilities.Input.Actions; using Bearded.Utilities.Linq; -namespace Bearded.Utilities.Input +namespace Bearded.Utilities.Input; + +public static class InputAction { - public static class InputAction - { - public static bool IsSameAs(this IAction me, IAction other) - => other != null && (ReferenceEquals(me, other) || me.ToString() == other.ToString()); + public static bool IsSameAs(this IAction me, IAction other) + => other != null && (ReferenceEquals(me, other) || me.ToString() == other.ToString()); - public static IAction None { get; } = new DummyAction("unbound"); + public static IAction None { get; } = new DummyAction("unbound"); - public static IAction AnyOf(params IAction[] actions) => AnyOf((IEnumerable)actions); + public static IAction AnyOf(params IAction[] actions) => AnyOf((IEnumerable)actions); - public static IAction AnyOf(IEnumerable actions) - { - if (actions == null) - throw new ArgumentNullException(nameof(actions)); - - var actionList = new List(); - foreach (var action in actions) - { - switch (action) - { - case null: - break; - case OrAction asOr: - actionList.Add(asOr.Child1); - actionList.Add(asOr.Child2); - break; - case AnyAction asAny: - actionList.AddRange(asAny.Actions); - break; - default: - actionList.Add(action); - break; - } - } + public static IAction AnyOf(IEnumerable actions) + { + if (actions == null) + throw new ArgumentNullException(nameof(actions)); - switch (actionList.Count) + var actionList = new List(); + foreach (var action in actions) + { + switch (action) { - case 0: - return None; - case 1: - return actionList[0]; - case 2: - return new OrAction(actionList[0], actionList[1]); + case null: + break; + case OrAction asOr: + actionList.Add(asOr.Child1); + actionList.Add(asOr.Child2); + break; + case AnyAction asAny: + actionList.AddRange(asAny.Actions); + break; default: - return new AnyAction(actionList); + actionList.Add(action); + break; } } - public static IAction Or(this IAction me, IAction other) + switch (actionList.Count) { - if (me == null) - throw new ArgumentNullException(nameof(me)); - if (other == null) - throw new ArgumentNullException(nameof(other)); - - return new OrAction(me, other); + case 0: + return None; + case 1: + return actionList[0]; + case 2: + return new OrAction(actionList[0], actionList[1]); + default: + return new AnyAction(actionList); } + } - public static IAction Or(this IAction me, params IAction[] others) => me.Or((IEnumerable)others); - - public static IAction Or(this IAction me, IEnumerable others) - { - if (me == null) - throw new ArgumentNullException(nameof(me)); - if (others == null) - throw new ArgumentNullException(nameof(others)); - - // Call extension method explicitly since there is ambiguity between System.Linq.Enumerable.Prepend and - // Bearded.Utilities.Linq.Extensions.Prepend. (Introduced in .NET Framework 4.8 and .NET Standard 2.0.) - // ReSharper disable once InvokeAsExtensionMethod - return AnyOf(Extensions.Prepend(others, me)); - } + public static IAction Or(this IAction me, IAction other) + { + if (me == null) + throw new ArgumentNullException(nameof(me)); + if (other == null) + throw new ArgumentNullException(nameof(other)); - private abstract class BinaryAction : IAction - { - public IAction Child1 { get; } - public IAction Child2 { get; } + return new OrAction(me, other); + } - protected BinaryAction(IAction child1, IAction child2) - { - Child1 = child1; - Child2 = child2; - } + public static IAction Or(this IAction me, params IAction[] others) => me.Or((IEnumerable)others); - protected abstract bool BoolOp(bool one, bool other); - protected abstract float FloatOp(float one, float other); + public static IAction Or(this IAction me, IEnumerable others) + { + if (me == null) + throw new ArgumentNullException(nameof(me)); + if (others == null) + throw new ArgumentNullException(nameof(others)); + + // Call extension method explicitly since there is ambiguity between System.Linq.Enumerable.Prepend and + // Bearded.Utilities.Linq.Extensions.Prepend. (Introduced in .NET Framework 4.8 and .NET Standard 2.0.) + // ReSharper disable once InvokeAsExtensionMethod + return AnyOf(Extensions.Prepend(others, me)); + } - public bool Hit => BoolOp(Child1.Hit, Child2.Hit); - public bool Active => BoolOp(Child1.Active, Child2.Active); - public bool Released => BoolOp(Child1.Released, Child2.Released); - public bool IsAnalog => Child1.IsAnalog || Child2.IsAnalog; - public float AnalogAmount => FloatOp(Child1.AnalogAmount, Child2.AnalogAmount); + private abstract class BinaryAction : IAction + { + public IAction Child1 { get; } + public IAction Child2 { get; } - public bool Equals(IAction other) => this.IsSameAs(other); + protected BinaryAction(IAction child1, IAction child2) + { + Child1 = child1; + Child2 = child2; } - private class OrAction : BinaryAction - { - public OrAction(IAction child1, IAction child2) - : base(child1, child2) - { - } + protected abstract bool BoolOp(bool one, bool other); + protected abstract float FloatOp(float one, float other); - protected override bool BoolOp(bool one, bool other) => one || other; - protected override float FloatOp(float one, float other) => Math.Max(one, other); - } + public bool Hit => BoolOp(Child1.Hit, Child2.Hit); + public bool Active => BoolOp(Child1.Active, Child2.Active); + public bool Released => BoolOp(Child1.Released, Child2.Released); + public bool IsAnalog => Child1.IsAnalog || Child2.IsAnalog; + public float AnalogAmount => FloatOp(Child1.AnalogAmount, Child2.AnalogAmount); + + public bool Equals(IAction other) => this.IsSameAs(other); + } - private class AnyAction : IAction + private class OrAction : BinaryAction + { + public OrAction(IAction child1, IAction child2) + : base(child1, child2) { - private readonly ReadOnlyCollection actions; + } - public IEnumerable Actions => actions; + protected override bool BoolOp(bool one, bool other) => one || other; + protected override float FloatOp(float one, float other) => Math.Max(one, other); + } - public AnyAction(IEnumerable actions) - { - this.actions = actions.ToList().AsReadOnly(); - } + private class AnyAction : IAction + { + private readonly ReadOnlyCollection actions; - public bool Hit => actions.Any(a => a.Hit); - public bool Active => actions.Any(a => a.Active); - public bool Released => actions.Any(a => a.Released); - public bool IsAnalog => actions.Any(a => a.IsAnalog); - public float AnalogAmount => actions.Max(a => a.AnalogAmount); + public IEnumerable Actions => actions; - public bool Equals(IAction other) => this.IsSameAs(other); + public AnyAction(IEnumerable actions) + { + this.actions = actions.ToList().AsReadOnly(); } + + public bool Hit => actions.Any(a => a.Hit); + public bool Active => actions.Any(a => a.Active); + public bool Released => actions.Any(a => a.Released); + public bool IsAnalog => actions.Any(a => a.IsAnalog); + public float AnalogAmount => actions.Max(a => a.AnalogAmount); + + public bool Equals(IAction other) => this.IsSameAs(other); } } diff --git a/Bearded.Utilities/Input/InputManager.Actions.Gamepad.cs b/Bearded.Utilities/Input/InputManager.Actions.Gamepad.cs index 7f131aa4..aa7c7e21 100644 --- a/Bearded.Utilities/Input/InputManager.Actions.Gamepad.cs +++ b/Bearded.Utilities/Input/InputManager.Actions.Gamepad.cs @@ -268,3 +268,4 @@ // } // } // } + diff --git a/Bearded.Utilities/Input/InputManager.Actions.Keyboard.cs b/Bearded.Utilities/Input/InputManager.Actions.Keyboard.cs index 73d246a3..e9d4fb92 100644 --- a/Bearded.Utilities/Input/InputManager.Actions.Keyboard.cs +++ b/Bearded.Utilities/Input/InputManager.Actions.Keyboard.cs @@ -4,61 +4,60 @@ using Bearded.Utilities.Input.Actions; using OpenTK.Windowing.GraphicsLibraryFramework; -namespace Bearded.Utilities.Input +namespace Bearded.Utilities.Input; + +partial class InputManager { - partial class InputManager + public readonly partial struct ActionConstructor + { + public KeyboardActions Keyboard => new KeyboardActions(manager); + } + + public readonly struct KeyboardActions { - public readonly partial struct ActionConstructor + private readonly InputManager manager; + + public KeyboardActions(InputManager inputManager) { - public KeyboardActions Keyboard => new KeyboardActions(manager); + manager = inputManager; } - public readonly struct KeyboardActions + public IEnumerable All { - private readonly InputManager manager; - - public KeyboardActions(InputManager inputManager) + get { - manager = inputManager; - } - - public IEnumerable All - { - get - { - var m = manager; - return ((Keys[])Enum.GetValues(typeof(Keys))).Select(k => new KeyboardAction(m, k)); - } + var m = manager; + return ((Keys[])Enum.GetValues(typeof(Keys))).Select(k => new KeyboardAction(m, k)); } + } - public IAction FromKey(Keys key) => new KeyboardAction(manager, key); + public IAction FromKey(Keys key) => new KeyboardAction(manager, key); - public IAction FromString(string value) - => TryParse(value, out var action) - ? action - : throw new FormatException($"Keyboard key '{value}' invalid."); + public IAction FromString(string value) + => TryParse(value, out var action) + ? action + : throw new FormatException($"Keyboard key '{value}' invalid."); - public bool TryParse(string value, out IAction action) - => TryParseLowerTrimmedString(value.ToLowerInvariant().Trim(), out action); + public bool TryParse(string value, out IAction action) + => TryParseLowerTrimmedString(value.ToLowerInvariant().Trim(), out action); - internal bool TryParseLowerTrimmedString(string value, out IAction action) - { - action = null; + internal bool TryParseLowerTrimmedString(string value, out IAction action) + { + action = null; - if (!value.StartsWith("keyboard:")) - return false; + if (!value.StartsWith("keyboard:")) + return false; - var keyName = value.Substring(9).Trim(); + var keyName = value.Substring(9).Trim(); - if (!Enum.TryParse(keyName, false, out Keys key)) - return false; + if (!Enum.TryParse(keyName, false, out Keys key)) + return false; - if (key == Keys.Unknown) - return false; + if (key == Keys.Unknown) + return false; - action = new KeyboardAction(manager, key); - return true; - } + action = new KeyboardAction(manager, key); + return true; } } } diff --git a/Bearded.Utilities/Input/InputManager.Actions.Mouse.cs b/Bearded.Utilities/Input/InputManager.Actions.Mouse.cs index c80bf5e8..a1cbbb55 100644 --- a/Bearded.Utilities/Input/InputManager.Actions.Mouse.cs +++ b/Bearded.Utilities/Input/InputManager.Actions.Mouse.cs @@ -4,40 +4,39 @@ using Bearded.Utilities.Input.Actions; using OpenTK.Windowing.GraphicsLibraryFramework; -namespace Bearded.Utilities.Input +namespace Bearded.Utilities.Input; + +partial class InputManager { - partial class InputManager + public readonly partial struct ActionConstructor { - public readonly partial struct ActionConstructor - { - public MouseActions Mouse => new MouseActions(manager); - } + public MouseActions Mouse => new MouseActions(manager); + } - public readonly struct MouseActions - { - private readonly InputManager manager; + public readonly struct MouseActions + { + private readonly InputManager manager; - public MouseActions(InputManager inputManager) - { - manager = inputManager; - } + public MouseActions(InputManager inputManager) + { + manager = inputManager; + } - public IAction LeftButton => FromButton(MouseButton.Left); - public IAction RightButton => FromButton(MouseButton.Right); - public IAction MiddleButton => FromButton(MouseButton.Middle); + public IAction LeftButton => FromButton(MouseButton.Left); + public IAction RightButton => FromButton(MouseButton.Right); + public IAction MiddleButton => FromButton(MouseButton.Middle); - public IEnumerable All + public IEnumerable All + { + get { - get - { - var inputManager = manager; - return ((MouseButton[])Enum.GetValues(typeof(MouseButton))) - .Select(k => new MouseButtonAction(inputManager, k)); - } + var inputManager = manager; + return ((MouseButton[])Enum.GetValues(typeof(MouseButton))) + .Select(k => new MouseButtonAction(inputManager, k)); } + } - public IAction FromButton(MouseButton button) => new MouseButtonAction(manager, button); + public IAction FromButton(MouseButton button) => new MouseButtonAction(manager, button); - } } } diff --git a/Bearded.Utilities/Input/InputManager.Actions.cs b/Bearded.Utilities/Input/InputManager.Actions.cs index 64a4ca5a..44aca2f0 100644 --- a/Bearded.Utilities/Input/InputManager.Actions.cs +++ b/Bearded.Utilities/Input/InputManager.Actions.cs @@ -1,40 +1,39 @@ -namespace Bearded.Utilities.Input +namespace Bearded.Utilities.Input; + +partial class InputManager { - partial class InputManager + public ActionConstructor Actions => new ActionConstructor(this); + + public readonly partial struct ActionConstructor { - public ActionConstructor Actions => new ActionConstructor(this); + private readonly InputManager manager; - public readonly partial struct ActionConstructor + public ActionConstructor(InputManager inputManager) { - private readonly InputManager manager; - - public ActionConstructor(InputManager inputManager) - { - manager = inputManager; - } + manager = inputManager; + } - public IAction None => InputAction.None; + public IAction None => InputAction.None; - public IAction FromString(string value) - => fromLowerCaseTrimmedString(value.ToLowerInvariant().Trim()); + public IAction FromString(string value) + => fromLowerCaseTrimmedString(value.ToLowerInvariant().Trim()); - private IAction fromLowerCaseTrimmedString(string value) + private IAction fromLowerCaseTrimmedString(string value) + { + switch (value) { - switch (value) + case "none": + case "unbound": + return None; + default: { - case "none": - case "unbound": - return None; - default: - { - // if (Keyboard.TryParseLowerTrimmedString(value, out var action) - // || Gamepad.TryParseLowerTrimmedString(value, out action)) - // return action; - if (Keyboard.TryParseLowerTrimmedString(value, out var action)) - return action; + // if (Keyboard.TryParseLowerTrimmedString(value, out var action) + // || Gamepad.TryParseLowerTrimmedString(value, out action)) + // return action; + if (Keyboard.TryParseLowerTrimmedString(value, out var action)) + return action; - return None; - } + return None; } } } diff --git a/Bearded.Utilities/Input/InputManager.cs b/Bearded.Utilities/Input/InputManager.cs index f15aa825..bad4e792 100644 --- a/Bearded.Utilities/Input/InputManager.cs +++ b/Bearded.Utilities/Input/InputManager.cs @@ -6,103 +6,102 @@ using OpenTK.Windowing.Desktop; using OpenTK.Windowing.GraphicsLibraryFramework; -namespace Bearded.Utilities.Input +namespace Bearded.Utilities.Input; + +public sealed partial class InputManager { - public sealed partial class InputManager - { - private readonly KeyboardEvents keyboardEvents; + private readonly KeyboardEvents keyboardEvents; - private readonly KeyboardState keyboardState; - private readonly MouseState mouseState; - private readonly AsyncAtomicUpdating keyboardStateSnapshot; - private readonly AsyncAtomicUpdating mouseStateSnapshot; + private readonly KeyboardState keyboardState; + private readonly MouseState mouseState; + private readonly AsyncAtomicUpdating keyboardStateSnapshot; + private readonly AsyncAtomicUpdating mouseStateSnapshot; - public ReadOnlyCollection GamePads { get; } + public ReadOnlyCollection GamePads { get; } - public InputManager(NativeWindow nativeWindow) - { - keyboardState = nativeWindow.KeyboardState; - mouseState = nativeWindow.MouseState; + public InputManager(NativeWindow nativeWindow) + { + keyboardState = nativeWindow.KeyboardState; + mouseState = nativeWindow.MouseState; - keyboardStateSnapshot = new AsyncAtomicUpdating(keyboardState); - mouseStateSnapshot = new AsyncAtomicUpdating(mouseState); + keyboardStateSnapshot = new AsyncAtomicUpdating(keyboardState); + mouseStateSnapshot = new AsyncAtomicUpdating(mouseState); - keyboardEvents = new KeyboardEvents(nativeWindow); + keyboardEvents = new KeyboardEvents(nativeWindow); + + GamePads = Enumerable.Empty().ToList().AsReadOnly(); + // GamePads = Enumerable.Range(0, int.MaxValue - 1) + // .TakeWhile(i => GamePad.GetState(i).IsConnected) + // .Select(GamePadStateManager.ForId) + // .ToList().AsReadOnly(); + } + + public void ProcessEventsAsync() + { + keyboardStateSnapshot.SetLastKnownState(keyboardState.GetSnapshot()); + mouseStateSnapshot.SetLastKnownState(mouseState.GetSnapshot()); - GamePads = Enumerable.Empty().ToList().AsReadOnly(); - // GamePads = Enumerable.Range(0, int.MaxValue - 1) - // .TakeWhile(i => GamePad.GetState(i).IsConnected) - // .Select(GamePadStateManager.ForId) - // .ToList().AsReadOnly(); + foreach (var gamePad in GamePads) + { + gamePad.ProcessEventsAsync(); } + } - public void ProcessEventsAsync() + public void Update(bool windowIsActive) + { + if (windowIsActive) { - keyboardStateSnapshot.SetLastKnownState(keyboardState.GetSnapshot()); - mouseStateSnapshot.SetLastKnownState(mouseState.GetSnapshot()); + keyboardEvents.Update(); + keyboardStateSnapshot.Update(); + mouseStateSnapshot.Update(); - foreach (var gamePad in GamePads) - { - gamePad.ProcessEventsAsync(); - } + MousePosition = mouseStateSnapshot.Current.Position; + } + else + { + keyboardStateSnapshot.UpdateTo(null); } - public void Update(bool windowIsActive) + foreach (var gamePad in GamePads) { - if (windowIsActive) - { - keyboardEvents.Update(); - keyboardStateSnapshot.Update(); - mouseStateSnapshot.Update(); - - MousePosition = mouseStateSnapshot.Current.Position; - } - else - { - keyboardStateSnapshot.UpdateTo(null); - } - - foreach (var gamePad in GamePads) - { - gamePad.Update(windowIsActive); - } + gamePad.Update(windowIsActive); } + } - public Vector2 MousePosition { get; private set; } - public int DeltaScroll => MoreMath.RoundToInt(DeltaScrollF); - public float DeltaScrollF => mouseStateSnapshot.Current.Scroll.Y - mouseStateSnapshot.Previous.Scroll.Y; + public Vector2 MousePosition { get; private set; } + public int DeltaScroll => MoreMath.RoundToInt(DeltaScrollF); + public float DeltaScrollF => mouseStateSnapshot.Current.Scroll.Y - mouseStateSnapshot.Previous.Scroll.Y; - public bool MouseMoved => - mouseStateSnapshot.Current.Position != mouseStateSnapshot.Previous.Position; + public bool MouseMoved => + mouseStateSnapshot.Current.Position != mouseStateSnapshot.Previous.Position; - public bool LeftMousePressed => IsMouseButtonPressed(MouseButton.Left); - public bool LeftMouseHit => IsMouseButtonHit(MouseButton.Left); - public bool LeftMouseReleased => IsMouseButtonReleased(MouseButton.Left); + public bool LeftMousePressed => IsMouseButtonPressed(MouseButton.Left); + public bool LeftMouseHit => IsMouseButtonHit(MouseButton.Left); + public bool LeftMouseReleased => IsMouseButtonReleased(MouseButton.Left); - public bool RightMousePressed => IsMouseButtonPressed(MouseButton.Right); - public bool RightMouseHit => IsMouseButtonHit(MouseButton.Right); - public bool RightMouseReleased => IsMouseButtonReleased(MouseButton.Right); + public bool RightMousePressed => IsMouseButtonPressed(MouseButton.Right); + public bool RightMouseHit => IsMouseButtonHit(MouseButton.Right); + public bool RightMouseReleased => IsMouseButtonReleased(MouseButton.Right); - public bool MiddleMousePressed => IsMouseButtonPressed(MouseButton.Middle); - public bool MiddleMouseHit => IsMouseButtonHit(MouseButton.Middle); - public bool MiddleMouseReleased => IsMouseButtonReleased(MouseButton.Middle); + public bool MiddleMousePressed => IsMouseButtonPressed(MouseButton.Middle); + public bool MiddleMouseHit => IsMouseButtonHit(MouseButton.Middle); + public bool MiddleMouseReleased => IsMouseButtonReleased(MouseButton.Middle); - public bool IsMouseButtonPressed(MouseButton button) => mouseStateSnapshot.Current.IsButtonDown(button); - private bool wasMouseButtonPressed(MouseButton button) => mouseStateSnapshot.Previous.IsButtonDown(button); - public bool IsMouseButtonHit(MouseButton button) => - IsMouseButtonPressed(button) && !wasMouseButtonPressed(button); - public bool IsMouseButtonReleased(MouseButton button) => - !IsMouseButtonPressed(button) && wasMouseButtonPressed(button); + public bool IsMouseButtonPressed(MouseButton button) => mouseStateSnapshot.Current.IsButtonDown(button); + private bool wasMouseButtonPressed(MouseButton button) => mouseStateSnapshot.Previous.IsButtonDown(button); + public bool IsMouseButtonHit(MouseButton button) => + IsMouseButtonPressed(button) && !wasMouseButtonPressed(button); + public bool IsMouseButtonReleased(MouseButton button) => + !IsMouseButtonPressed(button) && wasMouseButtonPressed(button); - public bool IsKeyPressed(Keys k) => keyboardStateSnapshot.Current?.IsKeyDown(k) ?? false; - private bool wasKeyPressed(Keys k) => keyboardStateSnapshot.Previous?.IsKeyDown(k) ?? false; - public bool IsKeyHit(Keys k) => IsKeyPressed(k) && !wasKeyPressed(k); - public bool IsKeyReleased(Keys k) => !IsKeyPressed(k) && wasKeyPressed(k); + public bool IsKeyPressed(Keys k) => keyboardStateSnapshot.Current?.IsKeyDown(k) ?? false; + private bool wasKeyPressed(Keys k) => keyboardStateSnapshot.Previous?.IsKeyDown(k) ?? false; + public bool IsKeyHit(Keys k) => IsKeyPressed(k) && !wasKeyPressed(k); + public bool IsKeyReleased(Keys k) => !IsKeyPressed(k) && wasKeyPressed(k); - public bool IsMouseInRectangle(System.Drawing.Rectangle rect) => - rect.Contains((int) MousePosition.X, (int) MousePosition.Y); + public bool IsMouseInRectangle(System.Drawing.Rectangle rect) => + rect.Contains((int) MousePosition.X, (int) MousePosition.Y); - public IReadOnlyList<(KeyboardKeyEventArgs args, bool isPressed)> KeyEvents => keyboardEvents.KeyEvents; - public IReadOnlyList PressedCharacters => keyboardEvents.PressedCharacters; - } + public IReadOnlyList<(KeyboardKeyEventArgs args, bool isPressed)> KeyEvents => keyboardEvents.KeyEvents; + public IReadOnlyList PressedCharacters => keyboardEvents.PressedCharacters; } diff --git a/Bearded.Utilities/Input/KeyboardEvents.cs b/Bearded.Utilities/Input/KeyboardEvents.cs index ae794f36..b57595bf 100644 --- a/Bearded.Utilities/Input/KeyboardEvents.cs +++ b/Bearded.Utilities/Input/KeyboardEvents.cs @@ -3,57 +3,56 @@ using OpenTK.Windowing.Common; using OpenTK.Windowing.Desktop; -namespace Bearded.Utilities.Input +namespace Bearded.Utilities.Input; + +sealed class KeyboardEvents { - sealed class KeyboardEvents + private readonly ConcurrentQueue<(KeyboardKeyEventArgs, bool)> keyEventsQueue + = new ConcurrentQueue<(KeyboardKeyEventArgs, bool)>(); + private readonly ConcurrentQueue pressedCharactersQueue = new ConcurrentQueue(); + + private readonly List<(KeyboardKeyEventArgs, bool)> keyEvents = new List<(KeyboardKeyEventArgs, bool)>(); + private readonly List pressedCharacters = new List(); + + internal IReadOnlyList<(KeyboardKeyEventArgs, bool)> KeyEvents { get; } + internal IReadOnlyList PressedCharacters { get; } + + internal KeyboardEvents(NativeWindow nativeWindow) + { + nativeWindow.KeyDown += onKeyDown; + nativeWindow.KeyUp += onKeyUp; + nativeWindow.TextInput += onTextInput; + + KeyEvents = keyEvents.AsReadOnly(); + PressedCharacters = pressedCharacters.AsReadOnly(); + } + + private void onKeyDown(KeyboardKeyEventArgs e) + { + keyEventsQueue.Enqueue((e, true)); + } + + private void onKeyUp(KeyboardKeyEventArgs e) + { + keyEventsQueue.Enqueue((e, false)); + } + + private void onTextInput(TextInputEventArgs e) + { + // FIXME: unicode may be represented by a string of more than one character + pressedCharactersQueue.Enqueue((char) e.Unicode); + } + + internal void Update() + { + updateCollectionFromConcurrentQueue(keyEvents, keyEventsQueue); + updateCollectionFromConcurrentQueue(pressedCharacters, pressedCharactersQueue); + } + + private static void updateCollectionFromConcurrentQueue(ICollection list, ConcurrentQueue queue) { - private readonly ConcurrentQueue<(KeyboardKeyEventArgs, bool)> keyEventsQueue - = new ConcurrentQueue<(KeyboardKeyEventArgs, bool)>(); - private readonly ConcurrentQueue pressedCharactersQueue = new ConcurrentQueue(); - - private readonly List<(KeyboardKeyEventArgs, bool)> keyEvents = new List<(KeyboardKeyEventArgs, bool)>(); - private readonly List pressedCharacters = new List(); - - internal IReadOnlyList<(KeyboardKeyEventArgs, bool)> KeyEvents { get; } - internal IReadOnlyList PressedCharacters { get; } - - internal KeyboardEvents(NativeWindow nativeWindow) - { - nativeWindow.KeyDown += onKeyDown; - nativeWindow.KeyUp += onKeyUp; - nativeWindow.TextInput += onTextInput; - - KeyEvents = keyEvents.AsReadOnly(); - PressedCharacters = pressedCharacters.AsReadOnly(); - } - - private void onKeyDown(KeyboardKeyEventArgs e) - { - keyEventsQueue.Enqueue((e, true)); - } - - private void onKeyUp(KeyboardKeyEventArgs e) - { - keyEventsQueue.Enqueue((e, false)); - } - - private void onTextInput(TextInputEventArgs e) - { - // FIXME: unicode may be represented by a string of more than one character - pressedCharactersQueue.Enqueue((char) e.Unicode); - } - - internal void Update() - { - updateCollectionFromConcurrentQueue(keyEvents, keyEventsQueue); - updateCollectionFromConcurrentQueue(pressedCharacters, pressedCharactersQueue); - } - - private static void updateCollectionFromConcurrentQueue(ICollection list, ConcurrentQueue queue) - { - list.Clear(); - while (queue.TryDequeue(out var result)) - list.Add(result); - } + list.Clear(); + while (queue.TryDequeue(out var result)) + list.Add(result); } } diff --git a/Bearded.Utilities/Linq/Extensions.cs b/Bearded.Utilities/Linq/Extensions.cs index cbd0b477..e00cd088 100644 --- a/Bearded.Utilities/Linq/Extensions.cs +++ b/Bearded.Utilities/Linq/Extensions.cs @@ -2,360 +2,359 @@ using System.Collections.Generic; using System.Linq; -namespace Bearded.Utilities.Linq +namespace Bearded.Utilities.Linq; + +/// +/// Contains useful Linq style and related extension methods. +/// +public static class Extensions { + #region Simple + /// - /// Contains useful Linq style and related extension methods. + /// Returns a sequence containing a single element. /// - public static class Extensions + /// The object to yield. + public static IEnumerable Yield(this T obj) { - #region Simple + yield return obj; + } - /// - /// Returns a sequence containing a single element. - /// - /// The object to yield. - public static IEnumerable Yield(this T obj) - { - yield return obj; - } + /// + /// Appends an item to the end of a sequence. + /// + /// The original sequence. + /// The item to append. + [Obsolete("Use Append of the System.Linq library instead.")] + public static IEnumerable Append(this IEnumerable target, T item) + { + foreach (var t in target) yield return t; + yield return item; + } + + /// + /// Prepends an item to the beginning of a sequence. + /// + /// The original sequence. + /// The item to prepend. + [Obsolete("Use Prepend of the System.Linq library instead.")] + public static IEnumerable Prepend(this IEnumerable target, T item) + { + yield return item; + foreach (var t in target) yield return t; + } + + /// + /// Filters a sequence, removing all elements that are null. + /// + public static IEnumerable NotNull(this IEnumerable source) + where T : class + { + return source.Where(arg => arg != null); + } + + #endregion + + #region Aggregates + + /// + /// Gets the element with minimum key in a sequence, as determined by a given selector function. + /// + /// The sequence. + /// The function selecting the key. + public static TSource MinBy(this IEnumerable source, Func selector) + where TKey : IComparable + { + return source.Aggregate((a, b) => selector(a).CompareTo(selector(b)) < 0 ? a : b); + } + + /// + /// Gets the element with maximum key in a sequence, as determined by a given selector function. + /// + /// The sequence. + /// The function selecting the key. + public static TSource MaxBy(this IEnumerable source, Func selector) + where TKey : IComparable + { + return source.Aggregate((a, b) => selector(a).CompareTo(selector(b)) > 0 ? a : b); + } + + #endregion - /// - /// Appends an item to the end of a sequence. - /// - /// The original sequence. - /// The item to append. - [Obsolete("Use Append of the System.Linq library instead.")] - public static IEnumerable Append(this IEnumerable target, T item) + #region List + + /// + /// Inserts an item into an already sorted list, maintaining the sort order and using the default comparer of the type. + /// If the list already contains elements of the same sort value, the method inserts the new item at an any of the valid indices, not necessarily the first or last. + /// If the given list is not sorted, the item will be inserted at an arbitrary index. + /// This method is an O(log n) operation, where n is the number of elements in the list. + /// + /// The list to insert the item into. + /// The item to be inserted. + /// The index at which the item was inserted. + /// List is null. + public static int AddSorted( this List list, T item) + where T : IComparable + { + return list.AddSorted(item, Comparer.Default); + } + + + /// + /// Inserts an item into an already sorted list, maintaining the sort order. + /// If the list already contains elements of the same sort value, the method inserts the new item at an any of the valid indices, not necessarily the first or last. + /// If the given list is not sorted, the item will be inserted at an arbitrary index. + /// This method is an O(log n) operation, where n is the number of elements in the list. + /// + /// The list to insert the item into. + /// The item to be inserted. + /// Compared to use when determining item sort order. + /// The index at which the item was inserted. + /// List or comparer is null. + public static int AddSorted(this List list, T item, IComparer comparer) + { + if (list == null) + throw new ArgumentNullException(nameof(list)); + if (comparer == null) + throw new ArgumentNullException(nameof(comparer)); + + if (list.Count == 0 || comparer.Compare(list[0], item) >= 0) { - foreach (var t in target) yield return t; - yield return item; + list.Insert(0, item); + return 0; } - /// - /// Prepends an item to the beginning of a sequence. - /// - /// The original sequence. - /// The item to prepend. - [Obsolete("Use Prepend of the System.Linq library instead.")] - public static IEnumerable Prepend(this IEnumerable target, T item) + var index = list.Count - 1; + if (comparer.Compare(list[index], item) <= 0) { - yield return item; - foreach (var t in target) yield return t; + list.Add(item); } - - /// - /// Filters a sequence, removing all elements that are null. - /// - public static IEnumerable NotNull(this IEnumerable source) - where T : class + else { - return source.Where(arg => arg != null); + index = list.BinarySearch(item, comparer); + if (index < 0) + index = ~index; + + list.Insert(index, item); } + return index; + } - #endregion + #endregion - #region Aggregates + #region Dictionary - /// - /// Gets the element with minimum key in a sequence, as determined by a given selector function. - /// - /// The sequence. - /// The function selecting the key. - public static TSource MinBy(this IEnumerable source, Func selector) - where TKey : IComparable + /// + /// Tries getting a value from a dictionary. If this succeeds further passes that value through a given function. + /// + /// The dictionary. + /// The key to look up. + /// The resulting transformed value. + /// The function transforming the value into the result. + /// True if the value was found, false otherwise. + public static bool TryGetTransformedValue(this Dictionary dictionary, TKey key, out TNewValue result, + Func transform) + { + if (dictionary.TryGetValue(key, out var value)) { - return source.Aggregate((a, b) => selector(a).CompareTo(selector(b)) < 0 ? a : b); + result = transform(value); + return true; } - /// - /// Gets the element with maximum key in a sequence, as determined by a given selector function. - /// - /// The sequence. - /// The function selecting the key. - public static TSource MaxBy(this IEnumerable source, Func selector) - where TKey : IComparable - { - return source.Aggregate((a, b) => selector(a).CompareTo(selector(b)) > 0 ? a : b); - } + result = default; + return false; + } - #endregion - - #region List - - /// - /// Inserts an item into an already sorted list, maintaining the sort order and using the default comparer of the type. - /// If the list already contains elements of the same sort value, the method inserts the new item at an any of the valid indices, not necessarily the first or last. - /// If the given list is not sorted, the item will be inserted at an arbitrary index. - /// This method is an O(log n) operation, where n is the number of elements in the list. - /// - /// The list to insert the item into. - /// The item to be inserted. - /// The index at which the item was inserted. - /// List is null. - public static int AddSorted( this List list, T item) - where T : IComparable + /// + /// Adds a sequence of key value paris to a dictionary. + /// + /// The dictionary. + /// KeyValuePairs to add. + public static void AddRange(this IDictionary dictionary, + IEnumerable> other) + { + foreach (var pair in other) { - return list.AddSorted(item, Comparer.Default); + dictionary.Add(pair); } + } + public static TValue? ValueOrNull(this Dictionary dict, TKey key) + where TValue : class + { + dict.TryGetValue(key, out var value); + return value; + } - /// - /// Inserts an item into an already sorted list, maintaining the sort order. - /// If the list already contains elements of the same sort value, the method inserts the new item at an any of the valid indices, not necessarily the first or last. - /// If the given list is not sorted, the item will be inserted at an arbitrary index. - /// This method is an O(log n) operation, where n is the number of elements in the list. - /// - /// The list to insert the item into. - /// The item to be inserted. - /// Compared to use when determining item sort order. - /// The index at which the item was inserted. - /// List or comparer is null. - public static int AddSorted(this List list, T item, IComparer comparer) - { - if (list == null) - throw new ArgumentNullException(nameof(list)); - if (comparer == null) - throw new ArgumentNullException(nameof(comparer)); - - if (list.Count == 0 || comparer.Compare(list[0], item) >= 0) - { - list.Insert(0, item); - return 0; - } + public static TValue ValueOrDefault(this Dictionary dict, TKey key) + where TValue : struct + { + dict.TryGetValue(key, out var value); + return value; + } - var index = list.Count - 1; - if (comparer.Compare(list[index], item) <= 0) - { - list.Add(item); - } - else - { - index = list.BinarySearch(item, comparer); - if (index < 0) - index = ~index; + #endregion - list.Insert(index, item); - } - return index; - } + #region Random - #endregion + /// + /// Selects a random element from a sequence. + /// + /// Type of the elements. + /// The sequance to choose a random element from. + /// A random element from the input. + public static T RandomElement(this IEnumerable source) + => source.RandomElement(StaticRandom.Random); - #region Dictionary + /// + /// Selects a random element from a sequence. + /// + /// Type of the elements. + /// The sequance to choose a random element from. + /// An optional random object to be used. If none is given, StaticRandom is used instead. + /// A random element from the input. + public static T RandomElement(this IEnumerable source, Random random) + { + if (source == null) + throw new ArgumentNullException(nameof(source)); + if (random == null) + throw new ArgumentNullException(nameof(random)); - /// - /// Tries getting a value from a dictionary. If this succeeds further passes that value through a given function. - /// - /// The dictionary. - /// The key to look up. - /// The resulting transformed value. - /// The function transforming the value into the result. - /// True if the value was found, false otherwise. - public static bool TryGetTransformedValue(this Dictionary dictionary, TKey key, out TNewValue result, - Func transform) + // optimisation for collections + if (source is ICollection collection) { - if (dictionary.TryGetValue(key, out var value)) - { - result = transform(value); - return true; - } - - result = default; - return false; + if (collection.Count == 0) + throw new InvalidOperationException("Sequence was empty."); + return collection.ElementAt(random.Next(collection.Count)); } - /// - /// Adds a sequence of key value paris to a dictionary. - /// - /// The dictionary. - /// KeyValuePairs to add. - public static void AddRange(this IDictionary dictionary, - IEnumerable> other) + var current = default(T); + var count = 0; + foreach (var element in source) { - foreach (var pair in other) + count++; + // this condition is guaranteed to be true on the first iteration + if (random.Next(count) == 0) { - dictionary.Add(pair); + current = element; } } - - public static TValue? ValueOrNull(this Dictionary dict, TKey key) - where TValue : class - { - dict.TryGetValue(key, out var value); - return value; - } - - public static TValue ValueOrDefault(this Dictionary dict, TKey key) - where TValue : struct + if (count == 0) { - dict.TryGetValue(key, out var value); - return value; + throw new InvalidOperationException("Sequence was empty."); } + // because of the count check, we are guaranteed to have assigned a value from the collection here + return current!; + } - #endregion - - #region Random - - /// - /// Selects a random element from a sequence. - /// - /// Type of the elements. - /// The sequance to choose a random element from. - /// A random element from the input. - public static T RandomElement(this IEnumerable source) - => source.RandomElement(StaticRandom.Random); - - /// - /// Selects a random element from a sequence. - /// - /// Type of the elements. - /// The sequance to choose a random element from. - /// An optional random object to be used. If none is given, StaticRandom is used instead. - /// A random element from the input. - public static T RandomElement(this IEnumerable source, Random random) - { - if (source == null) - throw new ArgumentNullException(nameof(source)); - if (random == null) - throw new ArgumentNullException(nameof(random)); - - // optimisation for collections - if (source is ICollection collection) - { - if (collection.Count == 0) - throw new InvalidOperationException("Sequence was empty."); - return collection.ElementAt(random.Next(collection.Count)); - } - - var current = default(T); - var count = 0; - foreach (var element in source) - { - count++; - // this condition is guaranteed to be true on the first iteration - if (random.Next(count) == 0) - { - current = element; - } - } - if (count == 0) - { - throw new InvalidOperationException("Sequence was empty."); - } - // because of the count check, we are guaranteed to have assigned a value from the collection here - return current!; - } + /// + /// Efficiently (O(n) with n the size of the input) selects a random number of elements from an enumerable. + /// Each element has an equal chance to be contained in the result. The order of the output is arbitrary. + /// + /// Type of the elements. + /// The enumerable that random elements are selected from. + /// The number of random elements to return. If this is smaller than the inputs size, the entire input is returned. + /// Random elements from the input. + public static List RandomSubset(this IEnumerable source, int count) + => source.RandomSubset(count, StaticRandom.Random); - /// - /// Efficiently (O(n) with n the size of the input) selects a random number of elements from an enumerable. - /// Each element has an equal chance to be contained in the result. The order of the output is arbitrary. - /// - /// Type of the elements. - /// The enumerable that random elements are selected from. - /// The number of random elements to return. If this is smaller than the inputs size, the entire input is returned. - /// Random elements from the input. - public static List RandomSubset(this IEnumerable source, int count) - => source.RandomSubset(count, StaticRandom.Random); - - /// - /// Efficiently (O(n) with n the size of the input) selects a random number of elements from an enumerable. - /// Each element has an equal chance to be contained in the result. The order of the output is arbitrary. - /// - /// Type of the elements. - /// The enumerable that random elements are selected from. - /// The number of random elements to return. If this is smaller than the inputs size, the entire input is returned. - /// The random object to be used. - /// Random elements from the input. - public static List RandomSubset(this IEnumerable source, int count, Random random) - { - if (source == null) - throw new ArgumentNullException(nameof(source)); - if (random == null) - throw new ArgumentNullException(nameof(random)); + /// + /// Efficiently (O(n) with n the size of the input) selects a random number of elements from an enumerable. + /// Each element has an equal chance to be contained in the result. The order of the output is arbitrary. + /// + /// Type of the elements. + /// The enumerable that random elements are selected from. + /// The number of random elements to return. If this is smaller than the inputs size, the entire input is returned. + /// The random object to be used. + /// Random elements from the input. + public static List RandomSubset(this IEnumerable source, int count, Random random) + { + if (source == null) + throw new ArgumentNullException(nameof(source)); + if (random == null) + throw new ArgumentNullException(nameof(random)); - if (count <= 0) - return new List(); + if (count <= 0) + return new List(); - // optimisation for collections - if (source is ICollection collection && count >= collection.Count) - // if we take as much or more than we have, return input - return source.ToList(); + // optimisation for collections + if (source is ICollection collection && count >= collection.Count) + // if we take as much or more than we have, return input + return source.ToList(); - var set = new List(count); + var set = new List(count); - var i = 0; - foreach (var item in source) + var i = 0; + foreach (var item in source) + { + // copy first 'count' elements + if (i < count) + set.Add(item); + else { - // copy first 'count' elements - if (i < count) - set.Add(item); - else - { - // add others with decreasing likelyhood - var index = random.Next(i + 1); - if (index < count) - set[index] = item; - } - i++; + // add others with decreasing likelyhood + var index = random.Next(i + 1); + if (index < count) + set[index] = item; } - - return set; + i++; } - #endregion - - #region Shuffle - - /// - /// Shuffles the list. This is a linear operation in the length of the list. - /// - public static void Shuffle(this IList list) - => list.Shuffle(StaticRandom.Random); + return set; + } - /// - /// Shuffles the list. This is a linear operation in the length of the list. - /// - public static void Shuffle(this IList list, Random random) - { - if (list == null) - throw new ArgumentNullException(nameof(list)); - if (random == null) - throw new ArgumentNullException(nameof(random)); + #endregion - var c = list.Count; - for (var i = 0; i < c; i++) - { - var j = random.Next(i, c); + #region Shuffle - var temp = list[i]; - list[i] = list[j]; - list[j] = temp; - } - } + /// + /// Shuffles the list. This is a linear operation in the length of the list. + /// + public static void Shuffle(this IList list) + => list.Shuffle(StaticRandom.Random); - /// - /// Returns a new shuffled list with the elements from the given sequence. - /// - public static IList Shuffled(this IEnumerable source) - => source.Shuffled(StaticRandom.Random); + /// + /// Shuffles the list. This is a linear operation in the length of the list. + /// + public static void Shuffle(this IList list, Random random) + { + if (list == null) + throw new ArgumentNullException(nameof(list)); + if (random == null) + throw new ArgumentNullException(nameof(random)); - /// - /// Returns a new shuffled list with the elements from the given sequence. - /// - public static IList Shuffled(this IEnumerable source, Random random) + var c = list.Count; + for (var i = 0; i < c; i++) { - if (source == null) - throw new ArgumentNullException(nameof(source)); - if (random == null) - throw new ArgumentNullException(nameof(random)); - - var list = source.ToList(); - list.Shuffle(random); - return list; + var j = random.Next(i, c); + + var temp = list[i]; + list[i] = list[j]; + list[j] = temp; } + } - #endregion + /// + /// Returns a new shuffled list with the elements from the given sequence. + /// + public static IList Shuffled(this IEnumerable source) + => source.Shuffled(StaticRandom.Random); + /// + /// Returns a new shuffled list with the elements from the given sequence. + /// + public static IList Shuffled(this IEnumerable source, Random random) + { + if (source == null) + throw new ArgumentNullException(nameof(source)); + if (random == null) + throw new ArgumentNullException(nameof(random)); + + var list = source.ToList(); + list.Shuffle(random); + return list; } + + #endregion + } diff --git a/Bearded.Utilities/Monads/Result.cs b/Bearded.Utilities/Monads/Result.cs index 16ffe599..99151153 100644 --- a/Bearded.Utilities/Monads/Result.cs +++ b/Bearded.Utilities/Monads/Result.cs @@ -3,131 +3,130 @@ using System.Diagnostics.CodeAnalysis; using System.Runtime.CompilerServices; -namespace Bearded.Utilities.Monads +namespace Bearded.Utilities.Monads; + +public readonly struct Result : IEquatable> { - public readonly struct Result : IEquatable> - { - private readonly bool isSuccess; - [AllowNull] - private readonly TResult result; - [AllowNull] - private readonly TError error; + private readonly bool isSuccess; + [AllowNull] + private readonly TResult result; + [AllowNull] + private readonly TError error; - private Result(bool isSuccess, [AllowNull] TResult result, [AllowNull] TError error) - { - this.isSuccess = isSuccess; - this.result = result; - this.error = error; - } + private Result(bool isSuccess, [AllowNull] TResult result, [AllowNull] TError error) + { + this.isSuccess = isSuccess; + this.result = result; + this.error = error; + } - public static Result Success(TResult result) => - new Result(true, result, default); + public static Result Success(TResult result) => + new Result(true, result, default); - public static Result Failure(TError error) => - new Result(false, default, error); + public static Result Failure(TError error) => + new Result(false, default, error); - public TResult ResultOrDefault(TResult @default) => isSuccess ? result : @default; + public TResult ResultOrDefault(TResult @default) => isSuccess ? result : @default; - public TResult ResultOrDefault(Func defaultProvider) => isSuccess ? result : defaultProvider(); + public TResult ResultOrDefault(Func defaultProvider) => isSuccess ? result : defaultProvider(); - public TResult ResultOrThrow(Func exceptionProvider) => - isSuccess ? result : throw exceptionProvider(error); + public TResult ResultOrThrow(Func exceptionProvider) => + isSuccess ? result : throw exceptionProvider(error); - public Maybe AsMaybe() => isSuccess ? Maybe.Just(result) : Maybe.Nothing; + public Maybe AsMaybe() => isSuccess ? Maybe.Just(result) : Maybe.Nothing; - public Result Select(Func selector) => - isSuccess ? (Result) Result.Success(selector(result)) : Result.Failure(error); + public Result Select(Func selector) => + isSuccess ? (Result) Result.Success(selector(result)) : Result.Failure(error); - public Result SelectMany(Func> selector) => - isSuccess ? selector(result) : Result.Failure(error); + public Result SelectMany(Func> selector) => + isSuccess ? selector(result) : Result.Failure(error); - public void Match(Action onSuccess) + public void Match(Action onSuccess) + { + if (isSuccess) { - if (isSuccess) - { - onSuccess(result); - } + onSuccess(result); } + } - public void Match(Action onSuccess, FailureResultCallback onFailure) + public void Match(Action onSuccess, FailureResultCallback onFailure) + { + if (isSuccess) { - if (isSuccess) - { - onSuccess(result); - } - else - { - onFailure(error); - } + onSuccess(result); } + else + { + onFailure(error); + } + } - public TOut Match(Func onSuccess, FailureResultTransformation onFailure) => - isSuccess ? onSuccess(result) : onFailure(error); + public TOut Match(Func onSuccess, FailureResultTransformation onFailure) => + isSuccess ? onSuccess(result) : onFailure(error); - public bool Equals(Result other) => - isSuccess == other.isSuccess - && EqualityComparer.Default.Equals(result, other.result) - && EqualityComparer.Default.Equals(error, other.error); + public bool Equals(Result other) => + isSuccess == other.isSuccess + && EqualityComparer.Default.Equals(result, other.result) + && EqualityComparer.Default.Equals(error, other.error); - public override bool Equals(object? obj) => obj is Result other && Equals(other); + public override bool Equals(object? obj) => obj is Result other && Equals(other); - public static bool operator ==(Result left, Result right) => - left.Equals(right); + public static bool operator ==(Result left, Result right) => + left.Equals(right); - public static bool operator !=(Result left, Result right) => - !left.Equals(right); + public static bool operator !=(Result left, Result right) => + !left.Equals(right); - public override int GetHashCode() => HashCode.Combine(isSuccess, result, error); + public override int GetHashCode() => HashCode.Combine(isSuccess, result, error); - public override string ToString() => isSuccess ? $"success {result}" : $"error {error}"; + public override string ToString() => isSuccess ? $"success {result}" : $"error {error}"; - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static implicit operator Result(Success success) => Success(success.Result); + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static implicit operator Result(Success success) => Success(success.Result); - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static implicit operator Result(Failure failure) => Failure(failure.Error); - } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static implicit operator Result(Failure failure) => Failure(failure.Error); +} - public static class Result - { - public static Result Success(TResult result) => - Result.Success(result); +public static class Result +{ + public static Result Success(TResult result) => + Result.Success(result); - public static Success Success(T result) => new Success(result); + public static Success Success(T result) => new Success(result); - public static Result Failure(TError error) => - Result.Failure(error); + public static Result Failure(TError error) => + Result.Failure(error); - public static Failure Failure(T error) => new Failure(error); + public static Failure Failure(T error) => new Failure(error); - public static TResult ResultOrThrow(this Result result) - where TError : Exception - { - return result.ResultOrThrow(e => e); - } + public static TResult ResultOrThrow(this Result result) + where TError : Exception + { + return result.ResultOrThrow(e => e); } +} - public readonly struct Success - { - public T Result { get; } +public readonly struct Success +{ + public T Result { get; } - internal Success(T result) - { - Result = result; - } + internal Success(T result) + { + Result = result; } +} - public readonly struct Failure - { - public T Error { get; } +public readonly struct Failure +{ + public T Error { get; } - internal Failure(T error) - { - Error = error; - } + internal Failure(T error) + { + Error = error; } +} - public delegate void FailureResultCallback([MaybeNull] TError error); +public delegate void FailureResultCallback([MaybeNull] TError error); - public delegate TOut FailureResultTransformation([MaybeNull] TError error); -} +public delegate TOut FailureResultTransformation([MaybeNull] TError error); \ No newline at end of file diff --git a/Bearded.Utilities/Noise/IProceduralTexture.cs b/Bearded.Utilities/Noise/IProceduralTexture.cs index f4abb984..17c3e57d 100644 --- a/Bearded.Utilities/Noise/IProceduralTexture.cs +++ b/Bearded.Utilities/Noise/IProceduralTexture.cs @@ -1,55 +1,54 @@ using OpenTK.Mathematics; -namespace Bearded.Utilities.Noise +namespace Bearded.Utilities.Noise; + +public interface IProceduralTexture { - public interface IProceduralTexture - { - /// - /// Returns the value of the noise map at the given coordinates in the noise map. - /// This method only works for values within the [0, 1) x [0, 1) range only (upper bounds exclusive). - /// - /// - /// - /// - public double ValueAt(double x, double y); + /// + /// Returns the value of the noise map at the given coordinates in the noise map. + /// This method only works for values within the [0, 1) x [0, 1) range only (upper bounds exclusive). + /// + /// + /// + /// + public double ValueAt(double x, double y); - /// - /// Returns the value of the noise map at the given coordinates in the noise map. - /// This method only works for values within the [0, 1) x [0, 1) range only (upper bounds exclusive). - /// - /// - /// - public double ValueAt(Vector2d xy) - { - var (x, y) = xy; - return ValueAt(x, y); - } + /// + /// Returns the value of the noise map at the given coordinates in the noise map. + /// This method only works for values within the [0, 1) x [0, 1) range only (upper bounds exclusive). + /// + /// + /// + public double ValueAt(Vector2d xy) + { + var (x, y) = xy; + return ValueAt(x, y); + } - /// - /// Transforms the noise map to a 2D array by dividing the entire noise map in a grid of width by height tiles. - /// We then sample the center of each of the grid tiles to get the discrete value. - /// - /// The width of the resulting array. That is, its size in the first dimension. - /// The height of the resulting array. That is, its size in the second dimension. - /// A 2D array with the given width and height with evaluated values based on the noise map. - public double[,] ToArray(int width, int height) - { - var tileWidth = 1.0 / width; - var tileHeight = 1.0 / height; - var halfTileWidth = 0.5 * tileWidth; - var halfTileHeight = 0.5 * tileHeight; + /// + /// Transforms the noise map to a 2D array by dividing the entire noise map in a grid of width by height tiles. + /// We then sample the center of each of the grid tiles to get the discrete value. + /// + /// The width of the resulting array. That is, its size in the first dimension. + /// The height of the resulting array. That is, its size in the second dimension. + /// A 2D array with the given width and height with evaluated values based on the noise map. + public double[,] ToArray(int width, int height) + { + var tileWidth = 1.0 / width; + var tileHeight = 1.0 / height; + var halfTileWidth = 0.5 * tileWidth; + var halfTileHeight = 0.5 * tileHeight; - var result = new double[width, height]; + var result = new double[width, height]; - for (var y = 0; y < height; y++) + for (var y = 0; y < height; y++) + { + for (var x = 0; x < width; x++) { - for (var x = 0; x < width; x++) - { - result[x, y] = ValueAt(x * tileWidth + halfTileWidth, y * tileHeight + halfTileHeight); - } + result[x, y] = ValueAt(x * tileWidth + halfTileWidth, y * tileHeight + halfTileHeight); } - - return result; } + + return result; } } diff --git a/Bearded.Utilities/Noise/NoiseUtils.cs b/Bearded.Utilities/Noise/NoiseUtils.cs index b42dd91b..8a24cad6 100644 --- a/Bearded.Utilities/Noise/NoiseUtils.cs +++ b/Bearded.Utilities/Noise/NoiseUtils.cs @@ -1,24 +1,23 @@ using System; -namespace Bearded.Utilities.Noise +namespace Bearded.Utilities.Noise; + +static class NoiseUtils { - static class NoiseUtils + internal static T[,] GenerateRandomArray(int width, int height, Func generator, Random? random) { - internal static T[,] GenerateRandomArray(int width, int height, Func generator, Random? random) - { - random ??= new Random(); + random ??= new Random(); - var array = new T[width, height]; + var array = new T[width, height]; - for (var y = 0; y < height; y++) + for (var y = 0; y < height; y++) + { + for (var x = 0; x < width; x++) { - for (var x = 0; x < width; x++) - { - array[x, y] = generator(random); - } + array[x, y] = generator(random); } - - return array; } + + return array; } } diff --git a/Bearded.Utilities/Noise/PerlinNoise.cs b/Bearded.Utilities/Noise/PerlinNoise.cs index 244ee8fc..9288d1fa 100644 --- a/Bearded.Utilities/Noise/PerlinNoise.cs +++ b/Bearded.Utilities/Noise/PerlinNoise.cs @@ -3,127 +3,126 @@ using Bearded.Utilities.Geometry; using OpenTK.Mathematics; -namespace Bearded.Utilities.Noise +namespace Bearded.Utilities.Noise; + +public static class PerlinNoise { - public static class PerlinNoise + // Use uniformly distributed vectors to force better gradient distribution. + private const int numVectorSamples = 12; + private static readonly Vector2d[] vectorSamples = vectorSamples = Enumerable.Range(0, numVectorSamples) + .Select(i => Direction2.FromRadians(MathConstants.TwoPi * i / numVectorSamples).Vector) + .Select(vector => new Vector2d(vector.X, vector.Y)) + .ToArray(); + + /// + /// Generates a procedural texture using Perlin noise. + /// + /// + /// Perlin noise is generated by first generating a grid of gradient vectors. The texture is then filled in by + /// for each point identifying the four surrounding gradient vectors, calculating the dot product between each + /// of the gradient vectors and corresponding offset vector, and interpolating between them. + /// + /// + /// The number of cells in which the texture is divided along the horizontal axis (see remarks). + /// + /// + /// The number of cells in which the texture is divided along the vertical axis (see remarks). + /// + /// + /// + /// + public static IProceduralTexture Generate(int numCellsX, int numCellsY, Random? random) { - // Use uniformly distributed vectors to force better gradient distribution. - private const int numVectorSamples = 12; - private static readonly Vector2d[] vectorSamples = vectorSamples = Enumerable.Range(0, numVectorSamples) - .Select(i => Direction2.FromRadians(MathConstants.TwoPi * i / numVectorSamples).Vector) - .Select(vector => new Vector2d(vector.X, vector.Y)) - .ToArray(); - - /// - /// Generates a procedural texture using Perlin noise. - /// - /// - /// Perlin noise is generated by first generating a grid of gradient vectors. The texture is then filled in by - /// for each point identifying the four surrounding gradient vectors, calculating the dot product between each - /// of the gradient vectors and corresponding offset vector, and interpolating between them. - /// - /// - /// The number of cells in which the texture is divided along the horizontal axis (see remarks). - /// - /// - /// The number of cells in which the texture is divided along the vertical axis (see remarks). - /// - /// - /// - /// - public static IProceduralTexture Generate(int numCellsX, int numCellsY, Random? random) + if (numCellsX <= 0) { - if (numCellsX <= 0) - { - throw new ArgumentOutOfRangeException(nameof(numCellsX)); - } - if (numCellsY <= 0) - { - throw new ArgumentOutOfRangeException(nameof(numCellsY)); - } - - var gradientArray = NoiseUtils.GenerateRandomArray( - // We generate the corners, but to make the noise map wrap along both dimensions, we reuse the same - // values as the left and top boundaries, so we don't have to generate the right and bottom boundaries. - numCellsX, - numCellsY, - r => vectorSamples[r.Next(vectorSamples.Length)], - random); - return new PerlinProceduralTexture(gradientArray); + throw new ArgumentOutOfRangeException(nameof(numCellsX)); + } + if (numCellsY <= 0) + { + throw new ArgumentOutOfRangeException(nameof(numCellsY)); } - private sealed class PerlinProceduralTexture : IProceduralTexture + var gradientArray = NoiseUtils.GenerateRandomArray( + // We generate the corners, but to make the noise map wrap along both dimensions, we reuse the same + // values as the left and top boundaries, so we don't have to generate the right and bottom boundaries. + numCellsX, + numCellsY, + r => vectorSamples[r.Next(vectorSamples.Length)], + random); + return new PerlinProceduralTexture(gradientArray); + } + + private sealed class PerlinProceduralTexture : IProceduralTexture + { + private readonly Vector2d[,] gradientArray; + private readonly int width; + private readonly int height; + + public PerlinProceduralTexture(Vector2d[,] gradientArray) { - private readonly Vector2d[,] gradientArray; - private readonly int width; - private readonly int height; + this.gradientArray = gradientArray; + width = gradientArray.GetLength(0); + height = gradientArray.GetLength(1); + } - public PerlinProceduralTexture(Vector2d[,] gradientArray) + public double ValueAt(double x, double y) + { + if (x < 0 || x >= 1) { - this.gradientArray = gradientArray; - width = gradientArray.GetLength(0); - height = gradientArray.GetLength(1); + throw new ArgumentOutOfRangeException(nameof(x)); } - - public double ValueAt(double x, double y) + if (y < 0 || y >= 1) { - if (x < 0 || x >= 1) - { - throw new ArgumentOutOfRangeException(nameof(x)); - } - if (y < 0 || y >= 1) - { - throw new ArgumentOutOfRangeException(nameof(y)); - } - - scaleToWidthHeight(ref x, ref y); - var (xBelow, xAbove, yBelow, yAbove) = getGridCorners(x, y); - var u = x - xBelow; - var v = y - yBelow; - - var topLeft = dotProductWithGridDirection(xBelow, yAbove, x, y); - var topRight = dotProductWithGridDirection(xAbove, yAbove, x, y); - var bottomLeft = dotProductWithGridDirection(xBelow, yBelow, x, y); - var bottomRight = dotProductWithGridDirection(xAbove, yBelow, x, y); - - var (tx, ty) = getInterpolationWeights(u, 1 - v); - - var r = Interpolation2d.BiLinear.Interpolate(bottomLeft, bottomRight, topLeft, topRight, tx, ty); - - return normalizeDotProductToUnitScale(r); + throw new ArgumentOutOfRangeException(nameof(y)); } - private void scaleToWidthHeight(ref double x, ref double y) - { - x *= width; - y *= height; - } + scaleToWidthHeight(ref x, ref y); + var (xBelow, xAbove, yBelow, yAbove) = getGridCorners(x, y); + var u = x - xBelow; + var v = y - yBelow; - private static (int xBelow, int xAbove, int yBelow, int yAbove) getGridCorners(double x, double y) - { - var xBelow = (int) x; - var xAbove = xBelow + 1; + var topLeft = dotProductWithGridDirection(xBelow, yAbove, x, y); + var topRight = dotProductWithGridDirection(xAbove, yAbove, x, y); + var bottomLeft = dotProductWithGridDirection(xBelow, yBelow, x, y); + var bottomRight = dotProductWithGridDirection(xAbove, yBelow, x, y); - var yBelow = (int) y; - var yAbove = yBelow + 1; + var (tx, ty) = getInterpolationWeights(u, 1 - v); - return (xBelow, xAbove, yBelow, yAbove); - } + var r = Interpolation2d.BiLinear.Interpolate(bottomLeft, bottomRight, topLeft, topRight, tx, ty); - private double dotProductWithGridDirection(int gridX, int gridY, double x, double y) - { - var distance = new Vector2d(x - gridX, y - gridY); - return Vector2d.Dot(distance, gradientArray[gridX % width, gridY % height]); - } + return normalizeDotProductToUnitScale(r); + } - private static (double tx, double ty) getInterpolationWeights(double u, double v) - { - var tx = Interpolation1d.SmoothStep.Interpolate(0, 1, u); - var ty = 1 - Interpolation1d.SmoothStep.Interpolate(0, 1, v); - return (tx, ty); - } + private void scaleToWidthHeight(ref double x, ref double y) + { + x *= width; + y *= height; + } + + private static (int xBelow, int xAbove, int yBelow, int yAbove) getGridCorners(double x, double y) + { + var xBelow = (int) x; + var xAbove = xBelow + 1; + + var yBelow = (int) y; + var yAbove = yBelow + 1; - private static double normalizeDotProductToUnitScale(double dotProduct) => (dotProduct + 1) * 0.5; + return (xBelow, xAbove, yBelow, yAbove); + } + + private double dotProductWithGridDirection(int gridX, int gridY, double x, double y) + { + var distance = new Vector2d(x - gridX, y - gridY); + return Vector2d.Dot(distance, gradientArray[gridX % width, gridY % height]); } + + private static (double tx, double ty) getInterpolationWeights(double u, double v) + { + var tx = Interpolation1d.SmoothStep.Interpolate(0, 1, u); + var ty = 1 - Interpolation1d.SmoothStep.Interpolate(0, 1, v); + return (tx, ty); + } + + private static double normalizeDotProductToUnitScale(double dotProduct) => (dotProduct + 1) * 0.5; } } diff --git a/Bearded.Utilities/Noise/ProceduralTexture.cs b/Bearded.Utilities/Noise/ProceduralTexture.cs index 4a8be79a..82f03931 100644 --- a/Bearded.Utilities/Noise/ProceduralTexture.cs +++ b/Bearded.Utilities/Noise/ProceduralTexture.cs @@ -1,84 +1,83 @@ using System; -namespace Bearded.Utilities.Noise +namespace Bearded.Utilities.Noise; + +public static class ProceduralTexture { - public static class ProceduralTexture + public static IProceduralTexture FromArray(double[,] array, IInterpolationMethod2d interpolation) + { + var arrayCopy = new double[array.GetLength(0), array.GetLength(1)]; + Array.Copy(array, arrayCopy, array.Length); + return new ArrayProceduralTexture(arrayCopy, interpolation); + } + + private sealed class ArrayProceduralTexture : IProceduralTexture { - public static IProceduralTexture FromArray(double[,] array, IInterpolationMethod2d interpolation) + private readonly int width; + private readonly int height; + private readonly double[,] array; + private readonly IInterpolationMethod2d interpolation; + + public ArrayProceduralTexture(double[,] array, IInterpolationMethod2d interpolation) { - var arrayCopy = new double[array.GetLength(0), array.GetLength(1)]; - Array.Copy(array, arrayCopy, array.Length); - return new ArrayProceduralTexture(arrayCopy, interpolation); + width = array.GetLength(0); + height = array.GetLength(1); + this.array = array; + + this.interpolation = interpolation; } - private sealed class ArrayProceduralTexture : IProceduralTexture + public double ValueAt(double x, double y) { - private readonly int width; - private readonly int height; - private readonly double[,] array; - private readonly IInterpolationMethod2d interpolation; - - public ArrayProceduralTexture(double[,] array, IInterpolationMethod2d interpolation) + if (x < 0 || x >= 1) { - width = array.GetLength(0); - height = array.GetLength(1); - this.array = array; - - this.interpolation = interpolation; + throw new ArgumentOutOfRangeException(nameof(x)); } - - public double ValueAt(double x, double y) + if (y < 0 || y >= 1) { - if (x < 0 || x >= 1) - { - throw new ArgumentOutOfRangeException(nameof(x)); - } - if (y < 0 || y >= 1) - { - throw new ArgumentOutOfRangeException(nameof(y)); - } + throw new ArgumentOutOfRangeException(nameof(y)); + } - scaleToWidthHeight(ref x, ref y); - alignCoordinatesWithPixelCenters(ref x, ref y); + scaleToWidthHeight(ref x, ref y); + alignCoordinatesWithPixelCenters(ref x, ref y); - var (xBelow, xAbove, yBelow, yAbove) = getGridCorners(x, y); - var u = x - xBelow; - var v = y - yBelow; + var (xBelow, xAbove, yBelow, yAbove) = getGridCorners(x, y); + var u = x - xBelow; + var v = y - yBelow; - xBelow = (xBelow + width) % width; - xAbove %= width; - yBelow = (yBelow + height) % height; - yAbove %= height; + xBelow = (xBelow + width) % width; + xAbove %= width; + yBelow = (yBelow + height) % height; + yAbove %= height; - return interpolation.Interpolate( - array[xBelow, yBelow], array[xAbove, yBelow], array[xBelow, yAbove], array[xAbove, yAbove], u, v); - } + return interpolation.Interpolate( + array[xBelow, yBelow], array[xAbove, yBelow], array[xBelow, yAbove], array[xAbove, yAbove], u, v); + } - private void scaleToWidthHeight(ref double x, ref double y) - { - x *= width; - y *= height; - } + private void scaleToWidthHeight(ref double x, ref double y) + { + x *= width; + y *= height; + } - private void alignCoordinatesWithPixelCenters(ref double x, ref double y) - { - // The texture keeps track of the values at the center of each "pixel", so for the interpolation, we - // need to move the array coordinates by (0.5, 0.5), which is the same as moving the x, y by - // (-0.5, -0.5). - x -= 0.5; - y -= 0.5; - } + private void alignCoordinatesWithPixelCenters(ref double x, ref double y) + { + // The texture keeps track of the values at the center of each "pixel", so for the interpolation, we + // need to move the array coordinates by (0.5, 0.5), which is the same as moving the x, y by + // (-0.5, -0.5). + x -= 0.5; + y -= 0.5; + } - private static (int xLower, int xUpper, int yLower, int yUpper) getGridCorners(double x, double y) - { - var xLower = (int) x; - var xUpper = xLower + 1; + private static (int xLower, int xUpper, int yLower, int yUpper) getGridCorners(double x, double y) + { + var xLower = (int) x; + var xUpper = xLower + 1; - var yLower = (int) y; - var yUpper = yLower + 1; + var yLower = (int) y; + var yUpper = yLower + 1; - return (xLower, xUpper, yLower, yUpper); - } + return (xLower, xUpper, yLower, yUpper); } } } diff --git a/Bearded.Utilities/Noise/UniformNoise.cs b/Bearded.Utilities/Noise/UniformNoise.cs index 04aa1847..9196356f 100644 --- a/Bearded.Utilities/Noise/UniformNoise.cs +++ b/Bearded.Utilities/Noise/UniformNoise.cs @@ -1,25 +1,24 @@ using System; using static Bearded.Utilities.Noise.NoiseUtils; -namespace Bearded.Utilities.Noise +namespace Bearded.Utilities.Noise; + +public static class UniformNoise { - public static class UniformNoise - { - public static IProceduralTexture Generate(int numCellsX, int numCellsY, Random? random) => - Generate(numCellsX, numCellsY, Interpolation2d.Nearest, random); + public static IProceduralTexture Generate(int numCellsX, int numCellsY, Random? random) => + Generate(numCellsX, numCellsY, Interpolation2d.Nearest, random); - public static IProceduralTexture Generate(int numCellsX, int numCellsY, IInterpolationMethod2d interpolation, Random? random) + public static IProceduralTexture Generate(int numCellsX, int numCellsY, IInterpolationMethod2d interpolation, Random? random) + { + if (numCellsX <= 0) { - if (numCellsX <= 0) - { - throw new ArgumentOutOfRangeException(nameof(numCellsX)); - } - if (numCellsY <= 0) - { - throw new ArgumentOutOfRangeException(nameof(numCellsY)); - } - - return ProceduralTexture.FromArray(GenerateRandomArray(numCellsX, numCellsY, r => r.NextDouble(), random), interpolation); + throw new ArgumentOutOfRangeException(nameof(numCellsX)); } + if (numCellsY <= 0) + { + throw new ArgumentOutOfRangeException(nameof(numCellsY)); + } + + return ProceduralTexture.FromArray(GenerateRandomArray(numCellsX, numCellsY, r => r.NextDouble(), random), interpolation); } } diff --git a/Bearded.Utilities/SpaceTime/2d/Acceleration2.cs b/Bearded.Utilities/SpaceTime/2d/Acceleration2.cs index ddeb4fd9..00054458 100644 --- a/Bearded.Utilities/SpaceTime/2d/Acceleration2.cs +++ b/Bearded.Utilities/SpaceTime/2d/Acceleration2.cs @@ -3,222 +3,221 @@ using Bearded.Utilities.Geometry; using OpenTK.Mathematics; -namespace Bearded.Utilities.SpaceTime +namespace Bearded.Utilities.SpaceTime; + +/// +/// A type-safe representation of a 2d directed acceleration vector. +/// +public readonly struct Acceleration2 : IEquatable, IFormattable { - /// - /// A type-safe representation of a 2d directed acceleration vector. - /// - public readonly struct Acceleration2 : IEquatable, IFormattable + private readonly Vector2 value; + + #region construction + + public Acceleration2(Vector2 value) { - private readonly Vector2 value; - - #region construction - - public Acceleration2(Vector2 value) - { - this.value = value; - } + this.value = value; + } - public Acceleration2(float x, float y) - : this(new Vector2(x, y)) - { - } + public Acceleration2(float x, float y) + : this(new Vector2(x, y)) + { + } - public Acceleration2(Acceleration x, Acceleration y) - : this(new Vector2(x.NumericValue, y.NumericValue)) - { - } + public Acceleration2(Acceleration x, Acceleration y) + : this(new Vector2(x.NumericValue, y.NumericValue)) + { + } - /// - /// Creates a new instance of the Acceleration2 type with a given direction and magnitude. - /// - public static Acceleration2 In(Direction2 direction, Acceleration u) => direction * u; + /// + /// Creates a new instance of the Acceleration2 type with a given direction and magnitude. + /// + public static Acceleration2 In(Direction2 direction, Acceleration u) => direction * u; - #endregion + #endregion - #region properties + #region properties - /// - /// Returns the numeric vector value of the acceleration vector. - /// - public Vector2 NumericValue => value; + /// + /// Returns the numeric vector value of the acceleration vector. + /// + public Vector2 NumericValue => value; - /// - /// Returns the X component of the acceleration vector. - /// - public Acceleration X => new Acceleration(value.X); + /// + /// Returns the X component of the acceleration vector. + /// + public Acceleration X => new Acceleration(value.X); - /// - /// Returns the Y component of the acceleration vector. - /// - public Acceleration Y => new Acceleration(value.Y); + /// + /// Returns the Y component of the acceleration vector. + /// + public Acceleration Y => new Acceleration(value.Y); - /// - /// Returns the direction of the acceleration vector. - /// - public Direction2 Direction => Direction2.Of(value); + /// + /// Returns the direction of the acceleration vector. + /// + public Direction2 Direction => Direction2.Of(value); - /// - /// Returns the typed magnitude of the acceleration vector. - /// - public Acceleration Length => new Acceleration(value.Length); + /// + /// Returns the typed magnitude of the acceleration vector. + /// + public Acceleration Length => new Acceleration(value.Length); - /// - /// Returns the typed square of the magnitude of the acceleration vector. - /// - public Squared LengthSquared => new Squared(value.LengthSquared); + /// + /// Returns the typed square of the magnitude of the acceleration vector. + /// + public Squared LengthSquared => new Squared(value.LengthSquared); - /// - /// Returns a Acceleration2 type with value 0. - /// - public static Acceleration2 Zero => new Acceleration2(0, 0); + /// + /// Returns a Acceleration2 type with value 0. + /// + public static Acceleration2 Zero => new Acceleration2(0, 0); - #endregion + #endregion - #region methods + #region methods - #region lerp + #region lerp - /// - /// Linearly interpolates between two typed acceleration vectors. - /// - /// The acceleration vector at t = 0. - /// The acceleration vector at t = 1. - /// The interpolation scalar. - public static Acceleration2 Lerp(Acceleration2 a0, Acceleration2 a1, float t) => a0 + (a1 - a0) * t; + /// + /// Linearly interpolates between two typed acceleration vectors. + /// + /// The acceleration vector at t = 0. + /// The acceleration vector at t = 1. + /// The interpolation scalar. + public static Acceleration2 Lerp(Acceleration2 a0, Acceleration2 a1, float t) => a0 + (a1 - a0) * t; - /// - /// Linearly interpolates towards another typed acceleration vector. - /// - /// The acceleration vector at t = 1. - /// The interpolation scalar. - public Acceleration2 LerpTo(Acceleration2 a, float t) => Lerp(this, a, t); + /// + /// Linearly interpolates towards another typed acceleration vector. + /// + /// The acceleration vector at t = 1. + /// The interpolation scalar. + public Acceleration2 LerpTo(Acceleration2 a, float t) => Lerp(this, a, t); - #endregion + #endregion - #region projection + #region projection - /// - /// Projects the acceleration vector onto an untyped vector, returning the acceleration component in that vector's direction. - /// - public Acceleration ProjectedOn(Vector2 vector) => projectedOn(vector.NormalizedSafe()); + /// + /// Projects the acceleration vector onto an untyped vector, returning the acceleration component in that vector's direction. + /// + public Acceleration ProjectedOn(Vector2 vector) => projectedOn(vector.NormalizedSafe()); - /// - /// Projects the acceleration vector onto a difference vector, returning the acceleration component in that vector's direction. - /// - public Acceleration ProjectedOn(Difference2 vector) => projectedOn(vector.NumericValue.NormalizedSafe()); + /// + /// Projects the acceleration vector onto a difference vector, returning the acceleration component in that vector's direction. + /// + public Acceleration ProjectedOn(Difference2 vector) => projectedOn(vector.NumericValue.NormalizedSafe()); - /// - /// Projects the acceleration vector onto a direction, returning the acceleration component in that direction. - /// - public Acceleration ProjectedOn(Direction2 direction) => projectedOn(direction.Vector); + /// + /// Projects the acceleration vector onto a direction, returning the acceleration component in that direction. + /// + public Acceleration ProjectedOn(Direction2 direction) => projectedOn(direction.Vector); - private Acceleration projectedOn(Vector2 normalisedVector) => new Acceleration(Vector2.Dot(value, normalisedVector)); + private Acceleration projectedOn(Vector2 normalisedVector) => new Acceleration(Vector2.Dot(value, normalisedVector)); - #endregion + #endregion - #region equality and hashcode + #region equality and hashcode - public bool Equals(Acceleration2 other) => value == other.value; + public bool Equals(Acceleration2 other) => value == other.value; - public override bool Equals(object? obj) => obj is Acceleration2 acceleration2 && Equals(acceleration2); + public override bool Equals(object? obj) => obj is Acceleration2 acceleration2 && Equals(acceleration2); - public override int GetHashCode() => value.GetHashCode(); + public override int GetHashCode() => value.GetHashCode(); - #endregion + #endregion - #region tostring + #region tostring - public override string ToString() => ToString(null, CultureInfo.CurrentCulture); + public override string ToString() => ToString(null, CultureInfo.CurrentCulture); - public string ToString(string? format, IFormatProvider? formatProvider) - => $"({value.X.ToString(format, formatProvider)}, {value.Y.ToString(format, formatProvider)}) u/t²"; + public string ToString(string? format, IFormatProvider? formatProvider) + => $"({value.X.ToString(format, formatProvider)}, {value.Y.ToString(format, formatProvider)}) u/t²"; - public string ToString(string? format) - => ToString(format, CultureInfo.CurrentCulture); + public string ToString(string? format) + => ToString(format, CultureInfo.CurrentCulture); - #endregion + #endregion - #endregion + #endregion - #region operators + #region operators - #region algebra + #region algebra - /// - /// Adds two acceleration vectors. - /// - public static Acceleration2 operator +(Acceleration2 a0, Acceleration2 a1) => new Acceleration2(a0.value + a1.value); + /// + /// Adds two acceleration vectors. + /// + public static Acceleration2 operator +(Acceleration2 a0, Acceleration2 a1) => new Acceleration2(a0.value + a1.value); - /// - /// Subtracts a acceleration vector from another. - /// - public static Acceleration2 operator -(Acceleration2 a0, Acceleration2 a1) => new Acceleration2(a0.value - a1.value); + /// + /// Subtracts a acceleration vector from another. + /// + public static Acceleration2 operator -(Acceleration2 a0, Acceleration2 a1) => new Acceleration2(a0.value - a1.value); - #endregion + #endregion - #region scaling + #region scaling - /// - /// Inverts the acceleration vector. - /// - public static Acceleration2 operator -(Acceleration2 a) => new Acceleration2(-a.value); + /// + /// Inverts the acceleration vector. + /// + public static Acceleration2 operator -(Acceleration2 a) => new Acceleration2(-a.value); - /// - /// Multiplies the acceleration vector with a scalar. - /// - public static Acceleration2 operator *(Acceleration2 a, float scalar) => new Acceleration2(a.value * scalar); + /// + /// Multiplies the acceleration vector with a scalar. + /// + public static Acceleration2 operator *(Acceleration2 a, float scalar) => new Acceleration2(a.value * scalar); - /// - /// Multiplies the acceleration vector with a scalar. - /// - public static Acceleration2 operator *(float scalar, Acceleration2 a) => new Acceleration2(a.value * scalar); + /// + /// Multiplies the acceleration vector with a scalar. + /// + public static Acceleration2 operator *(float scalar, Acceleration2 a) => new Acceleration2(a.value * scalar); - /// - /// Divides the acceleration vector by a divisor. - /// - public static Acceleration2 operator /(Acceleration2 a, float divisor) => new Acceleration2(a.value / divisor); + /// + /// Divides the acceleration vector by a divisor. + /// + public static Acceleration2 operator /(Acceleration2 a, float divisor) => new Acceleration2(a.value / divisor); - #endregion + #endregion - #region ratio + #region ratio - /// - /// Divides a acceleration vector by a speed, returning an untyped vector. - /// - public static Vector2 operator /(Acceleration2 a, Acceleration divisor) => a.value / divisor.NumericValue; + /// + /// Divides a acceleration vector by a speed, returning an untyped vector. + /// + public static Vector2 operator /(Acceleration2 a, Acceleration divisor) => a.value / divisor.NumericValue; - #endregion + #endregion - #region integrate + #region integrate - /// - /// Multiplies a acceleration vector by a timespan, returning a velocity vector. - /// - public static Velocity2 operator *(Acceleration2 a, TimeSpan t) => new Velocity2(a.value * (float)t.NumericValue); + /// + /// Multiplies a acceleration vector by a timespan, returning a velocity vector. + /// + public static Velocity2 operator *(Acceleration2 a, TimeSpan t) => new Velocity2(a.value * (float)t.NumericValue); - /// - /// Multiplies a acceleration vector by a timespan, returning a velocity vector. - /// - public static Velocity2 operator *(TimeSpan t, Acceleration2 a) => new Velocity2(a.value * (float)t.NumericValue); + /// + /// Multiplies a acceleration vector by a timespan, returning a velocity vector. + /// + public static Velocity2 operator *(TimeSpan t, Acceleration2 a) => new Velocity2(a.value * (float)t.NumericValue); - #endregion + #endregion - #region comparision + #region comparision - /// - /// Compares two acceleration vectors for equality. - /// - public static bool operator ==(Acceleration2 a0, Acceleration2 a1) => a0.Equals(a1); + /// + /// Compares two acceleration vectors for equality. + /// + public static bool operator ==(Acceleration2 a0, Acceleration2 a1) => a0.Equals(a1); - /// - /// Compares two acceleration vectors for inequality. - /// - public static bool operator !=(Acceleration2 a0, Acceleration2 a1) => !(a0 == a1); + /// + /// Compares two acceleration vectors for inequality. + /// + public static bool operator !=(Acceleration2 a0, Acceleration2 a1) => !(a0 == a1); - #endregion + #endregion - #endregion + #endregion - } } diff --git a/Bearded.Utilities/SpaceTime/2d/Difference2.cs b/Bearded.Utilities/SpaceTime/2d/Difference2.cs index 77ab19ce..8e72bdc3 100644 --- a/Bearded.Utilities/SpaceTime/2d/Difference2.cs +++ b/Bearded.Utilities/SpaceTime/2d/Difference2.cs @@ -3,221 +3,220 @@ using Bearded.Utilities.Geometry; using OpenTK.Mathematics; -namespace Bearded.Utilities.SpaceTime +namespace Bearded.Utilities.SpaceTime; + +/// +/// A type-safe representation of a 2d directed difference vector. +/// +public readonly struct Difference2 : IEquatable, IFormattable { - /// - /// A type-safe representation of a 2d directed difference vector. - /// - public readonly struct Difference2 : IEquatable, IFormattable + private readonly Vector2 value; + + #region constructing + + public Difference2(Vector2 value) { - private readonly Vector2 value; - - #region constructing - - public Difference2(Vector2 value) - { - this.value = value; - } + this.value = value; + } - public Difference2(float x, float y) - : this(new Vector2(x, y)) - { - } + public Difference2(float x, float y) + : this(new Vector2(x, y)) + { + } - public Difference2(Unit x, Unit y) - : this(new Vector2(x.NumericValue, y.NumericValue)) - { - } + public Difference2(Unit x, Unit y) + : this(new Vector2(x.NumericValue, y.NumericValue)) + { + } - /// - /// Creates a new instance of the Difference2 type with a given direction and magnitude. - /// - public static Difference2 In(Direction2 direction, Unit u) => direction * u; + /// + /// Creates a new instance of the Difference2 type with a given direction and magnitude. + /// + public static Difference2 In(Direction2 direction, Unit u) => direction * u; - #endregion + #endregion - #region properties + #region properties - /// - /// Returns the numeric vector value of the difference vector. - /// - public Vector2 NumericValue => value; + /// + /// Returns the numeric vector value of the difference vector. + /// + public Vector2 NumericValue => value; - /// - /// Returns the X component of the difference vector. - /// - public Unit X => new Unit(value.X); + /// + /// Returns the X component of the difference vector. + /// + public Unit X => new Unit(value.X); - /// - /// Returns the Y component of the difference vector. - /// - public Unit Y => new Unit(value.Y); + /// + /// Returns the Y component of the difference vector. + /// + public Unit Y => new Unit(value.Y); - /// - /// Returns the direction of the difference vector. - /// - public Direction2 Direction => Direction2.Of(value); + /// + /// Returns the direction of the difference vector. + /// + public Direction2 Direction => Direction2.Of(value); - /// - /// Returns the typed magnitude of the difference vector. - /// - public Unit Length => new Unit(value.Length); + /// + /// Returns the typed magnitude of the difference vector. + /// + public Unit Length => new Unit(value.Length); - /// - /// Returns the typed square of the magnitude of the difference vector. - /// - public Squared LengthSquared => new Squared(value.LengthSquared); + /// + /// Returns the typed square of the magnitude of the difference vector. + /// + public Squared LengthSquared => new Squared(value.LengthSquared); - /// - /// Returns a Difference2 type with value 0. - /// - public static Difference2 Zero => new Difference2(0, 0); + /// + /// Returns a Difference2 type with value 0. + /// + public static Difference2 Zero => new Difference2(0, 0); - #endregion + #endregion - #region methods + #region methods - #region lerp + #region lerp - /// - /// Linearly interpolates between two typed difference vectors. - /// - /// The difference vector at t = 0. - /// The difference vector at t = 1. - /// The interpolation scalar. - public static Difference2 Lerp(Difference2 d0, Difference2 d1, float t) => d0 + (d1 - d0) * t; + /// + /// Linearly interpolates between two typed difference vectors. + /// + /// The difference vector at t = 0. + /// The difference vector at t = 1. + /// The interpolation scalar. + public static Difference2 Lerp(Difference2 d0, Difference2 d1, float t) => d0 + (d1 - d0) * t; - /// - /// Linearly interpolates towards another typed difference vector. - /// - /// The difference vector at t = 1. - /// The interpolation scalar. - public Difference2 LerpTo(Difference2 d, float t) => Lerp(this, d, t); + /// + /// Linearly interpolates towards another typed difference vector. + /// + /// The difference vector at t = 1. + /// The interpolation scalar. + public Difference2 LerpTo(Difference2 d, float t) => Lerp(this, d, t); - #endregion + #endregion - #region projection + #region projection - /// - /// Projects the difference vector onto an untyped vector, returning the speed component in that vector's direction. - /// - public Unit ProjectedOn(Vector2 vector) => projectedOn(vector.NormalizedSafe()); + /// + /// Projects the difference vector onto an untyped vector, returning the speed component in that vector's direction. + /// + public Unit ProjectedOn(Vector2 vector) => projectedOn(vector.NormalizedSafe()); - /// - /// Projects the difference vector onto a difference vector, returning the speed component in that vector's direction. - /// - public Unit ProjectedOn(Difference2 vector) => projectedOn(vector.NumericValue.NormalizedSafe()); + /// + /// Projects the difference vector onto a difference vector, returning the speed component in that vector's direction. + /// + public Unit ProjectedOn(Difference2 vector) => projectedOn(vector.NumericValue.NormalizedSafe()); - /// - /// Projects the difference vector onto a direction, returning the speed component in that direction. - /// - public Unit ProjectedOn(Direction2 direction) => projectedOn(direction.Vector); + /// + /// Projects the difference vector onto a direction, returning the speed component in that direction. + /// + public Unit ProjectedOn(Direction2 direction) => projectedOn(direction.Vector); - private Unit projectedOn(Vector2 normalisedVector) => new Unit(Vector2.Dot(value, normalisedVector)); + private Unit projectedOn(Vector2 normalisedVector) => new Unit(Vector2.Dot(value, normalisedVector)); - #endregion + #endregion - #region equality and hashcode + #region equality and hashcode - public bool Equals(Difference2 other) => value == other.value; + public bool Equals(Difference2 other) => value == other.value; - public override bool Equals(object? obj) => obj is Difference2 difference2 && Equals(difference2); + public override bool Equals(object? obj) => obj is Difference2 difference2 && Equals(difference2); - public override int GetHashCode() => value.GetHashCode(); + public override int GetHashCode() => value.GetHashCode(); - #endregion + #endregion - #region tostring + #region tostring - public override string ToString() => ToString(null, CultureInfo.CurrentCulture); + public override string ToString() => ToString(null, CultureInfo.CurrentCulture); - public string ToString(string? format, IFormatProvider? formatProvider) - => $"({value.X.ToString(format, formatProvider)}, {value.Y.ToString(format, formatProvider)}) u"; + public string ToString(string? format, IFormatProvider? formatProvider) + => $"({value.X.ToString(format, formatProvider)}, {value.Y.ToString(format, formatProvider)}) u"; - public string ToString(string? format) - => ToString(format, CultureInfo.CurrentCulture); + public string ToString(string? format) + => ToString(format, CultureInfo.CurrentCulture); - #endregion + #endregion - #endregion + #endregion - #region operators + #region operators - #region algebra + #region algebra - /// - /// Adds two difference vectors. - /// - public static Difference2 operator +(Difference2 d0, Difference2 d1) => new Difference2(d0.value + d1.value); + /// + /// Adds two difference vectors. + /// + public static Difference2 operator +(Difference2 d0, Difference2 d1) => new Difference2(d0.value + d1.value); - /// - /// Subtracts a difference vector from another. - /// - public static Difference2 operator -(Difference2 d0, Difference2 d1) => new Difference2(d0.value - d1.value); + /// + /// Subtracts a difference vector from another. + /// + public static Difference2 operator -(Difference2 d0, Difference2 d1) => new Difference2(d0.value - d1.value); - #endregion + #endregion - #region scaling + #region scaling - /// - /// Inverts the difference vector. - /// - public static Difference2 operator -(Difference2 d) => new Difference2(-d.value); + /// + /// Inverts the difference vector. + /// + public static Difference2 operator -(Difference2 d) => new Difference2(-d.value); - /// - /// Multiplies the difference vector with a scalar. - /// - public static Difference2 operator *(Difference2 d, float scalar) => new Difference2(d.value * scalar); + /// + /// Multiplies the difference vector with a scalar. + /// + public static Difference2 operator *(Difference2 d, float scalar) => new Difference2(d.value * scalar); - /// - /// Multiplies the difference vector with a scalar. - /// - public static Difference2 operator *(float scalar, Difference2 d) => new Difference2(d.value * scalar); + /// + /// Multiplies the difference vector with a scalar. + /// + public static Difference2 operator *(float scalar, Difference2 d) => new Difference2(d.value * scalar); - /// - /// Divides the difference vector by a divisor. - /// - public static Difference2 operator /(Difference2 d, float divisor) => new Difference2(d.value / divisor); + /// + /// Divides the difference vector by a divisor. + /// + public static Difference2 operator /(Difference2 d, float divisor) => new Difference2(d.value / divisor); - #endregion + #endregion - #region ratio + #region ratio - /// - /// Divides a difference vector by a speed, returning an untyped vector. - /// - public static Vector2 operator /(Difference2 d, Unit divisor) => d.value / divisor.NumericValue; + /// + /// Divides a difference vector by a speed, returning an untyped vector. + /// + public static Vector2 operator /(Difference2 d, Unit divisor) => d.value / divisor.NumericValue; - #endregion + #endregion - #region differentiate + #region differentiate - /// - /// Divides a difference vector by a timespan, returning an velocity vector. - /// - public static Velocity2 operator /(Difference2 d, TimeSpan t) => new Velocity2(d.value / (float)t.NumericValue); + /// + /// Divides a difference vector by a timespan, returning an velocity vector. + /// + public static Velocity2 operator /(Difference2 d, TimeSpan t) => new Velocity2(d.value / (float)t.NumericValue); - public static Velocity2 operator *(Difference2 d, Frequency f) => new Velocity2(d.value * (float)f.NumericValue); + public static Velocity2 operator *(Difference2 d, Frequency f) => new Velocity2(d.value * (float)f.NumericValue); - public static Velocity2 operator *(Frequency f, Difference2 d) => new Velocity2(d.value * (float)f.NumericValue); + public static Velocity2 operator *(Frequency f, Difference2 d) => new Velocity2(d.value * (float)f.NumericValue); - #endregion + #endregion - #region comparision + #region comparision - /// - /// Compares two difference vectors for equality. - /// - public static bool operator ==(Difference2 d0, Difference2 d1) => d0.Equals(d1); + /// + /// Compares two difference vectors for equality. + /// + public static bool operator ==(Difference2 d0, Difference2 d1) => d0.Equals(d1); - /// - /// Compares two difference vectors for inequality. - /// - public static bool operator !=(Difference2 d0, Difference2 d1) => !(d0 == d1); + /// + /// Compares two difference vectors for inequality. + /// + public static bool operator !=(Difference2 d0, Difference2 d1) => !(d0 == d1); - #endregion + #endregion - #endregion + #endregion - } } diff --git a/Bearded.Utilities/SpaceTime/2d/Position2.cs b/Bearded.Utilities/SpaceTime/2d/Position2.cs index 7af3f990..c60e385a 100644 --- a/Bearded.Utilities/SpaceTime/2d/Position2.cs +++ b/Bearded.Utilities/SpaceTime/2d/Position2.cs @@ -2,140 +2,139 @@ using System.Globalization; using OpenTK.Mathematics; -namespace Bearded.Utilities.SpaceTime +namespace Bearded.Utilities.SpaceTime; + +/// +/// A type-safe representation of an absolute 2d position vector. +/// +public readonly struct Position2 : IEquatable, IFormattable { - /// - /// A type-safe representation of an absolute 2d position vector. - /// - public readonly struct Position2 : IEquatable, IFormattable - { - private readonly Vector2 value; + private readonly Vector2 value; - #region construction + #region construction - public Position2(Vector2 value) - { - this.value = value; - } + public Position2(Vector2 value) + { + this.value = value; + } - public Position2(float x, float y) - : this(new Vector2(x, y)) - { - } + public Position2(float x, float y) + : this(new Vector2(x, y)) + { + } - public Position2(Unit x, Unit y) - : this(new Vector2(x.NumericValue, y.NumericValue)) - { - } + public Position2(Unit x, Unit y) + : this(new Vector2(x.NumericValue, y.NumericValue)) + { + } - #endregion + #endregion - /// - /// Returns the numeric vector value of the position vector. - /// - public Vector2 NumericValue => value; + /// + /// Returns the numeric vector value of the position vector. + /// + public Vector2 NumericValue => value; - /// - /// Returns the X component of the position vector. - /// - public Unit X => new Unit(value.X); + /// + /// Returns the X component of the position vector. + /// + public Unit X => new Unit(value.X); - /// - /// Returns the Y component of the position vector. - /// - public Unit Y => new Unit(value.Y); + /// + /// Returns the Y component of the position vector. + /// + public Unit Y => new Unit(value.Y); - /// - /// Returns a Position2 type with value 0. - /// - public static Position2 Zero => new Position2(0, 0); + /// + /// Returns a Position2 type with value 0. + /// + public static Position2 Zero => new Position2(0, 0); - #region methods + #region methods - #region lerp + #region lerp - /// - /// Linearly interpolates between two typed position vectors. - /// - /// The position vector at t = 0. - /// The position vector at t = 1. - /// The interpolation scalar. - public static Position2 Lerp(Position2 p0, Position2 p1, float t) => p0 + (p1 - p0) * t; + /// + /// Linearly interpolates between two typed position vectors. + /// + /// The position vector at t = 0. + /// The position vector at t = 1. + /// The interpolation scalar. + public static Position2 Lerp(Position2 p0, Position2 p1, float t) => p0 + (p1 - p0) * t; - /// - /// Linearly interpolates towards another typed position vector. - /// - /// The position vector at t = 1. - /// The interpolation scalar. - public Position2 LerpTo(Position2 p, float t) => Lerp(this, p, t); + /// + /// Linearly interpolates towards another typed position vector. + /// + /// The position vector at t = 1. + /// The interpolation scalar. + public Position2 LerpTo(Position2 p, float t) => Lerp(this, p, t); - #endregion + #endregion - #region equality and hashcode + #region equality and hashcode - public bool Equals(Position2 other) => value == other.value; + public bool Equals(Position2 other) => value == other.value; - public override bool Equals(object? obj) => obj is Position2 position2 && Equals(position2); + public override bool Equals(object? obj) => obj is Position2 position2 && Equals(position2); - public override int GetHashCode() => value.GetHashCode(); + public override int GetHashCode() => value.GetHashCode(); - #endregion + #endregion - #region tostring + #region tostring - public override string ToString() => ToString(null, CultureInfo.CurrentCulture); + public override string ToString() => ToString(null, CultureInfo.CurrentCulture); - public string ToString(string? format, IFormatProvider? formatProvider) - => $"({value.X.ToString(format, formatProvider)}, {value.Y.ToString(format, formatProvider)}) u"; + public string ToString(string? format, IFormatProvider? formatProvider) + => $"({value.X.ToString(format, formatProvider)}, {value.Y.ToString(format, formatProvider)}) u"; - public string ToString(string? format) - => ToString(format, CultureInfo.CurrentCulture); + public string ToString(string? format) + => ToString(format, CultureInfo.CurrentCulture); - #endregion + #endregion - #endregion + #endregion - #region operators + #region operators - #region Difference2 interaction + #region Difference2 interaction - /// - /// Adds a difference vector to an absolute position. - /// - public static Position2 operator +(Position2 p, Difference2 d) => new Position2(p.value + d.NumericValue); + /// + /// Adds a difference vector to an absolute position. + /// + public static Position2 operator +(Position2 p, Difference2 d) => new Position2(p.value + d.NumericValue); - /// - /// Adds a difference vector to an absolute position. - /// - public static Position2 operator +(Difference2 d, Position2 p) => new Position2(p.value + d.NumericValue); + /// + /// Adds a difference vector to an absolute position. + /// + public static Position2 operator +(Difference2 d, Position2 p) => new Position2(p.value + d.NumericValue); - /// - /// Subtracts a difference vector from an absolute position. - /// - public static Position2 operator -(Position2 p, Difference2 d) => new Position2(p.value - d.NumericValue); + /// + /// Subtracts a difference vector from an absolute position. + /// + public static Position2 operator -(Position2 p, Difference2 d) => new Position2(p.value - d.NumericValue); - /// - /// Subtracts two absolute positions, returning a difference vector. - /// - public static Difference2 operator -(Position2 p0, Position2 p1) => new Difference2(p0.value - p1.value); + /// + /// Subtracts two absolute positions, returning a difference vector. + /// + public static Difference2 operator -(Position2 p0, Position2 p1) => new Difference2(p0.value - p1.value); - #endregion + #endregion - #region comparision + #region comparision - /// - /// Compares two position vectors for equality. - /// - public static bool operator ==(Position2 p0, Position2 p1) => p0.Equals(p1); + /// + /// Compares two position vectors for equality. + /// + public static bool operator ==(Position2 p0, Position2 p1) => p0.Equals(p1); - /// - /// Compares two position vectors for inequality. - /// - public static bool operator !=(Position2 p0, Position2 p1) => !(p0 == p1); + /// + /// Compares two position vectors for inequality. + /// + public static bool operator !=(Position2 p0, Position2 p1) => !(p0 == p1); - #endregion + #endregion - #endregion + #endregion - } } diff --git a/Bearded.Utilities/SpaceTime/2d/Velocity2.cs b/Bearded.Utilities/SpaceTime/2d/Velocity2.cs index 2079e5e1..af048200 100644 --- a/Bearded.Utilities/SpaceTime/2d/Velocity2.cs +++ b/Bearded.Utilities/SpaceTime/2d/Velocity2.cs @@ -3,237 +3,236 @@ using Bearded.Utilities.Geometry; using OpenTK.Mathematics; -namespace Bearded.Utilities.SpaceTime +namespace Bearded.Utilities.SpaceTime; + +/// +/// A type-safe representation of a 2d directed velocity vector. +/// +public readonly struct Velocity2 : IEquatable, IFormattable { - /// - /// A type-safe representation of a 2d directed velocity vector. - /// - public readonly struct Velocity2 : IEquatable, IFormattable + private readonly Vector2 value; + + #region construction + + public Velocity2(Vector2 value) { - private readonly Vector2 value; - - #region construction - - public Velocity2(Vector2 value) - { - this.value = value; - } + this.value = value; + } - public Velocity2(float x, float y) - : this(new Vector2(x, y)) - { - } + public Velocity2(float x, float y) + : this(new Vector2(x, y)) + { + } - public Velocity2(Speed x, Speed y) - : this(new Vector2(x.NumericValue, y.NumericValue)) - { - } + public Velocity2(Speed x, Speed y) + : this(new Vector2(x.NumericValue, y.NumericValue)) + { + } - /// - /// Creates a new instance of the Velocity2 type with a given direction and magnitude. - /// - public static Velocity2 In(Direction2 direction, Speed s) => direction * s; + /// + /// Creates a new instance of the Velocity2 type with a given direction and magnitude. + /// + public static Velocity2 In(Direction2 direction, Speed s) => direction * s; - #endregion + #endregion - #region properties + #region properties - /// - /// Returns the numeric vector value of the velocity vector. - /// - public Vector2 NumericValue => value; + /// + /// Returns the numeric vector value of the velocity vector. + /// + public Vector2 NumericValue => value; - /// - /// Returns the X component of the velocity vector. - /// - public Speed X => new Speed(value.X); + /// + /// Returns the X component of the velocity vector. + /// + public Speed X => new Speed(value.X); - /// - /// Returns the Y component of the velocity vector. - /// - public Speed Y => new Speed(value.Y); + /// + /// Returns the Y component of the velocity vector. + /// + public Speed Y => new Speed(value.Y); - /// - /// Returns the direction of the velocity vector. - /// - public Direction2 Direction => Direction2.Of(value); + /// + /// Returns the direction of the velocity vector. + /// + public Direction2 Direction => Direction2.Of(value); - /// - /// Returns the typed magnitude of the velocity vector. - /// - public Speed Length => new Speed(value.Length); + /// + /// Returns the typed magnitude of the velocity vector. + /// + public Speed Length => new Speed(value.Length); - /// - /// Returns the typed square of the magnitude of the velocity vector. - /// - public Squared LengthSquared => new Squared(value.LengthSquared); + /// + /// Returns the typed square of the magnitude of the velocity vector. + /// + public Squared LengthSquared => new Squared(value.LengthSquared); - /// - /// Returns a Velocity2 type with value 0. - /// - public static Velocity2 Zero => new Velocity2(0, 0); + /// + /// Returns a Velocity2 type with value 0. + /// + public static Velocity2 Zero => new Velocity2(0, 0); - #endregion + #endregion - #region methods + #region methods - #region lerp + #region lerp - /// - /// Linearly interpolates between two typed velocity vectors. - /// - /// The velocity vector at t = 0. - /// The velocity vector at t = 1. - /// The interpolation scalar. - public static Velocity2 Lerp(Velocity2 v0, Velocity2 v1, float t) => v0 + (v1 - v0) * t; + /// + /// Linearly interpolates between two typed velocity vectors. + /// + /// The velocity vector at t = 0. + /// The velocity vector at t = 1. + /// The interpolation scalar. + public static Velocity2 Lerp(Velocity2 v0, Velocity2 v1, float t) => v0 + (v1 - v0) * t; - /// - /// Linearly interpolates towards another typed velocity vector. - /// - /// The velocity vector at t = 1. - /// The interpolation scalar. - public Velocity2 LerpTo(Velocity2 v, float t) => Lerp(this, v, t); + /// + /// Linearly interpolates towards another typed velocity vector. + /// + /// The velocity vector at t = 1. + /// The interpolation scalar. + public Velocity2 LerpTo(Velocity2 v, float t) => Lerp(this, v, t); - #endregion + #endregion - #region projection + #region projection - /// - /// Projects the velocity vector onto an untyped vector, returning the speed component in that vector's direction. - /// - public Speed ProjectedOn(Vector2 vector) => projectedOn(vector.NormalizedSafe()); + /// + /// Projects the velocity vector onto an untyped vector, returning the speed component in that vector's direction. + /// + public Speed ProjectedOn(Vector2 vector) => projectedOn(vector.NormalizedSafe()); - /// - /// Projects the velocity vector onto a difference vector, returning the speed component in that vector's direction. - /// - public Speed ProjectedOn(Difference2 vector) => projectedOn(vector.NumericValue.NormalizedSafe()); + /// + /// Projects the velocity vector onto a difference vector, returning the speed component in that vector's direction. + /// + public Speed ProjectedOn(Difference2 vector) => projectedOn(vector.NumericValue.NormalizedSafe()); - /// - /// Projects the velocity vector onto a direction, returning the speed component in that direction. - /// - public Speed ProjectedOn(Direction2 direction) => projectedOn(direction.Vector); + /// + /// Projects the velocity vector onto a direction, returning the speed component in that direction. + /// + public Speed ProjectedOn(Direction2 direction) => projectedOn(direction.Vector); - private Speed projectedOn(Vector2 normalisedVector) => new Speed(Vector2.Dot(value, normalisedVector)); + private Speed projectedOn(Vector2 normalisedVector) => new Speed(Vector2.Dot(value, normalisedVector)); - #endregion + #endregion - #region equality and hashcode + #region equality and hashcode - public bool Equals(Velocity2 other) => value == other.value; + public bool Equals(Velocity2 other) => value == other.value; - public override bool Equals(object? obj) => obj is Velocity2 velocity2 && Equals(velocity2); + public override bool Equals(object? obj) => obj is Velocity2 velocity2 && Equals(velocity2); - public override int GetHashCode() => value.GetHashCode(); + public override int GetHashCode() => value.GetHashCode(); - #endregion + #endregion - #region tostring + #region tostring - public override string ToString() => ToString(null, CultureInfo.CurrentCulture); + public override string ToString() => ToString(null, CultureInfo.CurrentCulture); - public string ToString(string? format, IFormatProvider? formatProvider) - => $"({value.X.ToString(format, formatProvider)}, {value.Y.ToString(format, formatProvider)}) u/t"; + public string ToString(string? format, IFormatProvider? formatProvider) + => $"({value.X.ToString(format, formatProvider)}, {value.Y.ToString(format, formatProvider)}) u/t"; - public string ToString(string? format) - => ToString(format, CultureInfo.CurrentCulture); + public string ToString(string? format) + => ToString(format, CultureInfo.CurrentCulture); - #endregion + #endregion - #endregion + #endregion - #region operators + #region operators - #region algebra + #region algebra - /// - /// Adds two velocity vectors. - /// - public static Velocity2 operator +(Velocity2 v0, Velocity2 v1) => new Velocity2(v0.value + v1.value); + /// + /// Adds two velocity vectors. + /// + public static Velocity2 operator +(Velocity2 v0, Velocity2 v1) => new Velocity2(v0.value + v1.value); - /// - /// Subtracts a velocity vector from another. - /// - public static Velocity2 operator -(Velocity2 v0, Velocity2 v1) => new Velocity2(v0.value - v1.value); + /// + /// Subtracts a velocity vector from another. + /// + public static Velocity2 operator -(Velocity2 v0, Velocity2 v1) => new Velocity2(v0.value - v1.value); - #endregion + #endregion - #region scaling + #region scaling - /// - /// Inverts the velocity vector. - /// - public static Velocity2 operator -(Velocity2 v) => new Velocity2(-v.value); + /// + /// Inverts the velocity vector. + /// + public static Velocity2 operator -(Velocity2 v) => new Velocity2(-v.value); - /// - /// Multiplies the velocity vector with a scalar. - /// - public static Velocity2 operator *(Velocity2 v, float scalar) => new Velocity2(v.value * scalar); + /// + /// Multiplies the velocity vector with a scalar. + /// + public static Velocity2 operator *(Velocity2 v, float scalar) => new Velocity2(v.value * scalar); - /// - /// Multiplies the velocity vector with a scalar. - /// - public static Velocity2 operator *(float scalar, Velocity2 v) => new Velocity2(v.value * scalar); + /// + /// Multiplies the velocity vector with a scalar. + /// + public static Velocity2 operator *(float scalar, Velocity2 v) => new Velocity2(v.value * scalar); - /// - /// Divides the velocity vector by a divisor. - /// - public static Velocity2 operator /(Velocity2 v, float divisor) => new Velocity2(v.value / divisor); + /// + /// Divides the velocity vector by a divisor. + /// + public static Velocity2 operator /(Velocity2 v, float divisor) => new Velocity2(v.value / divisor); - #endregion + #endregion - #region ratio + #region ratio - /// - /// Divides a velocity vector by a speed, returning an untyped vector. - /// - public static Vector2 operator /(Velocity2 v, Speed divisor) => v.value / divisor.NumericValue; + /// + /// Divides a velocity vector by a speed, returning an untyped vector. + /// + public static Vector2 operator /(Velocity2 v, Speed divisor) => v.value / divisor.NumericValue; - #endregion + #endregion - #region differentiate + #region differentiate - /// - /// Divides a velocity vector by a timespan, returning an acceleration vector. - /// - public static Acceleration2 operator /(Velocity2 v, TimeSpan t) => new Acceleration2(v.value / (float)t.NumericValue); + /// + /// Divides a velocity vector by a timespan, returning an acceleration vector. + /// + public static Acceleration2 operator /(Velocity2 v, TimeSpan t) => new Acceleration2(v.value / (float)t.NumericValue); - public static Acceleration2 operator *(Velocity2 v, Frequency f) => new Acceleration2(v.value * (float)f.NumericValue); + public static Acceleration2 operator *(Velocity2 v, Frequency f) => new Acceleration2(v.value * (float)f.NumericValue); - public static Acceleration2 operator *(Frequency f, Velocity2 v) => new Acceleration2(v.value * (float)f.NumericValue); + public static Acceleration2 operator *(Frequency f, Velocity2 v) => new Acceleration2(v.value * (float)f.NumericValue); - #endregion + #endregion - #region integrate + #region integrate - /// - /// Multiplies a velocity vector by a timespan, returning a difference vector. - /// - public static Difference2 operator *(Velocity2 v, TimeSpan t) => new Difference2(v.value * (float)t.NumericValue); + /// + /// Multiplies a velocity vector by a timespan, returning a difference vector. + /// + public static Difference2 operator *(Velocity2 v, TimeSpan t) => new Difference2(v.value * (float)t.NumericValue); - /// - /// Multiplies a velocity vector by a timespan, returning a difference vector. - /// - public static Difference2 operator *(TimeSpan t, Velocity2 v) => new Difference2(v.value * (float)t.NumericValue); + /// + /// Multiplies a velocity vector by a timespan, returning a difference vector. + /// + public static Difference2 operator *(TimeSpan t, Velocity2 v) => new Difference2(v.value * (float)t.NumericValue); - public static Difference2 operator /(Velocity2 v, Frequency f) => new Difference2(v.value / (float)f.NumericValue); + public static Difference2 operator /(Velocity2 v, Frequency f) => new Difference2(v.value / (float)f.NumericValue); - #endregion + #endregion - #region comparision + #region comparision - /// - /// Compares two velocity vectors for equality. - /// - public static bool operator ==(Velocity2 v0, Velocity2 v1) => v0.Equals(v1); + /// + /// Compares two velocity vectors for equality. + /// + public static bool operator ==(Velocity2 v0, Velocity2 v1) => v0.Equals(v1); - /// - /// Compares two velocity vectors for inequality. - /// - public static bool operator !=(Velocity2 v0, Velocity2 v1) => !(v0 == v1); + /// + /// Compares two velocity vectors for inequality. + /// + public static bool operator !=(Velocity2 v0, Velocity2 v1) => !(v0 == v1); - #endregion + #endregion - #endregion + #endregion - } } diff --git a/Bearded.Utilities/SpaceTime/3d/Acceleration3.cs b/Bearded.Utilities/SpaceTime/3d/Acceleration3.cs index c87b3b88..a42d627a 100644 --- a/Bearded.Utilities/SpaceTime/3d/Acceleration3.cs +++ b/Bearded.Utilities/SpaceTime/3d/Acceleration3.cs @@ -2,221 +2,220 @@ using System.Globalization; using OpenTK.Mathematics; -namespace Bearded.Utilities.SpaceTime +namespace Bearded.Utilities.SpaceTime; + +/// +/// A type-safe representation of a 3d directed acceleration vector. +/// +public readonly struct Acceleration3 : IEquatable, IFormattable { - /// - /// A type-safe representation of a 3d directed acceleration vector. - /// - public readonly struct Acceleration3 : IEquatable, IFormattable - { - private readonly Vector3 value; + private readonly Vector3 value; - #region construction + #region construction - public Acceleration3(Vector3 value) - { - this.value = value; - } + public Acceleration3(Vector3 value) + { + this.value = value; + } - public Acceleration3(float x, float y, float z) - : this(new Vector3(x, y, z)) - { - } + public Acceleration3(float x, float y, float z) + : this(new Vector3(x, y, z)) + { + } - public Acceleration3(Acceleration x, Acceleration y, Acceleration z) - : this(new Vector3(x.NumericValue, y.NumericValue, z.NumericValue)) - { - } + public Acceleration3(Acceleration x, Acceleration y, Acceleration z) + : this(new Vector3(x.NumericValue, y.NumericValue, z.NumericValue)) + { + } - #endregion + #endregion - #region properties + #region properties - /// - /// Returns the numeric vector value of the acceleration vector. - /// - public Vector3 NumericValue => value; + /// + /// Returns the numeric vector value of the acceleration vector. + /// + public Vector3 NumericValue => value; - /// - /// Returns the X component of the acceleration vector. - /// - public Acceleration X => new Acceleration(value.X); + /// + /// Returns the X component of the acceleration vector. + /// + public Acceleration X => new Acceleration(value.X); - /// - /// Returns the Y component of the acceleration vector. - /// - public Acceleration Y => new Acceleration(value.Y); + /// + /// Returns the Y component of the acceleration vector. + /// + public Acceleration Y => new Acceleration(value.Y); - /// - /// Returns the Z component of the acceleration vector. - /// - public Acceleration Z => new Acceleration(value.Z); + /// + /// Returns the Z component of the acceleration vector. + /// + public Acceleration Z => new Acceleration(value.Z); - /// - /// Returns the typed magnitude of the acceleration vector. - /// - public Acceleration Length => new Acceleration(value.Length); + /// + /// Returns the typed magnitude of the acceleration vector. + /// + public Acceleration Length => new Acceleration(value.Length); - /// - /// Returns the typed square of the magnitude of the acceleration vector. - /// - public Squared LengthSquared => new Squared(value.LengthSquared); + /// + /// Returns the typed square of the magnitude of the acceleration vector. + /// + public Squared LengthSquared => new Squared(value.LengthSquared); - /// - /// Returns a Acceleration3 type with value 0. - /// - public static Acceleration3 Zero => new Acceleration3(0, 0, 0); + /// + /// Returns a Acceleration3 type with value 0. + /// + public static Acceleration3 Zero => new Acceleration3(0, 0, 0); - #endregion + #endregion - #region methods + #region methods - #region lerp + #region lerp - /// - /// Linearly interpolates between two typed acceleration vectors. - /// - /// The acceleration vector at t = 0. - /// The acceleration vector at t = 1. - /// The interpolation scalar. - public static Acceleration3 Lerp(Acceleration3 a0, Acceleration3 a1, float t) => a0 + (a1 - a0) * t; + /// + /// Linearly interpolates between two typed acceleration vectors. + /// + /// The acceleration vector at t = 0. + /// The acceleration vector at t = 1. + /// The interpolation scalar. + public static Acceleration3 Lerp(Acceleration3 a0, Acceleration3 a1, float t) => a0 + (a1 - a0) * t; - /// - /// Linearly interpolates towards another typed acceleration vector. - /// - /// The acceleration vector at t = 1. - /// The interpolation scalar. - public Acceleration3 LerpTo(Acceleration3 a, float t) => Lerp(this, a, t); + /// + /// Linearly interpolates towards another typed acceleration vector. + /// + /// The acceleration vector at t = 1. + /// The interpolation scalar. + public Acceleration3 LerpTo(Acceleration3 a, float t) => Lerp(this, a, t); - #endregion + #endregion - #region projection + #region projection - /// - /// Projects the acceleration vector onto an untyped vector, returning the acceleration component in that - /// vector's direction. - /// - public Acceleration ProjectedOn(Vector3 vector) => projectedOn(vector.NormalizedSafe()); + /// + /// Projects the acceleration vector onto an untyped vector, returning the acceleration component in that + /// vector's direction. + /// + public Acceleration ProjectedOn(Vector3 vector) => projectedOn(vector.NormalizedSafe()); - /// - /// Projects the acceleration vector onto a difference vector, returning the acceleration component in that - /// vector's direction. - /// - public Acceleration ProjectedOn(Difference3 vector) => projectedOn(vector.NumericValue.NormalizedSafe()); + /// + /// Projects the acceleration vector onto a difference vector, returning the acceleration component in that + /// vector's direction. + /// + public Acceleration ProjectedOn(Difference3 vector) => projectedOn(vector.NumericValue.NormalizedSafe()); - private Acceleration projectedOn(Vector3 normalisedVector) => - new Acceleration(Vector3.Dot(value, normalisedVector)); + private Acceleration projectedOn(Vector3 normalisedVector) => + new Acceleration(Vector3.Dot(value, normalisedVector)); - #endregion + #endregion - #region equality and hashcode + #region equality and hashcode - public bool Equals(Acceleration3 other) => value == other.value; + public bool Equals(Acceleration3 other) => value == other.value; - public override bool Equals(object? obj) => obj is Acceleration3 a && Equals(a); + public override bool Equals(object? obj) => obj is Acceleration3 a && Equals(a); - public override int GetHashCode() => value.GetHashCode(); + public override int GetHashCode() => value.GetHashCode(); - #endregion + #endregion - #region tostring + #region tostring - public override string ToString() => ToString(null, CultureInfo.CurrentCulture); + public override string ToString() => ToString(null, CultureInfo.CurrentCulture); - public string ToString(string? format, IFormatProvider? formatProvider) => "(" + - $"{value.X.ToString(format, formatProvider)}, " + - $"{value.Y.ToString(format, formatProvider)}, " + - $"{value.Z.ToString(format, formatProvider)}) u/t²"; + public string ToString(string? format, IFormatProvider? formatProvider) => "(" + + $"{value.X.ToString(format, formatProvider)}, " + + $"{value.Y.ToString(format, formatProvider)}, " + + $"{value.Z.ToString(format, formatProvider)}) u/t²"; - public string ToString(string? format) - => ToString(format, CultureInfo.CurrentCulture); + public string ToString(string? format) + => ToString(format, CultureInfo.CurrentCulture); - #endregion + #endregion - #endregion + #endregion - #region operators + #region operators - #region algebra + #region algebra - /// - /// Adds two acceleration vectors. - /// - public static Acceleration3 operator +(Acceleration3 a0, Acceleration3 a1) => - new Acceleration3(a0.value + a1.value); + /// + /// Adds two acceleration vectors. + /// + public static Acceleration3 operator +(Acceleration3 a0, Acceleration3 a1) => + new Acceleration3(a0.value + a1.value); - /// - /// Subtracts a acceleration vector from another. - /// - public static Acceleration3 operator -(Acceleration3 a0, Acceleration3 a1) => - new Acceleration3(a0.value - a1.value); + /// + /// Subtracts a acceleration vector from another. + /// + public static Acceleration3 operator -(Acceleration3 a0, Acceleration3 a1) => + new Acceleration3(a0.value - a1.value); - #endregion + #endregion - #region scaling + #region scaling - /// - /// Inverts the acceleration vector. - /// - public static Acceleration3 operator -(Acceleration3 a) => new Acceleration3(-a.value); + /// + /// Inverts the acceleration vector. + /// + public static Acceleration3 operator -(Acceleration3 a) => new Acceleration3(-a.value); - /// - /// Multiplies the acceleration vector with a scalar. - /// - public static Acceleration3 operator *(Acceleration3 a, float scalar) => new Acceleration3(a.value * scalar); + /// + /// Multiplies the acceleration vector with a scalar. + /// + public static Acceleration3 operator *(Acceleration3 a, float scalar) => new Acceleration3(a.value * scalar); - /// - /// Multiplies the acceleration vector with a scalar. - /// - public static Acceleration3 operator *(float scalar, Acceleration3 a) => new Acceleration3(a.value * scalar); + /// + /// Multiplies the acceleration vector with a scalar. + /// + public static Acceleration3 operator *(float scalar, Acceleration3 a) => new Acceleration3(a.value * scalar); - /// - /// Divides the acceleration vector by a divisor. - /// - public static Acceleration3 operator /(Acceleration3 a, float divisor) => new Acceleration3(a.value / divisor); + /// + /// Divides the acceleration vector by a divisor. + /// + public static Acceleration3 operator /(Acceleration3 a, float divisor) => new Acceleration3(a.value / divisor); - #endregion + #endregion - #region ratio + #region ratio - /// - /// Divides a acceleration vector by a speed, returning an untyped vector. - /// - public static Vector3 operator /(Acceleration3 a, Acceleration divisor) => a.value / divisor.NumericValue; + /// + /// Divides a acceleration vector by a speed, returning an untyped vector. + /// + public static Vector3 operator /(Acceleration3 a, Acceleration divisor) => a.value / divisor.NumericValue; - #endregion + #endregion - #region integrate + #region integrate - /// - /// Multiplies a acceleration vector by a timespan, returning a velocity vector. - /// - public static Velocity3 operator *(Acceleration3 a, TimeSpan t) => - new Velocity3(a.value * (float)t.NumericValue); + /// + /// Multiplies a acceleration vector by a timespan, returning a velocity vector. + /// + public static Velocity3 operator *(Acceleration3 a, TimeSpan t) => + new Velocity3(a.value * (float)t.NumericValue); - /// - /// Multiplies a acceleration vector by a timespan, returning a velocity vector. - /// - public static Velocity3 operator *(TimeSpan t, Acceleration3 a) => - new Velocity3(a.value * (float)t.NumericValue); + /// + /// Multiplies a acceleration vector by a timespan, returning a velocity vector. + /// + public static Velocity3 operator *(TimeSpan t, Acceleration3 a) => + new Velocity3(a.value * (float)t.NumericValue); - #endregion + #endregion - #region comparison + #region comparison - /// - /// Compares two acceleration vectors for equality. - /// - public static bool operator ==(Acceleration3 a0, Acceleration3 a1) => a0.Equals(a1); + /// + /// Compares two acceleration vectors for equality. + /// + public static bool operator ==(Acceleration3 a0, Acceleration3 a1) => a0.Equals(a1); - /// - /// Compares two acceleration vectors for inequality. - /// - public static bool operator !=(Acceleration3 a0, Acceleration3 a1) => !(a0 == a1); + /// + /// Compares two acceleration vectors for inequality. + /// + public static bool operator !=(Acceleration3 a0, Acceleration3 a1) => !(a0 == a1); - #endregion + #endregion - #endregion + #endregion - } } diff --git a/Bearded.Utilities/SpaceTime/3d/AddDimensionExtensions.cs b/Bearded.Utilities/SpaceTime/3d/AddDimensionExtensions.cs index 90645696..ae4a45f7 100644 --- a/Bearded.Utilities/SpaceTime/3d/AddDimensionExtensions.cs +++ b/Bearded.Utilities/SpaceTime/3d/AddDimensionExtensions.cs @@ -1,9 +1,8 @@ -namespace Bearded.Utilities.SpaceTime +namespace Bearded.Utilities.SpaceTime; + +public static class AddDimensionExtensions { - public static class AddDimensionExtensions - { - public static Position3 WithZ(this Position2 pos, float z) => pos.WithZ(new Unit(z)); + public static Position3 WithZ(this Position2 pos, float z) => pos.WithZ(new Unit(z)); - public static Position3 WithZ(this Position2 pos, Unit z) => new Position3(pos.X, pos.Y, z); - } + public static Position3 WithZ(this Position2 pos, Unit z) => new Position3(pos.X, pos.Y, z); } diff --git a/Bearded.Utilities/SpaceTime/3d/Difference3.cs b/Bearded.Utilities/SpaceTime/3d/Difference3.cs index 68d3a54c..f114b2ae 100644 --- a/Bearded.Utilities/SpaceTime/3d/Difference3.cs +++ b/Bearded.Utilities/SpaceTime/3d/Difference3.cs @@ -2,215 +2,214 @@ using System.Globalization; using OpenTK.Mathematics; -namespace Bearded.Utilities.SpaceTime +namespace Bearded.Utilities.SpaceTime; + +/// +/// A type-safe representation of a 3d directed difference vector. +/// +public readonly struct Difference3 : IEquatable, IFormattable { - /// - /// A type-safe representation of a 3d directed difference vector. - /// - public readonly struct Difference3 : IEquatable, IFormattable - { - private readonly Vector3 value; + private readonly Vector3 value; - #region constructing + #region constructing - public Difference3(Vector3 value) - { - this.value = value; - } + public Difference3(Vector3 value) + { + this.value = value; + } - public Difference3(float x, float y, float z) - : this(new Vector3(x, y, z)) - { - } + public Difference3(float x, float y, float z) + : this(new Vector3(x, y, z)) + { + } - public Difference3(Unit x, Unit y, Unit z) - : this(new Vector3(x.NumericValue, y.NumericValue, z.NumericValue)) - { - } + public Difference3(Unit x, Unit y, Unit z) + : this(new Vector3(x.NumericValue, y.NumericValue, z.NumericValue)) + { + } - #endregion + #endregion - #region properties + #region properties - /// - /// Returns the numeric vector value of the difference vector. - /// - public Vector3 NumericValue => value; + /// + /// Returns the numeric vector value of the difference vector. + /// + public Vector3 NumericValue => value; - /// - /// Returns the X component of the difference vector. - /// - public Unit X => new Unit(value.X); + /// + /// Returns the X component of the difference vector. + /// + public Unit X => new Unit(value.X); - /// - /// Returns the Y component of the difference vector. - /// - public Unit Y => new Unit(value.Y); + /// + /// Returns the Y component of the difference vector. + /// + public Unit Y => new Unit(value.Y); - /// - /// Returns the Z component of the difference vector. - /// - public Unit Z => new Unit(value.Z); + /// + /// Returns the Z component of the difference vector. + /// + public Unit Z => new Unit(value.Z); - /// - /// Returns the typed magnitude of the difference vector. - /// - public Unit Length => new Unit(value.Length); + /// + /// Returns the typed magnitude of the difference vector. + /// + public Unit Length => new Unit(value.Length); - /// - /// Returns the typed square of the magnitude of the difference vector. - /// - public Squared LengthSquared => new Squared(value.LengthSquared); + /// + /// Returns the typed square of the magnitude of the difference vector. + /// + public Squared LengthSquared => new Squared(value.LengthSquared); - /// - /// Returns a Difference3 type with value 0. - /// - public static Difference3 Zero => new Difference3(0, 0, 0); + /// + /// Returns a Difference3 type with value 0. + /// + public static Difference3 Zero => new Difference3(0, 0, 0); - #endregion + #endregion - #region methods + #region methods - #region lerp + #region lerp - /// - /// Linearly interpolates between two typed difference vectors. - /// - /// The difference vector at t = 0. - /// The difference vector at t = 1. - /// The interpolation scalar. - public static Difference3 Lerp(Difference3 d0, Difference3 d1, float t) => d0 + (d1 - d0) * t; + /// + /// Linearly interpolates between two typed difference vectors. + /// + /// The difference vector at t = 0. + /// The difference vector at t = 1. + /// The interpolation scalar. + public static Difference3 Lerp(Difference3 d0, Difference3 d1, float t) => d0 + (d1 - d0) * t; - /// - /// Linearly interpolates towards another typed difference vector. - /// - /// The difference vector at t = 1. - /// The interpolation scalar. - public Difference3 LerpTo(Difference3 d, float t) => Lerp(this, d, t); + /// + /// Linearly interpolates towards another typed difference vector. + /// + /// The difference vector at t = 1. + /// The interpolation scalar. + public Difference3 LerpTo(Difference3 d, float t) => Lerp(this, d, t); - #endregion + #endregion - #region projection + #region projection - /// - /// Projects the difference vector onto an untyped vector, returning the speed component in that vector's direction. - /// - public Unit ProjectedOn(Vector3 vector) => projectedOn(vector.NormalizedSafe()); + /// + /// Projects the difference vector onto an untyped vector, returning the speed component in that vector's direction. + /// + public Unit ProjectedOn(Vector3 vector) => projectedOn(vector.NormalizedSafe()); - /// - /// Projects the difference vector onto a difference vector, returning the speed component in that vector's direction. - /// - public Unit ProjectedOn(Difference3 vector) => projectedOn(vector.NumericValue.NormalizedSafe()); + /// + /// Projects the difference vector onto a difference vector, returning the speed component in that vector's direction. + /// + public Unit ProjectedOn(Difference3 vector) => projectedOn(vector.NumericValue.NormalizedSafe()); - private Unit projectedOn(Vector3 normalisedVector) => new Unit(Vector3.Dot(value, normalisedVector)); + private Unit projectedOn(Vector3 normalisedVector) => new Unit(Vector3.Dot(value, normalisedVector)); - #endregion + #endregion - #region equality and hashcode + #region equality and hashcode - public bool Equals(Difference3 other) => value == other.value; + public bool Equals(Difference3 other) => value == other.value; - public override bool Equals(object? obj) => obj is Difference3 diff && Equals(diff); + public override bool Equals(object? obj) => obj is Difference3 diff && Equals(diff); - public override int GetHashCode() => value.GetHashCode(); + public override int GetHashCode() => value.GetHashCode(); - #endregion + #endregion - #region tostring + #region tostring - public override string ToString() => ToString(null, CultureInfo.CurrentCulture); + public override string ToString() => ToString(null, CultureInfo.CurrentCulture); - public string ToString(string? format, IFormatProvider? formatProvider) => "(" + - $"{value.X.ToString(format, formatProvider)}, " + - $"{value.Y.ToString(format, formatProvider)}, " + - $"{value.Z.ToString(format, formatProvider)}) u"; + public string ToString(string? format, IFormatProvider? formatProvider) => "(" + + $"{value.X.ToString(format, formatProvider)}, " + + $"{value.Y.ToString(format, formatProvider)}, " + + $"{value.Z.ToString(format, formatProvider)}) u"; - public string ToString(string? format) - => ToString(format, CultureInfo.CurrentCulture); + public string ToString(string? format) + => ToString(format, CultureInfo.CurrentCulture); - #endregion + #endregion - #endregion + #endregion - #region operators + #region operators - #region algebra + #region algebra - /// - /// Adds two difference vectors. - /// - public static Difference3 operator +(Difference3 d0, Difference3 d1) => new Difference3(d0.value + d1.value); + /// + /// Adds two difference vectors. + /// + public static Difference3 operator +(Difference3 d0, Difference3 d1) => new Difference3(d0.value + d1.value); - /// - /// Subtracts a difference vector from another. - /// - public static Difference3 operator -(Difference3 d0, Difference3 d1) => new Difference3(d0.value - d1.value); + /// + /// Subtracts a difference vector from another. + /// + public static Difference3 operator -(Difference3 d0, Difference3 d1) => new Difference3(d0.value - d1.value); - #endregion + #endregion - #region scaling + #region scaling - /// - /// Inverts the difference vector. - /// - public static Difference3 operator -(Difference3 d) => new Difference3(-d.value); + /// + /// Inverts the difference vector. + /// + public static Difference3 operator -(Difference3 d) => new Difference3(-d.value); - /// - /// Multiplies the difference vector with a scalar. - /// - public static Difference3 operator *(Difference3 d, float scalar) => new Difference3(d.value * scalar); + /// + /// Multiplies the difference vector with a scalar. + /// + public static Difference3 operator *(Difference3 d, float scalar) => new Difference3(d.value * scalar); - /// - /// Multiplies the difference vector with a scalar. - /// - public static Difference3 operator *(float scalar, Difference3 d) => new Difference3(d.value * scalar); + /// + /// Multiplies the difference vector with a scalar. + /// + public static Difference3 operator *(float scalar, Difference3 d) => new Difference3(d.value * scalar); - /// - /// Divides the difference vector by a divisor. - /// - public static Difference3 operator /(Difference3 d, float divisor) => new Difference3(d.value / divisor); + /// + /// Divides the difference vector by a divisor. + /// + public static Difference3 operator /(Difference3 d, float divisor) => new Difference3(d.value / divisor); - #endregion + #endregion - #region ratio + #region ratio - /// - /// Divides a difference vector by a speed, returning an untyped vector. - /// - public static Vector3 operator /(Difference3 d, Unit divisor) => d.value / divisor.NumericValue; + /// + /// Divides a difference vector by a speed, returning an untyped vector. + /// + public static Vector3 operator /(Difference3 d, Unit divisor) => d.value / divisor.NumericValue; - #endregion + #endregion - #region differentiate + #region differentiate - /// - /// Divides a difference vector by a timespan, returning an velocity vector. - /// - public static Velocity3 operator /(Difference3 d, TimeSpan t) => - new Velocity3(d.value / (float)t.NumericValue); + /// + /// Divides a difference vector by a timespan, returning an velocity vector. + /// + public static Velocity3 operator /(Difference3 d, TimeSpan t) => + new Velocity3(d.value / (float)t.NumericValue); - public static Velocity3 operator *(Difference3 d, Frequency f) => - new Velocity3(d.value * (float)f.NumericValue); + public static Velocity3 operator *(Difference3 d, Frequency f) => + new Velocity3(d.value * (float)f.NumericValue); - public static Velocity3 operator *(Frequency f, Difference3 d) => - new Velocity3(d.value * (float)f.NumericValue); + public static Velocity3 operator *(Frequency f, Difference3 d) => + new Velocity3(d.value * (float)f.NumericValue); - #endregion + #endregion - #region comparison + #region comparison - /// - /// Compares two difference vectors for equality. - /// - public static bool operator ==(Difference3 d0, Difference3 d1) => d0.Equals(d1); + /// + /// Compares two difference vectors for equality. + /// + public static bool operator ==(Difference3 d0, Difference3 d1) => d0.Equals(d1); - /// - /// Compares two difference vectors for inequality. - /// - public static bool operator !=(Difference3 d0, Difference3 d1) => !(d0 == d1); + /// + /// Compares two difference vectors for inequality. + /// + public static bool operator !=(Difference3 d0, Difference3 d1) => !(d0 == d1); - #endregion + #endregion - #endregion - } + #endregion } diff --git a/Bearded.Utilities/SpaceTime/3d/Position3.cs b/Bearded.Utilities/SpaceTime/3d/Position3.cs index d9655321..e270a868 100644 --- a/Bearded.Utilities/SpaceTime/3d/Position3.cs +++ b/Bearded.Utilities/SpaceTime/3d/Position3.cs @@ -2,151 +2,150 @@ using System.Globalization; using OpenTK.Mathematics; -namespace Bearded.Utilities.SpaceTime +namespace Bearded.Utilities.SpaceTime; + +/// +/// A type-safe representation of an absolute 3d position vector. +/// +public readonly struct Position3 : IEquatable, IFormattable { - /// - /// A type-safe representation of an absolute 3d position vector. - /// - public readonly struct Position3 : IEquatable, IFormattable - { - private readonly Vector3 value; + private readonly Vector3 value; - #region construction + #region construction - public Position3(Vector3 value) - { - this.value = value; - } + public Position3(Vector3 value) + { + this.value = value; + } - public Position3(float x, float y, float z) - : this(new Vector3(x, y, z)) - { - } + public Position3(float x, float y, float z) + : this(new Vector3(x, y, z)) + { + } - public Position3(Unit x, Unit y, Unit z) - : this(new Vector3(x.NumericValue, y.NumericValue, z.NumericValue)) - { - } + public Position3(Unit x, Unit y, Unit z) + : this(new Vector3(x.NumericValue, y.NumericValue, z.NumericValue)) + { + } - #endregion + #endregion - #region properties + #region properties - /// - /// Returns the numeric vector value of the position vector. - /// - public Vector3 NumericValue => value; + /// + /// Returns the numeric vector value of the position vector. + /// + public Vector3 NumericValue => value; - /// - /// Returns the X component of the position vector. - /// - public Unit X => new Unit(value.X); + /// + /// Returns the X component of the position vector. + /// + public Unit X => new Unit(value.X); - /// - /// Returns the Y component of the position vector. - /// - public Unit Y => new Unit(value.Y); + /// + /// Returns the Y component of the position vector. + /// + public Unit Y => new Unit(value.Y); - /// - /// Returns the Z component of the position vector. - /// - public Unit Z => new Unit(value.Z); + /// + /// Returns the Z component of the position vector. + /// + public Unit Z => new Unit(value.Z); - /// - /// Returns a Position2 type with value 0. - /// - public static Position3 Zero => new Position3(0, 0, 0); + /// + /// Returns a Position2 type with value 0. + /// + public static Position3 Zero => new Position3(0, 0, 0); - #endregion + #endregion - #region methods + #region methods - #region lerp + #region lerp - /// - /// Linearly interpolates between two typed position vectors. - /// - /// The position vector at t = 0. - /// The position vector at t = 1. - /// The interpolation scalar. - public static Position3 Lerp(Position3 p0, Position3 p1, float t) => p0 + (p1 - p0) * t; + /// + /// Linearly interpolates between two typed position vectors. + /// + /// The position vector at t = 0. + /// The position vector at t = 1. + /// The interpolation scalar. + public static Position3 Lerp(Position3 p0, Position3 p1, float t) => p0 + (p1 - p0) * t; - /// - /// Linearly interpolates towards another typed position vector. - /// - /// The position vector at t = 1. - /// The interpolation scalar. - public Position3 LerpTo(Position3 p, float t) => Lerp(this, p, t); + /// + /// Linearly interpolates towards another typed position vector. + /// + /// The position vector at t = 1. + /// The interpolation scalar. + public Position3 LerpTo(Position3 p, float t) => Lerp(this, p, t); - #endregion + #endregion - #region equality and hashcode + #region equality and hashcode - public bool Equals(Position3 other) => value == other.value; + public bool Equals(Position3 other) => value == other.value; - public override bool Equals(object? obj) => obj is Position3 pos && Equals(pos); + public override bool Equals(object? obj) => obj is Position3 pos && Equals(pos); - public override int GetHashCode() => value.GetHashCode(); + public override int GetHashCode() => value.GetHashCode(); - #endregion + #endregion - #region tostring + #region tostring - public override string ToString() => ToString(null, CultureInfo.CurrentCulture); + public override string ToString() => ToString(null, CultureInfo.CurrentCulture); - public string ToString(string? format, IFormatProvider? formatProvider) => "(" + - $"{value.X.ToString(format, formatProvider)}, " + - $"{value.Y.ToString(format, formatProvider)}, " + - $"{value.Z.ToString(format, formatProvider)}) u"; + public string ToString(string? format, IFormatProvider? formatProvider) => "(" + + $"{value.X.ToString(format, formatProvider)}, " + + $"{value.Y.ToString(format, formatProvider)}, " + + $"{value.Z.ToString(format, formatProvider)}) u"; - public string ToString(string? format) - => ToString(format, CultureInfo.CurrentCulture); + public string ToString(string? format) + => ToString(format, CultureInfo.CurrentCulture); - #endregion + #endregion - #endregion + #endregion - #region operators + #region operators - #region Difference2 interaction + #region Difference2 interaction - /// - /// Adds a difference vector to an absolute position. - /// - public static Position3 operator +(Position3 p, Difference3 d) => new Position3(p.value + d.NumericValue); + /// + /// Adds a difference vector to an absolute position. + /// + public static Position3 operator +(Position3 p, Difference3 d) => new Position3(p.value + d.NumericValue); - /// - /// Adds a difference vector to an absolute position. - /// - public static Position3 operator +(Difference3 d, Position3 p) => new Position3(p.value + d.NumericValue); + /// + /// Adds a difference vector to an absolute position. + /// + public static Position3 operator +(Difference3 d, Position3 p) => new Position3(p.value + d.NumericValue); - /// - /// Subtracts a difference vector from an absolute position. - /// - public static Position3 operator -(Position3 p, Difference3 d) => new Position3(p.value - d.NumericValue); + /// + /// Subtracts a difference vector from an absolute position. + /// + public static Position3 operator -(Position3 p, Difference3 d) => new Position3(p.value - d.NumericValue); - /// - /// Subtracts two absolute positions, returning a difference vector. - /// - public static Difference3 operator -(Position3 p0, Position3 p1) => new Difference3(p0.value - p1.value); + /// + /// Subtracts two absolute positions, returning a difference vector. + /// + public static Difference3 operator -(Position3 p0, Position3 p1) => new Difference3(p0.value - p1.value); - #endregion + #endregion - #region comparison + #region comparison - /// - /// Compares two position vectors for equality. - /// - public static bool operator ==(Position3 p0, Position3 p1) => p0.Equals(p1); + /// + /// Compares two position vectors for equality. + /// + public static bool operator ==(Position3 p0, Position3 p1) => p0.Equals(p1); - /// - /// Compares two position vectors for inequality. - /// - public static bool operator !=(Position3 p0, Position3 p1) => !(p0 == p1); + /// + /// Compares two position vectors for inequality. + /// + public static bool operator !=(Position3 p0, Position3 p1) => !(p0 == p1); - #endregion + #endregion - #endregion + #endregion - } } diff --git a/Bearded.Utilities/SpaceTime/3d/Velocity3.cs b/Bearded.Utilities/SpaceTime/3d/Velocity3.cs index c6058b31..90c3882a 100644 --- a/Bearded.Utilities/SpaceTime/3d/Velocity3.cs +++ b/Bearded.Utilities/SpaceTime/3d/Velocity3.cs @@ -2,235 +2,234 @@ using System.Globalization; using OpenTK.Mathematics; -namespace Bearded.Utilities.SpaceTime +namespace Bearded.Utilities.SpaceTime; + +/// +/// A type-safe representation of a 3d directed velocity vector. +/// +public readonly struct Velocity3 : IEquatable, IFormattable { - /// - /// A type-safe representation of a 3d directed velocity vector. - /// - public readonly struct Velocity3 : IEquatable, IFormattable - { - private readonly Vector3 value; + private readonly Vector3 value; - #region construction + #region construction - public Velocity3(Vector3 value) - { - this.value = value; - } + public Velocity3(Vector3 value) + { + this.value = value; + } - public Velocity3(float x, float y, float z) - : this(new Vector3(x, y, z)) - { - } + public Velocity3(float x, float y, float z) + : this(new Vector3(x, y, z)) + { + } - public Velocity3(Speed x, Speed y, Speed z) - : this(new Vector3(x.NumericValue, y.NumericValue, z.NumericValue)) - { - } + public Velocity3(Speed x, Speed y, Speed z) + : this(new Vector3(x.NumericValue, y.NumericValue, z.NumericValue)) + { + } - #endregion + #endregion - #region properties + #region properties - /// - /// Returns the numeric vector value of the velocity vector. - /// - public Vector3 NumericValue => value; + /// + /// Returns the numeric vector value of the velocity vector. + /// + public Vector3 NumericValue => value; - /// - /// Returns the X component of the velocity vector. - /// - public Speed X => new Speed(value.X); + /// + /// Returns the X component of the velocity vector. + /// + public Speed X => new Speed(value.X); - /// - /// Returns the Y component of the velocity vector. - /// - public Speed Y => new Speed(value.Y); + /// + /// Returns the Y component of the velocity vector. + /// + public Speed Y => new Speed(value.Y); - /// - /// Returns the Z component of the velocity vector. - /// - public Speed Z => new Speed(value.Z); + /// + /// Returns the Z component of the velocity vector. + /// + public Speed Z => new Speed(value.Z); - /// - /// Returns the typed magnitude of the velocity vector. - /// - public Speed Length => new Speed(value.Length); + /// + /// Returns the typed magnitude of the velocity vector. + /// + public Speed Length => new Speed(value.Length); - /// - /// Returns the typed square of the magnitude of the velocity vector. - /// - public Squared LengthSquared => new Squared(value.LengthSquared); + /// + /// Returns the typed square of the magnitude of the velocity vector. + /// + public Squared LengthSquared => new Squared(value.LengthSquared); - /// - /// Returns a Velocity3 type with value 0. - /// - public static Velocity3 Zero => new Velocity3(0, 0, 0); + /// + /// Returns a Velocity3 type with value 0. + /// + public static Velocity3 Zero => new Velocity3(0, 0, 0); - #endregion + #endregion - #region methods + #region methods - #region lerp + #region lerp - /// - /// Linearly interpolates between two typed velocity vectors. - /// - /// The velocity vector at t = 0. - /// The velocity vector at t = 1. - /// The interpolation scalar. - public static Velocity3 Lerp(Velocity3 v0, Velocity3 v1, float t) => v0 + (v1 - v0) * t; + /// + /// Linearly interpolates between two typed velocity vectors. + /// + /// The velocity vector at t = 0. + /// The velocity vector at t = 1. + /// The interpolation scalar. + public static Velocity3 Lerp(Velocity3 v0, Velocity3 v1, float t) => v0 + (v1 - v0) * t; - /// - /// Linearly interpolates towards another typed velocity vector. - /// - /// The velocity vector at t = 1. - /// The interpolation scalar. - public Velocity3 LerpTo(Velocity3 v, float t) => Lerp(this, v, t); + /// + /// Linearly interpolates towards another typed velocity vector. + /// + /// The velocity vector at t = 1. + /// The interpolation scalar. + public Velocity3 LerpTo(Velocity3 v, float t) => Lerp(this, v, t); - #endregion + #endregion - #region projection + #region projection - /// - /// Projects the velocity vector onto an untyped vector, returning the speed component in that vector's direction. - /// - public Speed ProjectedOn(Vector3 vector) => projectedOn(vector.NormalizedSafe()); + /// + /// Projects the velocity vector onto an untyped vector, returning the speed component in that vector's direction. + /// + public Speed ProjectedOn(Vector3 vector) => projectedOn(vector.NormalizedSafe()); - /// - /// Projects the velocity vector onto a difference vector, returning the speed component in that vector's direction. - /// - public Speed ProjectedOn(Difference3 vector) => projectedOn(vector.NumericValue.NormalizedSafe()); + /// + /// Projects the velocity vector onto a difference vector, returning the speed component in that vector's direction. + /// + public Speed ProjectedOn(Difference3 vector) => projectedOn(vector.NumericValue.NormalizedSafe()); - private Speed projectedOn(Vector3 normalisedVector) => new Speed(Vector3.Dot(value, normalisedVector)); + private Speed projectedOn(Vector3 normalisedVector) => new Speed(Vector3.Dot(value, normalisedVector)); - #endregion + #endregion - #region equality and hashcode + #region equality and hashcode - public bool Equals(Velocity3 other) => value == other.value; + public bool Equals(Velocity3 other) => value == other.value; - public override bool Equals(object? obj) => obj is Velocity3 v && Equals(v); + public override bool Equals(object? obj) => obj is Velocity3 v && Equals(v); - public override int GetHashCode() => value.GetHashCode(); + public override int GetHashCode() => value.GetHashCode(); - #endregion + #endregion - #region tostring + #region tostring - public override string ToString() => ToString(null, CultureInfo.CurrentCulture); + public override string ToString() => ToString(null, CultureInfo.CurrentCulture); - public string ToString(string? format, IFormatProvider? formatProvider) => "(" + - $"{value.X.ToString(format, formatProvider)}, " + - $"{value.Y.ToString(format, formatProvider)}, " + - $"{value.Z.ToString(format, formatProvider)}) u/t"; + public string ToString(string? format, IFormatProvider? formatProvider) => "(" + + $"{value.X.ToString(format, formatProvider)}, " + + $"{value.Y.ToString(format, formatProvider)}, " + + $"{value.Z.ToString(format, formatProvider)}) u/t"; - public string ToString(string? format) - => ToString(format, CultureInfo.CurrentCulture); + public string ToString(string? format) + => ToString(format, CultureInfo.CurrentCulture); - #endregion + #endregion - #endregion + #endregion - #region operators + #region operators - #region algebra + #region algebra - /// - /// Adds two velocity vectors. - /// - public static Velocity3 operator +(Velocity3 v0, Velocity3 v1) => new Velocity3(v0.value + v1.value); + /// + /// Adds two velocity vectors. + /// + public static Velocity3 operator +(Velocity3 v0, Velocity3 v1) => new Velocity3(v0.value + v1.value); - /// - /// Subtracts a velocity vector from another. - /// - public static Velocity3 operator -(Velocity3 v0, Velocity3 v1) => new Velocity3(v0.value - v1.value); + /// + /// Subtracts a velocity vector from another. + /// + public static Velocity3 operator -(Velocity3 v0, Velocity3 v1) => new Velocity3(v0.value - v1.value); - #endregion + #endregion - #region scaling + #region scaling - /// - /// Inverts the velocity vector. - /// - public static Velocity3 operator -(Velocity3 v) => new Velocity3(-v.value); + /// + /// Inverts the velocity vector. + /// + public static Velocity3 operator -(Velocity3 v) => new Velocity3(-v.value); - /// - /// Multiplies the velocity vector with a scalar. - /// - public static Velocity3 operator *(Velocity3 v, float scalar) => new Velocity3(v.value * scalar); + /// + /// Multiplies the velocity vector with a scalar. + /// + public static Velocity3 operator *(Velocity3 v, float scalar) => new Velocity3(v.value * scalar); - /// - /// Multiplies the velocity vector with a scalar. - /// - public static Velocity3 operator *(float scalar, Velocity3 v) => new Velocity3(v.value * scalar); + /// + /// Multiplies the velocity vector with a scalar. + /// + public static Velocity3 operator *(float scalar, Velocity3 v) => new Velocity3(v.value * scalar); - /// - /// Divides the velocity vector by a divisor. - /// - public static Velocity3 operator /(Velocity3 v, float divisor) => new Velocity3(v.value / divisor); + /// + /// Divides the velocity vector by a divisor. + /// + public static Velocity3 operator /(Velocity3 v, float divisor) => new Velocity3(v.value / divisor); - #endregion + #endregion - #region ratio + #region ratio - /// - /// Divides a velocity vector by a speed, returning an untyped vector. - /// - public static Vector3 operator /(Velocity3 v, Speed divisor) => v.value / divisor.NumericValue; + /// + /// Divides a velocity vector by a speed, returning an untyped vector. + /// + public static Vector3 operator /(Velocity3 v, Speed divisor) => v.value / divisor.NumericValue; - #endregion + #endregion - #region differentiate + #region differentiate - /// - /// Divides a velocity vector by a timespan, returning an acceleration vector. - /// - public static Acceleration3 operator /(Velocity3 v, TimeSpan t) => - new Acceleration3(v.value / (float)t.NumericValue); + /// + /// Divides a velocity vector by a timespan, returning an acceleration vector. + /// + public static Acceleration3 operator /(Velocity3 v, TimeSpan t) => + new Acceleration3(v.value / (float)t.NumericValue); - public static Acceleration3 operator *(Velocity3 v, Frequency f) => - new Acceleration3(v.value * (float)f.NumericValue); + public static Acceleration3 operator *(Velocity3 v, Frequency f) => + new Acceleration3(v.value * (float)f.NumericValue); - public static Acceleration3 operator *(Frequency f, Velocity3 v) => - new Acceleration3(v.value * (float)f.NumericValue); + public static Acceleration3 operator *(Frequency f, Velocity3 v) => + new Acceleration3(v.value * (float)f.NumericValue); - #endregion + #endregion - #region integrate + #region integrate - /// - /// Multiplies a velocity vector by a timespan, returning a difference vector. - /// - public static Difference3 operator *(Velocity3 v, TimeSpan t) => - new Difference3(v.value * (float)t.NumericValue); + /// + /// Multiplies a velocity vector by a timespan, returning a difference vector. + /// + public static Difference3 operator *(Velocity3 v, TimeSpan t) => + new Difference3(v.value * (float)t.NumericValue); - /// - /// Multiplies a velocity vector by a timespan, returning a difference vector. - /// - public static Difference3 operator *(TimeSpan t, Velocity3 v) => - new Difference3(v.value * (float)t.NumericValue); + /// + /// Multiplies a velocity vector by a timespan, returning a difference vector. + /// + public static Difference3 operator *(TimeSpan t, Velocity3 v) => + new Difference3(v.value * (float)t.NumericValue); - public static Difference3 operator /(Velocity3 v, Frequency f) => - new Difference3(v.value / (float)f.NumericValue); + public static Difference3 operator /(Velocity3 v, Frequency f) => + new Difference3(v.value / (float)f.NumericValue); - #endregion + #endregion - #region comparison + #region comparison - /// - /// Compares two velocity vectors for equality. - /// - public static bool operator ==(Velocity3 v0, Velocity3 v1) => v0.Equals(v1); + /// + /// Compares two velocity vectors for equality. + /// + public static bool operator ==(Velocity3 v0, Velocity3 v1) => v0.Equals(v1); - /// - /// Compares two velocity vectors for inequality. - /// - public static bool operator !=(Velocity3 v0, Velocity3 v1) => !(v0 == v1); + /// + /// Compares two velocity vectors for inequality. + /// + public static bool operator !=(Velocity3 v0, Velocity3 v1) => !(v0 == v1); - #endregion + #endregion - #endregion + #endregion - } } diff --git a/Bearded.Utilities/SpaceTime/Extensions.cs b/Bearded.Utilities/SpaceTime/Extensions.cs index 6cc87489..4bdb1977 100644 --- a/Bearded.Utilities/SpaceTime/Extensions.cs +++ b/Bearded.Utilities/SpaceTime/Extensions.cs @@ -1,38 +1,37 @@ -namespace Bearded.Utilities.SpaceTime +namespace Bearded.Utilities.SpaceTime; + +/// +/// Contains a variety of extension methods for the SpaceTime namespace. +/// +public static class Extensions { /// - /// Contains a variety of extension methods for the SpaceTime namespace. + /// Creates a new instance of the Unit type. /// - public static class Extensions - { - /// - /// Creates a new instance of the Unit type. - /// - public static Unit U(this double value) => new Unit((float)value); + public static Unit U(this double value) => new Unit((float)value); - /// - /// Creates a new instance of the Unit type. - /// - public static Unit U(this float value) => new Unit(value); + /// + /// Creates a new instance of the Unit type. + /// + public static Unit U(this float value) => new Unit(value); - /// - /// Creates a new instance of the Unit type. - /// - public static Unit U(this int value) => new Unit(value); + /// + /// Creates a new instance of the Unit type. + /// + public static Unit U(this int value) => new Unit(value); - /// - /// Returns the typed square root of the squared unit value. - /// - public static Unit Sqrt(this Squared square) => new Unit(square.NumericValue.Sqrted()); + /// + /// Returns the typed square root of the squared unit value. + /// + public static Unit Sqrt(this Squared square) => new Unit(square.NumericValue.Sqrted()); - /// - /// Returns the typed square root of the squared speed. - /// - public static Speed Sqrt(this Squared square) => new Speed(square.NumericValue.Sqrted()); + /// + /// Returns the typed square root of the squared speed. + /// + public static Speed Sqrt(this Squared square) => new Speed(square.NumericValue.Sqrted()); - /// - /// Returns the typed square root of the squared acceleration. - /// - public static Acceleration Sqrt(this Squared square) => new Acceleration(square.NumericValue.Sqrted()); - } + /// + /// Returns the typed square root of the squared acceleration. + /// + public static Acceleration Sqrt(this Squared square) => new Acceleration(square.NumericValue.Sqrted()); } diff --git a/Bearded.Utilities/SpaceTime/Squared.cs b/Bearded.Utilities/SpaceTime/Squared.cs index a725465c..3a24c659 100644 --- a/Bearded.Utilities/SpaceTime/Squared.cs +++ b/Bearded.Utilities/SpaceTime/Squared.cs @@ -1,162 +1,161 @@ using System; using System.Globalization; -namespace Bearded.Utilities.SpaceTime +namespace Bearded.Utilities.SpaceTime; + +/// +/// Represents a type-safe squared value, backed by a float. +/// +/// The squared type. +public readonly struct Squared : IEquatable>, IComparable>, IFormattable + where T : struct { - /// - /// Represents a type-safe squared value, backed by a float. - /// - /// The squared type. - public readonly struct Squared : IEquatable>, IComparable>, IFormattable - where T : struct - { - private readonly float value; + private readonly float value; - #region construcing + #region construcing - internal Squared(float value) - { - this.value = value; - } + internal Squared(float value) + { + this.value = value; + } - /// - /// Creates a new instance of the Squared type, from a given root value. - /// - public static Squared FromRoot(float root) - { - return new Squared(root.Squared()); - } + /// + /// Creates a new instance of the Squared type, from a given root value. + /// + public static Squared FromRoot(float root) + { + return new Squared(root.Squared()); + } - /// - /// Creates a new instance of the Squared type, from a given value. - /// - /// If value is negative. - public static Squared FromValue(float value) - { - if (value < 0) - throw new ArgumentOutOfRangeException(nameof(value), "Must be non-negative."); + /// + /// Creates a new instance of the Squared type, from a given value. + /// + /// If value is negative. + public static Squared FromValue(float value) + { + if (value < 0) + throw new ArgumentOutOfRangeException(nameof(value), "Must be non-negative."); - return new Squared(value); - } + return new Squared(value); + } - #endregion + #endregion - #region properties + #region properties - /// - /// Returns the numeric value of the square. - /// - public float NumericValue => value; + /// + /// Returns the numeric value of the square. + /// + public float NumericValue => value; - /// - /// Returns a Square type of value 0. - /// - public static Squared Zero => new Squared(0); + /// + /// Returns a Square type of value 0. + /// + public static Squared Zero => new Squared(0); - /// - /// Returns a Square type of value 1. - /// - public static Squared One => new Squared(1); + /// + /// Returns a Square type of value 1. + /// + public static Squared One => new Squared(1); - #endregion + #endregion - #region methods + #region methods - #region equality and hashcode + #region equality and hashcode - // ReSharper disable once CompareOfFloatsByEqualityOperator - public bool Equals(Squared other) => value == other.value; + // ReSharper disable once CompareOfFloatsByEqualityOperator + public bool Equals(Squared other) => value == other.value; - public override bool Equals(object? obj) => obj is Squared squared && Equals(squared); + public override bool Equals(object? obj) => obj is Squared squared && Equals(squared); - public override int GetHashCode() => value.GetHashCode(); + public override int GetHashCode() => value.GetHashCode(); - #endregion + #endregion - #region compare + #region compare - public int CompareTo(Squared other) => value.CompareTo(other.value); + public int CompareTo(Squared other) => value.CompareTo(other.value); - #endregion + #endregion - #region tostring + #region tostring - public override string ToString() => ToString(null, CultureInfo.CurrentCulture); + public override string ToString() => ToString(null, CultureInfo.CurrentCulture); - public string ToString(string? format, IFormatProvider? formatProvider) - => $"|{value.ToString(format, formatProvider)}|"; + public string ToString(string? format, IFormatProvider? formatProvider) + => $"|{value.ToString(format, formatProvider)}|"; - public string ToString(string? format) - => ToString(format, CultureInfo.CurrentCulture); + public string ToString(string? format) + => ToString(format, CultureInfo.CurrentCulture); - #endregion + #endregion - #endregion + #endregion - #region operators + #region operators - #region algebra + #region algebra - /// - /// Adds two squares. - /// - public static Squared operator +(Squared s0, Squared s1) => new Squared(s0.value + s1.value); + /// + /// Adds two squares. + /// + public static Squared operator +(Squared s0, Squared s1) => new Squared(s0.value + s1.value); - #endregion + #endregion - #region ratio + #region ratio - /// - /// Divides a square by another, returning a type-less fraction. - /// - public static float operator /(Squared dividend, Squared divisor) => dividend.value / divisor.value; + /// + /// Divides a square by another, returning a type-less fraction. + /// + public static float operator /(Squared dividend, Squared divisor) => dividend.value / divisor.value; - #endregion + #endregion - #region comparision + #region comparision - /// - /// Compares two squares for equality. - /// - public static bool operator ==(Squared s0, Squared s1) => s0.Equals(s1); + /// + /// Compares two squares for equality. + /// + public static bool operator ==(Squared s0, Squared s1) => s0.Equals(s1); - /// - /// Compares two squares for inequality. - /// - public static bool operator !=(Squared s0, Squared s1) => !(s0 == s1); + /// + /// Compares two squares for inequality. + /// + public static bool operator !=(Squared s0, Squared s1) => !(s0 == s1); - /// - /// Checks if one square is smaller than another. - /// - public static bool operator <(Squared s0, Squared s1) => s0.value < s1.value; + /// + /// Checks if one square is smaller than another. + /// + public static bool operator <(Squared s0, Squared s1) => s0.value < s1.value; - /// - /// Checks if one square is larger than another. - /// - public static bool operator >(Squared s0, Squared s1) => s0.value > s1.value; + /// + /// Checks if one square is larger than another. + /// + public static bool operator >(Squared s0, Squared s1) => s0.value > s1.value; - /// - /// Checks if one square is smaller or equal to another. - /// - public static bool operator <=(Squared s0, Squared s1) => s0.value <= s1.value; + /// + /// Checks if one square is smaller or equal to another. + /// + public static bool operator <=(Squared s0, Squared s1) => s0.value <= s1.value; - /// - /// Checks if one square is larger or equal to another. - /// - public static bool operator >=(Squared s0, Squared s1) => s0.value >= s1.value; + /// + /// Checks if one square is larger or equal to another. + /// + public static bool operator >=(Squared s0, Squared s1) => s0.value >= s1.value; - #endregion + #endregion - #endregion + #endregion - #region static methods + #region static methods - public static Squared Min(Squared s1, Squared s2) - => new Squared(Math.Min(s1.NumericValue, s2.NumericValue)); + public static Squared Min(Squared s1, Squared s2) + => new Squared(Math.Min(s1.NumericValue, s2.NumericValue)); - public static Squared Max(Squared s1, Squared s2) - => new Squared(Math.Max(s1.NumericValue, s2.NumericValue)); + public static Squared Max(Squared s1, Squared s2) + => new Squared(Math.Max(s1.NumericValue, s2.NumericValue)); - #endregion - } + #endregion } diff --git a/Bearded.Utilities/SpaceTime/angular/AngularAcceleration.cs b/Bearded.Utilities/SpaceTime/angular/AngularAcceleration.cs index ccc67849..a75912fa 100644 --- a/Bearded.Utilities/SpaceTime/angular/AngularAcceleration.cs +++ b/Bearded.Utilities/SpaceTime/angular/AngularAcceleration.cs @@ -2,192 +2,191 @@ using System.Globalization; using Bearded.Utilities.Geometry; -namespace Bearded.Utilities.SpaceTime +namespace Bearded.Utilities.SpaceTime; + +/// +/// A type-safe representation of a signed angular acceleration. +/// +public readonly struct AngularAcceleration : IEquatable, IComparable, IFormattable { - /// - /// A type-safe representation of a signed angular acceleration. - /// - public readonly struct AngularAcceleration : IEquatable, IComparable, IFormattable - { - private readonly float value; + private readonly float value; - #region constructing + #region constructing - public AngularAcceleration(Angle value) - : this(value.Radians) - { - } + public AngularAcceleration(Angle value) + : this(value.Radians) + { + } - private AngularAcceleration(float value) - { - this.value = value; - } + private AngularAcceleration(float value) + { + this.value = value; + } - /// - /// Creates a new instance of the AngularAcceleration type from an angle in radians. - /// - public static AngularAcceleration FromRadians(float radians) => new AngularAcceleration(radians); + /// + /// Creates a new instance of the AngularAcceleration type from an angle in radians. + /// + public static AngularAcceleration FromRadians(float radians) => new AngularAcceleration(radians); - /// - /// Creates a new instance of the AngularAcceleration type from an angle in degrees. - /// - public static AngularAcceleration FromDegrees(float degrees) => - new AngularAcceleration(MoreMath.DegreesToRadians(degrees)); + /// + /// Creates a new instance of the AngularAcceleration type from an angle in degrees. + /// + public static AngularAcceleration FromDegrees(float degrees) => + new AngularAcceleration(MoreMath.DegreesToRadians(degrees)); - #endregion + #endregion - #region properties + #region properties - /// - /// Returns the numeric value of the angular acceleration in radians. - /// - public float NumericValue => value; + /// + /// Returns the numeric value of the angular acceleration in radians. + /// + public float NumericValue => value; - /// - /// Returns the angular value of the angular acceleration. - /// - public Angle AngleValue => Angle.FromRadians(value); + /// + /// Returns the angular value of the angular acceleration. + /// + public Angle AngleValue => Angle.FromRadians(value); - /// - /// Returns an angular acceleration with value 0. - /// - public static AngularAcceleration Zero => new AngularAcceleration(0); + /// + /// Returns an angular acceleration with value 0. + /// + public static AngularAcceleration Zero => new AngularAcceleration(0); - #endregion + #endregion - #region methods + #region methods - #region equality and hashcode + #region equality and hashcode - // ReSharper disable once CompareOfFloatsByEqualityOperator - public bool Equals(AngularAcceleration other) => value == other.value; + // ReSharper disable once CompareOfFloatsByEqualityOperator + public bool Equals(AngularAcceleration other) => value == other.value; - public override bool Equals(object? obj) => obj is AngularAcceleration angularAcceleration && Equals(angularAcceleration); + public override bool Equals(object? obj) => obj is AngularAcceleration angularAcceleration && Equals(angularAcceleration); - public override int GetHashCode() => value.GetHashCode(); + public override int GetHashCode() => value.GetHashCode(); - #endregion + #endregion - #region tostring + #region tostring - public override string ToString() => ToString(null, CultureInfo.CurrentCulture); + public override string ToString() => ToString(null, CultureInfo.CurrentCulture); - public string ToString(string? format, IFormatProvider? formatProvider) - => $"{MoreMath.RadiansToDegrees(value).ToString(format, formatProvider)} °/t²"; + public string ToString(string? format, IFormatProvider? formatProvider) + => $"{MoreMath.RadiansToDegrees(value).ToString(format, formatProvider)} °/t²"; - public string ToString(string? format) - => ToString(format, CultureInfo.CurrentCulture); + public string ToString(string? format) + => ToString(format, CultureInfo.CurrentCulture); - #endregion + #endregion - #region compare + #region compare - public int CompareTo(AngularAcceleration other) => value.CompareTo(other.value); + public int CompareTo(AngularAcceleration other) => value.CompareTo(other.value); - #endregion + #endregion - #endregion + #endregion - #region operators + #region operators - #region algebra + #region algebra - /// - /// Adds two angular acceleration values. - /// - public static AngularAcceleration operator +(AngularAcceleration v0, AngularAcceleration v1) => new AngularAcceleration(v0.value + v1.value); + /// + /// Adds two angular acceleration values. + /// + public static AngularAcceleration operator +(AngularAcceleration v0, AngularAcceleration v1) => new AngularAcceleration(v0.value + v1.value); - /// - /// Adds two angular acceleration values. - /// - public static AngularAcceleration operator -(AngularAcceleration v0, AngularAcceleration v1) => new AngularAcceleration(v0.value - v1.value); + /// + /// Adds two angular acceleration values. + /// + public static AngularAcceleration operator -(AngularAcceleration v0, AngularAcceleration v1) => new AngularAcceleration(v0.value - v1.value); - #endregion + #endregion - #region scaling + #region scaling - /// - /// Inverts the angular acceleration. - /// - public static AngularAcceleration operator -(AngularAcceleration s) => new AngularAcceleration(-s.value); + /// + /// Inverts the angular acceleration. + /// + public static AngularAcceleration operator -(AngularAcceleration s) => new AngularAcceleration(-s.value); - /// - /// Multiplies the angular acceleration with a scalar. - /// - public static AngularAcceleration operator *(AngularAcceleration s, float scalar) => new AngularAcceleration(s.value * scalar); + /// + /// Multiplies the angular acceleration with a scalar. + /// + public static AngularAcceleration operator *(AngularAcceleration s, float scalar) => new AngularAcceleration(s.value * scalar); - /// - /// Multiplies the angular acceleration with a scalar. - /// - public static AngularAcceleration operator *(float scalar, AngularAcceleration s) => new AngularAcceleration(s.value * scalar); + /// + /// Multiplies the angular acceleration with a scalar. + /// + public static AngularAcceleration operator *(float scalar, AngularAcceleration s) => new AngularAcceleration(s.value * scalar); - /// - /// Divides the angular acceleration by a divisor. - /// - public static AngularAcceleration operator /(AngularAcceleration s, float divisor) => new AngularAcceleration(s.value / divisor); + /// + /// Divides the angular acceleration by a divisor. + /// + public static AngularAcceleration operator /(AngularAcceleration s, float divisor) => new AngularAcceleration(s.value / divisor); - #endregion + #endregion - #region ratio + #region ratio - /// - /// Divides an angular acceleration by another, returning a type-less fraction. - /// - public static float operator /(AngularAcceleration dividend, AngularAcceleration divisor) => dividend.value / divisor.value; + /// + /// Divides an angular acceleration by another, returning a type-less fraction. + /// + public static float operator /(AngularAcceleration dividend, AngularAcceleration divisor) => dividend.value / divisor.value; - #endregion + #endregion - #region integrate + #region integrate - /// - /// Multiplies an angular acceleration by a timespan, returning an anglular velocity. - /// - public static AngularVelocity operator *(AngularAcceleration s, TimeSpan t) => AngularVelocity.FromRadians(s.value * (float)t.NumericValue); + /// + /// Multiplies an angular acceleration by a timespan, returning an anglular velocity. + /// + public static AngularVelocity operator *(AngularAcceleration s, TimeSpan t) => AngularVelocity.FromRadians(s.value * (float)t.NumericValue); - /// - /// Multiplies an angular acceleration by a timespan, returning an anglular velocity. - /// - public static AngularVelocity operator *(TimeSpan t, AngularAcceleration s) => AngularVelocity.FromRadians(s.value * (float)t.NumericValue); + /// + /// Multiplies an angular acceleration by a timespan, returning an anglular velocity. + /// + public static AngularVelocity operator *(TimeSpan t, AngularAcceleration s) => AngularVelocity.FromRadians(s.value * (float)t.NumericValue); - public static AngularVelocity operator /(AngularAcceleration a, Frequency f) => AngularVelocity.FromRadians(a.value / (float)f.NumericValue); + public static AngularVelocity operator /(AngularAcceleration a, Frequency f) => AngularVelocity.FromRadians(a.value / (float)f.NumericValue); - #endregion + #endregion - #region comparision + #region comparision - /// - /// Compares two angular accelerations for equality. - /// - public static bool operator ==(AngularAcceleration v0, AngularAcceleration v1) => v0.Equals(v1); + /// + /// Compares two angular accelerations for equality. + /// + public static bool operator ==(AngularAcceleration v0, AngularAcceleration v1) => v0.Equals(v1); - /// - /// Compares two angular accelerations for inequality. - /// - public static bool operator !=(AngularAcceleration v0, AngularAcceleration v1) => !(v0 == v1); + /// + /// Compares two angular accelerations for inequality. + /// + public static bool operator !=(AngularAcceleration v0, AngularAcceleration v1) => !(v0 == v1); - /// - /// Checks if one angular acceleration is smaller than another. - /// - public static bool operator <(AngularAcceleration v0, AngularAcceleration v1) => v0.value < v1.value; + /// + /// Checks if one angular acceleration is smaller than another. + /// + public static bool operator <(AngularAcceleration v0, AngularAcceleration v1) => v0.value < v1.value; - /// - /// Checks if one angular acceleration is larger than another. - /// - public static bool operator >(AngularAcceleration v0, AngularAcceleration v1) => v0.value > v1.value; + /// + /// Checks if one angular acceleration is larger than another. + /// + public static bool operator >(AngularAcceleration v0, AngularAcceleration v1) => v0.value > v1.value; - /// - /// Checks if one angular acceleration is smaller or equal to another. - /// - public static bool operator <=(AngularAcceleration v0, AngularAcceleration v1) => v0.value <= v1.value; + /// + /// Checks if one angular acceleration is smaller or equal to another. + /// + public static bool operator <=(AngularAcceleration v0, AngularAcceleration v1) => v0.value <= v1.value; - /// - /// Checks if one angular acceleration is larger or equal to another. - /// - public static bool operator >=(AngularAcceleration v0, AngularAcceleration v1) => v0.value >= v1.value; + /// + /// Checks if one angular acceleration is larger or equal to another. + /// + public static bool operator >=(AngularAcceleration v0, AngularAcceleration v1) => v0.value >= v1.value; - #endregion + #endregion - #endregion + #endregion - } } diff --git a/Bearded.Utilities/SpaceTime/angular/AngularVelocity.cs b/Bearded.Utilities/SpaceTime/angular/AngularVelocity.cs index 215fa90e..4db3be49 100644 --- a/Bearded.Utilities/SpaceTime/angular/AngularVelocity.cs +++ b/Bearded.Utilities/SpaceTime/angular/AngularVelocity.cs @@ -2,199 +2,198 @@ using System.Globalization; using Bearded.Utilities.Geometry; -namespace Bearded.Utilities.SpaceTime +namespace Bearded.Utilities.SpaceTime; + +/// +/// A type-safe representation of a signed angular velocity. +/// +public readonly struct AngularVelocity : IEquatable, IComparable, IFormattable { - /// - /// A type-safe representation of a signed angular velocity. - /// - public readonly struct AngularVelocity : IEquatable, IComparable, IFormattable - { - private readonly float value; + private readonly float value; - #region constructing + #region constructing - public AngularVelocity(Angle value) - : this(value.Radians) - { - } + public AngularVelocity(Angle value) + : this(value.Radians) + { + } - private AngularVelocity(float value) - { - this.value = value; - } + private AngularVelocity(float value) + { + this.value = value; + } - /// - /// Creates a new instance of the AngularVelocity type from an angle in radians. - /// - public static AngularVelocity FromRadians(float radians) => new AngularVelocity(radians); + /// + /// Creates a new instance of the AngularVelocity type from an angle in radians. + /// + public static AngularVelocity FromRadians(float radians) => new AngularVelocity(radians); - /// - /// Creates a new instance of the AngularVelocity type from an angle in degrees. - /// - public static AngularVelocity FromDegrees(float degrees) => - new AngularVelocity(MoreMath.DegreesToRadians(degrees)); + /// + /// Creates a new instance of the AngularVelocity type from an angle in degrees. + /// + public static AngularVelocity FromDegrees(float degrees) => + new AngularVelocity(MoreMath.DegreesToRadians(degrees)); - #endregion + #endregion - #region properties + #region properties - /// - /// Returns the numeric value of the angular velocity in radians. - /// - public float NumericValue => value; + /// + /// Returns the numeric value of the angular velocity in radians. + /// + public float NumericValue => value; - /// - /// Returns the angular value of the angular velocity. - /// - public Angle AngleValue => Angle.FromRadians(value); + /// + /// Returns the angular value of the angular velocity. + /// + public Angle AngleValue => Angle.FromRadians(value); - /// - /// Returns an angular velocity with value 0. - /// - public static AngularVelocity Zero => new AngularVelocity(0); + /// + /// Returns an angular velocity with value 0. + /// + public static AngularVelocity Zero => new AngularVelocity(0); - #endregion + #endregion - #region methods + #region methods - #region equality and hashcode + #region equality and hashcode - // ReSharper disable once CompareOfFloatsByEqualityOperator - public bool Equals(AngularVelocity other) => value == other.value; + // ReSharper disable once CompareOfFloatsByEqualityOperator + public bool Equals(AngularVelocity other) => value == other.value; - public override bool Equals(object? obj) => obj is AngularVelocity angularVelocity && Equals(angularVelocity); + public override bool Equals(object? obj) => obj is AngularVelocity angularVelocity && Equals(angularVelocity); - public override int GetHashCode() => value.GetHashCode(); + public override int GetHashCode() => value.GetHashCode(); - #endregion + #endregion - #region tostring + #region tostring - public override string ToString() => ToString(null, CultureInfo.CurrentCulture); + public override string ToString() => ToString(null, CultureInfo.CurrentCulture); - public string ToString(string? format, IFormatProvider? formatProvider) - => $"{MoreMath.RadiansToDegrees(value).ToString(format, formatProvider)} °/t"; + public string ToString(string? format, IFormatProvider? formatProvider) + => $"{MoreMath.RadiansToDegrees(value).ToString(format, formatProvider)} °/t"; - public string ToString(string? format) - => ToString(format, CultureInfo.CurrentCulture); + public string ToString(string? format) + => ToString(format, CultureInfo.CurrentCulture); - #endregion + #endregion - #region compare + #region compare - public int CompareTo(AngularVelocity other) => value.CompareTo(other.value); + public int CompareTo(AngularVelocity other) => value.CompareTo(other.value); - #endregion + #endregion - #endregion + #endregion - #region operators + #region operators - #region algebra + #region algebra - /// - /// Adds two angular velocity values. - /// - public static AngularVelocity operator +(AngularVelocity v0, AngularVelocity v1) => new AngularVelocity(v0.value + v1.value); + /// + /// Adds two angular velocity values. + /// + public static AngularVelocity operator +(AngularVelocity v0, AngularVelocity v1) => new AngularVelocity(v0.value + v1.value); - /// - /// Adds two angular velocity values. - /// - public static AngularVelocity operator -(AngularVelocity v0, AngularVelocity v1) => new AngularVelocity(v0.value - v1.value); + /// + /// Adds two angular velocity values. + /// + public static AngularVelocity operator -(AngularVelocity v0, AngularVelocity v1) => new AngularVelocity(v0.value - v1.value); - #endregion + #endregion - #region scaling + #region scaling - /// - /// Inverts the angular velocity. - /// - public static AngularVelocity operator -(AngularVelocity s) => new AngularVelocity(-s.value); + /// + /// Inverts the angular velocity. + /// + public static AngularVelocity operator -(AngularVelocity s) => new AngularVelocity(-s.value); - /// - /// Multiplies the angular velocity with a scalar. - /// - public static AngularVelocity operator *(AngularVelocity s, float scalar) => new AngularVelocity(s.value * scalar); + /// + /// Multiplies the angular velocity with a scalar. + /// + public static AngularVelocity operator *(AngularVelocity s, float scalar) => new AngularVelocity(s.value * scalar); - /// - /// Multiplies the angular velocity with a scalar. - /// - public static AngularVelocity operator *(float scalar, AngularVelocity s) => new AngularVelocity(s.value * scalar); + /// + /// Multiplies the angular velocity with a scalar. + /// + public static AngularVelocity operator *(float scalar, AngularVelocity s) => new AngularVelocity(s.value * scalar); - /// - /// Divides the angular velocity by a divisor. - /// - public static AngularVelocity operator /(AngularVelocity s, float divisor) => new AngularVelocity(s.value / divisor); + /// + /// Divides the angular velocity by a divisor. + /// + public static AngularVelocity operator /(AngularVelocity s, float divisor) => new AngularVelocity(s.value / divisor); - #endregion + #endregion - #region ratio + #region ratio - /// - /// Divides an angular velocity by another, returning a type-less fraction. - /// - public static float operator /(AngularVelocity dividend, AngularVelocity divisor) => dividend.value / divisor.value; + /// + /// Divides an angular velocity by another, returning a type-less fraction. + /// + public static float operator /(AngularVelocity dividend, AngularVelocity divisor) => dividend.value / divisor.value; - #endregion + #endregion - #region differentiate + #region differentiate - /// - /// Divides an angular velocity by a timespan, returning an angular acceleration. - /// - public static AngularAcceleration operator /(AngularVelocity s, TimeSpan t) => AngularAcceleration.FromRadians(s.value / (float)t.NumericValue); + /// + /// Divides an angular velocity by a timespan, returning an angular acceleration. + /// + public static AngularAcceleration operator /(AngularVelocity s, TimeSpan t) => AngularAcceleration.FromRadians(s.value / (float)t.NumericValue); - #endregion + #endregion - #region integrate + #region integrate - /// - /// Multiplies an angular velocity by a timespan, returning an angle. - /// - public static Angle operator *(AngularVelocity s, TimeSpan t) => Angle.FromRadians(s.value * (float)t.NumericValue); + /// + /// Multiplies an angular velocity by a timespan, returning an angle. + /// + public static Angle operator *(AngularVelocity s, TimeSpan t) => Angle.FromRadians(s.value * (float)t.NumericValue); - /// - /// Multiplies an angular velocity by a timespan, returning an angle. - /// - public static Angle operator *(TimeSpan t, AngularVelocity s) => Angle.FromRadians(s.value * (float)t.NumericValue); + /// + /// Multiplies an angular velocity by a timespan, returning an angle. + /// + public static Angle operator *(TimeSpan t, AngularVelocity s) => Angle.FromRadians(s.value * (float)t.NumericValue); - #endregion + #endregion - #region comparision + #region comparision - /// - /// Compares two angular velocities for equality. - /// - public static bool operator ==(AngularVelocity v0, AngularVelocity v1) => v0.Equals(v1); + /// + /// Compares two angular velocities for equality. + /// + public static bool operator ==(AngularVelocity v0, AngularVelocity v1) => v0.Equals(v1); - /// - /// Compares two angular velocities for inequality. - /// - public static bool operator !=(AngularVelocity v0, AngularVelocity v1) => !(v0 == v1); + /// + /// Compares two angular velocities for inequality. + /// + public static bool operator !=(AngularVelocity v0, AngularVelocity v1) => !(v0 == v1); - /// - /// Checks if one angular velocity is smaller than another. - /// - public static bool operator <(AngularVelocity v0, AngularVelocity v1) => v0.value < v1.value; + /// + /// Checks if one angular velocity is smaller than another. + /// + public static bool operator <(AngularVelocity v0, AngularVelocity v1) => v0.value < v1.value; - /// - /// Checks if one angular velocity is larger than another. - /// - public static bool operator >(AngularVelocity v0, AngularVelocity v1) => v0.value > v1.value; + /// + /// Checks if one angular velocity is larger than another. + /// + public static bool operator >(AngularVelocity v0, AngularVelocity v1) => v0.value > v1.value; - /// - /// Checks if one angular velocity is smaller or equal to another. - /// - public static bool operator <=(AngularVelocity v0, AngularVelocity v1) => v0.value <= v1.value; + /// + /// Checks if one angular velocity is smaller or equal to another. + /// + public static bool operator <=(AngularVelocity v0, AngularVelocity v1) => v0.value <= v1.value; - /// - /// Checks if one angular velocity is larger or equal to another. - /// - public static bool operator >=(AngularVelocity v0, AngularVelocity v1) => v0.value >= v1.value; + /// + /// Checks if one angular velocity is larger or equal to another. + /// + public static bool operator >=(AngularVelocity v0, AngularVelocity v1) => v0.value >= v1.value; - #endregion + #endregion - #endregion + #endregion - } } diff --git a/Bearded.Utilities/SpaceTime/gravity/GravitationalConstant.cs b/Bearded.Utilities/SpaceTime/gravity/GravitationalConstant.cs index 62e651c3..a066d766 100644 --- a/Bearded.Utilities/SpaceTime/gravity/GravitationalConstant.cs +++ b/Bearded.Utilities/SpaceTime/gravity/GravitationalConstant.cs @@ -1,55 +1,54 @@ using System; -namespace Bearded.Utilities.SpaceTime +namespace Bearded.Utilities.SpaceTime; + +public readonly struct GravitationalConstant { - public readonly struct GravitationalConstant - { - private readonly float value; + private readonly float value; - public GravitationalConstant(float value) - { - this.value = value; - } + public GravitationalConstant(float value) + { + this.value = value; + } - #region properties + #region properties - public float NumericValue => value; + public float NumericValue => value; - public static GravitationalConstant Zero => new GravitationalConstant(0); + public static GravitationalConstant Zero => new GravitationalConstant(0); - public static GravitationalConstant One => new GravitationalConstant(1); + public static GravitationalConstant One => new GravitationalConstant(1); - #endregion + #endregion - #region accelerations + #region accelerations - public Acceleration2 AccelerationTowards(Mass mass, Difference2 differenceToOther) - { - var distanceSquared = differenceToOther.LengthSquared; - var directionVector = differenceToOther / distanceSquared.Sqrt(); - var acceleration = AccelerationAtDistance(mass, distanceSquared); + public Acceleration2 AccelerationTowards(Mass mass, Difference2 differenceToOther) + { + var distanceSquared = differenceToOther.LengthSquared; + var directionVector = differenceToOther / distanceSquared.Sqrt(); + var acceleration = AccelerationAtDistance(mass, distanceSquared); - return acceleration * directionVector; - } + return acceleration * directionVector; + } - public Acceleration AccelerationTowards(Mass mass, Unit differenceToOther) - { - var directionVector = Math.Sign(differenceToOther.NumericValue); - var acceleration = AccelerationAtDistance(mass, differenceToOther); + public Acceleration AccelerationTowards(Mass mass, Unit differenceToOther) + { + var directionVector = Math.Sign(differenceToOther.NumericValue); + var acceleration = AccelerationAtDistance(mass, differenceToOther); - return acceleration * directionVector; - } + return acceleration * directionVector; + } - public Acceleration AccelerationAtDistance(Mass mass, Difference2 differenceToOther) - => AccelerationAtDistance(mass, differenceToOther.LengthSquared); + public Acceleration AccelerationAtDistance(Mass mass, Difference2 differenceToOther) + => AccelerationAtDistance(mass, differenceToOther.LengthSquared); - public Acceleration AccelerationAtDistance(Mass mass, Unit distance) - => AccelerationAtDistance(mass, distance.Squared); + public Acceleration AccelerationAtDistance(Mass mass, Unit distance) + => AccelerationAtDistance(mass, distance.Squared); - public Acceleration AccelerationAtDistance(Mass mass, Squared distanceSquared) - => new Acceleration(value * mass.NumericValue / distanceSquared.NumericValue); + public Acceleration AccelerationAtDistance(Mass mass, Squared distanceSquared) + => new Acceleration(value * mass.NumericValue / distanceSquared.NumericValue); - #endregion + #endregion - } } diff --git a/Bearded.Utilities/SpaceTime/gravity/Mass.cs b/Bearded.Utilities/SpaceTime/gravity/Mass.cs index bb706fbd..e2cb6559 100644 --- a/Bearded.Utilities/SpaceTime/gravity/Mass.cs +++ b/Bearded.Utilities/SpaceTime/gravity/Mass.cs @@ -1,114 +1,113 @@ using System; using System.Globalization; -namespace Bearded.Utilities.SpaceTime +namespace Bearded.Utilities.SpaceTime; + +public readonly struct Mass : IEquatable, IComparable, IFormattable { - public readonly struct Mass : IEquatable, IComparable, IFormattable - { - private readonly float value; + private readonly float value; - public Mass(float value) - { - this.value = value; - } + public Mass(float value) + { + this.value = value; + } - #region properties + #region properties - public float NumericValue => value; + public float NumericValue => value; - public static Mass Zero => new Mass(0); + public static Mass Zero => new Mass(0); - public static Mass One => new Mass(1); + public static Mass One => new Mass(1); - #endregion + #endregion - #region methods + #region methods - #region equality and hashcode + #region equality and hashcode - // ReSharper disable once CompareOfFloatsByEqualityOperator - public bool Equals(Mass other) => value == other.value; + // ReSharper disable once CompareOfFloatsByEqualityOperator + public bool Equals(Mass other) => value == other.value; - public override bool Equals(object? obj) => obj is Mass mass && Equals(mass); + public override bool Equals(object? obj) => obj is Mass mass && Equals(mass); - public override int GetHashCode() => value.GetHashCode(); + public override int GetHashCode() => value.GetHashCode(); - #endregion + #endregion - #region tostring + #region tostring - public override string ToString() => ToString(null, CultureInfo.CurrentCulture); + public override string ToString() => ToString(null, CultureInfo.CurrentCulture); - public string ToString(string? format, IFormatProvider? formatProvider) - => $"{value.ToString(format, formatProvider)} m"; + public string ToString(string? format, IFormatProvider? formatProvider) + => $"{value.ToString(format, formatProvider)} m"; - public string ToString(string? format) - => ToString(format, CultureInfo.CurrentCulture); + public string ToString(string? format) + => ToString(format, CultureInfo.CurrentCulture); - #endregion + #endregion - #region compare + #region compare - public int CompareTo(Mass other) => value.CompareTo(other.value); + public int CompareTo(Mass other) => value.CompareTo(other.value); - #endregion + #endregion - #endregion + #endregion - #region operators + #region operators - #region algebra + #region algebra - public static Mass operator +(Mass m0, Mass m1) => new Mass(m0.value + m1.value); + public static Mass operator +(Mass m0, Mass m1) => new Mass(m0.value + m1.value); - public static Mass operator -(Mass m0, Mass m1) => new Mass(m0.value - m1.value); + public static Mass operator -(Mass m0, Mass m1) => new Mass(m0.value - m1.value); - public static Mass operator -(Mass m) => new Mass(-m.value); + public static Mass operator -(Mass m) => new Mass(-m.value); - #endregion + #endregion - #region scaling + #region scaling - public static Mass operator *(Mass m, float scalar) => new Mass(m.value * scalar); + public static Mass operator *(Mass m, float scalar) => new Mass(m.value * scalar); - public static Mass operator *(float scalar, Mass m) => new Mass(m.value * scalar); + public static Mass operator *(float scalar, Mass m) => new Mass(m.value * scalar); - public static Mass operator /(Mass m, float divisor) => new Mass(m.value / divisor); + public static Mass operator /(Mass m, float divisor) => new Mass(m.value / divisor); - #endregion + #endregion - #region ratio + #region ratio - public static float operator /(Mass dividend, Mass divisor) => dividend.value / divisor.value; + public static float operator /(Mass dividend, Mass divisor) => dividend.value / divisor.value; - #endregion + #endregion - #region comparison + #region comparison - public static bool operator ==(Mass m0, Mass m1) => m0.Equals(m1); + public static bool operator ==(Mass m0, Mass m1) => m0.Equals(m1); - public static bool operator !=(Mass m0, Mass m1) => !(m0 == m1); + public static bool operator !=(Mass m0, Mass m1) => !(m0 == m1); - public static bool operator <(Mass m0, Mass m1) => m0.value < m1.value; + public static bool operator <(Mass m0, Mass m1) => m0.value < m1.value; - public static bool operator >(Mass m0, Mass m1) => m0.value > m1.value; + public static bool operator >(Mass m0, Mass m1) => m0.value > m1.value; - public static bool operator <=(Mass m0, Mass m1) => m0.value <= m1.value; + public static bool operator <=(Mass m0, Mass m1) => m0.value <= m1.value; - public static bool operator >=(Mass m0, Mass m1) => m0.value >= m1.value; + public static bool operator >=(Mass m0, Mass m1) => m0.value >= m1.value; - #endregion + #endregion - #endregion + #endregion - #region static methods + #region static methods - public static Mass Min(Mass m0, Mass m1) - => new Mass(Math.Min(m0.NumericValue, m1.NumericValue)); + public static Mass Min(Mass m0, Mass m1) + => new Mass(Math.Min(m0.NumericValue, m1.NumericValue)); - public static Mass Max(Mass m0, Mass m1) - => new Mass(Math.Max(m0.NumericValue, m1.NumericValue)); + public static Mass Max(Mass m0, Mass m1) + => new Mass(Math.Max(m0.NumericValue, m1.NumericValue)); - #endregion - } + #endregion } diff --git a/Bearded.Utilities/SpaceTime/time/Frequency.cs b/Bearded.Utilities/SpaceTime/time/Frequency.cs index b1fa459b..108e3187 100644 --- a/Bearded.Utilities/SpaceTime/time/Frequency.cs +++ b/Bearded.Utilities/SpaceTime/time/Frequency.cs @@ -2,128 +2,127 @@ using System.Globalization; using Bearded.Utilities.Geometry; -namespace Bearded.Utilities.SpaceTime +namespace Bearded.Utilities.SpaceTime; + +/// +/// A type-safe representation of a signed frequency. +/// +public readonly struct Frequency : IEquatable, IComparable, IFormattable { - /// - /// A type-safe representation of a signed frequency. - /// - public readonly struct Frequency : IEquatable, IComparable, IFormattable - { - private readonly double value; + private readonly double value; - #region construction + #region construction - public Frequency(double value) - { - this.value = value; - } + public Frequency(double value) + { + this.value = value; + } - #endregion + #endregion - #region properties + #region properties - public double NumericValue => value; + public double NumericValue => value; - public static Frequency Never => new Frequency(0); + public static Frequency Never => new Frequency(0); - public static Frequency One => new Frequency(1); + public static Frequency One => new Frequency(1); - #endregion + #endregion - #region methods + #region methods - #region equality and hashcode + #region equality and hashcode - // ReSharper disable once CompareOfFloatsByEqualityOperator - public bool Equals(Frequency other) => value == other.value; + // ReSharper disable once CompareOfFloatsByEqualityOperator + public bool Equals(Frequency other) => value == other.value; - public override bool Equals(object? obj) => obj is Frequency f && Equals(f); + public override bool Equals(object? obj) => obj is Frequency f && Equals(f); - public override int GetHashCode() => value.GetHashCode(); + public override int GetHashCode() => value.GetHashCode(); - #endregion + #endregion - #region tostring + #region tostring - public override string ToString() => ToString(null, CultureInfo.CurrentCulture); + public override string ToString() => ToString(null, CultureInfo.CurrentCulture); - public string ToString(string? format, IFormatProvider? formatProvider) - => $"{value.ToString(format, formatProvider)} 1/t"; + public string ToString(string? format, IFormatProvider? formatProvider) + => $"{value.ToString(format, formatProvider)} 1/t"; - public string ToString(string? format) - => ToString(format, CultureInfo.CurrentCulture); + public string ToString(string? format) + => ToString(format, CultureInfo.CurrentCulture); - #endregion + #endregion - #region compare + #region compare - public int CompareTo(Frequency other) => value.CompareTo(other.value); + public int CompareTo(Frequency other) => value.CompareTo(other.value); - #endregion + #endregion - #endregion + #endregion - #region operators + #region operators - #region scaling + #region scaling - public static Frequency operator *(Frequency t, double scalar) => new Frequency(t.value * scalar); + public static Frequency operator *(Frequency t, double scalar) => new Frequency(t.value * scalar); - public static Frequency operator *(double scalar, Frequency t) => new Frequency(t.value * scalar); + public static Frequency operator *(double scalar, Frequency t) => new Frequency(t.value * scalar); - public static Frequency operator /(Frequency t, double divisor) => new Frequency(t.value / divisor); + public static Frequency operator /(Frequency t, double divisor) => new Frequency(t.value / divisor); - #endregion + #endregion - #region ratio + #region ratio - public static double operator /(Frequency dividend, Frequency divisor) => dividend.value / divisor.value; + public static double operator /(Frequency dividend, Frequency divisor) => dividend.value / divisor.value; - #endregion + #endregion - #region comparison + #region comparison - public static bool operator ==(Frequency f0, Frequency f1) => f0.Equals(f1); + public static bool operator ==(Frequency f0, Frequency f1) => f0.Equals(f1); - public static bool operator !=(Frequency f0, Frequency f1) => !(f0 == f1); + public static bool operator !=(Frequency f0, Frequency f1) => !(f0 == f1); - public static bool operator <(Frequency f0, Frequency f1) => f0.value < f1.value; + public static bool operator <(Frequency f0, Frequency f1) => f0.value < f1.value; - public static bool operator >(Frequency f0, Frequency f1) => f0.value > f1.value; + public static bool operator >(Frequency f0, Frequency f1) => f0.value > f1.value; - public static bool operator <=(Frequency f0, Frequency f1) => f0.value <= f1.value; + public static bool operator <=(Frequency f0, Frequency f1) => f0.value <= f1.value; - public static bool operator >=(Frequency f0, Frequency f1) => f0.value >= f1.value; + public static bool operator >=(Frequency f0, Frequency f1) => f0.value >= f1.value; - #endregion + #endregion - #region angle differentiation + #region angle differentiation - public static AngularVelocity operator *(Angle s, Frequency f) - => AngularVelocity.FromRadians(s.Radians * (float)f.value); + public static AngularVelocity operator *(Angle s, Frequency f) + => AngularVelocity.FromRadians(s.Radians * (float)f.value); - #endregion + #endregion - #region TimeSpan interaction + #region TimeSpan interaction - public static double operator *(Frequency f, TimeSpan t) => f.value * t.NumericValue; + public static double operator *(Frequency f, TimeSpan t) => f.value * t.NumericValue; - public static double operator *(TimeSpan t, Frequency f) => f.value * t.NumericValue; + public static double operator *(TimeSpan t, Frequency f) => f.value * t.NumericValue; - public static TimeSpan operator /(double d, Frequency f) => new TimeSpan(d / f.value); + public static TimeSpan operator /(double d, Frequency f) => new TimeSpan(d / f.value); - #endregion + #endregion - #endregion + #endregion - #region static methods + #region static methods - public static Frequency Min(Frequency f1, Frequency t2) - => new Frequency(Math.Min(f1.NumericValue, t2.NumericValue)); + public static Frequency Min(Frequency f1, Frequency t2) + => new Frequency(Math.Min(f1.NumericValue, t2.NumericValue)); - public static Frequency Max(Frequency f1, Frequency t2) - => new Frequency(Math.Max(f1.NumericValue, t2.NumericValue)); + public static Frequency Max(Frequency f1, Frequency t2) + => new Frequency(Math.Max(f1.NumericValue, t2.NumericValue)); - #endregion - } + #endregion } diff --git a/Bearded.Utilities/SpaceTime/time/Instant.cs b/Bearded.Utilities/SpaceTime/time/Instant.cs index 577b9eef..4042a09b 100644 --- a/Bearded.Utilities/SpaceTime/time/Instant.cs +++ b/Bearded.Utilities/SpaceTime/time/Instant.cs @@ -1,132 +1,131 @@ using System; using System.Globalization; -namespace Bearded.Utilities.SpaceTime +namespace Bearded.Utilities.SpaceTime; + +/// +/// A type-safe representation of an absolute instant in time. +/// +public readonly struct Instant : IEquatable, IComparable, IFormattable { - /// - /// A type-safe representation of an absolute instant in time. - /// - public readonly struct Instant : IEquatable, IComparable, IFormattable - { - private readonly double value; + private readonly double value; - #region construction + #region construction - public Instant(double value) - { - this.value = value; - } + public Instant(double value) + { + this.value = value; + } - #endregion + #endregion - #region properties + #region properties - /// - /// Returns the numeric value of the time instant. - /// - public double NumericValue => value; + /// + /// Returns the numeric value of the time instant. + /// + public double NumericValue => value; - /// - /// Returns an Instant type with value 0. - /// - public static Instant Zero => new Instant(0); + /// + /// Returns an Instant type with value 0. + /// + public static Instant Zero => new Instant(0); - #endregion + #endregion - #region methods + #region methods - #region equality and hashcode + #region equality and hashcode - // ReSharper disable once CompareOfFloatsByEqualityOperator - public bool Equals(Instant other) => value == other.value; + // ReSharper disable once CompareOfFloatsByEqualityOperator + public bool Equals(Instant other) => value == other.value; - public override bool Equals(object? obj) => obj is Instant instant && Equals(instant); + public override bool Equals(object? obj) => obj is Instant instant && Equals(instant); - public override int GetHashCode() => value.GetHashCode(); + public override int GetHashCode() => value.GetHashCode(); - #endregion + #endregion - #region compare + #region compare - public int CompareTo(Instant other) => value.CompareTo(other.value); + public int CompareTo(Instant other) => value.CompareTo(other.value); - #endregion + #endregion - #region tostring + #region tostring - public override string ToString() => ToString(null, CultureInfo.CurrentCulture); + public override string ToString() => ToString(null, CultureInfo.CurrentCulture); - public string ToString(string? format, IFormatProvider? formatProvider) - => $"{value.ToString(format, formatProvider)} t"; + public string ToString(string? format, IFormatProvider? formatProvider) + => $"{value.ToString(format, formatProvider)} t"; - public string ToString(string? format) - => ToString(format, CultureInfo.CurrentCulture); + public string ToString(string? format) + => ToString(format, CultureInfo.CurrentCulture); - #endregion + #endregion - #endregion + #endregion - #region operators + #region operators - #region TimeSpan interaction + #region TimeSpan interaction - /// - /// Adds a timespan to a time instant. - /// - public static Instant operator +(Instant i, TimeSpan t) => new Instant(i.value + t.NumericValue); + /// + /// Adds a timespan to a time instant. + /// + public static Instant operator +(Instant i, TimeSpan t) => new Instant(i.value + t.NumericValue); - /// - /// Adds a timespan to a time instant. - /// - public static Instant operator +(TimeSpan t, Instant i) => new Instant(i.value + t.NumericValue); + /// + /// Adds a timespan to a time instant. + /// + public static Instant operator +(TimeSpan t, Instant i) => new Instant(i.value + t.NumericValue); - /// - /// Subtracts a timespan from a time instant. - /// - public static Instant operator -(Instant i, TimeSpan t) => new Instant(i.value - t.NumericValue); + /// + /// Subtracts a timespan from a time instant. + /// + public static Instant operator -(Instant i, TimeSpan t) => new Instant(i.value - t.NumericValue); - /// - /// Subtracts two time instants, returning a timespan. - /// - public static TimeSpan operator -(Instant i0, Instant i1) => new TimeSpan(i0.value - i1.value); + /// + /// Subtracts two time instants, returning a timespan. + /// + public static TimeSpan operator -(Instant i0, Instant i1) => new TimeSpan(i0.value - i1.value); - #endregion + #endregion - #region comparision + #region comparision - /// - /// Compares two time instants for equality. - /// - public static bool operator ==(Instant i0, Instant i1) => i0.Equals(i1); + /// + /// Compares two time instants for equality. + /// + public static bool operator ==(Instant i0, Instant i1) => i0.Equals(i1); - /// - /// Compares two time instants for inequality. - /// - public static bool operator !=(Instant i0, Instant i1) => !(i0 == i1); + /// + /// Compares two time instants for inequality. + /// + public static bool operator !=(Instant i0, Instant i1) => !(i0 == i1); - /// - /// Checks if one time instant is smaller than another. - /// - public static bool operator <(Instant i0, Instant i1) => i0.value < i1.value; + /// + /// Checks if one time instant is smaller than another. + /// + public static bool operator <(Instant i0, Instant i1) => i0.value < i1.value; - /// - /// Checks if one time instant is larger than another. - /// - public static bool operator >(Instant i0, Instant i1) => i0.value > i1.value; + /// + /// Checks if one time instant is larger than another. + /// + public static bool operator >(Instant i0, Instant i1) => i0.value > i1.value; - /// - /// Checks if one time instant is smaller or equal to another. - /// - public static bool operator <=(Instant i0, Instant i1) => i0.value <= i1.value; + /// + /// Checks if one time instant is smaller or equal to another. + /// + public static bool operator <=(Instant i0, Instant i1) => i0.value <= i1.value; - /// - /// Checks if one time instant is larger or equal to another. - /// - public static bool operator >=(Instant i0, Instant i1) => i0.value >= i1.value; + /// + /// Checks if one time instant is larger or equal to another. + /// + public static bool operator >=(Instant i0, Instant i1) => i0.value >= i1.value; - #endregion + #endregion - #endregion + #endregion - } } diff --git a/Bearded.Utilities/SpaceTime/time/TimeSpan.cs b/Bearded.Utilities/SpaceTime/time/TimeSpan.cs index ffd55f33..ca1cf4a2 100644 --- a/Bearded.Utilities/SpaceTime/time/TimeSpan.cs +++ b/Bearded.Utilities/SpaceTime/time/TimeSpan.cs @@ -2,134 +2,133 @@ using System.Globalization; using Bearded.Utilities.Geometry; -namespace Bearded.Utilities.SpaceTime +namespace Bearded.Utilities.SpaceTime; + +/// +/// A type-safe representation of a signed timespan. +/// +public readonly struct TimeSpan : IEquatable, IComparable, IFormattable { - /// - /// A type-safe representation of a signed timespan. - /// - public readonly struct TimeSpan : IEquatable, IComparable, IFormattable - { - private readonly double value; + private readonly double value; - #region construction + #region construction - public TimeSpan(double value) - { - this.value = value; - } + public TimeSpan(double value) + { + this.value = value; + } - #endregion + #endregion - #region properties + #region properties - public double NumericValue => value; + public double NumericValue => value; - public static TimeSpan Zero => new TimeSpan(0); + public static TimeSpan Zero => new TimeSpan(0); - public static TimeSpan One => new TimeSpan(1); + public static TimeSpan One => new TimeSpan(1); - #endregion + #endregion - #region methods + #region methods - #region equality and hashcode + #region equality and hashcode - // ReSharper disable once CompareOfFloatsByEqualityOperator - public bool Equals(TimeSpan other) => value == other.value; + // ReSharper disable once CompareOfFloatsByEqualityOperator + public bool Equals(TimeSpan other) => value == other.value; - public override bool Equals(object? obj) => obj is TimeSpan t && Equals(t); + public override bool Equals(object? obj) => obj is TimeSpan t && Equals(t); - public override int GetHashCode() => value.GetHashCode(); + public override int GetHashCode() => value.GetHashCode(); - #endregion + #endregion - #region tostring + #region tostring - public override string ToString() => ToString(null, CultureInfo.CurrentCulture); + public override string ToString() => ToString(null, CultureInfo.CurrentCulture); - public string ToString(string? format, IFormatProvider? formatProvider) - => $"{value.ToString(format, formatProvider)} t"; + public string ToString(string? format, IFormatProvider? formatProvider) + => $"{value.ToString(format, formatProvider)} t"; - public string ToString(string? format) - => ToString(format, CultureInfo.CurrentCulture); + public string ToString(string? format) + => ToString(format, CultureInfo.CurrentCulture); - #endregion + #endregion - #region compare + #region compare - public int CompareTo(TimeSpan other) => value.CompareTo(other.value); + public int CompareTo(TimeSpan other) => value.CompareTo(other.value); - #endregion + #endregion - #endregion + #endregion - #region operators + #region operators - #region algebra + #region algebra - public static TimeSpan operator +(TimeSpan t0, TimeSpan t1) => new TimeSpan(t0.value + t1.value); + public static TimeSpan operator +(TimeSpan t0, TimeSpan t1) => new TimeSpan(t0.value + t1.value); - public static TimeSpan operator -(TimeSpan t0, TimeSpan t1) => new TimeSpan(t0.value - t1.value); + public static TimeSpan operator -(TimeSpan t0, TimeSpan t1) => new TimeSpan(t0.value - t1.value); - public static TimeSpan operator -(TimeSpan t) => new TimeSpan(-t.value); + public static TimeSpan operator -(TimeSpan t) => new TimeSpan(-t.value); - #endregion + #endregion - #region scaling + #region scaling - public static TimeSpan operator *(TimeSpan t, double scalar) => new TimeSpan(t.value * scalar); + public static TimeSpan operator *(TimeSpan t, double scalar) => new TimeSpan(t.value * scalar); - public static TimeSpan operator *(double scalar, TimeSpan t) => new TimeSpan(t.value * scalar); + public static TimeSpan operator *(double scalar, TimeSpan t) => new TimeSpan(t.value * scalar); - public static TimeSpan operator /(TimeSpan t, double divisor) => new TimeSpan(t.value / divisor); + public static TimeSpan operator /(TimeSpan t, double divisor) => new TimeSpan(t.value / divisor); - #endregion + #endregion - #region ratio + #region ratio - public static double operator /(TimeSpan dividend, TimeSpan divisor) => dividend.value / divisor.value; + public static double operator /(TimeSpan dividend, TimeSpan divisor) => dividend.value / divisor.value; - #endregion + #endregion - #region comparison + #region comparison - public static bool operator ==(TimeSpan t0, TimeSpan t1) => t0.Equals(t1); + public static bool operator ==(TimeSpan t0, TimeSpan t1) => t0.Equals(t1); - public static bool operator !=(TimeSpan t0, TimeSpan t1) => !(t0 == t1); + public static bool operator !=(TimeSpan t0, TimeSpan t1) => !(t0 == t1); - public static bool operator <(TimeSpan t0, TimeSpan t1) => t0.value < t1.value; + public static bool operator <(TimeSpan t0, TimeSpan t1) => t0.value < t1.value; - public static bool operator >(TimeSpan t0, TimeSpan t1) => t0.value > t1.value; + public static bool operator >(TimeSpan t0, TimeSpan t1) => t0.value > t1.value; - public static bool operator <=(TimeSpan t0, TimeSpan t1) => t0.value <= t1.value; + public static bool operator <=(TimeSpan t0, TimeSpan t1) => t0.value <= t1.value; - public static bool operator >=(TimeSpan t0, TimeSpan t1) => t0.value >= t1.value; + public static bool operator >=(TimeSpan t0, TimeSpan t1) => t0.value >= t1.value; - #endregion + #endregion - #region angle differentiation + #region angle differentiation - public static AngularVelocity operator /(Angle s, TimeSpan t) - => AngularVelocity.FromRadians(s.Radians / (float)t.value); + public static AngularVelocity operator /(Angle s, TimeSpan t) + => AngularVelocity.FromRadians(s.Radians / (float)t.value); - #endregion + #endregion - #region Frequency interaction + #region Frequency interaction - public static Frequency operator /(double d, TimeSpan t) => new Frequency(d / t.NumericValue); + public static Frequency operator /(double d, TimeSpan t) => new Frequency(d / t.NumericValue); - #endregion + #endregion - #endregion + #endregion - #region static methods + #region static methods - public static TimeSpan Min(TimeSpan t1, TimeSpan t2) - => new TimeSpan(Math.Min(t1.NumericValue, t2.NumericValue)); + public static TimeSpan Min(TimeSpan t1, TimeSpan t2) + => new TimeSpan(Math.Min(t1.NumericValue, t2.NumericValue)); - public static TimeSpan Max(TimeSpan t1, TimeSpan t2) - => new TimeSpan(Math.Max(t1.NumericValue, t2.NumericValue)); + public static TimeSpan Max(TimeSpan t1, TimeSpan t2) + => new TimeSpan(Math.Max(t1.NumericValue, t2.NumericValue)); - #endregion - } + #endregion } diff --git a/Bearded.Utilities/SpaceTime/undirected/Acceleration.cs b/Bearded.Utilities/SpaceTime/undirected/Acceleration.cs index d4de968f..384a999b 100644 --- a/Bearded.Utilities/SpaceTime/undirected/Acceleration.cs +++ b/Bearded.Utilities/SpaceTime/undirected/Acceleration.cs @@ -3,229 +3,228 @@ using Bearded.Utilities.Geometry; using OpenTK.Mathematics; -namespace Bearded.Utilities.SpaceTime +namespace Bearded.Utilities.SpaceTime; + +/// +/// A type-safe representation of an undirected signed acceleration. +/// +public readonly struct Acceleration : IEquatable, IComparable, IFormattable { - /// - /// A type-safe representation of an undirected signed acceleration. - /// - public readonly struct Acceleration : IEquatable, IComparable, IFormattable - { - private readonly float value; + private readonly float value; - #region construction + #region construction - public Acceleration(float value) - { - this.value = value; - } + public Acceleration(float value) + { + this.value = value; + } - #endregion + #endregion - #region properties + #region properties - /// - /// Returns the numeric value of the acceleration value. - /// - public float NumericValue => value; + /// + /// Returns the numeric value of the acceleration value. + /// + public float NumericValue => value; - /// - /// Returns the type-safe square of the acceleration value. - /// - public Squared Squared => Squared.FromRoot(value); + /// + /// Returns the type-safe square of the acceleration value. + /// + public Squared Squared => Squared.FromRoot(value); - /// - /// Returns an Acceleration type with value 0. - /// - public static Acceleration Zero => new Acceleration(0); + /// + /// Returns an Acceleration type with value 0. + /// + public static Acceleration Zero => new Acceleration(0); - /// - /// Returns an Acceleration type with value 1. - /// - public static Acceleration One => new Acceleration(1); + /// + /// Returns an Acceleration type with value 1. + /// + public static Acceleration One => new Acceleration(1); - #endregion + #endregion - #region methods + #region methods - #region equality and hashcode + #region equality and hashcode - // ReSharper disable once CompareOfFloatsByEqualityOperator - public bool Equals(Acceleration other) => value == other.value; + // ReSharper disable once CompareOfFloatsByEqualityOperator + public bool Equals(Acceleration other) => value == other.value; - public override bool Equals(object? obj) => obj is Acceleration acceleration && Equals(acceleration); + public override bool Equals(object? obj) => obj is Acceleration acceleration && Equals(acceleration); - public override int GetHashCode() => value.GetHashCode(); + public override int GetHashCode() => value.GetHashCode(); - #endregion + #endregion - #region tostring + #region tostring - public override string ToString() => ToString(null, CultureInfo.CurrentCulture); + public override string ToString() => ToString(null, CultureInfo.CurrentCulture); - public string ToString(string? format, IFormatProvider? formatProvider) - => $"{value.ToString(format, formatProvider)} u/t²"; + public string ToString(string? format, IFormatProvider? formatProvider) + => $"{value.ToString(format, formatProvider)} u/t²"; - public string ToString(string? format) - => ToString(format, CultureInfo.CurrentCulture); + public string ToString(string? format) + => ToString(format, CultureInfo.CurrentCulture); - #endregion + #endregion - #region compare + #region compare - public int CompareTo(Acceleration other) => value.CompareTo(other.value); + public int CompareTo(Acceleration other) => value.CompareTo(other.value); - #endregion + #endregion - #endregion + #endregion - #region operators + #region operators - #region algebra + #region algebra - /// - /// Adds two acceleration values. - /// - public static Acceleration operator +(Acceleration a0, Acceleration a1) => new Acceleration(a0.value + a1.value); + /// + /// Adds two acceleration values. + /// + public static Acceleration operator +(Acceleration a0, Acceleration a1) => new Acceleration(a0.value + a1.value); - /// - /// Subtracts an acceleration value from another. - /// - public static Acceleration operator -(Acceleration a0, Acceleration a1) => new Acceleration(a0.value - a1.value); + /// + /// Subtracts an acceleration value from another. + /// + public static Acceleration operator -(Acceleration a0, Acceleration a1) => new Acceleration(a0.value - a1.value); - #endregion + #endregion - #region scaling + #region scaling - /// - /// Inverts the acceleration value. - /// - public static Acceleration operator -(Acceleration a) => new Acceleration(-a.value); + /// + /// Inverts the acceleration value. + /// + public static Acceleration operator -(Acceleration a) => new Acceleration(-a.value); - /// - /// Multiples the acceleration value with a scalar. - /// - public static Acceleration operator *(Acceleration a, float scalar) => new Acceleration(a.value * scalar); + /// + /// Multiples the acceleration value with a scalar. + /// + public static Acceleration operator *(Acceleration a, float scalar) => new Acceleration(a.value * scalar); - /// - /// Multiples the acceleration value with a scalar. - /// - public static Acceleration operator *(float scalar, Acceleration a) => new Acceleration(a.value * scalar); + /// + /// Multiples the acceleration value with a scalar. + /// + public static Acceleration operator *(float scalar, Acceleration a) => new Acceleration(a.value * scalar); - /// - /// Divides the acceleration value by a divisor. - /// - public static Acceleration operator /(Acceleration a, float divisor) => new Acceleration(a.value / divisor); + /// + /// Divides the acceleration value by a divisor. + /// + public static Acceleration operator /(Acceleration a, float divisor) => new Acceleration(a.value / divisor); - #endregion + #endregion - #region ratio + #region ratio - /// - /// Devides an acceleration value by another, returning a type-less fraction. - /// - public static float operator /(Acceleration dividend, Acceleration divisor) => dividend.value / divisor.value; + /// + /// Devides an acceleration value by another, returning a type-less fraction. + /// + public static float operator /(Acceleration dividend, Acceleration divisor) => dividend.value / divisor.value; - #endregion + #endregion - #region integrate + #region integrate - /// - /// Multiplies an acceleration value by a timespan, returning a speed. - /// - public static Speed operator *(Acceleration a, TimeSpan t) => new Speed(a.value * (float)t.NumericValue); + /// + /// Multiplies an acceleration value by a timespan, returning a speed. + /// + public static Speed operator *(Acceleration a, TimeSpan t) => new Speed(a.value * (float)t.NumericValue); - /// - /// Multiplies an acceleration value by a timespan, returning a speed. - /// - public static Speed operator *(TimeSpan t, Acceleration a) => new Speed(a.value * (float)t.NumericValue); + /// + /// Multiplies an acceleration value by a timespan, returning a speed. + /// + public static Speed operator *(TimeSpan t, Acceleration a) => new Speed(a.value * (float)t.NumericValue); - public static Speed operator /(Acceleration a, Frequency f) => new Speed(a.value / (float)f.NumericValue); + public static Speed operator /(Acceleration a, Frequency f) => new Speed(a.value / (float)f.NumericValue); - #endregion + #endregion - #region add dimension + #region add dimension - /// - /// Multiplies a direction with an acceleration value, returning a typed acceleration vector of the given direction and length. - /// - public static Acceleration2 operator *(Acceleration a, Direction2 d) => new Acceleration2(d.Vector * a.value); + /// + /// Multiplies a direction with an acceleration value, returning a typed acceleration vector of the given direction and length. + /// + public static Acceleration2 operator *(Acceleration a, Direction2 d) => new Acceleration2(d.Vector * a.value); - /// - /// Multiplies a direction with an acceleration value, returning a typed acceleration vector of the given direction and length. - /// - public static Acceleration2 operator *(Direction2 d, Acceleration a) => new Acceleration2(d.Vector * a.value); + /// + /// Multiplies a direction with an acceleration value, returning a typed acceleration vector of the given direction and length. + /// + public static Acceleration2 operator *(Direction2 d, Acceleration a) => new Acceleration2(d.Vector * a.value); - /// - /// Multiplies an acceleration value with an untyped vector, returning a typed acceleration vector. - /// - public static Acceleration2 operator *(Acceleration u, Vector2 v) => new Acceleration2(v * u.value); + /// + /// Multiplies an acceleration value with an untyped vector, returning a typed acceleration vector. + /// + public static Acceleration2 operator *(Acceleration u, Vector2 v) => new Acceleration2(v * u.value); - /// - /// Multiplies an acceleration value with an untyped vector, returning a typed acceleration vector. - /// - public static Acceleration2 operator *(Vector2 v, Acceleration u) => new Acceleration2(v * u.value); + /// + /// Multiplies an acceleration value with an untyped vector, returning a typed acceleration vector. + /// + public static Acceleration2 operator *(Vector2 v, Acceleration u) => new Acceleration2(v * u.value); - #endregion + #endregion - #region add two dimensions + #region add two dimensions - /// - /// Multiplies an acceleration value with an untyped vector, returning a typed acceleration vector. - /// - public static Acceleration3 operator *(Acceleration u, Vector3 v) => new Acceleration3(v * u.value); + /// + /// Multiplies an acceleration value with an untyped vector, returning a typed acceleration vector. + /// + public static Acceleration3 operator *(Acceleration u, Vector3 v) => new Acceleration3(v * u.value); - /// - /// Multiplies an acceleration value with an untyped vector, returning a typed acceleration vector. - /// - public static Acceleration3 operator *(Vector3 v, Acceleration u) => new Acceleration3(v * u.value); + /// + /// Multiplies an acceleration value with an untyped vector, returning a typed acceleration vector. + /// + public static Acceleration3 operator *(Vector3 v, Acceleration u) => new Acceleration3(v * u.value); - #endregion + #endregion - #region comparision + #region comparision - /// - /// Compares two acceleration values for equality. - /// - public static bool operator ==(Acceleration a0, Acceleration a1) => a0.Equals(a1); + /// + /// Compares two acceleration values for equality. + /// + public static bool operator ==(Acceleration a0, Acceleration a1) => a0.Equals(a1); - /// - /// Compares two acceleration values for inequality. - /// - public static bool operator !=(Acceleration a0, Acceleration a1) => !(a0 == a1); + /// + /// Compares two acceleration values for inequality. + /// + public static bool operator !=(Acceleration a0, Acceleration a1) => !(a0 == a1); - /// - /// Checks if one acceleration value is smaller than another. - /// - public static bool operator <(Acceleration a0, Acceleration a1) => a0.value < a1.value; + /// + /// Checks if one acceleration value is smaller than another. + /// + public static bool operator <(Acceleration a0, Acceleration a1) => a0.value < a1.value; - /// - /// Checks if one acceleration value is larger than another. - /// - public static bool operator >(Acceleration a0, Acceleration a1) => a0.value > a1.value; + /// + /// Checks if one acceleration value is larger than another. + /// + public static bool operator >(Acceleration a0, Acceleration a1) => a0.value > a1.value; - /// - /// Checks if one acceleration value is smaller or equal to another. - /// - public static bool operator <=(Acceleration a0, Acceleration a1) => a0.value <= a1.value; + /// + /// Checks if one acceleration value is smaller or equal to another. + /// + public static bool operator <=(Acceleration a0, Acceleration a1) => a0.value <= a1.value; - /// - /// Checks if one acceleration value is larger or equal to another. - /// - public static bool operator >=(Acceleration a0, Acceleration a1) => a0.value >= a1.value; + /// + /// Checks if one acceleration value is larger or equal to another. + /// + public static bool operator >=(Acceleration a0, Acceleration a1) => a0.value >= a1.value; - #endregion + #endregion - #endregion + #endregion - #region static methods + #region static methods - public static Acceleration Min(Acceleration s1, Acceleration s2) - => new Acceleration(Math.Min(s1.NumericValue, s2.NumericValue)); + public static Acceleration Min(Acceleration s1, Acceleration s2) + => new Acceleration(Math.Min(s1.NumericValue, s2.NumericValue)); - public static Acceleration Max(Acceleration s1, Acceleration s2) - => new Acceleration(Math.Max(s1.NumericValue, s2.NumericValue)); + public static Acceleration Max(Acceleration s1, Acceleration s2) + => new Acceleration(Math.Max(s1.NumericValue, s2.NumericValue)); - #endregion + #endregion - } } diff --git a/Bearded.Utilities/SpaceTime/undirected/Speed.cs b/Bearded.Utilities/SpaceTime/undirected/Speed.cs index efbc7092..76325345 100644 --- a/Bearded.Utilities/SpaceTime/undirected/Speed.cs +++ b/Bearded.Utilities/SpaceTime/undirected/Speed.cs @@ -3,242 +3,241 @@ using Bearded.Utilities.Geometry; using OpenTK.Mathematics; -namespace Bearded.Utilities.SpaceTime +namespace Bearded.Utilities.SpaceTime; + +/// +/// A type-safe representation of an undirected signed speed. +/// +public readonly struct Speed : IEquatable, IComparable, IFormattable { - /// - /// A type-safe representation of an undirected signed speed. - /// - public readonly struct Speed : IEquatable, IComparable, IFormattable - { - private readonly float value; + private readonly float value; - #region construction + #region construction - public Speed(float value) - { - this.value = value; - } + public Speed(float value) + { + this.value = value; + } - #endregion + #endregion - #region properties + #region properties - /// - /// Returns the numeric value of the speed value. - /// - public float NumericValue => value; + /// + /// Returns the numeric value of the speed value. + /// + public float NumericValue => value; - /// - /// Returns the type-safe square of the speed value. - /// - public Squared Squared => Squared.FromRoot(value); + /// + /// Returns the type-safe square of the speed value. + /// + public Squared Squared => Squared.FromRoot(value); - /// - /// Returns a Speed type with value 0. - /// - public static Speed Zero => new Speed(0); + /// + /// Returns a Speed type with value 0. + /// + public static Speed Zero => new Speed(0); - /// - /// Returns a Speed type with value 1. - /// - public static Speed One => new Speed(1); + /// + /// Returns a Speed type with value 1. + /// + public static Speed One => new Speed(1); - #endregion + #endregion - #region methods + #region methods - #region equality and hashcode + #region equality and hashcode - // ReSharper disable once CompareOfFloatsByEqualityOperator - public bool Equals(Speed other) => value == other.value; + // ReSharper disable once CompareOfFloatsByEqualityOperator + public bool Equals(Speed other) => value == other.value; - public override bool Equals(object? obj) => obj is Speed speed && Equals(speed); + public override bool Equals(object? obj) => obj is Speed speed && Equals(speed); - public override int GetHashCode() => value.GetHashCode(); + public override int GetHashCode() => value.GetHashCode(); - #endregion + #endregion - #region compare + #region compare - public int CompareTo(Speed other) => value.CompareTo(other.value); + public int CompareTo(Speed other) => value.CompareTo(other.value); - #endregion + #endregion - #region tostring + #region tostring - public override string ToString() => ToString(null, CultureInfo.CurrentCulture); + public override string ToString() => ToString(null, CultureInfo.CurrentCulture); - public string ToString(string? format, IFormatProvider? formatProvider) - => $"{value.ToString(format, formatProvider)} u/t"; + public string ToString(string? format, IFormatProvider? formatProvider) + => $"{value.ToString(format, formatProvider)} u/t"; - public string ToString(string? format) - => ToString(format, CultureInfo.CurrentCulture); + public string ToString(string? format) + => ToString(format, CultureInfo.CurrentCulture); - #endregion + #endregion - #endregion + #endregion - #region operators + #region operators - #region algebra + #region algebra - /// - /// Adds two speed values. - /// - public static Speed operator +(Speed s0, Speed s1) => new Speed(s0.value + s1.value); + /// + /// Adds two speed values. + /// + public static Speed operator +(Speed s0, Speed s1) => new Speed(s0.value + s1.value); - /// - /// Subtracts a speed value from another. - /// - public static Speed operator -(Speed s0, Speed s1) => new Speed(s0.value - s1.value); + /// + /// Subtracts a speed value from another. + /// + public static Speed operator -(Speed s0, Speed s1) => new Speed(s0.value - s1.value); - #endregion + #endregion - #region scaling + #region scaling - /// - /// Inverts the speed value. - /// - public static Speed operator -(Speed s) => new Speed(-s.value); + /// + /// Inverts the speed value. + /// + public static Speed operator -(Speed s) => new Speed(-s.value); - /// - /// Multiplies the speed value with a scalar. - /// - public static Speed operator *(Speed s, float scalar) => new Speed(s.value * scalar); + /// + /// Multiplies the speed value with a scalar. + /// + public static Speed operator *(Speed s, float scalar) => new Speed(s.value * scalar); - /// - /// Multiplies the speed value with a scalar. - /// - public static Speed operator *(float scalar, Speed s) => new Speed(s.value * scalar); + /// + /// Multiplies the speed value with a scalar. + /// + public static Speed operator *(float scalar, Speed s) => new Speed(s.value * scalar); - /// - /// Divides the speed value by a divisor. - /// - public static Speed operator /(Speed s, float divisor) => new Speed(s.value / divisor); + /// + /// Divides the speed value by a divisor. + /// + public static Speed operator /(Speed s, float divisor) => new Speed(s.value / divisor); - #endregion + #endregion - #region ratio + #region ratio - /// - /// Divides a speed value by another, returning a type-less fraction. - /// - public static float operator /(Speed dividend, Speed divisor) => dividend.value / divisor.value; + /// + /// Divides a speed value by another, returning a type-less fraction. + /// + public static float operator /(Speed dividend, Speed divisor) => dividend.value / divisor.value; - #endregion + #endregion - #region differentiate + #region differentiate - /// - /// Divides a speed value by a timespan, returning an acceleration. - /// - public static Acceleration operator /(Speed s, TimeSpan t) => new Acceleration(s.value / (float)t.NumericValue); + /// + /// Divides a speed value by a timespan, returning an acceleration. + /// + public static Acceleration operator /(Speed s, TimeSpan t) => new Acceleration(s.value / (float)t.NumericValue); - public static Acceleration operator *(Speed s, Frequency f) => new Acceleration(s.value * (float)f.NumericValue); + public static Acceleration operator *(Speed s, Frequency f) => new Acceleration(s.value * (float)f.NumericValue); - public static Acceleration operator *(Frequency f, Speed s) => new Acceleration(s.value * (float)f.NumericValue); + public static Acceleration operator *(Frequency f, Speed s) => new Acceleration(s.value * (float)f.NumericValue); - #endregion + #endregion - #region integrate + #region integrate - /// - /// Multiplies a speed value by a timespan, returning a unit value. - /// - public static Unit operator *(Speed s, TimeSpan t) => new Unit(s.value * (float)t.NumericValue); + /// + /// Multiplies a speed value by a timespan, returning a unit value. + /// + public static Unit operator *(Speed s, TimeSpan t) => new Unit(s.value * (float)t.NumericValue); - /// - /// Multiplies a speed value by a timespan, returning a unit value. - /// - public static Unit operator *(TimeSpan t, Speed s) => new Unit(s.value * (float)t.NumericValue); + /// + /// Multiplies a speed value by a timespan, returning a unit value. + /// + public static Unit operator *(TimeSpan t, Speed s) => new Unit(s.value * (float)t.NumericValue); - public static Unit operator /(Speed s, Frequency f) => new Unit(s.value / (float)f.NumericValue); + public static Unit operator /(Speed s, Frequency f) => new Unit(s.value / (float)f.NumericValue); - #endregion + #endregion - #region add dimension + #region add dimension - /// - /// Multiplies a direction with a speed value, returning a typed speed vector of the given direction and length. - /// - public static Velocity2 operator *(Speed s, Direction2 d) => new Velocity2(d.Vector * s.value); + /// + /// Multiplies a direction with a speed value, returning a typed speed vector of the given direction and length. + /// + public static Velocity2 operator *(Speed s, Direction2 d) => new Velocity2(d.Vector * s.value); - /// - /// Multiplies a direction with a speed value, returning a typed speed vector of the given direction and length. - /// - public static Velocity2 operator *(Direction2 d, Speed s) => new Velocity2(d.Vector * s.value); + /// + /// Multiplies a direction with a speed value, returning a typed speed vector of the given direction and length. + /// + public static Velocity2 operator *(Direction2 d, Speed s) => new Velocity2(d.Vector * s.value); - /// - /// Multiplies a speed value with an untyped vector, returning a typed speed vector. - /// - public static Velocity2 operator *(Speed u, Vector2 v) => new Velocity2(v * u.value); + /// + /// Multiplies a speed value with an untyped vector, returning a typed speed vector. + /// + public static Velocity2 operator *(Speed u, Vector2 v) => new Velocity2(v * u.value); - /// - /// Multiplies a speed value with an untyped vector, returning a typed speed vector. - /// - public static Velocity2 operator *(Vector2 v, Speed u) => new Velocity2(v * u.value); + /// + /// Multiplies a speed value with an untyped vector, returning a typed speed vector. + /// + public static Velocity2 operator *(Vector2 v, Speed u) => new Velocity2(v * u.value); - #endregion + #endregion - #region add two dimensions + #region add two dimensions - /// - /// Multiplies a speed value with an untyped vector, returning a typed speed vector. - /// - public static Velocity3 operator *(Speed u, Vector3 v) => new Velocity3(v * u.value); + /// + /// Multiplies a speed value with an untyped vector, returning a typed speed vector. + /// + public static Velocity3 operator *(Speed u, Vector3 v) => new Velocity3(v * u.value); - /// - /// Multiplies a speed value with an untyped vector, returning a typed speed vector. - /// - public static Velocity3 operator *(Vector3 v, Speed u) => new Velocity3(v * u.value); + /// + /// Multiplies a speed value with an untyped vector, returning a typed speed vector. + /// + public static Velocity3 operator *(Vector3 v, Speed u) => new Velocity3(v * u.value); - #endregion + #endregion - #region comparision + #region comparision - /// - /// Compares two speed values for equality. - /// - public static bool operator ==(Speed s0, Speed s1) => s0.Equals(s1); + /// + /// Compares two speed values for equality. + /// + public static bool operator ==(Speed s0, Speed s1) => s0.Equals(s1); - /// - /// Compares two speed values for inequality. - /// - public static bool operator !=(Speed s0, Speed s1) => !(s0 == s1); + /// + /// Compares two speed values for inequality. + /// + public static bool operator !=(Speed s0, Speed s1) => !(s0 == s1); - /// - /// Checks if one speed value is smaller than another. - /// - public static bool operator <(Speed s0, Speed s1) => s0.value < s1.value; + /// + /// Checks if one speed value is smaller than another. + /// + public static bool operator <(Speed s0, Speed s1) => s0.value < s1.value; - /// - /// Checks if one speed value is larger than another. - /// - public static bool operator >(Speed s0, Speed s1) => s0.value > s1.value; + /// + /// Checks if one speed value is larger than another. + /// + public static bool operator >(Speed s0, Speed s1) => s0.value > s1.value; - /// - /// Checks if one speed value is smaller or equal to another. - /// - public static bool operator <=(Speed s0, Speed s1) => s0.value <= s1.value; + /// + /// Checks if one speed value is smaller or equal to another. + /// + public static bool operator <=(Speed s0, Speed s1) => s0.value <= s1.value; - /// - /// Checks if one speed value is larger or equal to another. - /// - public static bool operator >=(Speed s0, Speed s1) => s0.value >= s1.value; + /// + /// Checks if one speed value is larger or equal to another. + /// + public static bool operator >=(Speed s0, Speed s1) => s0.value >= s1.value; - #endregion + #endregion - #endregion + #endregion - #region static methods + #region static methods - public static Speed Min(Speed s1, Speed s2) - => new Speed(Math.Min(s1.NumericValue, s2.NumericValue)); + public static Speed Min(Speed s1, Speed s2) + => new Speed(Math.Min(s1.NumericValue, s2.NumericValue)); - public static Speed Max(Speed s1, Speed s2) - => new Speed(Math.Max(s1.NumericValue, s2.NumericValue)); + public static Speed Max(Speed s1, Speed s2) + => new Speed(Math.Max(s1.NumericValue, s2.NumericValue)); - #endregion + #endregion - } } diff --git a/Bearded.Utilities/SpaceTime/undirected/Unit.cs b/Bearded.Utilities/SpaceTime/undirected/Unit.cs index b8f002e2..42d65731 100644 --- a/Bearded.Utilities/SpaceTime/undirected/Unit.cs +++ b/Bearded.Utilities/SpaceTime/undirected/Unit.cs @@ -3,226 +3,225 @@ using Bearded.Utilities.Geometry; using OpenTK.Mathematics; -namespace Bearded.Utilities.SpaceTime +namespace Bearded.Utilities.SpaceTime; + +/// +/// A type-safe representation of an undirected signed distance or length. +/// +public readonly struct Unit : IEquatable, IComparable, IFormattable { - /// - /// A type-safe representation of an undirected signed distance or length. - /// - public readonly struct Unit : IEquatable, IComparable, IFormattable - { - private readonly float value; + private readonly float value; - #region construction + #region construction - public Unit(float value) - { - this.value = value; - } + public Unit(float value) + { + this.value = value; + } - #endregion + #endregion - #region properties + #region properties - /// - /// Returns the numeric value of the unit value. - /// - public float NumericValue => value; + /// + /// Returns the numeric value of the unit value. + /// + public float NumericValue => value; - /// - /// Returns the type-safe square of the unit value. - /// - public Squared Squared => Squared.FromRoot(value); + /// + /// Returns the type-safe square of the unit value. + /// + public Squared Squared => Squared.FromRoot(value); - /// - /// Returns a Unit type with value 0. - /// - public static Unit Zero => new Unit(0); + /// + /// Returns a Unit type with value 0. + /// + public static Unit Zero => new Unit(0); - /// - /// Returns a Unit type with value 1. - /// - public static Unit One => new Unit(1); + /// + /// Returns a Unit type with value 1. + /// + public static Unit One => new Unit(1); - #endregion + #endregion - #region methods + #region methods - #region equality and hashcode + #region equality and hashcode - // ReSharper disable once CompareOfFloatsByEqualityOperator - public bool Equals(Unit other) => value == other.value; + // ReSharper disable once CompareOfFloatsByEqualityOperator + public bool Equals(Unit other) => value == other.value; - public override bool Equals(object? obj) => obj is Unit unit && Equals(unit); + public override bool Equals(object? obj) => obj is Unit unit && Equals(unit); - public override int GetHashCode() => value.GetHashCode(); + public override int GetHashCode() => value.GetHashCode(); - #endregion + #endregion - #region compare + #region compare - public int CompareTo(Unit other) => value.CompareTo(other.value); + public int CompareTo(Unit other) => value.CompareTo(other.value); - #endregion + #endregion - #region tostring + #region tostring - public override string ToString() => ToString(null, CultureInfo.CurrentCulture); + public override string ToString() => ToString(null, CultureInfo.CurrentCulture); - public string ToString(string? format, IFormatProvider? formatProvider) - => $"{value.ToString(format, formatProvider)} u"; + public string ToString(string? format, IFormatProvider? formatProvider) + => $"{value.ToString(format, formatProvider)} u"; - public string ToString(string? format) - => ToString(format, CultureInfo.CurrentCulture); + public string ToString(string? format) + => ToString(format, CultureInfo.CurrentCulture); - #endregion + #endregion - #endregion + #endregion - #region operators + #region operators - #region algebra + #region algebra - /// - /// Adds two unit values. - /// - public static Unit operator +(Unit u0, Unit u1) => new Unit(u0.value + u1.value); + /// + /// Adds two unit values. + /// + public static Unit operator +(Unit u0, Unit u1) => new Unit(u0.value + u1.value); - /// - /// Subtracts a unit value from another. - /// - public static Unit operator -(Unit u0, Unit u1) => new Unit(u0.value - u1.value); + /// + /// Subtracts a unit value from another. + /// + public static Unit operator -(Unit u0, Unit u1) => new Unit(u0.value - u1.value); - #endregion + #endregion - #region scaling + #region scaling - /// - /// Inverts the unit value. - /// - public static Unit operator -(Unit u) => new Unit(-u.value); + /// + /// Inverts the unit value. + /// + public static Unit operator -(Unit u) => new Unit(-u.value); - /// - /// Multiples the unit value with a scalar. - /// - public static Unit operator *(Unit u, float scalar) => new Unit(u.value * scalar); + /// + /// Multiples the unit value with a scalar. + /// + public static Unit operator *(Unit u, float scalar) => new Unit(u.value * scalar); - /// - /// Multiples the unit value with a scalar. - /// - public static Unit operator *(float scalar, Unit u) => new Unit(u.value * scalar); + /// + /// Multiples the unit value with a scalar. + /// + public static Unit operator *(float scalar, Unit u) => new Unit(u.value * scalar); - /// - /// Divides the unit value by a divisor. - /// - public static Unit operator /(Unit u, float divisor) => new Unit(u.value / divisor); + /// + /// Divides the unit value by a divisor. + /// + public static Unit operator /(Unit u, float divisor) => new Unit(u.value / divisor); - #endregion + #endregion - #region ratio + #region ratio - /// - /// Devides a unit value by another, returning a type-less fraction. - /// - public static float operator /(Unit dividend, Unit divisor) => dividend.value / divisor.value; + /// + /// Devides a unit value by another, returning a type-less fraction. + /// + public static float operator /(Unit dividend, Unit divisor) => dividend.value / divisor.value; - #endregion + #endregion - #region differentiate + #region differentiate - /// - /// Divides a unit value by a timespan, returning a speed. - /// - public static Speed operator /(Unit u, TimeSpan t) => new Speed(u.value / (float)t.NumericValue); + /// + /// Divides a unit value by a timespan, returning a speed. + /// + public static Speed operator /(Unit u, TimeSpan t) => new Speed(u.value / (float)t.NumericValue); - public static Speed operator *(Unit u, Frequency f) => new Speed(u.value * (float)f.NumericValue); + public static Speed operator *(Unit u, Frequency f) => new Speed(u.value * (float)f.NumericValue); - public static Speed operator *(Frequency f, Unit u) => new Speed(u.value * (float)f.NumericValue); + public static Speed operator *(Frequency f, Unit u) => new Speed(u.value * (float)f.NumericValue); - #endregion + #endregion - #region add dimension + #region add dimension - /// - /// Multiplies a direction with a unit value, returning a typed vector of the given direction and length. - /// - public static Difference2 operator *(Unit u, Direction2 d) => new Difference2(d.Vector * u.value); + /// + /// Multiplies a direction with a unit value, returning a typed vector of the given direction and length. + /// + public static Difference2 operator *(Unit u, Direction2 d) => new Difference2(d.Vector * u.value); - /// - /// Multiplies a direction with a unit value, returning a typed vector of the given direction and length. - /// - public static Difference2 operator *(Direction2 d, Unit u) => new Difference2(d.Vector * u.value); + /// + /// Multiplies a direction with a unit value, returning a typed vector of the given direction and length. + /// + public static Difference2 operator *(Direction2 d, Unit u) => new Difference2(d.Vector * u.value); - /// - /// Multiplies a unit value with an untyped vector, returning a typed vector. - /// - public static Difference2 operator *(Unit u, Vector2 v) => new Difference2(v * u.value); + /// + /// Multiplies a unit value with an untyped vector, returning a typed vector. + /// + public static Difference2 operator *(Unit u, Vector2 v) => new Difference2(v * u.value); - /// - /// Multiplies a unit value with an untyped vector, returning a typed vector. - /// - public static Difference2 operator *(Vector2 v, Unit u) => new Difference2(v * u.value); + /// + /// Multiplies a unit value with an untyped vector, returning a typed vector. + /// + public static Difference2 operator *(Vector2 v, Unit u) => new Difference2(v * u.value); - #endregion + #endregion - #region add two dimensions + #region add two dimensions - /// - /// Multiplies a unit value with an untyped vector, returning a typed vector. - /// - public static Difference3 operator *(Unit u, Vector3 v) => new Difference3(v * u.value); + /// + /// Multiplies a unit value with an untyped vector, returning a typed vector. + /// + public static Difference3 operator *(Unit u, Vector3 v) => new Difference3(v * u.value); - /// - /// Multiplies a unit value with an untyped vector, returning a typed vector. - /// - public static Difference3 operator *(Vector3 v, Unit u) => new Difference3(v * u.value); + /// + /// Multiplies a unit value with an untyped vector, returning a typed vector. + /// + public static Difference3 operator *(Vector3 v, Unit u) => new Difference3(v * u.value); - #endregion + #endregion - #region comparision + #region comparision - /// - /// Compares two unit values for equality. - /// - public static bool operator ==(Unit u0, Unit u1) => u0.Equals(u1); + /// + /// Compares two unit values for equality. + /// + public static bool operator ==(Unit u0, Unit u1) => u0.Equals(u1); - /// - /// Compares two unit values for inequality. - /// - public static bool operator !=(Unit u0, Unit u1) => !(u0 == u1); + /// + /// Compares two unit values for inequality. + /// + public static bool operator !=(Unit u0, Unit u1) => !(u0 == u1); - /// - /// Checks if one unit value is smaller than another. - /// - public static bool operator <(Unit u0, Unit u1) => u0.value < u1.value; + /// + /// Checks if one unit value is smaller than another. + /// + public static bool operator <(Unit u0, Unit u1) => u0.value < u1.value; - /// - /// Checks if one unit value is larger than another. - /// - public static bool operator >(Unit u0, Unit u1) => u0.value > u1.value; + /// + /// Checks if one unit value is larger than another. + /// + public static bool operator >(Unit u0, Unit u1) => u0.value > u1.value; - /// - /// Checks if one unit value is smaller or equal to another. - /// - public static bool operator <=(Unit u0, Unit u1) => u0.value <= u1.value; + /// + /// Checks if one unit value is smaller or equal to another. + /// + public static bool operator <=(Unit u0, Unit u1) => u0.value <= u1.value; - /// - /// Checks if one unit value is larger or equal to another. - /// - public static bool operator >=(Unit u0, Unit u1) => u0.value >= u1.value; + /// + /// Checks if one unit value is larger or equal to another. + /// + public static bool operator >=(Unit u0, Unit u1) => u0.value >= u1.value; - #endregion + #endregion - #endregion + #endregion - #region static methods + #region static methods - public static Unit Min(Unit u1, Unit u2) - => new Unit(Math.Min(u1.NumericValue, u2.NumericValue)); + public static Unit Min(Unit u1, Unit u2) + => new Unit(Math.Min(u1.NumericValue, u2.NumericValue)); - public static Unit Max(Unit u1, Unit u2) - => new Unit(Math.Max(u1.NumericValue, u2.NumericValue)); + public static Unit Max(Unit u1, Unit u2) + => new Unit(Math.Max(u1.NumericValue, u2.NumericValue)); - #endregion + #endregion - } } diff --git a/Bearded.Utilities/Threading/BackgroundActionQueue.cs b/Bearded.Utilities/Threading/BackgroundActionQueue.cs index 5473aa12..e697bad4 100644 --- a/Bearded.Utilities/Threading/BackgroundActionQueue.cs +++ b/Bearded.Utilities/Threading/BackgroundActionQueue.cs @@ -2,79 +2,78 @@ using System.Threading; using System.Threading.Tasks; -namespace Bearded.Utilities.Threading -{ - /// - /// A thread-safe queue that runs scheduled actions on a separate thread. - /// Typical usage is to schedule actions from one or multiple threads to have them executed in the background. - /// The actions are guaranteed to be executed in the order they are scheduled. - /// - public sealed class BackgroundActionQueue : IActionQueue - { - private readonly ManualActionQueue queue = new ManualActionQueue(); +namespace Bearded.Utilities.Threading; - private readonly Thread thread; +/// +/// A thread-safe queue that runs scheduled actions on a separate thread. +/// Typical usage is to schedule actions from one or multiple threads to have them executed in the background. +/// The actions are guaranteed to be executed in the order they are scheduled. +/// +public sealed class BackgroundActionQueue : IActionQueue +{ + private readonly ManualActionQueue queue = new ManualActionQueue(); - private bool finishing; + private readonly Thread thread; - /// - /// Gets a value indicating whether this queue has finished or was aborted. - /// - public bool Finished { get; private set; } + private bool finishing; - /// - /// Creates a new background action queue. - /// - public BackgroundActionQueue() - : this("Unknown thread", false) - { - } + /// + /// Gets a value indicating whether this queue has finished or was aborted. + /// + public bool Finished { get; private set; } - /// - /// Creates a new background action queue. - /// - /// Name for the thread executing the actions of this queue. - /// Whether the thread should run in the background or not. - public BackgroundActionQueue(string name, bool backgroundThread) - { - thread = new Thread(run) - { - Name = name, - IsBackground = backgroundThread - }; - thread.Start(); - } + /// + /// Creates a new background action queue. + /// + public BackgroundActionQueue() + : this("Unknown thread", false) + { + } - private void run() + /// + /// Creates a new background action queue. + /// + /// Name for the thread executing the actions of this queue. + /// Whether the thread should run in the background or not. + public BackgroundActionQueue(string name, bool backgroundThread) + { + thread = new Thread(run) { - while (!finishing) - { - queue.ExecuteOne(); - } - Finished = true; - } + Name = name, + IsBackground = backgroundThread + }; + thread.Start(); + } - /// - /// Stops the execution of further actions. Actions already queued will still be run. - /// - public Task Finish() + private void run() + { + while (!finishing) { - return Finish(true); + queue.ExecuteOne(); } + Finished = true; + } - /// - /// Stops the execution of further actions. - /// - /// If true, finishes executing all actions currently scheduled, otherwise stops after the current action. - public Task Finish(bool executeScheduled) - { - if (!executeScheduled) - finishing = true; - return queue.Run(() => finishing = true); - } + /// + /// Stops the execution of further actions. Actions already queued will still be run. + /// + public Task Finish() + { + return Finish(true); + } - public void Queue(Action action) => queue.Queue(action); - public Task Run(Action action) => queue.Run(action); - public Task Run(Func function) => queue.Run(function); + /// + /// Stops the execution of further actions. + /// + /// If true, finishes executing all actions currently scheduled, otherwise stops after the current action. + public Task Finish(bool executeScheduled) + { + if (!executeScheduled) + finishing = true; + return queue.Run(() => finishing = true); } + + public void Queue(Action action) => queue.Queue(action); + public Task Run(Action action) => queue.Run(action); + public Task Run(Func function) => queue.Run(function); } diff --git a/Bearded.Utilities/Threading/IActionQueue.cs b/Bearded.Utilities/Threading/IActionQueue.cs index 188710f8..748289f5 100644 --- a/Bearded.Utilities/Threading/IActionQueue.cs +++ b/Bearded.Utilities/Threading/IActionQueue.cs @@ -1,29 +1,28 @@ using System; using System.Threading.Tasks; -namespace Bearded.Utilities.Threading +namespace Bearded.Utilities.Threading; + +/// +/// Interface for queuing actions to run. +/// +public interface IActionQueue { /// - /// Interface for queuing actions to run. + /// Queues an action to run. Returns immediately. /// - public interface IActionQueue - { - /// - /// Queues an action to run. Returns immediately. - /// - /// The action to run. - void Queue(Action action); + /// The action to run. + void Queue(Action action); - /// - /// Queues an action to run. Returns a task that completes when the action has been executed. - /// - /// The action to run. - Task Run(Action action); + /// + /// Queues an action to run. Returns a task that completes when the action has been executed. + /// + /// The action to run. + Task Run(Action action); - /// - /// Queues a parameterless function to run. Returns a task that completes when the function has been executed. - /// - /// The function to run. - Task Run(Func function); - } + /// + /// Queues a parameterless function to run. Returns a task that completes when the function has been executed. + /// + /// The function to run. + Task Run(Func function); } diff --git a/Bearded.Utilities/Threading/ManualActionQueue.cs b/Bearded.Utilities/Threading/ManualActionQueue.cs index e3a35dfd..12643f9a 100644 --- a/Bearded.Utilities/Threading/ManualActionQueue.cs +++ b/Bearded.Utilities/Threading/ManualActionQueue.cs @@ -3,113 +3,112 @@ using System.Diagnostics; using System.Threading.Tasks; -namespace Bearded.Utilities.Threading +namespace Bearded.Utilities.Threading; + +/// +/// A thread-safe queue to run actions from. +/// Typical usage is to schedule actions from multiple threads and execute them on one main thread. +/// However, actions can also be executed by multiple threads. +/// If only one thread is used to execute, the actions are guaranteed to be executed in the order they were scheduled. +/// +public sealed class ManualActionQueue : IActionQueue { + private readonly BlockingCollection actions = new BlockingCollection(); + /// - /// A thread-safe queue to run actions from. - /// Typical usage is to schedule actions from multiple threads and execute them on one main thread. - /// However, actions can also be executed by multiple threads. - /// If only one thread is used to execute, the actions are guaranteed to be executed in the order they were scheduled. + /// Executes one scheduled action. + /// If no action is scheduled, this will wait until one is scheduled, and then execute that. + /// This will never return without having executed exactly one scheduled action. /// - public sealed class ManualActionQueue : IActionQueue + public void ExecuteOne() { - private readonly BlockingCollection actions = new BlockingCollection(); + var action = actions.Take(); + action(); + } - /// - /// Executes one scheduled action. - /// If no action is scheduled, this will wait until one is scheduled, and then execute that. - /// This will never return without having executed exactly one scheduled action. - /// - public void ExecuteOne() + /// + /// Executes one scheduled action, if any are available. + /// Returns immediately if no actions are scheduled. + /// + /// Whether an action was executed. + public bool TryExecuteOne() + { + if (actions.TryTake(out var action)) { - var action = actions.Take(); action(); + return true; } + return false; + } - /// - /// Executes one scheduled action, if any are available. - /// Returns immediately if no actions are scheduled. - /// - /// Whether an action was executed. - public bool TryExecuteOne() + /// + /// Tries to execute one scheduled action. + /// If no action is available immediately, it will wait the given time span for an action to be scheduled before returning. + /// + /// The time span. + /// Whether an action was executed. + public bool TryExecuteOne(TimeSpan timeout) + { + if (actions.TryTake(out var action, timeout)) { - if (actions.TryTake(out var action)) - { - action(); - return true; - } - return false; + action(); + return true; } + return false; + } - /// - /// Tries to execute one scheduled action. - /// If no action is available immediately, it will wait the given time span for an action to be scheduled before returning. - /// - /// The time span. - /// Whether an action was executed. - public bool TryExecuteOne(TimeSpan timeout) + /// + /// Executes scheduled actions for a given time span. + /// Returns after the first action, if the given time has elapsed. + /// May run longer than the given time, depending on the scheduled actions, but will never take less. + /// + /// The time span. + /// The number of actions executed. + public int ExecuteFor(TimeSpan time) + { + var timer = Stopwatch.StartNew(); + var executed = 0; + while (true) { - if (actions.TryTake(out var action, timeout)) - { - action(); - return true; - } - return false; + var timeLeft = time - timer.Elapsed; + if (timeLeft < new TimeSpan(0)) + break; + if (!actions.TryTake(out var action, timeLeft)) + break; + executed++; + action(); } + return executed; + } - /// - /// Executes scheduled actions for a given time span. - /// Returns after the first action, if the given time has elapsed. - /// May run longer than the given time, depending on the scheduled actions, but will never take less. - /// - /// The time span. - /// The number of actions executed. - public int ExecuteFor(TimeSpan time) - { - var timer = Stopwatch.StartNew(); - var executed = 0; - while (true) - { - var timeLeft = time - timer.Elapsed; - if (timeLeft < new TimeSpan(0)) - break; - if (!actions.TryTake(out var action, timeLeft)) - break; - executed++; - action(); - } - return executed; - } + public void Queue(Action action) + { + actions.Add(action); + } - public void Queue(Action action) - { - actions.Add(action); - } + public Task Run(Action action) + { + var task = new TaskCompletionSource(); - public Task Run(Action action) + actions.Add(() => { - var task = new TaskCompletionSource(); + action(); + task.SetResult(null); + }); - actions.Add(() => - { - action(); - task.SetResult(null); - }); + return task.Task; + } - return task.Task; - } + public Task Run(Func function) + { + var task = new TaskCompletionSource(); - public Task Run(Func function) + actions.Add(() => { - var task = new TaskCompletionSource(); - - actions.Add(() => - { - var result = function(); - task.SetResult(result); - }); + var result = function(); + task.SetResult(result); + }); - return task.Task; - } + return task.Task; } } diff --git a/Bearded.Utilities/Tilemaps/Rectangular/Direction.cs b/Bearded.Utilities/Tilemaps/Rectangular/Direction.cs index 5cd29ca9..20719d92 100644 --- a/Bearded.Utilities/Tilemaps/Rectangular/Direction.cs +++ b/Bearded.Utilities/Tilemaps/Rectangular/Direction.cs @@ -1,45 +1,44 @@ -namespace Bearded.Utilities.Tilemaps.Rectangular +namespace Bearded.Utilities.Tilemaps.Rectangular; + +/// +/// Represents the eight directions of movement in a rectangular tilemap. +/// +public enum Direction : byte { /// - /// Represents the eight directions of movement in a rectangular tilemap. - /// - public enum Direction : byte - { - /// - /// Unknown/no direction value. Steps in this direction have no effect. - /// - Unknown = 0, - /// - /// Step to the right (0 degrees). - /// - Right = 1, - /// - /// Step to the upper right. (45 degrees). - /// - UpRight = 2, - /// - /// Step upwards. (90 degrees). - /// - Up = 3, - /// - /// Step to the upper left (135 degrees). - /// - UpLeft = 4, - /// - /// Step to the left (180 degrees). - /// - Left = 5, - /// - /// Step to the lower left (225 degrees). - /// - DownLeft = 6, - /// - /// Downwards step. (270 degrees). - /// - Down = 7, - /// - /// Step to the lower right (315 degrees). - /// - DownRight = 8, - } + /// Unknown/no direction value. Steps in this direction have no effect. + /// + Unknown = 0, + /// + /// Step to the right (0 degrees). + /// + Right = 1, + /// + /// Step to the upper right. (45 degrees). + /// + UpRight = 2, + /// + /// Step upwards. (90 degrees). + /// + Up = 3, + /// + /// Step to the upper left (135 degrees). + /// + UpLeft = 4, + /// + /// Step to the left (180 degrees). + /// + Left = 5, + /// + /// Step to the lower left (225 degrees). + /// + DownLeft = 6, + /// + /// Downwards step. (270 degrees). + /// + Down = 7, + /// + /// Step to the lower right (315 degrees). + /// + DownRight = 8, } diff --git a/Bearded.Utilities/Tilemaps/Rectangular/Directions.cs b/Bearded.Utilities/Tilemaps/Rectangular/Directions.cs index 5d657fd1..7e44132e 100644 --- a/Bearded.Utilities/Tilemaps/Rectangular/Directions.cs +++ b/Bearded.Utilities/Tilemaps/Rectangular/Directions.cs @@ -1,53 +1,52 @@ using System; -namespace Bearded.Utilities.Tilemaps.Rectangular +namespace Bearded.Utilities.Tilemaps.Rectangular; + +/// +/// Represents any combinations of the eight directions in a rectangular tilemap. +/// +[Flags] +public enum Directions : byte { /// - /// Represents any combinations of the eight directions in a rectangular tilemap. - /// - [Flags] - public enum Directions : byte - { - /// - /// No direction. - /// - None = 0, - /// - /// Step to the right (0 degrees). - /// - Right = 1 << (Direction.Right - 1), - /// - /// Step to the upper right. (45 degrees). - /// - UpRight = 1 << (Direction.UpRight - 1), - /// - /// Step upwards. (90 degrees). - /// - Up = 1 << (Direction.Up - 1), - /// - /// Step to the upper left (135 degrees). - /// - UpLeft = 1 << (Direction.UpLeft - 1), - /// - /// Step to the left (180 degrees). - /// - Left = 1 << (Direction.Left - 1), - /// - /// Step to the lower left (225 degrees). - /// - DownLeft = 1 << (Direction.DownLeft - 1), - /// - /// Downwards step. (270 degrees). - /// - Down = 1 << (Direction.Down - 1), - /// - /// Step to the lower right (315 degrees). - /// - DownRight = 1 << (Direction.DownRight - 1), + /// No direction. + /// + None = 0, + /// + /// Step to the right (0 degrees). + /// + Right = 1 << (Direction.Right - 1), + /// + /// Step to the upper right. (45 degrees). + /// + UpRight = 1 << (Direction.UpRight - 1), + /// + /// Step upwards. (90 degrees). + /// + Up = 1 << (Direction.Up - 1), + /// + /// Step to the upper left (135 degrees). + /// + UpLeft = 1 << (Direction.UpLeft - 1), + /// + /// Step to the left (180 degrees). + /// + Left = 1 << (Direction.Left - 1), + /// + /// Step to the lower left (225 degrees). + /// + DownLeft = 1 << (Direction.DownLeft - 1), + /// + /// Downwards step. (270 degrees). + /// + Down = 1 << (Direction.Down - 1), + /// + /// Step to the lower right (315 degrees). + /// + DownRight = 1 << (Direction.DownRight - 1), - /// - /// All directions. - /// - All = Right | UpRight | Up | UpLeft | Left | DownLeft | Down | DownRight, - } + /// + /// All directions. + /// + All = Right | UpRight | Up | UpLeft | Left | DownLeft | Down | DownRight, } diff --git a/Bearded.Utilities/Tilemaps/Rectangular/Extensions.cs b/Bearded.Utilities/Tilemaps/Rectangular/Extensions.cs index a6fa463d..0ca3d39d 100644 --- a/Bearded.Utilities/Tilemaps/Rectangular/Extensions.cs +++ b/Bearded.Utilities/Tilemaps/Rectangular/Extensions.cs @@ -4,211 +4,210 @@ using System.Linq; using Bearded.Utilities.Geometry; -namespace Bearded.Utilities.Tilemaps.Rectangular +namespace Bearded.Utilities.Tilemaps.Rectangular; + +/// +/// Extension methods for rectengular tilemaps. +/// +public static class Extensions { + #region Lookup Tables + + internal static readonly Step[] DirectionDeltas = + { + new Step(0, 0), + new Step(1, 0), + new Step(1, 1), + new Step(0, 1), + new Step(-1, 1), + new Step(-1, 0), + new Step(-1, -1), + new Step(0, -1), + new Step(1, -1), + }; + private static readonly Direction[] directionOpposite = + { + Direction.Unknown, + Direction.Left, + Direction.DownLeft, + Direction.Down, + Direction.DownRight, + Direction.Right, + Direction.UpRight, + Direction.Up, + Direction.UpLeft, + }; + + private static readonly Direction[] directions = + { + Direction.Right, + Direction.UpRight, + Direction.Up, + Direction.UpLeft, + Direction.Left, + Direction.DownLeft, + Direction.Down, + Direction.DownRight, + }; + + private static readonly ReadOnlyCollection directionsAsReadOnly + = Array.AsReadOnly(directions); + + /// + /// A list of the eight directions in rectangular tilemaps. + /// + public static ReadOnlyCollection Directions + { + get { return directionsAsReadOnly; } + } + + #endregion + + #region Direction + + internal static Step Step(this Direction direction) + { + return DirectionDeltas[(int)direction]; + } + + /// + /// Returns the direction opposite to this one. + /// + public static Direction Opposite(this Direction direction) + { + return directionOpposite[(int)direction]; + } + + /// + /// Returns the closest of the eight directions inside a rectangular tilemap, by angle. + /// + public static Direction Octagonal(this Direction2 direction) + { + return (Direction)((int)Math.Floor(direction.Degrees * (1 / 45f) + 0.5f) % 8 + 1); + } + + /// + /// Returns the closest of the four non-diagonal directions inside a rectangular tilemap, by angle. + /// + public static Direction Quadrogonal(this Direction2 direction) + { + return (Direction)(( + (int)Math.Floor(direction.Degrees * (1 / 90f) + 0.5f) % 4 + ) * 2 + 1); + } + + #endregion + + #region Directions + + /// + /// Checks if any directions are set. + /// + /// True if any direction is set. False otherwise. + public static bool Any(this Directions direction) + { + return direction != Rectangular.Directions.None; + } + + /// + /// Checks if any of the given directions are set, i.e. if the directions intersect. + /// + /// The directions to check. + /// The directions to search for. + /// True if the directions intersect. False otherwise + public static bool Any(this Directions direction, Directions match) + { + return direction.Intersect(match) != Rectangular.Directions.None; + } + + /// + /// Checks if all directions are set. + /// + /// True if all direction are set. False otherwise. + public static bool All(this Directions direction) + { + return direction == Rectangular.Directions.All; + } + + /// + /// Checks if all of the given directions are set, i.e. if their intersection equals the parameter. + /// + /// The directions to check. + /// The directions to search for. + /// True if all directions are included. False otherwise + public static bool All(this Directions direction, Directions match) + { + return direction.HasFlag(match); + } + /// - /// Extension methods for rectengular tilemaps. + /// Returns the union of two sets of directions. /// - public static class Extensions - { - #region Lookup Tables - - internal static readonly Step[] DirectionDeltas = - { - new Step(0, 0), - new Step(1, 0), - new Step(1, 1), - new Step(0, 1), - new Step(-1, 1), - new Step(-1, 0), - new Step(-1, -1), - new Step(0, -1), - new Step(1, -1), - }; - private static readonly Direction[] directionOpposite = - { - Direction.Unknown, - Direction.Left, - Direction.DownLeft, - Direction.Down, - Direction.DownRight, - Direction.Right, - Direction.UpRight, - Direction.Up, - Direction.UpLeft, - }; - - private static readonly Direction[] directions = - { - Direction.Right, - Direction.UpRight, - Direction.Up, - Direction.UpLeft, - Direction.Left, - Direction.DownLeft, - Direction.Down, - Direction.DownRight, - }; - - private static readonly ReadOnlyCollection directionsAsReadOnly - = Array.AsReadOnly(directions); - - /// - /// A list of the eight directions in rectangular tilemaps. - /// - public static ReadOnlyCollection Directions - { - get { return directionsAsReadOnly; } - } - - #endregion - - #region Direction - - internal static Step Step(this Direction direction) - { - return DirectionDeltas[(int)direction]; - } - - /// - /// Returns the direction opposite to this one. - /// - public static Direction Opposite(this Direction direction) - { - return directionOpposite[(int)direction]; - } - - /// - /// Returns the closest of the eight directions inside a rectangular tilemap, by angle. - /// - public static Direction Octagonal(this Direction2 direction) - { - return (Direction)((int)Math.Floor(direction.Degrees * (1 / 45f) + 0.5f) % 8 + 1); - } - - /// - /// Returns the closest of the four non-diagonal directions inside a rectangular tilemap, by angle. - /// - public static Direction Quadrogonal(this Direction2 direction) - { - return (Direction)(( - (int)Math.Floor(direction.Degrees * (1 / 90f) + 0.5f) % 4 - ) * 2 + 1); - } - - #endregion - - #region Directions - - /// - /// Checks if any directions are set. - /// - /// True if any direction is set. False otherwise. - public static bool Any(this Directions direction) - { - return direction != Rectangular.Directions.None; - } - - /// - /// Checks if any of the given directions are set, i.e. if the directions intersect. - /// - /// The directions to check. - /// The directions to search for. - /// True if the directions intersect. False otherwise - public static bool Any(this Directions direction, Directions match) - { - return direction.Intersect(match) != Rectangular.Directions.None; - } - - /// - /// Checks if all directions are set. - /// - /// True if all direction are set. False otherwise. - public static bool All(this Directions direction) - { - return direction == Rectangular.Directions.All; - } - - /// - /// Checks if all of the given directions are set, i.e. if their intersection equals the parameter. - /// - /// The directions to check. - /// The directions to search for. - /// True if all directions are included. False otherwise - public static bool All(this Directions direction, Directions match) - { - return direction.HasFlag(match); - } - - /// - /// Returns the union of two sets of directions. - /// - public static Directions Union(this Directions directions, Directions directions2) - { - return directions | directions2; - } - - /// - /// Excludes one set of directions from another. - /// - /// The original value, without the given directions. - public static Directions Except(this Directions directions, Directions directions2) - { - return directions & ~directions2; - } - - /// - /// Returns the intersection of two sets of directions. - /// - public static Directions Intersect(this Directions directions, Directions directions2) - { - return directions & directions2; - } - - #endregion - - #region Direction and Directions - - private static Directions toDirections(this Direction direction) - { - return (Directions)(1 << ((int)direction - 1)); - } - - /// - /// Enumerates all the set directions. - /// - public static IEnumerable Enumerate(this Directions directions) - { - return Extensions.directions.Where(direction => directions.Includes(direction)); - } - - /// - /// Checks if a specific direction is set. - /// - /// True if the given direction is set. False otherwise. - public static bool Includes(this Directions directions, Direction direction) - { - return directions.HasFlag(direction.toDirections()); - } - - /// - /// Sets a given direction. - /// - /// A union of the original value and the given direction. - public static Directions And(this Directions directions, Direction direction) - { - return directions | direction.toDirections(); - } - - /// - /// Un-sets a given direction. - /// - /// The original value, without the given direction. - public static Directions Except(this Directions directions, Direction direction) - { - return directions & ~direction.toDirections(); - } - - #endregion + public static Directions Union(this Directions directions, Directions directions2) + { + return directions | directions2; } + + /// + /// Excludes one set of directions from another. + /// + /// The original value, without the given directions. + public static Directions Except(this Directions directions, Directions directions2) + { + return directions & ~directions2; + } + + /// + /// Returns the intersection of two sets of directions. + /// + public static Directions Intersect(this Directions directions, Directions directions2) + { + return directions & directions2; + } + + #endregion + + #region Direction and Directions + + private static Directions toDirections(this Direction direction) + { + return (Directions)(1 << ((int)direction - 1)); + } + + /// + /// Enumerates all the set directions. + /// + public static IEnumerable Enumerate(this Directions directions) + { + return Extensions.directions.Where(direction => directions.Includes(direction)); + } + + /// + /// Checks if a specific direction is set. + /// + /// True if the given direction is set. False otherwise. + public static bool Includes(this Directions directions, Direction direction) + { + return directions.HasFlag(direction.toDirections()); + } + + /// + /// Sets a given direction. + /// + /// A union of the original value and the given direction. + public static Directions And(this Directions directions, Direction direction) + { + return directions | direction.toDirections(); + } + + /// + /// Un-sets a given direction. + /// + /// The original value, without the given direction. + public static Directions Except(this Directions directions, Direction direction) + { + return directions & ~direction.toDirections(); + } + + #endregion } diff --git a/Bearded.Utilities/Tilemaps/Rectangular/Tile.cs b/Bearded.Utilities/Tilemaps/Rectangular/Tile.cs index 85ba5252..dcb56e8c 100644 --- a/Bearded.Utilities/Tilemaps/Rectangular/Tile.cs +++ b/Bearded.Utilities/Tilemaps/Rectangular/Tile.cs @@ -1,164 +1,163 @@ using System; using System.Collections.Generic; -namespace Bearded.Utilities.Tilemaps.Rectangular +namespace Bearded.Utilities.Tilemaps.Rectangular; + +/// +/// Represents a reference to a specific tile of a rectangular tilemap. +/// +/// The kind of data contained in the tilemap. +public readonly struct Tile + : IEquatable> { + private readonly Tilemap tilemap; + private readonly int x; + private readonly int y; + /// - /// Represents a reference to a specific tile of a rectangular tilemap. + /// Creates a new tile reference. /// - /// The kind of data contained in the tilemap. - public readonly struct Tile - : IEquatable> + /// The tilemap. + /// X location of tile. + /// Y location of tile. + /// Throws if tilemap is null. + public Tile(Tilemap tilemap, int x, int y) { - private readonly Tilemap tilemap; - private readonly int x; - private readonly int y; - - /// - /// Creates a new tile reference. - /// - /// The tilemap. - /// X location of tile. - /// Y location of tile. - /// Throws if tilemap is null. - public Tile(Tilemap tilemap, int x, int y) - { - if (tilemap == null) - throw new ArgumentNullException("tilemap"); + if (tilemap == null) + throw new ArgumentNullException("tilemap"); - this.tilemap = tilemap; - this.x = x; - this.y = y; - } + this.tilemap = tilemap; + this.x = x; + this.y = y; + } - #region properties + #region properties - /// - /// X location of the tile. - /// - public int X { get { return x; } } + /// + /// X location of the tile. + /// + public int X { get { return x; } } - /// - /// Y location of the tile. - /// - public int Y { get { return y; } } + /// + /// Y location of the tile. + /// + public int Y { get { return y; } } - /// - /// The data contained in the tile. Will throw if is false. - /// - public TTileValue Value { get { return tilemap[X, Y]; } } + /// + /// The data contained in the tile. Will throw if is false. + /// + public TTileValue Value { get { return tilemap[X, Y]; } } - /// - /// True if the tile is located within its tilemap. False otherwise. - /// - public bool IsValid - { - get { return tilemap != null && tilemap.IsValidTile(this); } - } + /// + /// True if the tile is located within its tilemap. False otherwise. + /// + public bool IsValid + { + get { return tilemap != null && tilemap.IsValidTile(this); } + } - /// - /// Returns all eight tiles neighbouring this one, except those outside the tilemap. - /// - public IEnumerable> ValidNeighbours + /// + /// Returns all eight tiles neighbouring this one, except those outside the tilemap. + /// + public IEnumerable> ValidNeighbours + { + get { - get + for (int i = 1; i < 9; i++) { - for (int i = 1; i < 9; i++) - { - var tile = Neighbour(Extensions.DirectionDeltas[i]); - if (tile.IsValid) - yield return tile; - } + var tile = Neighbour(Extensions.DirectionDeltas[i]); + if (tile.IsValid) + yield return tile; } } + } - /// - /// Returns all eight tiles neighbouring this one, whether or now they are outside the tilemap. - /// - public IEnumerable> PossibleNeighbours + /// + /// Returns all eight tiles neighbouring this one, whether or now they are outside the tilemap. + /// + public IEnumerable> PossibleNeighbours + { + get { - get + for (int i = 1; i < 9; i++) { - for (int i = 1; i < 9; i++) - { - yield return Neighbour(Extensions.DirectionDeltas[i]); - } + yield return Neighbour(Extensions.DirectionDeltas[i]); } } + } - #endregion - - #region public methods + #endregion - /// - /// Returns the tile neighbouring this one in a given direction. - /// - public Tile Neighbour(Direction direction) - { - return Neighbour(direction.Step()); - } + #region public methods - internal Tile Neighbour(Step step) - { - return new Tile( - tilemap, - x + step.X, - y + step.Y - ); - } + /// + /// Returns the tile neighbouring this one in a given direction. + /// + public Tile Neighbour(Direction direction) + { + return Neighbour(direction.Step()); + } - #endregion + internal Tile Neighbour(Step step) + { + return new Tile( + tilemap, + x + step.X, + y + step.Y + ); + } - #region Equals, GetHashCode, euqality operators + #endregion - /// - /// Indicates whether this instance references the same tile as another one. - /// - public bool Equals(Tile other) - { - return x == other.x && y == other.y && tilemap == other.tilemap; - } + #region Equals, GetHashCode, euqality operators - /// - /// Indicates whether this instance and a specified object are equal. - /// - public override bool Equals(object? obj) - { - if (ReferenceEquals(null, obj)) - return false; - return obj is Tile && Equals((Tile)obj); - } + /// + /// Indicates whether this instance references the same tile as another one. + /// + public bool Equals(Tile other) + { + return x == other.x && y == other.y && tilemap == other.tilemap; + } - /// - /// Returns the hash code for this instance. - /// - public override int GetHashCode() - { - unchecked - { - var hashCode = (tilemap != null ? tilemap.GetHashCode() : 0); - hashCode = (hashCode * 397) ^ x; - hashCode = (hashCode * 397) ^ y; - return hashCode; - } - } + /// + /// Indicates whether this instance and a specified object are equal. + /// + public override bool Equals(object? obj) + { + if (ReferenceEquals(null, obj)) + return false; + return obj is Tile && Equals((Tile)obj); + } - /// - /// Indicates whether this instance references the same tile as another one. - /// - public static bool operator ==(Tile t1, Tile t2) + /// + /// Returns the hash code for this instance. + /// + public override int GetHashCode() + { + unchecked { - return t1.Equals(t2); + var hashCode = (tilemap != null ? tilemap.GetHashCode() : 0); + hashCode = (hashCode * 397) ^ x; + hashCode = (hashCode * 397) ^ y; + return hashCode; } + } - /// - /// Indicates whether this instance references a different tile than another one. - /// - public static bool operator !=(Tile t1, Tile t2) - { - return !(t1 == t2); - } + /// + /// Indicates whether this instance references the same tile as another one. + /// + public static bool operator ==(Tile t1, Tile t2) + { + return t1.Equals(t2); + } - #endregion + /// + /// Indicates whether this instance references a different tile than another one. + /// + public static bool operator !=(Tile t1, Tile t2) + { + return !(t1 == t2); } + + #endregion } diff --git a/Bearded.Utilities/Tilemaps/Rectangular/Tilemap.cs b/Bearded.Utilities/Tilemaps/Rectangular/Tilemap.cs index 87a442c2..26b10779 100644 --- a/Bearded.Utilities/Tilemaps/Rectangular/Tilemap.cs +++ b/Bearded.Utilities/Tilemaps/Rectangular/Tilemap.cs @@ -1,129 +1,128 @@ using System.Collections; using System.Collections.Generic; -namespace Bearded.Utilities.Tilemaps.Rectangular +namespace Bearded.Utilities.Tilemaps.Rectangular; + +/// +/// A rectangular tilemap. +/// +/// The kind of data contained in the tilemap. +public class Tilemap + : IReadOnlyCollection> { + private readonly int width; + private readonly int height; + private readonly TTileValue[,] tiles; + /// - /// A rectangular tilemap. + /// Creates a new rectangular tilemap. /// - /// The kind of data contained in the tilemap. - public class Tilemap - : IReadOnlyCollection> + /// The width of the tilemap. + /// The height of the tilemap. + public Tilemap(int width, int height) { - private readonly int width; - private readonly int height; - private readonly TTileValue[,] tiles; - - /// - /// Creates a new rectangular tilemap. - /// - /// The width of the tilemap. - /// The height of the tilemap. - public Tilemap(int width, int height) - { - this.width = width; - this.height = height; - tiles = new TTileValue[width, height]; - } - - #region properties + this.width = width; + this.height = height; + tiles = new TTileValue[width, height]; + } - /// - /// The width of the tilemap. - /// - public int Width - { - get { return width; } - } + #region properties - /// - /// The height of the tilemap. - /// - public int Height - { - get { return height; } - } + /// + /// The width of the tilemap. + /// + public int Width + { + get { return width; } + } - /// - /// Gets the number of tiles in the tilemap. - /// - public int Count - { - get { return width * height; } - } + /// + /// The height of the tilemap. + /// + public int Height + { + get { return height; } + } - #endregion + /// + /// Gets the number of tiles in the tilemap. + /// + public int Count + { + get { return width * height; } + } - #region indexers + #endregion - /// - /// Gets or sets the data of a tile by its coordinates. - /// - /// The X location of the tile. - /// The Y location of the tile. - public TTileValue this[int x, int y] - { - get { return tiles[x, y]; } - set { tiles[x, y] = value; } - } + #region indexers - /// - /// Gets or sets the data of a tile. - /// - /// The tile. - public TTileValue this[Tile tile] - { - get { return this[tile.X, tile.Y]; } - set { this[tile.X, tile.Y] = value; } - } + /// + /// Gets or sets the data of a tile by its coordinates. + /// + /// The X location of the tile. + /// The Y location of the tile. + public TTileValue this[int x, int y] + { + get { return tiles[x, y]; } + set { tiles[x, y] = value; } + } - #endregion + /// + /// Gets or sets the data of a tile. + /// + /// The tile. + public TTileValue this[Tile tile] + { + get { return this[tile.X, tile.Y]; } + set { this[tile.X, tile.Y] = value; } + } - #region public methods + #endregion - /// - /// Checks whether a specific tile location is inside the tilemap. - /// - /// The X location of the tile. - /// The Y location of the tile. - public bool IsValidTile(int x, int y) - { - return x >= 0 && x < width - && y >= 0 && y < height; - } + #region public methods - /// - /// Checks whether a specific tile is located inside the tilemap. - /// - public bool IsValidTile(Tile tile) - { - return IsValidTile(tile.X, tile.Y); - } + /// + /// Checks whether a specific tile location is inside the tilemap. + /// + /// The X location of the tile. + /// The Y location of the tile. + public bool IsValidTile(int x, int y) + { + return x >= 0 && x < width + && y >= 0 && y < height; + } - #endregion + /// + /// Checks whether a specific tile is located inside the tilemap. + /// + public bool IsValidTile(Tile tile) + { + return IsValidTile(tile.X, tile.Y); + } - #region IEnumerable implementation + #endregion - /// - /// Returns an enumerator that iterates all tiles in the tilemap. - /// - public IEnumerator> GetEnumerator() - { - for (int y = 0; y < height; y++) - for (int x = 0; x < width; x++) - { - yield return new Tile(this, x, y); - } - } + #region IEnumerable implementation - /// - /// Returns an enumerator that iterates all tiles in the tilemap. - /// - IEnumerator IEnumerable.GetEnumerator() + /// + /// Returns an enumerator that iterates all tiles in the tilemap. + /// + public IEnumerator> GetEnumerator() + { + for (int y = 0; y < height; y++) + for (int x = 0; x < width; x++) { - return GetEnumerator(); + yield return new Tile(this, x, y); } + } - #endregion + /// + /// Returns an enumerator that iterates all tiles in the tilemap. + /// + IEnumerator IEnumerable.GetEnumerator() + { + return GetEnumerator(); } + + #endregion } diff --git a/Bearded.Utilities/Tilemaps/Step.cs b/Bearded.Utilities/Tilemaps/Step.cs index e2cde531..c1da73ef 100644 --- a/Bearded.Utilities/Tilemaps/Step.cs +++ b/Bearded.Utilities/Tilemaps/Step.cs @@ -1,14 +1,13 @@ -namespace Bearded.Utilities.Tilemaps +namespace Bearded.Utilities.Tilemaps; + +internal struct Step { - internal struct Step - { - public readonly sbyte X; - public readonly sbyte Y; + public readonly sbyte X; + public readonly sbyte Y; - public Step(sbyte x, sbyte y) - { - X = x; - Y = y; - } + public Step(sbyte x, sbyte y) + { + X = x; + Y = y; } }