Lotsa fun

This commit is contained in:
Marc Hernandez 2025-10-15 21:50:37 -07:00
parent 5a0adfc2b3
commit da399343e2
6 changed files with 465 additions and 63 deletions

View File

@ -298,7 +298,7 @@ public record class Timed<T> : Recorded<T> where T : Timed<T>
public override T Process( public override T Process(
Func<T, T> fn, Func<T, T> fn,
string reason = "", string reason = "",
[CallerMemberName] string dbgName = "", [CallerMemberName] string dbgMethod = "",
[CallerFilePath] string dbgPath = "", [CallerFilePath] string dbgPath = "",
[CallerLineNumber] int dbgLine = 0, [CallerLineNumber] int dbgLine = 0,
[CallerArgumentExpression( "fn" )] string dbgExpression = "" ) [CallerArgumentExpression( "fn" )] string dbgExpression = "" )
@ -313,7 +313,7 @@ public record class Timed<T> : Recorded<T> where T : Timed<T>
{ {
Version = current.Meta.Version + 1, Version = current.Meta.Version + 1,
Reason = reason, Reason = reason,
MemberName = dbgName, MemberName = dbgMethod,
FilePath = dbgPath, FilePath = dbgPath,
LineNumber = dbgLine, LineNumber = dbgLine,
Expression = dbgExpression, Expression = dbgExpression,

View File

@ -24,39 +24,44 @@ public record class List<T> : Timed<List<T>>, IImmutableList<T>
// Helper to apply changes using the Process method // Helper to apply changes using the Process method
private List<T> Change( private List<T> Change(
Func<ImmutableList<T>, ImmutableList<T>> listChange, Func<ImmutableList<T>, ImmutableList<T>> listChange,
[CallerMemberName] string memberName = "", [CallerMemberName] string dbgMethod = "",
[CallerFilePath] string filePath = "", [CallerFilePath] string dbgPath = "",
[CallerLineNumber] int lineNumber = 0, [CallerLineNumber] int dbgLine = -1,
[CallerArgumentExpression("listChange")] string reason = "") [CallerArgumentExpression( "listChange" )] string reason = "" )
{ {
var newValues = listChange(Values); var newValues = listChange(Values);
return ReferenceEquals(Values, newValues) return ReferenceEquals(Values, newValues)
? this ? this
: Process(l => l with { Values = newValues }, reason, memberName, filePath, lineNumber, reason); : Process(l => l with { Values = newValues }, reason, dbgMethod, dbgPath, dbgLine, reason);
} }
// --- IImmutableList<T> implementation using the Change helper --- // --- IImmutableList<T> implementation using the Change helper ---
public T this[int index] => Values[index]; public T this[int index] => Values[index];
public int Count => Values.Count; public int Count => Values.Count;
public List<T> Add(T value) => Change(v => v.Add(value)); public List<T> Add(T value, [CallerMemberName] string dbgMethod = "", [CallerFilePath] string dbgPath = "", [CallerLineNumber] int dbgLine = -1) => Change(v => v.Add(value), dbgMethod, dbgPath, dbgLine);
public List<T> AddRange(IEnumerable<T> items) => Change(v => v.AddRange(items)); public List<T> AddRange(IEnumerable<T> items, [CallerMemberName] string dbgMethod = "", [CallerFilePath] string dbgPath = "", [CallerLineNumber] int dbgLine = -1) => Change(v => v.AddRange(items), dbgMethod, dbgPath, dbgLine);
public List<T> Clear() => Change(v => v.Clear()); public List<T> Clear([CallerMemberName] string dbgMethod = "", [CallerFilePath] string dbgPath = "", [CallerLineNumber] int dbgLine = -1) => Change(v => v.Clear(), dbgMethod, dbgPath, dbgLine);
// ... Implement all other IImmutableList methods similarly ... #region IImmutableList Implementati, dbgMethod, dbgPath, dbgLineon
#region IImmutableList Implementation public List<T> Insert( int index, T element, [CallerMemberName] string dbgMethod = "", [CallerFilePath] string dbgPath = "", [CallerLineNumber] int dbgLine = -1 ) => Change( v => v.Insert( index, element ) , dbgMethod, dbgPath, dbgLine);
public List<T> Insert( int index, T element ) => Change( v => v.Insert( index, element ) ); public List<T> InsertRange( int index, IEnumerable<T> items, [CallerMemberName] string dbgMethod = "", [CallerFilePath] string dbgPath = "", [CallerLineNumber] int dbgLine = -1 ) => Change( v => v.InsertRange( index, items ) , dbgMethod, dbgPath, dbgLine);
public List<T> InsertRange( int index, IEnumerable<T> items ) => Change( v => v.InsertRange( index, items ) ); public List<T> Remove( T value, IEqualityComparer<T>? equalityComparer, [CallerMemberName] string dbgMethod = "", [CallerFilePath] string dbgPath = "", [CallerLineNumber] int dbgLine = -1 ) => Change( v => v.Remove( value, equalityComparer ) , dbgMethod, dbgPath, dbgLine);
public List<T> Remove( T value, IEqualityComparer<T>? equalityComparer ) => Change( v => v.Remove( value, equalityComparer ) ); public List<T> Remove( T value, [CallerMemberName] string dbgMethod = "", [CallerFilePath] string dbgPath = "", [CallerLineNumber] int dbgLine = -1 ) => Remove( value, EqualityComparer<T>.Default , dbgMethod, dbgPath, dbgLine);
public List<T> Remove( T value ) => Remove( value, EqualityComparer<T>.Default ); public List<T> RemoveAll( Predicate<T> match, [CallerMemberName] string dbgMethod = "", [CallerFilePath] string dbgPath = "", [CallerLineNumber] int dbgLine = -1 ) => Change( v => v.RemoveAll( match ) , dbgMethod, dbgPath, dbgLine);
public List<T> RemoveAll( Predicate<T> match ) => Change( v => v.RemoveAll( match ) ); public List<T> RemoveAt( int index, [CallerMemberName] string dbgMethod = "", [CallerFilePath] string dbgPath = "", [CallerLineNumber] int dbgLine = -1 ) => Change( v => v.RemoveAt( index ) , dbgMethod, dbgPath, dbgLine);
public List<T> RemoveAt( int index ) => Change( v => v.RemoveAt( index ) ); public List<T> RemoveRange( int index, int count, [CallerMemberName] string dbgMethod = "", [CallerFilePath] string dbgPath = "", [CallerLineNumber] int dbgLine = -1 ) => Change( v => v.RemoveRange( index, count ) , dbgMethod, dbgPath, dbgLine);
public List<T> RemoveRange( IEnumerable<T> items, IEqualityComparer<T>? equalityComparer ) => Change( v => v.RemoveRange( items, equalityComparer ) ); public List<T> RemoveRange( IEnumerable<T> items, IEqualityComparer<T>? equalityComparer, [CallerMemberName] string dbgMethod = "", [CallerFilePath] string dbgPath = "", [CallerLineNumber] int dbgLine = -1 ) => Change( v => v.RemoveRange( items, equalityComparer ) , dbgMethod, dbgPath, dbgLine);
public List<T> RemoveRange( int index, int count ) => Change( v => v.RemoveRange( index, count ) ); public List<T> Replace( T oldValue, T newValue, IEqualityComparer<T>? equalityComparer, [CallerMemberName] string dbgMethod = "", [CallerFilePath] string dbgPath = "", [CallerLineNumber] int dbgLine = -1 ) => Change( v => v.Replace( oldValue, newValue, equalityComparer ) , dbgMethod, dbgPath, dbgLine);
public List<T> Replace( T oldValue, T newValue, IEqualityComparer<T>? equalityComparer ) => Change( v => v.Replace( oldValue, newValue, equalityComparer ) ); public List<T> SetItem( int index, T value, [CallerMemberName] string dbgMethod = "", [CallerFilePath] string dbgPath = "", [CallerLineNumber] int dbgLine = -1 ) => Change( v => v.SetItem( index, value ) , dbgMethod, dbgPath, dbgLine);
public List<T> SetItem( int index, T value ) => Change( v => v.SetItem( index, value ) );
public int IndexOf( T item, int index, int count, IEqualityComparer<T>? equalityComparer ) => Values.IndexOf( item, index, count, equalityComparer ?? EqualityComparer<T>.Default ); public int IndexOf( T item, int index, int count, IEqualityComparer<T>? equalityComparer ) => Values.IndexOf( item, index, count, equalityComparer ?? EqualityComparer<T>.Default );
public int IndexOf( T item ) => IndexOf( item, 0, Count, EqualityComparer<T>.Default ); public int LastIndexOf( T item, int index, int count, IEqualityComparer<T>? equalityComparer) => Values.LastIndexOf( item, index, count, equalityComparer ?? EqualityComparer<T>.Default );
public int LastIndexOf( T item, int index, int count, IEqualityComparer<T>? equalityComparer ) => Values.LastIndexOf( item, index, count, equalityComparer ?? EqualityComparer<T>.Default );
public int IndexOf( T item, [CallerMemberName] string dbgMethod = "", [CallerFilePath] string dbgPath = "", [CallerLineNumber] int dbgLine = -1 ) => IndexOf( item, 0, Count, EqualityComparer<T>.Default);
IImmutableList<T> IImmutableList<T>.Clear() => Clear(); IImmutableList<T> IImmutableList<T>.Clear() => Clear();
IImmutableList<T> IImmutableList<T>.Add( T value ) => Add( value ); IImmutableList<T> IImmutableList<T>.Add( T value ) => Add( value );
IImmutableList<T> IImmutableList<T>.AddRange( IEnumerable<T> items ) => AddRange( items ); IImmutableList<T> IImmutableList<T>.AddRange( IEnumerable<T> items ) => AddRange( items );

View File

@ -204,25 +204,25 @@ static public class log
static ImmutableDictionary<int, string> s_shortname = ImmutableDictionary<int, string>.Empty; static ImmutableDictionary<int, string> s_shortname = ImmutableDictionary<int, string>.Empty;
public LogEvent( LogType logType, string msg, string path, int line, string member, string cat, string exp, object? obj ) public LogEvent( LogType logType, string msg, string dbgPath, int dbgLine, string dbgMethod, string cat, string exp, object? obj )
{ {
//Cache the automatic category names //Cache the automatic category names
// R A R E and S L O W and S A F E // R A R E and S L O W and S A F E
if( string.IsNullOrEmpty( cat ) ) if( string.IsNullOrEmpty( cat ) )
{ {
var pathHash = path.GetHashCode(); var pathHash = dbgPath.GetHashCode();
if( s_shortname.TryGetValue( pathHash, out var autoCat ) ) if( s_shortname.TryGetValue( pathHash, out var autoCat ) )
{ {
cat = autoCat; cat = autoCat;
} }
else else
{ {
var pathPieces = path.Split( '\\' ); var pathPieces = dbgPath.Split( '\\' );
if( pathPieces.Length < 2 ) if( pathPieces.Length < 2 )
{ {
pathPieces = path.Split( '/' ); pathPieces = dbgPath.Split( '/' );
} }
var lastDir = pathPieces[pathPieces.Length - 2]; var lastDir = pathPieces[pathPieces.Length - 2];
@ -236,18 +236,18 @@ static public class log
Time = DateTime.Now; Time = DateTime.Now;
LogType = logType; LogType = logType;
Msg = msg; Msg = msg;
Path = path; Path = dbgPath;
Line = line; Line = dbgLine;
Member = member; Member = dbgMethod;
Cat = cat; Cat = cat;
Exp = exp; Exp = exp;
Obj = obj; Obj = obj;
} }
} }
static LogEvent CreateLogEvent( LogType logType, string msg, string cat, object? obj, [CallerFilePath] string path = "", [CallerLineNumber] int line = -1, [CallerMemberName] string member = "", string exp = "" ) static LogEvent CreateLogEvent( LogType logType, string msg, string cat, object? obj, [CallerFilePath] string dbgPath = "", [CallerLineNumber] int dbgLine = -1, [CallerMemberName] string dbgMethod = "", string exp = "" )
{ {
var logEvent = new LogEvent( logType, msg, path, line, member, cat, exp, obj ); var logEvent = new LogEvent( logType, msg, dbgPath, dbgLine, dbgMethod, cat, exp, obj );
return logEvent; return logEvent;
} }
@ -324,9 +324,9 @@ static public class log
return rel; return rel;
} }
static public string thisFilePath( [CallerFilePath] string path = "" ) static public string thisFilePath( [CallerFilePath] string dbgPath = "" )
{ {
return relativePath( path ); return relativePath( dbgPath );
} }
#endregion // Util #endregion // Util
@ -342,7 +342,7 @@ static public class log
/* /*
static public void info( string msg, string cat = "", object? obj = null, static public void info( string msg, string cat = "", object? obj = null,
[CallerFilePath] string path = "", [CallerLineNumber] int line = -1, [CallerMemberName] string member = "", [CallerArgumentExpression( "msg" )] string dbgExp = "" ) [CallerFilePath] string dbgPath = "", [CallerLineNumber] int dbgLine = -1, [CallerMemberName] string dbgMethod = "", [CallerArgumentExpression( "msg" )] string dbgExp = "" )
*/ */
static public T var<T>( T val, [CallerMemberName] string dbgName = "", [CallerFilePath] string dbgPath = "", [CallerLineNumber] int dbgLine = -1, [CallerArgumentExpression( "val" )] string dbgExp = "" ) static public T var<T>( T val, [CallerMemberName] string dbgName = "", [CallerFilePath] string dbgPath = "", [CallerLineNumber] int dbgLine = -1, [CallerArgumentExpression( "val" )] string dbgExp = "" )
@ -358,42 +358,42 @@ static public class log
log.info( $"| Done", "", null, dbgPath, dbgLine, dbgName, dbgExp ); log.info( $"| Done", "", null, dbgPath, dbgLine, dbgName, dbgExp );
} }
static public void fatal( string msg, string cat = "", object? obj = null, [CallerFilePath] string path = "", [CallerLineNumber] int line = -1, [CallerMemberName] string member = "", [CallerArgumentExpression( "msg" )] string dbgExp = "" ) static public void fatal( string msg, string cat = "", object? obj = null, [CallerFilePath] string dbgPath = "", [CallerLineNumber] int dbgLine = -1, [CallerMemberName] string dbgMethod = "", [CallerArgumentExpression( "msg" )] string dbgExp = "" )
{ {
logBase( msg, LogType.Fatal, path, line, member, cat, dbgExp, obj ); logBase( msg, LogType.Fatal, dbgPath, dbgLine, dbgMethod, cat, dbgExp, obj );
} }
[StackTraceHidden] [StackTraceHidden]
static public void error( string msg, string cat = "", object? obj = null, [CallerFilePath] string path = "", [CallerLineNumber] int line = -1, [CallerMemberName] string member = "", [CallerArgumentExpression( "msg" )] string dbgExp = "" ) static public void error( string msg, string cat = "", object? obj = null, [CallerFilePath] string dbgPath = "", [CallerLineNumber] int dbgLine = -1, [CallerMemberName] string dbgMethod = "", [CallerArgumentExpression( "msg" )] string dbgExp = "" )
{ {
logBase( msg, LogType.Error, path, line, member, cat, dbgExp, obj ); logBase( $"{dbgMethod}: {msg}", LogType.Error, dbgPath, dbgLine, dbgMethod, cat, dbgExp, obj );
} }
[StackTraceHidden] [StackTraceHidden]
static public void warn( string msg, string cat = "", object? obj = null, [CallerFilePath] string path = "", [CallerLineNumber] int line = -1, [CallerMemberName] string member = "", [CallerArgumentExpression( "msg" )] string dbgExp = "" ) static public void warn( string msg, string cat = "", object? obj = null, [CallerFilePath] string dbgPath = "", [CallerLineNumber] int dbgLine = -1, [CallerMemberName] string dbgMethod = "", [CallerArgumentExpression( "msg" )] string dbgExp = "" )
{ {
logBase( msg, LogType.Warn, path, line, member, cat, dbgExp, obj ); logBase( msg, LogType.Warn, dbgPath, dbgLine, dbgMethod, cat, dbgExp, obj );
} }
static public void high( string msg, string cat = "", object? obj = null, [CallerFilePath] string path = "", [CallerLineNumber] int line = -1, [CallerMemberName] string member = "", [CallerArgumentExpression( "msg" )] string dbgExp = "" ) static public void high( string msg, string cat = "", object? obj = null, [CallerFilePath] string dbgPath = "", [CallerLineNumber] int dbgLine = -1, [CallerMemberName] string dbgMethod = "", [CallerArgumentExpression( "msg" )] string dbgExp = "" )
{ {
logBase( msg, LogType.High, path, line, member, cat, dbgExp, obj ); logBase( msg, LogType.High, dbgPath, dbgLine, dbgMethod, cat, dbgExp, obj );
} }
static public void info( string msg, string cat = "", object? obj = null, static public void info( string msg, string cat = "", object? obj = null,
[CallerFilePath] string path = "", [CallerLineNumber] int line = -1, [CallerMemberName] string member = "", [CallerArgumentExpression( "msg" )] string dbgExp = "" ) [CallerFilePath] string dbgPath = "", [CallerLineNumber] int dbgLine = -1, [CallerMemberName] string dbgMethod = "", [CallerArgumentExpression( "msg" )] string dbgExp = "" )
{ {
logBase( msg, LogType.Info, path, line, member, cat, dbgExp, obj ); logBase( msg, LogType.Info, dbgPath, dbgLine, dbgMethod, cat, dbgExp, obj );
} }
static public void debug( string msg, string cat = "", object? obj = null, [CallerFilePath] string path = "", [CallerLineNumber] int line = -1, [CallerMemberName] string member = "", [CallerArgumentExpression( "msg" )] string dbgExp = "" ) static public void debug( string msg, string cat = "", object? obj = null, [CallerFilePath] string dbgPath = "", [CallerLineNumber] int dbgLine = -1, [CallerMemberName] string dbgMethod = "", [CallerArgumentExpression( "msg" )] string dbgExp = "" )
{ {
logBase( msg, LogType.Debug, path, line, member, cat, dbgExp, obj ); logBase( msg, LogType.Debug, dbgPath, dbgLine, dbgMethod, cat, dbgExp, obj );
} }
static public void trace( string msg, string cat = "", object? obj = null, [CallerFilePath] string path = "", [CallerLineNumber] int line = -1, [CallerMemberName] string member = "", [CallerArgumentExpression( "msg" )] string dbgExp = "" ) static public void trace( string msg, string cat = "", object? obj = null, [CallerFilePath] string dbgPath = "", [CallerLineNumber] int dbgLine = -1, [CallerMemberName] string dbgMethod = "", [CallerArgumentExpression( "msg" )] string dbgExp = "" )
{ {
logBase( msg, LogType.Trace, path, line, member, cat, dbgExp, obj ); logBase( msg, LogType.Trace, dbgPath, dbgLine, dbgMethod, cat, dbgExp, obj );
} }
@ -402,13 +402,13 @@ static public class log
#endregion #endregion
#region Helpers #region Helpers
static public void logProps( object obj, string header, LogType type = LogType.Debug, string cat = "", string prefix = "", [CallerFilePath] string path = "", [CallerLineNumber] int line = -1, [CallerMemberName] string member = "", [CallerArgumentExpression( "obj" )] string dbgExpObj = "" ) static public void logProps( object obj, string header, LogType type = LogType.Debug, string cat = "", string prefix = "", [CallerFilePath] string dbgPath = "", [CallerLineNumber] int dbgLine = -1, [CallerMemberName] string dbgMethod = "", [CallerArgumentExpression( "obj" )] string dbgExpObj = "" )
{ {
var list = refl.GetAllProperties( obj.GetType() ); var list = refl.GetAllProperties( obj.GetType() );
lock( s_lock ) lock( s_lock )
{ {
var evt = new LogEvent( type, header, path, line, member, cat, dbgExpObj, obj ); var evt = new LogEvent( type, header, dbgPath, dbgLine, dbgMethod, cat, dbgExpObj, obj );
{ {
// Use Add instead of Enqueue // Use Add instead of Enqueue
s_events.Add( evt ); s_events.Add( evt );
@ -419,11 +419,11 @@ static public class log
{ {
var v = pi.GetValue( obj ); var v = pi.GetValue( obj );
logBase( $"{prefix}{pi.Name} = {v}", type, path, line, member, dbgExpObj, cat ); logBase( $"{prefix}{pi.Name} = {v}", type, dbgPath, dbgLine, dbgMethod, dbgExpObj, cat );
} }
catch( Exception ex ) catch( Exception ex )
{ {
logBase( $"Exception processing {pi.Name} {ex.Message}", LogType.Error, path, line, member, cat, dbgExpObj, obj ); logBase( $"Exception processing {pi.Name} {ex.Message}", LogType.Error, dbgPath, dbgLine, dbgMethod, cat, dbgExpObj, obj );
} }
} }
@ -462,16 +462,16 @@ static public class log
} }
static public LogEvent logCreateEvent( string msg, LogType type = LogType.Debug, string path = "", int line = -1, string member = "", string cat = "unk", string exp = "", object? obj = null ) static public LogEvent logCreateEvent( string msg, LogType type = LogType.Debug, string dbgPath = "", int dbgLine = -1, string dbgMethod = "", string cat = "unk", string exp = "", object? obj = null )
{ {
LogEvent evt = new LogEvent( type, msg, path, line, member, cat, exp, obj ); LogEvent evt = new LogEvent( type, msg, dbgPath, dbgLine, dbgMethod, cat, exp, obj );
return evt; return evt;
} }
[StackTraceHidden] [StackTraceHidden]
static public void logBase( string msg, LogType type = LogType.Debug, string path = "", int line = -1, string member = "", string cat = "unk", string exp = "", object? obj = null ) static public void logBase( string msg, LogType type = LogType.Debug, string dbgPath = "", int dbgLine = -1, string dbgMethod = "", string cat = "unk", string exp = "", object? obj = null )
{ {
var evt = logCreateEvent( msg, type, path, line, member, cat, exp ); var evt = logCreateEvent( msg, type, dbgPath, dbgLine, dbgMethod, cat, exp );
s_callbacks.TryGetValue( type, out var callback ); s_callbacks.TryGetValue( type, out var callback );

365
math/Cent.cs Normal file
View File

@ -0,0 +1,365 @@
using System;
using System.Diagnostics.CodeAnalysis;
using System.Globalization;
using System.Numerics;
#nullable enable
sealed public record Vector3Cm( Cm x, Cm y, Cm z )
{
public bool Equals( Vector3Cm? other ) => x == other?.x && y == other?.y && z == other?.z;
public override int GetHashCode() => x.GetHashCode() * 10000019 + z.GetHashCode() * 50000047 + y.GetHashCode();
}
/// <summary>
/// Represents a fixed-point value in centimeters, stored as an integer.
/// Implements INumber<Cm> for full generic math support.
/// </summary>
sealed public record Cm(int value) : INumber<Cm>
{
// --- Constants and Identities ---
/// <summary>
/// Represents the multiplicative identity (1.0).
/// The underlying value is 100 because 1.0 meter = 100 centimeters.
/// </summary>
public static Cm One => new Cm(100);
public static Cm Zero => new Cm(0);
public static int Radix => 10;
public static Cm AdditiveIdentity => Zero;
public static Cm MultiplicativeIdentity => One;
public float Float => ((float)value) * 0.01f;
public double Double => ((double)value) * 0.01;
// --- Static Conversion Methods ---
public static Cm From(float v) => new Cm((int)(v * 100.0f + 0.5f));
public static Cm From(double v) => new Cm((int)(v * 100.0 + 0.5));
// --- Standard Functions ---
public static Cm Abs(Cm c) => new Cm(Math.Abs(c.value));
public static Cm MaxMagnitude(Cm x, Cm y) => Abs(x) > Abs(y) ? x : y;
public static Cm MaxMagnitudeNumber(Cm x, Cm y) => MaxMagnitude(x, y); // Same for this type
public static Cm MinMagnitude(Cm x, Cm y) => Abs(x) < Abs(y) ? x : y;
public static Cm MinMagnitudeNumber(Cm x, Cm y) => MinMagnitude(x, y); // Same for this type
// --- Type Property Checks ---
public static bool IsCanonical(Cm c) => true;
public static bool IsComplexNumber(Cm c) => false;
public static bool IsEvenInteger(Cm c) => (c.value % 100 == 0) && (c.value / 100 % 2 == 0);
public static bool IsFinite(Cm c) => true;
public static bool IsImaginaryNumber(Cm c) => false;
public static bool IsInfinity(Cm c) => false;
public static bool IsInteger(Cm c) => c.value % 100 == 0;
public static bool IsNaN(Cm c) => false;
public static bool IsNegative(Cm c) => c.value < 0;
public static bool IsNegativeInfinity(Cm c) => false;
public static bool IsNormal(Cm c) => c.value != 0;
public static bool IsOddInteger(Cm c) => (c.value % 100 == 0) && (c.value / 100 % 2 != 0);
public static bool IsPositive(Cm c) => c.value > 0;
public static bool IsPositiveInfinity(Cm c) => false;
public static bool IsRealNumber(Cm c) => true; // It is a real number
public static bool IsSubnormal(Cm c) => false;
public static bool IsZero(Cm c) => c.value == 0;
// --- Parsing ---
public static Cm Parse(string s, IFormatProvider? provider) => Parse(s, NumberStyles.Number, provider);
public static Cm Parse(ReadOnlySpan<char> s, IFormatProvider? provider) => Parse(s, NumberStyles.Number, provider);
public static Cm Parse(string s, NumberStyles style, IFormatProvider? provider)
{
if (TryParse(s, style, provider, out var result))
{
return result;
}
throw new FormatException($"Input string '{s}' was not in a correct format.");
}
public static Cm Parse(ReadOnlySpan<char> s, NumberStyles style, IFormatProvider? provider)
{
if (TryParse(s, style, provider, out var result))
{
return result;
}
throw new FormatException($"Input string was not in a correct format.");
}
public static bool TryParse([NotNullWhen(true)] string? s, IFormatProvider? provider, [MaybeNullWhen(false)] out Cm result) => TryParse(s, NumberStyles.Number, provider, out result);
public static bool TryParse(ReadOnlySpan<char> s, IFormatProvider? provider, [MaybeNullWhen(false)] out Cm result) => TryParse(s, NumberStyles.Number, provider, out result);
public static bool TryParse([NotNullWhen(true)] string? s, NumberStyles style, IFormatProvider? provider, [MaybeNullWhen(false)] out Cm result)
{
if (decimal.TryParse(s, style, provider, out decimal decValue))
{
result = new Cm((int)(decValue * 100m));
return true;
}
result = Cm.Zero;
return false;
}
public static bool TryParse(ReadOnlySpan<char> s, NumberStyles style, IFormatProvider? provider, [MaybeNullWhen(false)] out Cm result)
{
if (decimal.TryParse(s, style, provider, out decimal decValue))
{
result = new Cm((int)(decValue * 100m));
return true;
}
result = Cm.Zero;
return false;
}
// --- Generic Type Conversion ---
public static bool TryConvertFromChecked<TOther>(TOther value, out Cm result) where TOther : INumberBase<TOther>
{
// For integer types, scale up. For floating point, convert.
if (TOther.IsInteger(value))
{
try
{
checked
{
result = new Cm(int.CreateChecked(value) * 100);
return true;
}
}
catch (OverflowException)
{
result = Cm.Zero;
return false;
}
}
// Convert floating point types
if (typeof(TOther) == typeof(double))
{
double d = double.CreateChecked(value);
result = From(d);
return true;
}
if (typeof(TOther) == typeof(float))
{
float f = float.CreateChecked(value);
result = From(f);
return true;
}
if (typeof(TOther) == typeof(decimal))
{
decimal m = decimal.CreateChecked(value);
result = new Cm((int)(m * 100m));
return true;
}
result = Cm.Zero;
return false;
}
public static bool TryConvertFromSaturating<TOther>(TOther value, out Cm result) where TOther : INumberBase<TOther>
{
if (TOther.IsInteger(value))
{
result = new Cm(int.CreateSaturating(value) * 100);
return true;
}
if (typeof(TOther) == typeof(double))
{
double d = double.CreateSaturating(value);
result = From(d);
return true;
}
if (typeof(TOther) == typeof(float))
{
float f = float.CreateSaturating(value);
result = From(f);
return true;
}
if (typeof(TOther) == typeof(decimal))
{
decimal m = decimal.CreateSaturating(value);
result = new Cm((int)(m * 100m));
return true;
}
result = Cm.Zero;
return false;
}
public static bool TryConvertFromTruncating<TOther>(TOther value, out Cm result) where TOther : INumberBase<TOther>
{
if (TOther.IsInteger(value))
{
result = new Cm(int.CreateTruncating(value) * 100);
return true;
}
if (typeof(TOther) == typeof(double))
{
double d = double.CreateTruncating(value);
result = From(d);
return true;
}
if (typeof(TOther) == typeof(float))
{
float f = float.CreateTruncating(value);
result = From(f);
return true;
}
if (typeof(TOther) == typeof(decimal))
{
decimal m = decimal.CreateTruncating(value);
result = new Cm((int)(m * 100m));
return true;
}
result = Cm.Zero;
return false;
}
public static bool TryConvertToChecked<TOther>(Cm value, out TOther result) where TOther : INumberBase<TOther>
{
// Convert our value (a count of centimeters) to another type.
// This typically involves scaling down by 100.
try
{
checked
{
if (TOther.IsInteger(TOther.Zero))
{
result = TOther.CreateChecked(value.value / 100);
return true;
}
// For floating points, perform floating point division
result = TOther.CreateChecked(value.value) / TOther.CreateChecked(100);
return true;
}
}
catch (OverflowException)
{
result = default!;
return false;
}
}
public static bool TryConvertToSaturating<TOther>(Cm value, out TOther result) where TOther : INumberBase<TOther>
{
if (TOther.IsInteger(TOther.Zero))
{
result = TOther.CreateSaturating(value.value / 100);
return true;
}
result = TOther.CreateSaturating(value.value) / TOther.CreateSaturating(100);
return true;
}
public static bool TryConvertToTruncating<TOther>(Cm value, out TOther result) where TOther : INumberBase<TOther>
{
if (TOther.IsInteger(TOther.Zero))
{
result = TOther.CreateTruncating(value.value / 100);
return true;
}
result = TOther.CreateTruncating(value.value) / TOther.CreateTruncating(100);
return true;
}
// --- Comparison ---
public int CompareTo(object? obj)
{
if (obj is Cm other)
{
return CompareTo(other);
}
return obj is null ? 1 : throw new ArgumentException("Object must be of type Cm.", nameof(obj));
}
public int CompareTo(Cm? other) => value.CompareTo(other?.value);
public bool Equals(Cm? other) => value == other?.value;
public override int GetHashCode() => value.GetHashCode();
// --- Formatting ---
public override string ToString() => (value / 100.0).ToString("F2", CultureInfo.InvariantCulture);
public string ToString(string? format, IFormatProvider? formatProvider)
{
// Format as a floating point number
return (value / 100.0).ToString(format, formatProvider);
}
public bool TryFormat(Span<char> destination, out int charsWritten, ReadOnlySpan<char> format, IFormatProvider? provider)
{
return (value / 100.0).TryFormat(destination, out charsWritten, format, provider);
}
// --- Operators ---
public static Cm operator +(Cm c) => c;
public static Cm operator +(Cm left, Cm right) => new Cm(left.value + right.value);
public static Cm operator -(Cm c) => new Cm(-c.value);
public static Cm operator -(Cm left, Cm right) => new Cm(left.value - right.value);
public static Cm operator ++(Cm c) => new Cm(c.value + 1);
public static Cm operator --(Cm c) => new Cm(c.value - 1);
// For fixed-point, multiplication/division require scaling
public static Cm operator *(Cm left, Cm right)
{
// (a/100) * (b/100) = (a*b)/10000. To get back to our format, multiply by 100.
// So, (a*b)/100. Use long to prevent intermediate overflow.
return new Cm((int)(((long)left.value * right.value) / 100));
}
public static Cm operator /(Cm left, Cm right)
{
// (a/100) / (b/100) = a/b. To get back to our format, multiply by 100.
// So, (a*100)/b. Use long to prevent intermediate overflow.
return new Cm((int)(((long)left.value * 100) / right.value));
}
public static Cm operator %(Cm left, Cm right) => new Cm(left.value % right.value);
// --- Comparison Operators ---
public static bool operator <(Cm left, Cm right) => left.value < right.value;
public static bool operator >(Cm left, Cm right) => left.value > right.value;
public static bool operator <=(Cm left, Cm right) => left.value <= right.value;
public static bool operator >=(Cm left, Cm right) => left.value >= right.value;
}
/// <summary>
/// Provides extension methods for easy conversion to the Cm type.
/// </summary>
public static class CentEx
{
// --- Static Conversion Methods ---
public static Cm From(float v) => new Cm((int)(v * 100.0f + 0.5f));
public static Cm From(double v) => new Cm((int)(v * 100.0 + 0.5));
//public static Cm Cm(this float r) => CentEx.From(r);
//public static Cm Cm(this double r) => CentEx.From(r);
extension(float r)
{
public Cm Cm => CentEx.From( r );
}
extension(double r)
{
public Cm Cm => CentEx.From( r );
}
}

View File

@ -651,12 +651,25 @@ public class ObjectHandler : ITypeHandler
// If POD-Attribute, write attribute // If POD-Attribute, write attribute
if( memberMeta.IsPodAttribute ) if( memberMeta.IsPodAttribute )
{ {
writer.WriteAttributeString( memberMeta.XmlName, value.ToString() ); try
{
writer.WriteAttributeString( memberMeta.XmlName, value.ToString() );
}
catch( Exception ex )
{
log.error( $"Writing Att {memberMeta.XmlName} = [{value}]" );
}
} }
else // Else, write element else // Else, write element
{ {
xml.WriteNode( writer, value, memberMeta.XmlName, memberMeta.Type, false ); try
} {
xml.WriteNode( writer, value, memberMeta.XmlName, memberMeta.Type, false );
}
catch( Exception ex )
{
log.error( $"Writing Node {memberMeta.XmlName} = [{value}]" );
}
} }
} }
} }

View File

@ -3,10 +3,15 @@
using System; using System;
namespace lib; //namespace lib;
public static class Guid public static class GuidExt
{ {
extension(Guid g)
{
public string Log => $"{g.LastByte():X}";
}
public static System.Guid GuidFromLongs( long a, long b ) public static System.Guid GuidFromLongs( long a, long b )
{ {
byte[] guidData = new byte[16]; byte[] guidData = new byte[16];
@ -59,6 +64,14 @@ public static class Guid
return (a, b, c, d); return (a, b, c, d);
} }
public static uint LastUInt( this System.Guid guid )
{
var bytes = guid.ToByteArray();
var d = BitConverter.ToUInt32( bytes, 12 );
return d;
}
public static System.Guid GuidFromUInts( uint a, uint b, uint c, uint d ) public static System.Guid GuidFromUInts( uint a, uint b, uint c, uint d )
{ {
byte[] guidData = new byte[16]; byte[] guidData = new byte[16];
@ -78,4 +91,10 @@ public static class Guid
var d = BitConverter.ToUInt32( bytes, 12 ); var d = BitConverter.ToUInt32( bytes, 12 );
return (a, b, c, d); return (a, b, c, d);
} }
public static byte LastByte( this System.Guid guid )
{
var bytes = guid.ToByteArray();
return bytes[7];
}
} }