=encoding utf8

=head1 NAME

Module::Generic::Number - Number Manipulation Object Class

=head1 SYNOPSIS

    my $n = Module::Generic::Number->new(10);
    # or
    my $n = Module::Generic::Number->new( 10, 
    {
        thousand => ',',
        decimal => '.',
        precision => 2,
        # Currency symbol
        symbol => '€',
        # Display currency symbol before or after the number
        precede => 1,
    });
    # Even accepts numbers in Japanese double bytes
    # Will be converted automatically to regular digits.
    my $n = Moule::Generic::Number->new( "−１２３４５６７" ); # becomes -1234567
    # or, to get all the defaults based on language code
    my $n = Module::Generic::Number->new( 10, 
    {
        lang => 'fr_FR',
        # or
        # locale => 'fr_FR',
    });
    # this would set the decimal separator to ',', the thousand separator to ' ', and precede to 0 (false).
    print( "Number is: $n\n" );
    # prints: 10

    $n ** 2 # 100
    # and all other operators work

    my $n_neg = Module::Generic::Number->new(-10);
    $n_neg->abs # 10
    $n->atan # 1.47112767430373
    $n->atan2(2) # 1.37340076694502
    $n->cbrt # 2.15443469003188
    $n->cbrt->ceil # 3
    $n->clone # Cloning the number object
    $n->cos # -0.839071529076452
    $n->currency # €
    $n->decimal # .
    $n->exp # 22026.4657948067
    $n->cbrt->floor # 2
    $n *= 100;
    $n->format # 1,000.00
    $n->format(0) # 1,000
    $n->format(
        precision => 0,
        # Boolean value
        decimal_fill => 0,
        thousand => ',',
        decimal => '.',
    );
    $n->format_binary # 1111101000
    my $n2 = $n->clone;
    $n2 += 24
    $n2->format_bytes # 1K
    $n2->format_hex # 0x400
    $n2->format_money # € 1,024.00
    $n2->format_money( '$' ) # $1,024.00
    $n2->format_negative # -1,024.00
    $n2->format_picture( '(x)' ) # (1,024.00)
    $n2->formatter( $new_Number_Format_object );
    $n->from_binary( "1111101000" ) # 1000
    $n->from_hex( "0x400" ) # 1000
    my $n3 = $n->clone( 3.14159265358979323846 )->int # 3
    $n3->is_even # false
    $n3->is_odd # true
    # Uses POSIX::signbit
    $n3->is_negative # 0
    $n3->is_positive # 1
    $n->log # 6.90775527898214
    $n->log2 # 9.96578428466209
    $n->log10 # 3
    $n->max( 2000 ) # 2000
    $n->min( 2000 ) # 1000
    $n->mod(3) # 1
    my $perm = Module::Generic::Number->new( '0700' );
    $perm->oct # 448
    printf( "%o\n", 448 ) # 700
    $n->clone( 2 )->pow( 3 ) # 8
    # Change position of the currency sign
    $n->precede( 1 ) # Set it to precede the number
    # Change precision
    $n->precision( 0 )
    # Based on 1000
    $n->rand # For example, returns 77.775465338589
    $n->rand->int # For example, would return a random integer 77
    $n->clone( 3.14159265358979323846 )->round( 4 ) # 3.1416
    $n->sin # 0.826879540532003
    $n2->sqrt # 32
    $n->symbol # €
    $n->tan # 1.47032415570272
    $n->thousand # ,
    $n->unformat( "€ 1,024.00" ) # 1024

    # Return the raw, underlying non-object real number
    my $num = $n->real;

=head1 VERSION

    v2.3.4

=head1 DESCRIPTION

The purpos of this class/package is to provide a lightweight object-oriented approach to number manipulation.

This uses perl core functions and POSIX functions only. This module's methods act as a wrapper to them.

The object is overloaded, so it returns the embedded number when used as a string.

    print( "I have $n friends\n" );

Would produce: I have 1000 friends

Because the object is overloaded, you can use the variable with any perl operators, such as:

    $n /= 2 # 5
    $n + 3 # 8
    $n **= 2 # $n is now 64
    # etc...

L<Module::Generic::Number> also handles infinity and numbers that are not numbers, a.k.a. C<NaN>. Ot uses 2 special classes: L<Module::Generic::Infinity> and L<Module::Generic::Nan>

While C<NaN> is very straightforward, C<Inf> or C<-Inf> is a bit trickier, because although it is not a number, it is still possible to perform some operations. For example :

    # Here the use of abs is meaningless, and just to test chaining
    $inf->abs->max(10)->floor

Would yield C<Inf> object (L<Module::Generic::Infinity>), but

    $inf->abs->max(10)->mod(3)

Would yield a C<NaN> object (L<Module::Generic::Nan>) and of course

    $inf->abs->min(10)

Would yield C<10> as a L<Module::Generic::Number> object, so the results possibly becomes an object of a different class based on the result.

Operators also works on the infinity object:

    my $inf = Module::Generic::Infinity->ne(-Inf);
    $inf *= -1 # Yields a new infinity object with value Inf

Those are just basic arithmetics wrapped in object to enable object oriented interface and chaining. It does not do anything special and rely on perl and L<POSIX> for computation, depending on the function.

=head1 METHODS

=head2 new

Provided with a number and optional parameters, this returns a new L<Module::Generic::Number> object. On error, it sets an L<error|Module::Generic/error> and returns C<undef> in scalar context or an empty list in list context.

Possible optional parameters are:

=over 4

=item C<decimal>

Specifies the decimal separator. This can also be changed or retrieved with the method L</"decimal">

=item C<grouping>

The sizes of the groups of digits, except for currency values. unpack( "C*", $grouping ) will give the number in question. This is typically 3.

=item C<lang>

If provided with a language tag as specified in L<rfc5646|https://tools.ietf.org/html/rfc5646>, and this will the number format properties based on the locale dictionary. It uses L<POSIX/"setlocale"> to achieve that, but without disturbing your own locale settings.

WIth the number format properties retrieved, it will populate the other parameters here, if not provided already. For example :

    my $n = Module::Generic::Number->new( 1000, { lang => 'fr_FR' });
    $n->format # 1.000,00 €

Would set the thousand separator to C<.>, the decimal separator to C<,>, the currency symbol to C<€> and precede to false.

    my $n = Module::Generic::Number->new( 1000, {
        lang => 'fr_FR',
        precede => 1,
    });

Uses the standard default format properties, except for precede which we set to true

    $n->format # € 1.000,00

When no C<lang> is provided, it uses the default language set in the system to retrieve the number formatting properties.

Any of those properties can be overriden by specifying its value when creating an object.

=item C<position_neg>

Boolea value to define whether the negative sign (typically "-") should be positioned at the begining (true) or at the end (false) of the negative numbers.

=item C<position_pos>

Boolea value to define whether the positive sign (typically and empty string) should be positioned at the begining (true) or at the end (false) of the positive numbers.

=item C<precede>

If set to true, this will set the currency symbol before the number and when set to false, it will set it after the number

This can also be changed or retrieved with the method L</"precede">

=item C<precede_neg>

If set to true, this will set the currency symbol before the negative number and when set to false, it will set it after the negative number

This can also be changed or retrieved with the method L</"precede_neg">

=item C<precision>

Sets the decimal precision of the number. This can also be changed or retrieved with the method L</"precision">

=item C<sign_neg>

The character used to denote negative currency values, usually a minus sign.

=item C<sign_pos>

The separator between groups of digits before the decimal point, except for currency values.

=item C<space>

Boolean value to define whether there should be a space between the currency sign and the number value.

=item C<space_neg>

Boolean value to define whether there should be a space between the currency sign and the number value for negative numbers.

=item C<symbol>

Sets the currency symbol to be used upon formatting of the number as money with the method L</"format_money">

 This can also be changed or retrieved with the method L</"symbol">

=item C<thousand>

Sets the thousand separator to be used uppon formatting.

 This can also be changed or retrieved with the method L</"thousand">

=back

=head2 abs

Return the absolute value of the number object. Same as L<perlfunc/"abs">

=head2 as_array

Return the number object as a L<Module::Generic::Array> object.

=head2 as_boolean

Return the number object as  a L<Module::Generic::Boolean> object.

=head2 as_number

Returns the object as a regular perl number.

Internally, it returns the underlying number + 0 to ensure perl converts it to a number.

=head2 as_scalar

Return the number object as  a L<Module::Generic::Scalar> object.

=head2 as_string

Returns the object string as a string.

    my $n = Module::Generic::Number->new( 1000 );
    print( "I have $n books\n" );
    # I have 1000 books
    # But better like ths:
    printf( "I have %s bools\n", $n->format( 0 ) );
    # I have 1,000 books

=head2 atan

Returns the arcus tangent for the number object. See L<POSIX/"atan">

    # Assuming $n is an object for 1000
    # atan2( Y, X ). Y = 1000 here
    $n->atan2( 20 ) # produces 1.55079899282175

=head2 atan2

Returns the arctangent of Y/X in the range -PI to PI. See L<perlfunc/"atan2">

=head2 cbrt

Returns the cube root. See L<POSIX/"cbrt">

=head2 ceil

Returns the smallest integer value greater than or equal to the number object. See L<POSIX/"ceil">

    # Assuming $n is an object for 3.14159265358979323846
    $n->ceil # 4

=head2 chr

Returns the character matching our number object. See L<perlfunc/"chr">

    # Assuming $n is 74
    $n->chr # J

=head2 clone

Returns a clone of the current object, keeping its original formatting properties

It can take an optional number that will be used

    my $n = Moduke::Generic::Number->new(1000);
    # $n is no 1000 with thousand separator set to "","", etc
    my $n2 = $n->clone( 2020 );
    # Same properties as $n, but now the number is 2020 instead of 1000 and this is a new object

=for Pod::Coverage compute

=head2 cos

Returns the cosine of the number object. See L<perlfunc/"cos">

=head2 currency

Sets or gets the currency symbol to be used for formatting the number object with L</"format_money">

=head2 decimal

Sets or gets the decimal separator to be used for formatting the number object

=head2 decimal_fill

Boolean. Sets or gets whether to pad the decimal with zeroes. This is used in conjonction with L</precision>

=head2 decode_lconv

Provided with an hash reference that came from L<POSIX::localeconv|POSIX/localeconv>, and this will ensure all its non-utf8 values set are properly decoded.

This is especially true for the property C<currency_symbol>

It returns the hash reference upon success, and upon error, it sets an L<error object|Module::Generic::Exception> and return C<undef> in scalar context, or an empty list in list context.

=head2 default

Sets the dictionary (hash reference) of property-value pairs used for the number object formatting.

=head2 encoding

Sets or gets the encoding of the data provided by L<localeconv|POSIX/localeconv>

There is no reason for you to set or change this value. It is rather meant to be retrieved and is set automatically once the language has been set for this object.

=head2 exp

Returns the natural logarithm base to the power of the number object. See L<perlfunc/"exp">

    # Assuming the number object is 2
    $n->exp # 7.38905609893065

=head2 floor

Returns the largest integer value less than or equal to the number object. See L<POSIX/"floor">

    # Assuming $n is an object for 3.14159265358979323846
    $n->ceil # 3

=head2 format

Provided with an optional precision and this format the number in a human readable way using thousand and decimal separators and floating number precision

    $n->format # 1,000.00
    $n->format(
        precision => 2,
        # Override object value
        thousand => ',',
        decimal => '.',
        # Boolean
        decimal_fill => 1,
    );

If the number is too large or great to work with as a regular number, but instead must be shown in scientific notation, returns that number in scientific notation without further formatting.

    Module::Generic::Number->new("0.000020000E+00")->format(7); # 2e-05

It returns a L<scalar object|Module::Generic::Scalar> upon success, or sets an L<error|Module::Generic/error> and returns C<undef> in scalar context or an empty list in list context if an error occurred.

=head2 format_binary

    # Assuming the number object is 1000
    $n->format_binary # 1111101000

=head2 format_bytes

    # Assuming the number object is 1,234,567
    $n->format_bytes # 1.18M

Provided with an hash or hash reference of options, and this formats number with suffix K, M or G depending if it exceeds gigabytes, megabytes or kilobytes; or the IEC
standard 60027 C<KiB>, C<MiB>, or C<GiB> depending on the option C<mode>

It returns a L<scalar object|Module::Generic::Scalar> upon success or an L<error|Module::Generic/error> if an error occurred.

The following options are supported:

=over 4

=item * C<base>

Sets the number at which the suffix set with L</kilo_suffix> is added.
Default is 1024. Set to any value; the only other useful value is probably 1000.

If the mode (see below) is set to C<iec> or C<iec60027> then setting the C<base> option returns an error.

=item * C<mode>

This can be C<trad>, C<traditional>, C<iec> or C<iec60027>

=item * C<precision>

The decimal precision. Defaults to the value set with L</precision>

=item * C<unit>

By default, this is guessed based on the value of the number, but can be explicitly specified here.

In other words, numbers greater than or equal to 1024 (or other number given by the C<base> option) will be divided by 1024 and suffix set with L</kilo_suffix> or L</kibi_suffix> added; if greater than or equal to 1048576 (1024*1024), it will be divided by 1048576 and suffix set with L</mega_suffix> or L</mebi_suffix> appended to the end; etc.

Possible values are: C<auto> (default), C<kilo>, C<mega>, C<giga>

If a value other than C<auto> is specified, that value will be used instead no matter the number. For example:

    Module::Generic::Number->new( 1048576 )->format_bytes( unit => 'k' );
    # Produces 1,024K and not 1M

=back

=head2 format_hex

    # Assuming the number object is 1000
    $n->format_hex # 0x3E8

=head2 format_money

Provided with an optional precision, and an optional currency symbol and this format the number accordingly.
It uses the object initial value set with L</precision> and L</currency> if not explicitly specified.
object, using the inital format parameters specified during object instantiation.

    # Assuming the number object is 1000
    $n->format_money # € 1,000.00
    $n->format_money(3) # € 1,000.000

It returns a L<scalar object|Module::Generic::Scalar> upon success or an L<error|Module::Generic/error> if an error occurred.

=head2 format_negative

Provided with an optional format, or by default uses the value set with L</neg_format> which must include the character C<x> and this format the number object, assuming it is negative.

For example, suitable for accounting:

    $n->format_negative( '(x)' ); # (1,000)

It returns a L<scalar object|Module::Generic::Scalar> upon success or an L<error|Module::Generic/error> if an error occurred.

=head2 format_picture

Format the string based on the pattern provided, which will have the C<#> characters replaced by the digits from the number.

    $n->format_picture( '##,###.##' ); # 1,000.00

If the length of the integer part of $number is too large to fit, the C<#> characters are replaced with asterisks (C<*>) instead. For examples:

    # Assuming 100023
    $n->format_picture( 'EUR ##,###.##' ); # EUR **,***.**
    # Assuming 1.00023
    $n->format_picture( 'EUR #.###,###' ); # EUR 1.002,300

The comma C<,> and period C<.> used in the example above are taken from the value set with L</thousand> and L</decimal> respectively.
However, the C<thousand> characters in the C<picture> provided, does not need to occur every three digits; the
I<only> use of that variable by this function is to remove leading commas (see the first example above).

There may not be more than one instance of C<decimal> in the C<picture> provided though, or an error will be returned.

It returns a L<scalar object|Module::Generic::Scalar> upon success or an L<error|Module::Generic/error> if an error occurred.

=head2 from_binary

Returns a number object based on a binary number.

    my $n2 = $n->from_binary( "1111101000" ); # 1000

=head2 from_hex

Returns a number object based on an hex number.

    my $n2 = $n->from_hex( "0x400" ); # 1024

=head2 gibi_suffix

Sets or gets the gibi suffix.

=head2 giga_suffix

Sets or gets the gigabytes suffix.

=head2 grouping

The sizes of the groups of digits, except for currency values. unpack( "C*", $grouping ) will reveal the number in question.

=head2 int

Returns the integer portion of the number object. See L<perlfunc/"int"> for more details.

    # Assuming $n is an object for 3.14159265358979323846
    $n->int # 3

=head2 is_decimal

Returns true if the number is a decimal number.

=head2 is_empty

Returns true if the length of the underlying number is zero. This always returns true, because an instance of this class can never be C<undef>. This is here for consistency with other classes of L<Module::Generic>

=head2 is_even

Returns true if the number is even, i.e. if the modulus of the number divided by 2 is 0.

See L</is_odd>

=head2 is_finite

Rturns true if the number is finite, i.e. not infinity. See L<POSIX/"isfinite">

=head2 is_float

Returns true if the number is a floating decimal number. It uses L<POSIX/"modf"> to find out.

=head2 is_infinite

Rturns true if the number is infinite. See L<POSIX/"isinf">

=head2 is_int

Returns true if the number is an integer. It uses L<POSIX/"modf"> to find out.

=head2 is_nan

Returns true if the number is not a number, i.e. NaN. See L<POSIX/"isnan">

=head2 is_neg

Alias for I</is_negative>

=head2 is_negative

Returns true if the number object is negative, false otherwise. See L<POSIX/"signbit">

=head2 is_normal

Returns true if the argument is normal (that is, not a subnormal/denormal, and not an infinity, or a not-a-number). See L<POSIX/"isnormal">

=head2 is_odd

Returns true if the number is odd, i.e. if the modulus of the number divided by 2 is 1.

See L</is_even>

=head2 is_pos

Alias for L</is_positive>

=head2 is_positive

Returns true if the number object is positive, false otherwise. See L<POSIX/"signbit">

=head2 kibi_suffix

Sets or gets the kibi suffix.

=head2 kilo_suffix

Sets or gets the kilobytes suffix.

=head2 lang

Returns the current language used for the number formatting properties.

=head2 length

Returns the number of digits this number object contains. The value returned is a L<Module::Generic::Number> object

=head2 locale

Same as L</"lang">

=head2 log

Returns the natural logarithm of the number object. See L<perlfunc/"log"> for more details.

    $n->log # 6.90775527898214

=head2 log2

Logarithm base two of the number object. See L<POSIX/"log2"> for more details.

    $n->log2 # 9.96578428466209

=head2 log10

Returns the 10-base logarithm of the number object. See L<POSIX/"log10"> for more details.

    $n->log10 # 3

=head2 max

Returns the highest number of either the number object, or the additional number provided as arguement. If the latter is undef, the number object is returned. See L<POSIX/"fmax">

    $n->max( 2000 ) # 2000

Returns the lowest number of either the number object, or the additional number provided as arguement. If the latter is undef, the number object is returned. See L<POSIX/"fmin">

    $n->min( 2000 ) # 2000

=head2 mebi_suffix

Sets or gets the mebi suffix.

=head2 mega_suffix

Sets or gets the megabytes suffix.

=head2 min

Provided with another number and this returns the smallest of the two as an L<Module::Generic::Number> object.

=head2 neg_format

Sets or gets the format for formatting negative numbers.

Returns a L<scalar object|Module::Generic::Scalar>

=head2 mod

Returns the remainder for the number bject divided by another number provided as additional argument. See L<POSIX/"fmod"> for more details.

    # Assuming 1000
    $n->mod(3) # 1

=head2 oct

Provided an octal value, this returns the corresponding number as an object. See L<perlfunc/"oct"> for more details.

=head2 position_neg

Set to true or false if the negative sign (typically "-") should be positioned at the begining (true) or at the end (false) of the number.

=head2 position_pos

Set to true or false if the positive sign (typically "", i.e. empty, but could be set to "+") should be positioned at the begining (true) or at the end (false) of the number.

=head2 pow

Returns the number object to the power of the number provided as arguments. See L<POSIX/"pow"> for more details.

    # Assuming $n is an object representing 2
    $n->pow( 3 ) # 8

=head2 precede

Sets or gets the I<precede> property of this object. This is used by L<Number::Format> to determine if the currency symbol should be set before or after the number

=head2 precede_neg

Sets or gets the I<precede_neg> property of this object. This is used by L<Number::Format> to determine if the currency symbol should be set before or after the number when it is a negative number.

=head2 precede_pos

Sets or gets the property value for I<precede>.

1 if the currency symbol precedes the currency value for nonnegative values, 0 if it follows.

=head2 precision

Sets or gets the floating precision of the number.

    # Assuming $n is an object for 3.14159265358979323846
    $n->precision( 4 );
    $n->format # 3.1416

=head2 rand

Returns a random fractional number greater than or equal to 0 and less than the value of the number object.
See L<perlfunc/"rand"> for more information.

=head2 real

Read-only. This returns the real underlying number used by this class.

=head2 round

Provided with an optional precision, this will round the number object. Internally it uses L<perldoc/"sprintf"> to achieve that.

This returns an error if more than 1 argument was provided. To use two arguments, use L</round2>

=head2 round_zero

This will round the number using L<POSIX/round>, which will return "the integer (but still as floating point) nearest to the argument"

=head2 round2

Provided with a number and an optional precision, or by default the one set with L</precision>, and this will round the number using an alternative approach based on L<Number::Format/round>.

=head2 scalar

Same as L</as_string>. This forces the return of the object as a raw number.

=head2 sign_neg

Sets or gets the I<sign_neg> property of this object. The character used to denote negative currency values, usually a minus sign.

=head2 sign_pos

Sets or gets the I<sign_pos> property of this object. The character used to denote nonnegative currency values, usually the empty string.

=head2 sin

Returns the sine of the number object. See L<perlfunc/"sine"> for more details.

=head2 space

Sets or gets the I<space> property of this object. 1 if a space is inserted between the currency symbol and the currency value for non-negative values, 0 otherwise.

=head2 space_neg

Sets or gets the I<space_neg> property of this object. 1 if a space is inserted between the currency symbol and the currency value for negative values, 0 otherwise.

=head2 space_pos

Sets or gets the I<space> property. 1 if a space is inserted between the currency symbol and the currency value for nonnegative values, 0 otherwise.

=head2 sqrt

Return the positive square root of the number object. See L<perlfunc/"sqrt"> for more details.

=head2 symbol

Set or gets the currency symbol to be used in L</"format_money">

=head2 tan

Returns the tangent of the number object. See L<POSIX/"tan"> for more details.

=head2 thousand

Set or gets the thousand separator used in formatting the number.

=head2 TO_JSON

Special method called by L<JSON> to transform this object into a string suitable to be added in a json data.

=head2 unformat

Provided with a string containing a number, and an optional hash or hash reference of options, and this returns a number as a L<Module::Generic::Number> object.

It returns an L<error|Module::Generic/error> if the string provided does not contain any number.

    my $n = Module::Generic::Number->unformat('USD 12.95'); # 12.95
    # Same
    my $n = $n1->unformat('USD 12.95'); # 12.95
    my $n = Module::Generic::Number->unformat('USD 12.00'); # 12
    my $n = Module::Generic::Number->unformat('foobar'); # return error (undef)
    my $n = Module::Generic::Number->unformat('1234-567@.8'); # 1234567.8

=head1 SERIALISATION

=for Pod::Coverage FREEZE

=for Pod::Coverage STORABLE_freeze

=for Pod::Coverage STORABLE_thaw

=for Pod::Coverage THAW

Serialisation by L<CBOR|CBOR::XS>, L<Sereal> and L<Storable::Improved> (or the legacy L<Storable>) is supported by this package. To that effect, the following subroutines are implemented: C<FREEZE>, C<THAW>, C<STORABLE_freeze> and C<STORABLE_thaw>

=head1 THREAD-SAFETY

L<Module::Generic::Number> is thread-safe for all its operations, as it operates on per-object state, uses thread-safe external libraries, and synchronises locale changes to prevent race conditions.

Key considerations for thread-safety:

=over 4

=item * B<Shared Variables>

There are no shared variables that are modified at runtime in L<Module::Generic::Number>, except for a lock variable used to synchronise locale changes (see below). The global C<$DEBUG> variable (inherited from L<Module::Generic>) is typically set before threads are created, and it is the user's responsibility to ensure thread-safety if modified at runtime:

    use threads;
    local $Module::Generic::Number::DEBUG = 0; # Set before threads
    my @threads = map
    {
        threads->create(sub
        {
            my $num = Module::Generic::Number->new(1000);
            $num->format; # Thread-safe
        });
    } 1..5;
    $_->join for( @threads );

=item * B<Object State>

Number data (e.g., C<_number>, formatting properties like C<thousands_sep>) is stored per-object, ensuring thread isolation:

    use threads;
    my @threads = map
    {
        threads->create(sub
        {
            my $num = Module::Generic::Number->new(1000);
            $num += threads->tid; # Thread-safe
            $num->format; # Thread-safe
        });
    } 1..5;
    $_->join for( @threads );

=item * B<Locale Handling>

The L</init> method uses L<POSIX/setlocale> to retrieve locale-specific formatting properties when a C<lang> option is specified or when detecting the system's locale. This modifies process-wide state, which could potentially cause race conditions in a multi-threaded environment. To address this, the module uses a shared lock variable (C<$LOCALE_LOCK>) to synchronise access during locale changes, ensuring thread-safety:

    use threads;
    my @threads = map
    {
        threads->create(sub
        {
            my $num = Module::Generic::Number->new( 1000, lang => 'fr_FR' );
            $num->format; # Thread-safe
        });
    } 1..5;
    $_->join for( @threads );

The lock ensures that only one thread can modify the locale at a time, preventing interference with other threads.

=item * B<External Libraries>

This module relies on L<POSIX> functions (e.g., L<POSIX/atan>, L<POSIX/isinf>) for mathematical operations, which are thread-safe as they operate on local data.

=item * B<Serialisation>

Serialisation methods (L</FREEZE>, L</THAW>) operate on per-object state, making them thread-safe.

=back

For debugging in threaded environments (depending on your Operating System):

    ls -l /proc/$$/fd  # List open file descriptors

=head1 SEE ALSO

L<Module::Generic::Scalar>, L<Module::Generic::Array>, L<Module::Generic::Boolean>, L<Module::Generic::Hash>, L<Module::Generic::Dynamic>

L<Math::BigInt>

L<POSIX/localeconv>, L<perllocale/"The localeconv function">

=head1 AUTHOR

Jacques Deguest E<lt>F<jack@deguest.jp>E<gt>

=head1 COPYRIGHT & LICENSE

Copyright (c) 2000-2024 DEGUEST Pte. Ltd.

You can use, copy, modify and redistribute this package and associated
files under the same terms as Perl itself.

=cut

