44
55namespace Backdash ;
66
7+ using Vector2I = ( int X , int Y ) ;
8+
79/// <summary>
8- /// Integer Math
10+ /// Integer Math Helpers
911/// </summary>
1012public static class MathI
1113{
@@ -61,7 +63,7 @@ public static T Sum<T>(T[] values)
6163 /// <summary>
6264 /// Returns the sum of a span of <see cref="IBinaryInteger{TSelf}"/>
6365 /// </summary>
64- public static T SumRaw < T > ( ReadOnlySpan < T > span )
66+ public static T SumSimple < T > ( ReadOnlySpan < T > span )
6567 where T : unmanaged, IBinaryInteger < T > , IAdditionOperators < T , T , T >
6668 {
6769 unchecked
@@ -80,8 +82,8 @@ public static T SumRaw<T>(ReadOnlySpan<T> span)
8082 }
8183 }
8284
83- /// <inheritdoc cref="SumRaw {T}(ReadOnlySpan{T})"/>
84- public static T SumRaw < T > ( T [ ] values )
85+ /// <inheritdoc cref="SumSimple {T}(ReadOnlySpan{T})"/>
86+ public static T SumSimple < T > ( T [ ] values )
8587 where T : unmanaged, IBinaryInteger < T > , IAdditionOperators < T , T , T > => Sum ( ( ReadOnlySpan < T > ) values ) ;
8688
8789 /// <summary>
@@ -97,7 +99,168 @@ public static double Avg(ReadOnlySpan<int> span)
9799 public static double Avg ( int [ ] values ) => Avg ( ( ReadOnlySpan < int > ) values ) ;
98100
99101 /// <summary>
100- /// Return the next power of two number greater than <paramref name="number" />
102+ /// Returns the number of digits of value
103+ /// </summary>
104+ public static int CountDigits ( int value ) => ( int ) Math . Floor ( Math . Log10 ( value ) + 1 ) ;
105+
106+ /// <summary>
107+ /// Returns true if the number is even
108+ /// </summary>
109+ [ MethodImpl ( MethodImplOptions . AggressiveInlining ) ]
110+ public static bool IsEven ( int number ) => ( number & 1 ) is 0 ;
111+
112+ /// <summary>
113+ /// Returns true if the number is odd
114+ /// </summary>
115+ [ MethodImpl ( MethodImplOptions . AggressiveInlining ) ]
116+ public static bool IsOdd ( int number ) => ( number & 1 ) is not 0 ;
117+
118+ /// <summary>
119+ /// Returns 1 if value is true, 0 otherwise
120+ /// </summary>
121+ [ MethodImpl ( MethodImplOptions . AggressiveInlining ) ]
122+ public static int ToInt ( bool value ) => Unsafe . As < bool , byte > ( ref value ) ;
123+
124+ /// <summary>
125+ /// Returns false if value is 0, true otherwise
126+ /// </summary>
127+ [ MethodImpl ( MethodImplOptions . AggressiveInlining ) ]
128+ public static bool ToBool ( int value ) => Unsafe . As < int , bool > ( ref value ) ;
129+
130+ /// <summary>
131+ /// Returns the square root of value
132+ /// </summary>
133+ public static int Sqrt ( int value )
134+ {
135+ var result = 0 ;
136+ var bit = 1 << 30 ;
137+ value = Math . Abs ( value ) ;
138+ while ( bit > value ) bit >>= 2 ;
139+
140+ while ( bit is not 0 )
141+ {
142+ if ( value >= result + bit )
143+ {
144+ value -= result + bit ;
145+ result = ( result >> 1 ) + bit ;
146+ }
147+ else
148+ result >>= 1 ;
149+
150+ bit >>= 2 ;
151+ }
152+
153+ return result ;
154+ }
155+
156+ /// <summary>
157+ /// Returns the square root of value
158+ /// </summary>
159+ public static long Sqrt ( long value )
160+ {
161+ var result = 0L ;
162+ value = Math . Abs ( value ) ;
163+ var bit = 1L << 62 ;
164+ while ( bit > value ) bit >>= 2 ;
165+
166+ while ( bit is not 0 )
167+ {
168+ if ( value >= result + bit )
169+ {
170+ value -= result + bit ;
171+ result = ( result >> 1 ) + bit ;
172+ }
173+ else
174+ result >>= 1 ;
175+
176+ bit >>= 2 ;
177+ }
178+
179+ return result ;
180+ }
181+
182+ /// <summary>
183+ /// Remap a value between to ranges
184+ /// </summary>
185+ [ MethodImpl ( MethodImplOptions . AggressiveInlining ) ]
186+ public static int Remap (
187+ int value ,
188+ int inMin , int inMax ,
189+ int outMin , int outMax
190+ )
191+ {
192+ var inRange = inMax - inMin ;
193+ if ( inRange is 0 ) return 0 ;
194+
195+ var num = ( long ) ( value - inMin ) * ( outMax - outMin ) ;
196+ var result = ( num / inRange ) + outMin ;
197+
198+ if ( outMin < outMax )
199+ {
200+ if ( result < outMin ) return outMin ;
201+ if ( result > outMax ) return outMax ;
202+ }
203+ else
204+ {
205+ if ( result > outMin ) return outMin ;
206+ if ( result < outMax ) return outMax ;
207+ }
208+
209+ return ( int ) result ;
210+ }
211+
212+ /// <summary>
213+ /// Returns the value of applying the <paramref name="percentage"/> to <paramref name="value"/>
214+ /// Percentage is defined from 0 to 100
215+ /// </summary>
216+ [ MethodImpl ( MethodImplOptions . AggressiveInlining ) ]
217+ public static int ApplyPercentage ( int value , int percentage ) => ( int ) ( value * ( long ) percentage / 100L ) ;
218+
219+ /// <summary>
220+ /// Finds how is the percentage of <paramref name="part"/> from <paramref name="total"/>
221+ /// Percentage is defined from 0 to 100
222+ /// </summary>
223+ [ MethodImpl ( MethodImplOptions . AggressiveInlining ) ]
224+ public static int FindPercentage ( int part , int total )
225+ {
226+ if ( total is 0 ) return 0 ;
227+ var scaled = ( long ) part * 100 ;
228+
229+ if ( scaled >= 0 )
230+ scaled += total / 2 ;
231+ else
232+ scaled -= total / 2 ;
233+
234+ return ( scaled / total ) switch
235+ {
236+ < 0 => 0 ,
237+ > 100 => 100 ,
238+ var result => ( int ) result ,
239+ } ;
240+ }
241+
242+ /// <summary>
243+ /// Clamps the value between the limits of type <typeparamref name="T"/>
244+ /// </summary>
245+ /// <seealso cref="IMinMaxValue{TSelf}"/>
246+ public static int Clamp < T > ( int value ) where T : INumberBase < T > , IMinMaxValue < T > =>
247+ Math . Clamp ( value , int . CreateSaturating ( T . MinValue ) , int . CreateSaturating ( T . MaxValue ) ) ;
248+
249+ /// <summary>
250+ /// Converts the value into <typeparamref name="T"/> sarutating the result
251+ /// </summary>
252+ public static T Cast < T > ( int value ) where T : IBinaryInteger < T > => T . CreateSaturating ( value ) ;
253+
254+ #region PowerOf2
255+
256+ /// <summary>
257+ /// Returns true if the value in power of two
258+ /// </summary>
259+ public static bool IsPowerOfTwo < T > ( T value ) where T : unmanaged, IBinaryInteger < T > =>
260+ value != T . Zero && ( value & ( value - T . One ) ) == T . Zero ;
261+
262+ /// <summary>
263+ /// Return the next power of two number greater than <paramref name="number" />
101264 /// </summary>
102265 [ MethodImpl ( MethodImplOptions . AggressiveInlining ) ]
103266 public static ulong NextPowerOfTwo ( ulong number ) =>
@@ -137,4 +300,91 @@ public static int NextPowerOfTwo(int number) =>
137300 > 1 << 30 => int . MaxValue ,
138301 _ => 1 << ( 32 - BitOperations . LeadingZeroCount ( ( uint ) ( number - 1 ) ) ) ,
139302 } ;
303+
304+ /// <summary>
305+ /// Returns each power of 2 component from the value
306+ /// </summary>
307+ public static IEnumerable < int > DecomposePowerOfTwo ( int value )
308+ {
309+ while ( value is not 0 )
310+ {
311+ var flag = value & - value ;
312+ yield return flag ;
313+ value ^= flag ;
314+ }
315+ }
316+
317+ #endregion
318+
319+ #region Trig
320+
321+ const int MaxAngleDeg = 360 ;
322+ static readonly short [ ] sinTable = new short [ MaxAngleDeg ] ;
323+ static readonly short [ ] cosTable = new short [ MaxAngleDeg ] ;
324+
325+ static MathI ( ) => InitTrigTables ( ) ;
326+
327+ static void InitTrigTables ( )
328+ {
329+ for ( var i = 0 ; i < MaxAngleDeg ; i ++ )
330+ {
331+ var rad = i * Math . PI / 180.0 ;
332+ sinTable [ i ] = ( short ) ( Math . Sin ( rad ) * short . MaxValue ) ;
333+ cosTable [ i ] = ( short ) ( Math . Cos ( rad ) * short . MaxValue ) ;
334+ }
335+ }
336+
337+ /// <summary>
338+ /// Calculates the angle for the coordinates x and y (degrees)
339+ /// </summary>
340+ public static int AngleDegrees ( int x , int y )
341+ {
342+ if ( x is 0 && y is 0 ) return 0 ;
343+ var absY = Math . Abs ( y ) + 1 ;
344+ var angle = x >= 0
345+ ? 45 - ( 45 * ( x - absY ) / ( x + absY ) )
346+ : 135 - ( 45 * ( x + absY ) / ( absY - x ) ) ;
347+
348+ return y < 0 ? 360 - angle : angle ;
349+ }
350+
351+ /// <summary>
352+ /// Calculates the Sin value of the angle (degrees)
353+ /// </summary>
354+ [ MethodImpl ( MethodImplOptions . AggressiveInlining ) ]
355+ public static int Sin ( int angleDeg ) => sinTable [ angleDeg ] ;
356+
357+ /// <summary>
358+ /// Calculates the Cos value of the angle (degrees)
359+ /// </summary>
360+ [ MethodImpl ( MethodImplOptions . AggressiveInlining ) ]
361+ public static int Cos ( int angleDeg ) => cosTable [ angleDeg ] ;
362+
363+ /// <summary>
364+ /// Calculates the circle point for the <paramref name="angleDeg"/> (degrees) with size of <paramref name="radius"/>
365+ /// </summary>
366+ public static Vector2I CirclePoint ( int angleDeg , int radius )
367+ {
368+ angleDeg %= MaxAngleDeg ;
369+ if ( angleDeg < 0 ) angleDeg += MaxAngleDeg ;
370+ var x = ( int ) ( ( ( long ) Cos ( angleDeg ) * radius ) >> 15 ) ;
371+ var y = ( int ) ( ( ( long ) Sin ( angleDeg ) * radius ) >> 15 ) ;
372+ return ( x , y ) ;
373+ }
374+
375+ /// <summary>
376+ /// Calculates the rotation of a point by <paramref name="degrees"/>
377+ /// </summary>
378+ public static Vector2I Rotate ( in Vector2I v , int degrees )
379+ {
380+ degrees %= MaxAngleDeg ;
381+ if ( degrees < 0 ) degrees += MaxAngleDeg ;
382+ var cos = Cos ( degrees ) ;
383+ var sin = Sin ( degrees ) ;
384+ var x = ( ( v . X * cos ) - ( v . Y * sin ) ) >> 14 ;
385+ var y = ( ( v . X * sin ) + ( v . Y * cos ) ) >> 14 ;
386+ return new ( x , y ) ;
387+ }
388+
389+ #endregion
140390}
0 commit comments