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();
}
///
/// Represents a fixed-point value in centimeters, stored as an integer.
/// Implements INumber for full generic math support.
///
sealed public record Cm( int value ) : INumber
{
// --- Constants and Identities ---
///
/// Represents the multiplicative identity (1.0).
/// The underlying value is 100 because 1.0 meter = 100 centimeters.
///
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 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 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 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 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 value, out Cm result ) where TOther : INumberBase
{
// 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 value, out Cm result ) where TOther : INumberBase
{
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 value, out Cm result ) where TOther : INumberBase
{
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( Cm value, out TOther result ) where TOther : INumberBase
{
// 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( Cm value, out TOther result ) where TOther : INumberBase
{
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( Cm value, out TOther result ) where TOther : INumberBase
{
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 destination, out int charsWritten, ReadOnlySpan 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;
}
///
/// Provides extension methods for easy conversion to the Cm type.
///
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 );
}
}