Jodosoft / Libraries

Simple, reliable .NET libraries covering numbers, geometry and data structures

Home Page:https://libraries.jodosoft.com

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool


Logo

The Jodosoft Libraries

GitHub Nuget (with prereleases)

Contents

1. Introduction

2. Quickstart

3. About the project
    3.1. Design goals
    3.2. Roadmap
    3.3. Contributing
    3.4. Documentation
    3.5. Releases
    3.6. Changelog

4. Jodosoft.Numerics
    4.1. Fixed-point numbers
    4.2. Non-overflowing numbers
    4.3. Framework for numbers
    4.4. Structures
    4.5. Random extensions
    4.6. Performance considerations

5. Jodosoft.Geometry (preview)

6. Jodosoft.Collections (preview)

7. Jodosoft.Primitives
    7.1. Random variants
    7.2. Default providers
    7.3. Shims
    7.4. Binary IO Extensions


* * *


1. Introduction

Welcome to The Jodosoft Libraries, a project to make simple, reliable .NET libraries covering numerics, geometry and data structures.

This document describes the goals of the project, the features of each library, and steps for getting started.

[Back to top]


* * *


2. Quickstart

To install a Jodosoft library, use your NuGet package manager or run dotnet add, e.g.

dotnet add Jodosoft.Numerics

No configuration or dependency injection is required. Simply import the relevant Jodosoft namespace and begin using the types in your code.

using Jodosoft.Numerics;

var fixedPointNumber = (Fix64)1.234;

Most platforms and versions of .NET are supported, and the libraries can be used freely in commercial work under the terms of the MIT License.

The available libraries are:

  • Jodosoft.Numerics - extra number types (such as fixed-point and non-overflowing) that support maths, string-parsing, operators and more.
  • Jodosoft.Geometry (preview) - shapes, angles and trigonometric functions that work with number types from Jodosoft.Numerics
  • Jodosoft.Collections (preview) - data-structures, utilities and collection abstractions
  • Jodosoft.Primitives - utilities and abstractions used throughout the Jodosoft libraries

[Back to top]


* * *


3. About the project

The Jodosoft Libraries started as a collection of classes from the personal projects of Joe Lawry-Short. The types have been reorganized, refactored, and tested rigorously with the aim of making them useful for the .NET community.

This section describes the design goals, roadmap, and other details of the project.

[Back to top]

3.1. Design Goals

The table below summarizes the design goals of the project.

Item Description
Simple

The Jodosoft Libraries are designed to provide simple data structures and algorithms to use as the building blocks for more complex applications.

As a rule of thumb, nothing within the libraries should require configuration or dependency injection. A competent developer should be able to use the libraries intuitively, without needing to refer to extensive API documentation.

The libraries adhere to the .NET Framework Design Guidelines to ensure ease-of-use and consistency with the .NET API.

Reliable

The Jodosoft Libraries are designed to be dependable. Unit tests, benchmarks, and continuous integration tools are used to ensure they remain fit for purpose.

Unit tests are designed to cover boundary conditions, edge-cases, and error scenarios—not just happy paths. Code coverage is used, but is not considered to be the definitive metric of adequate testing. The code coverage target is 90%.

The pull request validation build executes the tests against multiple .NET targets and operating systems. This helps to ensure that the libraries behave as intended and are unaffected by .NET implementation details. Currently, this includes .NET 7 (net7.0), .NET 6 (net6.0), .NET Core 2.1 (netcoreapp2.1), .NET Framework 4.8 (net48), Windows, Ubuntu, and macOS.

Azure DevOps tests Sonar Coverage

Compatible

The Jodosoft Libraries are designed to work with a wide array of .NET versions, platforms and programming languages.

.NET Standard 2.0 (netstandard2.0) and .NET Framework 4.6 (net461) targets are used in order to maximize cross-platform support, and no platform-dependant features are used.

Newer targets, such as .NET Standard 2.1 (netstandard2.1), are used to incorporate language developments like default interface methods, but this is implemented in a backwards-compatible way.

Language-agnostic naming conventions are used and public types are marked as CLS compliant wherever possible. This ensures that the libraries can be used in F# and Visual Basic as well as in their native language, C#.

Care is taken to avoid name clashes with types from the .NET API or commonly-used NuGet packages.

Semantic Versioning is used so that version numbers convey the presence of breaking changes, and package validation is used to ensure backwards compatibility within each major version.

Maintainable

The source code of the Jodosoft Libraries is designed to be easy to understand and change.

SonarCloud and CodeFactor are used to detect code smells such as unused variables or overly complex functions.

The project files are configured to flag as many issues as possible. TreatWarningsAsErrors is set to True, WarningLevel is set to 4 and Rosyln analysers are enabled with maximum scope and severity. This helps to flag issues during development.

Warnings are only suppressed in exceptional circumstances, and suppression tags are always accompanied by a justification message.

Sonar Violations (long format) CodeFactor Grade

[Back to top]

3.2. Roadmap

The table below summarizes the high-level development goals for upcoming versions of the Jodosoft Libraries.

Version Goals
2.1.0
  • Create the first release of Jodosoft.Geometry.
2.2.0
  • Tweaks, fixes and suggestions for Jodosoft.Geometry.
  • Other issues depending on priority.
2.3.0
  • Create the first release of Jodosoft.Collections.
2.4.0
  • Tweaks, fixes and suggestions for Jodosoft.Collections.
  • Other issues depending on priority.
3.0.0
  • Refactoring and quality improvements that require breaking changes.

[Back to top]

3.3. Contributing

Community contributions are welcome at https://github.com/Jodosoft/Libraries (the home of this repository). A list of reported issues can be found at https://github.com/Jodosoft/Libraries/issues. Contributors are requested to adhere to the code of conduct.

This work is licensed under the MIT License.

GitHub GitHub issues

[Back to top]

3.4. Documentation

Work-in-progress API documentation is available at https://libraries.jodosoft.com/docs.

Documentation

[Back to top]

3.5. Releases

Builds of this project are available as NuGet packages on NuGet.org (for help, see: "Quickstart: Install and use a package").

Binaries are available on GitHub.com at https://github.com/Jodosoft/Libraries/releases.

Alternatively the libraries can be built from the source code in this repository using Visual Studio Community Edition (or alternative) with nothing more than the appropriate .NET SDKs. Every released version is tagged in the repository's history.

Nuget (with prereleases) GitHub release (latest SemVer including pre-releases)

[Back to top]

3.6. Changelog

The following table summarizes the changes that were made for each published version of the Jodosoft Libraries.

Version Release date Changes
2.0.0-preview1 2023-09-04

  • Rebranded the libraries from "Jodo" to "Jodosoft" to reflect their new organizational home.

1.1.0 2022-12-07

  • Automated API documentation website.
  • Increased documentation coverage.
  • Jodosoft.Numerics
GitHub milestone

1.0.0 2022-10-15

  • Initial release of Jodosoft.Numerics and Jodosoft.Primitives.
  • Preview releases of Jodosoft.Geometry and Jodosoft.Collections.
  • Added cross-platform support.
  • Reached high level of test coverage.
  • Established code quality rules.
  • Created benchmarks.
GitHub milestone

[Back to top]


* * *


4. Jodosoft.Numerics

Provides custom number types, numeric utilities, and a generic interface for defining numbers.

Nuget (with prereleases)

[Back to top]

4.1. Fixed-point numbers

Unlike floating-point numbers, fixed-point numbers maintain a constant degree of precision regardless of magnitude. This can be useful in situations where precision remains important whilst numbers grow. As a trade-off, fixed-point numbers have a much lower maximum magnitude than floating-point numbers of the same size.

Fix64 and UFix64 are fixed-point number types with 6 decimal digits of precision. As with all number types provided by this library, they support a full range of mathematical functions, operators, conversions, string formatting, etc. (see §4.3. Framework for numbers).

using Jodosoft.Numerics;
using System;

Fix64 x = 100;
Fix64 y = 2 * MathN.Cos(x);
Fix64 z = Fix64.Parse("1000000.123456");
Fix64 r = new Random(1).NextNumeric<Fix64>(100, 200);
float f = ConvertN.ToSingle(z);
byte[] bytes = BitConverterN.GetBytes(y);

Console.WriteLine(x); // output: 100
Console.WriteLine(y); // output: 1.724636
Console.WriteLine(z); // output: 1000000.123456
Console.WriteLine(r); // output: 124.866858
Console.WriteLine(f); // output: 1000000.1
Console.WriteLine(bytes.Length); // output: 8

The table belows summarizes the capabilities of these types.

Type Description
readonly struct
Fix64

Signed fixed-point number type with 6 decimal digits of precision, represented internally by a 64-bit integer.

Supports a range of values from ±1.0 x 10−6 to ±9.2 x 1012.

readonly struct
UFix64

Unsigned fixed-point number type with 6 decimal digits of precision, represented internally by an unsigned 64-bit integer.

Supports a range of values from 1.0 x 10−6 to 1.8 x 1013.

static class
Scaled

Provides static methods for performing arithmetic and string conversion on integers with a scaling factor. Used in the implementation of Fix64 and UFix64.

[Back to top]

4.2 Non-overflowing numbers

Number types in the Jodosoft.Numerics.Clamped namespace have built-in prevention of overflow. Operations that would normally error or overflow instead return MinValue or MaxValue, and operations that would normally return NaN instead return zero.

This provides an alternative to using the checked keyword, removing the need for repetitive error handling logic.

As with all number types provided by this library, non-overflowing number types support a full range of mathematical functions, operators, conversions, string formatting, etc. (see §4.3. Framework for numbers).

var i = Int32M.MaxValue + 1;
Console.WriteLine(i);  // output: 2147483647

var f = (SingleM)4 / 0;
Console.WriteLine(f);  // output: 3.402823E+38

The table below summarizes the non-overflowing number types and utilities provided by this library:

Type Description
readonly struct
ByteM,
SByteM,
Int16M,
UInt16M,
Int32M,
UInt32M,
Int64M,
UInt64M

Non-overflowing variants of the built-in integral number types.

Operations that would normally overflow from positive to negative instead return MaxValue. Operations that would normally overflow from negative to positive instead return MinValue. Division by zero does NOT throw a System.DivideByZeroException but returns MaxValue.

readonly struct
SingleM,
DoubleM

Non-overflowing variants of the built-in floating-point number types.

Operations that would normally return PositiveInfinity instead return MaxValue. Operations that would normally return NegativeInfinity instead return MinValue. Operations that would normally return NaN instead return 0.

readonly struct
DecimalM

Non-overflowing variants of System.Decimal.

Operations that would normally overflow from positive to negative instead return MaxValue. Operations that would normally overflow from negative to positive instead return MinValue. Division by zero does NOT throw a System.DivideByZeroException but returns MaxValue.

readonly struct
Fix64M,
UFix64M

Non-overflowing variants of fixed-point numbers (see §4.1. Fixed-point numbers).

Operations that would overflow instead return MinValue or MaxValue depending on the direction of the overflow. Division by zero does NOT throw a DivideByZeroException but returns MaxValue.

static class
Clamped
Provides static methods for performing non-overflowing arithmetic. Used in the implementation of the preceeding non-overflowing number types.

[Back to top]

4.3. Framework for numbers

The INumeric<TSelf> interface provides a definition for number types with support for operators, maths, string-conversion, random generation, and more.

Static utility classes, such as MathN and ConvertN, expose these features in a way that is similar to .NET API.

using Jodosoft.Numerics;
using System;

MyNumberType fromLiteral = 3.123;
MyNumberType usingOperators = (fromLiteral + 1) % 2;
MyNumberType usingMath = MathN.Pow(fromLiteral, 2);
MyNumberType fromRandom = new Random(1).NextNumeric<MyNumberType>(10, 20);
MyNumberType fromString = MyNumberType.Parse("-7.4E+5");
short conversion = ConvertN.ToInt16(usingMath);
string stringFormat = $"{fromLiteral:N3}";
byte[] asBytes = BitConverterN.GetBytes(usingMath);

The table below gives a full list of features supported by number types that implement INumeric<TSelf>.

Feature Description
static class
MathN

Provides equivalent methods to System.Math for types that implement INumeric<TSelf>, e.g. Log(N), Acosh(N) and Round(N, int).

var result = MathN.Log10(1000 * MathN.PI<MyNumberType>());
static class
BitConverterN

Provides equivalent methods to System.BitConverter for types that implement INumeric<TSelf>, allowing conversion to and from byte arrays.

byte[] result = BitConverterN.GetBytes((MyNumberType)256.512);
static class
ConvertN

Provides equivalent methods to System.Convert for types that implement INumeric<TSelf> (e.g. ToBoolean(N) and ToDecimal(N)). Overloads are provided to support alternative modes of conversion, e.g. Default, Cast and Clamp.

var defaultResult = ConvertN.ToNumeric<ByteN>(199.956, Conversion.Default);
var castResult = ConvertN.ToNumeric<ByteN>(199.956, Conversion.Cast);
static class
Numeric

Provides access to constants and static methods for number types in a generic context.

public void ExampleMethod<T>() where T : struct, INumeric
{
    var zero = Numeric.Zero<T>();
    var parsed = Numeric.Parse<T>("1.2");
    var isFinite = Numeric.IsFinite(parsed);
}
Overloaded operators

All the number types in this library have a full suite of overloaded operators, including:

Additionally, INumeric<TSelf> defines overloads for <, >, <=, >=, ++, --, *, /, %, +, -, ~, <<, >>, &, | and ^, allowing for limited expressions in a generic context (note that equality and conversion operators are not supported on interfaces).

Note: The bitwise and shift operators are overloaded for non-integral types. These operators perform the correct bitwise operations, but are unlikely to produce useful results.

String formatting

All the number types in this library can be used with numeric format strings, as in the following code sample:

var var1 = (MyNumberType)1023;
var var2 = (MyNumberType)99.54322f;
Console.WriteLine($"{var1:N}"); // outputs: 1,023.00
Console.WriteLine($"{var1:X}"); // outputs: 3FF
Console.WriteLine($"{var2:E}"); // outputs: 9.954322E+001
Console.WriteLine($"{var2:000.000}"); // outputs: 099.543

Random generation Extension methods on System.Random provide randomly generated values. Values can be generated between bounds or without bounds.

var var1 = Random.NextNumeric<MyNumberType>();
var var2 = Random.NextNumeric<MyNumberType>(100, 120);
Console.WriteLine(var1); // outputs: 0.405808417991177 (example)
Console.WriteLine(var2); // outputs: 102.85086051826445 (example)

Binary streaming Extension methods on System.IO.BinaryReader and System.IO.BinaryWriter allow number types to be represented as simple binary using streams.

MyNumberType value = 3.141;
using Stream stream = new MemoryStream(new byte[8]);
using BinaryWriter writer = new BinaryWriter(stream);
using BinaryReader reader = new BinaryReader(stream);
writer.Write(value);
stream.Position = 0;
var result = reader.Read<MyNumberType>();
Console.WriteLine(result); // output: 3.141

Commonly-used abstractions All the number types in this library implement System.IComparable, System.IComparable<T>, System.IConvertible, System.IEquatable<T>, System.IFormattable and System.ISerializable. They also override Equals(object), GetHashCode() and ToString(), and have the DebuggerDisplay attribute.
readonly struct
ByteN, SByteN,
Int16N, UInt16N,
Int32N, UInt32N,
Int64N, UInt64N,
SingleN, DoubleN,
DecimalN
Wrappers for the built-in numeric types that implement INumeric<TSelf>, allowing them to be used in a generic context.

[Back to top]

4.4. Structures

Numeric structures, such as vectors, are provided for use in mathematical applications. These structures are generic on number type, supporting any implementation of INumeric<TSelf> (see §4.3. Framework for numbers). The table below summarizes the available structs and accompanying utilities.

Type Description
readonly struct
UnitN<TNumeric>
A wrapper for numeric types that clamps values between -1 and 1 (or 0 and 1 when unsigned).
readonly struct
Vector2N<TNumeric>
A collection of two numeric values, X and Y, with extensive interface and operator support.
readonly struct
Vector3N<TNumeric>
A collection of three numeric values, X, Y and Z, with extensive interface and operator support.
static class
Vector2N
Provides static methods for performing vector-based mathematics on instances of Vector2N<TNumeric>, such as dot product.
static class
Vector3N
Provides static methods for performing vector-based mathematics on instances of Vector3N<TNumeric>, such as dot product.

[Back to top]

4.5. Random extensions

Extension methods for System.Random add support for generating every built-in number type and types that implement INumeric<TSelf> (see §4.3. Framework for numbers).

Overloads are provided that allow greater flexibility with bounds via the Generation enum:

Generation.Default Uses the conventions established System.Random.
Generation.Extended Bounds are inclusive, and can be specified in any order.
using Jodosoft.Numerics;
using System;

var value1 = new Random().NextDouble(double.MinValue, double.MaxValue); // Returns any finite double.
var value2 = new Random().NextUInt64(200, 100, Generation.Extended); // Returns a ulong between 100 and 200 (inclusive).

[Back to top]

4.6 Performance considerations

The number types provided by this library are structs that wrap built-in types and operations. Therefore they require additional memory and CPU time compared to using the built-in types alone.

Additionally, the number types within the Jodosoft.Numerics.Clamped namespace make use of the checked keyword for conversion and arithmetic. This further increases CPU time compared to using unchecked operations, especially in cases of overflow.

If developing a performance-sensitive application, use a profiler to assess the impact of introducing these types. Generally speaking, the impact is likely to be acceptable unless CPU-bound arithmetic is already on the hot path for the given application (e.g. in machine learning or 3D physics applications).

Benchmarks are provided to facilitate comparison with the built-in number types. To run the benchmarks, clone this repository then build and run Jodosoft.Numerics.Benchmarks in RELEASE mode (without a attaching a debugger).

Sample output can be seen below:

Jodosoft.Numerics.Benchmarks - Results from 2022-09-28 07:58:50Z

  • Processor: 11th Gen Intel(R) Core(TM) i7-11800H @ 2.30GHz
  • Architecture: x64-based processor
  • .NET Version: .NET 5.0.17
  • Architecture: X64
  • OS: win10-x64
  • Seconds per Benchmark: 60.0
Subjects "(1)_Versus_(2)" Operations Per Second (1) Operations Per Second (2) Average Time (1) Average Time (2) Relative Speed (1) Relative Speed (2)
Fix64Logarithm_Versus_DoubleLogarithm 7.789E+06 5.183E+07 <1μs <1μs 0.15 6.65
Fix64Rounding_Versus_DoubleRounding 3.846E+07 3.637E+07 <1μs <1μs 1.06 0.95
Fix64StringParsing_Versus_DoubleStringParsing 1.842E+07 1.973E+07 <1μs <1μs 0.93 1.07
Fix64Random_Versus_DoubleRandom 2.4E+07 3.995E+07 <1μs <1μs 0.60 1.66
Fix64ToByteArray_Versus_DoubleToByteArray 4.831E+07 4.876E+07 <1μs <1μs 0.99 1.01
Fix64FromByteArray_Versus_DoubleFromByteArray 5.29E+07 5.225E+07 <1μs <1μs 1.01 0.99
Int32NArithmetic_Versus_Int32Arithmetic 5.03E+07 5.282E+07 <1μs <1μs 0.95 1.05
Int32MArithmetic_Versus_Int32Arithmetic 4.425E+07 5.215E+07 <1μs <1μs 0.85 1.18
SingleNArithmetic_Versus_SingleArithmetic 4.122E+07 5.623E+07 <1μs <1μs 0.73 1.36
SingleMArithmetic_Versus_SingleArithmetic 3.418E+07 5.634E+07 <1μs <1μs 0.61 1.65
DoubleNArithmetic_Versus_DoubleArithmetic 4.129E+07 5.565E+07 <1μs <1μs 0.74 1.35
DoubleMArithmetic_Versus_DoubleArithmetic 3.402E+07 5.527E+07 <1μs <1μs 0.62 1.62
DoubleNDivision_Versus_DoubleDivision 4.833E+07 5.392E+07 <1μs <1μs 0.90 1.12
DoubleNLogarithm_Versus_DoubleLogarithm 4.913E+07 5.244E+07 <1μs <1μs 0.94 1.07
DoubleNRounding_Versus_DoubleRounding 3.396E+07 3.597E+07 <1μs <1μs 0.94 1.06

[Back to top]


* * *


5. Jodosoft.Geometry (preview)

Provides geometric structs and utilities that support custom number types.

Coming soon (see section 2.2. "Roadmap")

[Back to top]


* * *


6. Jodosoft.Collections (preview)

Provides extra collection classes and interfaces to complement the .NET API.

Coming soon (see section 2.2. "Roadmap")

[Back to top]

7. Jodosoft.Primitives

Provides utilities and abstractions that are used throughout the Jodosoft Libraries.

Nuget (with prereleases)

[Back to top]

7.1. Random variants

Provides a specification for randomly generating objects based on variants (described below). This feature is used extensively by the Jodosoft test libraries to ensure that tests cover a variety of scenarios. Although the exact definition of each variant is left to the implementor, the following table serves as a guide:

Variant Description
Defaults Null, zero, or any other default state for a given object.
LowMagnitude Small values and values with reduced significance.
AnyMagnitude Any value from the set of all possible values, excluding errors.
Boundaries Minimum and maximum values.
Errors Values that are typical of error scenarios, or values intended to elicit errors.
All Encompasses all variants.
NonError Encompasses all variants, except for errors.

Extension methods are provided for System.Random to enable random generation of values within each variant.

var random = new Random();
short num1 = random.NextVariant<Int16N>(Variants.LowMagnitude);
short num2 = random.NextVariant<Int16N>(Variants.Defaults | Variants.Boundaries);

Console.WriteLine(num1); // output: 24 (example)
Console.WriteLine(num2); // output: -32768 (example)

[Back to top]

7.2. Default providers

Documentation tbc

[Back to top]

7.3. Shims

Documentation tbc

[Back to top]

7.4. Binary IO extensions

Documentation tbc

[Back to top]


* * *


About

Simple, reliable .NET libraries covering numbers, geometry and data structures

https://libraries.jodosoft.com

License:MIT License


Languages

Language:C# 100.0%