### ----------------------------------------------------
### -- Intl::Format::Number
### -- Licenses: Artistic-2.0
### -- Authors: Matthew Stephen Stuckwisch
### -- File: lib/Intl/Format/Number.rakumod
### ----------------------------------------------------
### -- Chunk 3 of 3
 )
        !! Empty;
}


sub rast-integer-grouping-FORSIG( \format,\symbols) {
#`<<<
    # NOOP if $primary-grouping-size ≤ 0
    # ELSE
    #   my $grouping-location = PRIMARY-GROUPING-SIZE;
    #   while $grouping-location > 0 {
    #     $result = $result.substr(0,$grouping-location) ~ GROUP-SYMBOL ~ $result.substr($grouping-location))
    format<primary-grouping-size> > 0
        ?? (RakuAST::Statement::Expression.new(
                expression => RakuAST::VarDeclaration::Simple.new( :scope<my>, :name<$grouping-location>,
                    initializer => RakuAST::Initializer::Bind.new(
                        RakuAST::ApplyInfix.new(
                            #left => RakuAST::Var::Lexical.new('$completed-digits'),
                            left => RakuAST::ApplyPostfix.new( :operand(Result),
                                postfix => RakuAST::Call::Method.new( RakuAST::Name.from-identifier('chars'))
                            ),
                            infix => Minus,
                            right => RakuAST::IntLiteral.new(format<primary-grouping-size>)
                        )
                    )
                )
            ),
            RakuAST::Statement::Loop::While.new(
                condition => RakuAST::ApplyInfix.new( :right(One), :infix(LessThan),
                    left => RakuAST::Var::Lexical.new('$grouping-location')
                ),
                body => block-blockoid-statement-list(
                    RakuAST::Statement::Expression.new(
                        RakuAST::ApplyInfix.new( :left(Result), :infix(Assign),
                            right => RakuAST::ApplyListInfix.new(
                                infix => Concat,
                                operands => (
                                    RakuAST::ApplyPostfix.new(operand => Result,
                                        postfix => RakuAST::Call::Method.new(
                                            name => RakuAST::Name.from-identifier('substr'),
                                            args => RakuAST::ArgList.new((Zero,RakuAST::Var::Lexical.new('$grouping-location')))
                                        )
                                    ),
                                    RakuAST::StrLiteral.new(symbols<group>),
                                    RakuAST::ApplyPostfix.new(operand => Result,
                                        postfix => RakuAST::Call::Method.new(
                                            name => RakuAST::Name.from-identifier('substr'),
                                            args => RakuAST::ArgList.new(RakuAST::Var::Lexical.new('$grouping-location'))
                                        )
                                    ),

                                )
                                ))),
            RakuAST::Statement::Expression.new(
                expression => RakuAST::ApplyInfix.new(
                    left => RakuAST::ApplyInfix.new(
                        left => Result,
                        infix => Assign,
                        right => RakuAST::ApplyListInfix.new(
                            infix => Concat,
                            operands => (
                                RakuAST::ApplyPostfix.new(operand => Result,
                                    postfix => RakuAST::Call::Method.new(
                                        name => RakuAST::Name.from-identifier('substr'),
                                        args => RakuAST::ArgList.new((Zero,RakuAST::Var::Lexical.new('$grouping-location')))
                                    )
                                ),
                                RakuAST::StrLiteral.new(symbols.group),
                                RakuAST::ApplyPostfix.new(operand => Result,
                                    postfix => RakuAST::Call::Method.new(
                                        name => RakuAST::Name.from-identifier('substr'),
                                        args => RakuAST::ArgList.new(Zero)
                                    )
                                ),
                            ),
                        )
                    ),
                    infix => RakuAST::Infix.new('and'),
                    right => RakuAST::ApplyInfix.new(
                        left => RakuAST::ApplyInfix.new(
                            left => RakuAST::Var::Lexical.new('$grouping-location'),
                            infix => Assign,
                            right => RakuAST::ApplyInfix.new(
                                left => RakuAST::Var::Lexical.new('$grouping-location'),
                                infix => Minus,
                                right => RakuAST::IntLiteral.new(pattern<secondary-grouping-size>)
                            )
                        ),
                        infix => RakuAST::Infix.new('and'),
                        right => RakuAST::ApplyInfix.new(
                            left => RakuAST::Var::Lexical.new('$completed-digits'),
                            infix => Assign,
                            right => RakuAST::ApplyInfix.new( left =>RakuAST::Var::Lexical.new('$completed-digits'), :infix(Plus), :right(One))
                        )
                    )
                ),
                loop-modifier => RakuAST::StatementModifier::While.new(
                    RakuAST::ApplyInfix.new( :left(RakuAST::Var::Lexical.new: '$grouping-location'), :infix(LessThan), :right(Zero))
                )
            )>>>
        #)
        #!! Empty;
}

multi sub rast-transliterate ($?, :$ordhyper!) {
    # $result.ords.map(* + 49)>>.chr.join
    RakuAST::Sub.new( :scope<my>,
        name => RakuAST::Name.from-identifier('transliterate'),
        signature => RakuAST::Signature.new(
            parameters => RakuAST::Parameter.new(
                target => RakuAST::ParameterTarget::Var.new('$source'),
                traits => (RakuAST::Trait::Is.new( name => RakuAST::Name.from-identifier('raw')), )
        )   ),
        body => RakuAST::Blockoid.new( RakuAST::StatementList.new(
            RakuAST::Statement::Expression.new( expression =>
                RakuAST::ApplyPostfix.new( postfix => RakuAST::Call::Method.new(name => RakuAST::Name.from-identifier('join')),
                    operand => RakuAST::ApplyPostfix.new(
                        postfix => RakuAST::Call::Method.new(
                            name => RakuAST::Name.from-identifier('map'),
                            args => RakuAST::ArgList.new(
                                RakuAST::Block.new( body => RakuAST::Blockoid.new(
                                    RakuAST::StatementList.new(
                                        RakuAST::Statement::Expression.new(
                                            expression => RakuAST::ApplyPostfix.new(
                                                postfix => RakuAST::Call::Method.new(name => RakuAST::Name.from-identifier('chr')),
                                                operand => RakuAST::ApplyInfix.new( :infix(Plus),
                                                    left => RakuAST::Var::Lexical.new('$_'),
                                                    right => RakuAST::IntLiteral.new($*zero.ord - '0'.ord),
                        )   )   )), )   )   )   ),
                        operand => RakuAST::ApplyPostfix.new(
                            operand => RakuAST::Var::Lexical.new('$source'),
                            postfix => RakuAST::Call::Method.new(name => RakuAST::Name.from-identifier('ords'))
                        )))))))
}

# Creates a sub that transliterates a given string to a different decimal system
multi sub rast-transliterate($number-system, :$nqp!) {
    # This is currently the fastest (regardless of length) version I can come up with
    # It does require ALL codes to be in the convertable range.
    # The value passed into zero should indicate what the Unicode decimal zero is
    #
    # sub transliterate($source is raw) {
    #     my int32 @temp;
    #     nqp::strtocodes($source, nqp::const::NORMALIZE_NFC, @temp);
    #     my int32 i = nqp::elems(@temp);
    #     nqp::while(
    #         ($temp2 = nqp::sub_i($i,1)),
    #         {
    #             nqp::bindpos_i(
    #                 @temp, $i,
    #                 nqp::add_i(nqp::atpos_i(@temp,$i),$adj)
    #             )
    #         }
    #     )
    #     nqp::strfromcodes(@temp);
    # }
    return Empty if $number-system eq 'latn';
    my $adjust = %digit-offset{$number-system} - 48; # 48 is '0' in ASCII/Unicode
    use nqp;
    RakuAST::Statement::Expression.new( expression =>
        RakuAST::Sub.new(
            name => RakuAST::Name.from-identifier('transliterate'),
            signature => RakuAST::Signature.new(
                parameters =>  (
                    RakuAST::Parameter.new(
                        target =>  RakuAST::ParameterTarget::Var.new('$source'),
                        traits => (RakuAST::Trait::Is.new( name => RakuAST::Name.from-identifier('raw') ),)
            ),  )   ),
            body => blockoid-statement-list(
                RakuAST::Statement::Expression.new( expression =>
                    RakuAST::VarDeclaration::Simple.new(
                        name => '@temp',
                        type => RakuAST::Type::Simple.new( RakuAST::Name.from-identifier('int32') ),
                )   ),
                RakuAST::Statement::Expression.new( expression =>
                    RakuAST::Nqp.new(
                        'strtocodes',
                        RakuAST::Var::Lexical.new('$source'),
                        RakuAST::Nqp::Const.new('NORMALIZE_NFC'),
                        RakuAST::Var::Lexical.new('@temp'),
                )   ),
                RakuAST::Statement::Expression.new( expression =>
                    RakuAST::VarDeclaration::Simple.new(
                        name => '$i',
                        type => RakuAST::Type::Simple.new( RakuAST::Name.from-identifier('int32') ),
                        initializer => RakuAST::Initializer::Assign.new(
                            RakuAST::Nqp.new(
                                'elems',
                                RakuAST::Var::Lexical.new('@temp')
                )   )   )   ),
                RakuAST::Statement::Expression.new( expression =>
                    RakuAST::Nqp.new(
                        'while',
                        RakuAST::SemiList.new(
                            RakuAST::Statement::Expression.new( expression =>
                                RakuAST::ApplyInfix.new(
                                    left => RakuAST::Var::Lexical.new('$i'),
                                    infix => RakuAST::Infix.new('='),
                                    right => RakuAST::Nqp.new(
                                        'sub_i',
                                        RakuAST::Var::Lexical.new('$i'),
                                        RakuAST::IntLiteral.new(1),
                        )   )   )   ),
                        RakuAST::Nqp.new(
                            'bindpos_i',
                            RakuAST::Var::Lexical.new('@temp'),
                            RakuAST::Var::Lexical.new('$i'),
                            RakuAST::Nqp.new(
                                'add_i',
                                RakuAST::Nqp.new('atpos_i',RakuAST::Var::Lexical.new('@temp'),RakuAST::Var::Lexical.new('$i')),
                                RakuAST::IntLiteral.new($adjust), #RakuAST::Var::Lexical.new('$adj'),
                )   )   )   ),
                RakuAST::Statement::Expression.new( expression =>
                    RakuAST::Nqp.new(
                        'bindpos_i',
                        RakuAST::Var::Lexical.new('@temp'),
                        RakuAST::Var::Lexical.new('$i'),
                        RakuAST::Nqp.new(
                            'add_i',
                            RakuAST::Nqp.new('atpos_i',RakuAST::Var::Lexical.new('@temp'),Zero),
                            RakuAST::IntLiteral.new($adjust), #RakuAST::Var::Lexical.new('$adj'),
                )   )   ),
                RakuAST::Statement::Expression.new( expression =>
                    RakuAST::Call::Name.new(
                        name => RakuAST::Name.from-identifier('return'),
                        args => RakuAST::ArgList.new(RakuAST::Nqp.new('strfromcodes',RakuAST::Var::Lexical.new('@temp')))
    )   )   )   )   )
}

#| Declares a reusable sub to determine the magnitude of a number
sub rast-magnitude-declare is pure {
    # Equivalent to $x.log10.ceiling, but without the float inaccuracies that
    # are a major headache where $x ≈ 10ⁿ
    #
    # sub magnitude(Any $x is raw) {
    #     return 1 if $x == 0;
    #     my $n := $x;
    #     my $r := 1;
    #     my $c := 1;
    #     while $n <                1 { $r -= 8; $n *= 1_0000_0000 }
    #     while $n ≥ $c * 1_0000_0000 { $r += 8; $c *= 1_0000_0000 }
    #     while $n ≥ $c *      1_0000 { $r += 4; $c *=      1_0000 }
    #     while $n ≥ $c *          10 { $r += 1; $c *=          10 }
    #     return $r
    # }
    #
    # Magnitude is defined in $r which effectively tracks how many digits we've shifted
    # each way. Multiply until $n is over 1, reducing $r according, to shift us into the
    # positive. Then adjust a comparison value $c (multiplying $c is faster than dividing
    # $n) to until it's in the same magnitude as $n, adjusting $r accordingly.

    RakuAST::Statement::Expression.new(
        expression => RakuAST::Sub.new(
            name      => RakuAST::Name.from-identifier("magnitude"),
            signature => RakuAST::Signature.new(
                parameters => (
                    RakuAST::Parameter.new(
                        type   => RakuAST::Type::Simple.new(RakuAST::Name.from-identifier("Any")),
                        target => RakuAST::ParameterTarget::Var.new('$x'),
                        traits => (RakuAST::Trait::Is.new(name => RakuAST::Name.from-identifier("raw")),)
            ),  )   ),
            body => blockoid-statement-list(
                RakuAST::Statement::Expression.new(
                    expression => RakuAST::Call::Name.new(
                        name => RakuAST::Name.from-identifier("return"),
                        args => RakuAST::ArgList.new(One)
                    ),
                    condition-modifier => RakuAST::StatementModifier::If.new(
                        RakuAST::ApplyInfix.new(
                            left  => Var_X,
                            infix => NumEqual,
                            right => Zero
                )   )   ),
                RakuAST::Statement::Expression.new(
                    expression => RakuAST::VarDeclaration::Simple.new(
                        name        => '$n',
                        initializer => RakuAST::Initializer::Bind.new(Var_X)
                )   ),
                RakuAST::Statement::Expression.new(
                    expression => RakuAST::VarDeclaration::Simple.new(
                        name        => '$r',
                        initializer => RakuAST::Initializer::Bind.new(One)
                )   ),
                RakuAST::Statement::Expression.new(
                    expression => RakuAST::VarDeclaration::Simple.new(
                        name        => '$c',
                        initializer => RakuAST::Initializer::Bind.new(One)
                )   ),
                RakuAST::Statement::Loop::While.new(
                    condition => RakuAST::ApplyInfix.new(:left(Var_N),:infix(LessThan),:right(One)),
                    body      => block-blockoid-statement-list(
                        RakuAST::Statement::Expression.new(
                            expression => RakuAST::ApplyInfix.new(:left(Var_R), :infix(Assign),
                                right => RakuAST::ApplyInfix.new(:left(Var_R),:infix(Minus),:right(RakuAST::IntLiteral.new(8)))
                        )   ),
                        RakuAST::Statement::Expression.new(
                            expression => RakuAST::ApplyInfix.new(:left(Var_N),:infix(Assign),
                                right => RakuAST::ApplyInfix.new(:left(Var_N),:infix(Times),:right(RakuAST::IntLiteral.new(100000000)))
                )   )   )   ),
                RakuAST::Statement::Expression.new(
                    expression => RakuAST::ApplyInfix.new(:left(Var_N),:infix(Assign),
                        right => RakuAST::ApplyPostfix.new(:operand(Var_N),:postfix(MakeInt))
                )   ),
                RakuAST::Statement::Loop::While.new(
                    condition => RakuAST::ApplyInfix.new(:left(Var_N),:infix(GreaterEqual),
                        right => RakuAST::ApplyInfix.new(:left(Var_C),:infix(Times),:right(RakuAST::IntLiteral.new(100000000)))
                    ),
                    body => block-blockoid-statement-list(
                        RakuAST::Statement::Expression.new(
                            expression => RakuAST::ApplyInfix.new(:left(Var_R),:infix(Assign),
                                right => RakuAST::ApplyInfix.new(:left(Var_R),:infix(Plus),:right(RakuAST::IntLiteral.new(8)))
                        )   ),
                        RakuAST::Statement::Expression.new(
                            expression => RakuAST::ApplyInfix.new(:left(Var_C),:infix(Assign),
                                right => RakuAST::ApplyInfix.new(:left(Var_C),:infix(Times),:right(RakuAST::IntLiteral.new(100000000)))
                )   )   )   ),
                RakuAST::Statement::Loop::While.new(
                    condition => RakuAST::ApplyInfix.new(:left(Var_N),:infix(GreaterEqual),
                        right => RakuAST::ApplyInfix.new(:left(Var_C),:infix(Times),:right(RakuAST::IntLiteral.new(10000)))
                    ),
                    body => block-blockoid-statement-list(
                        RakuAST::Statement::Expression.new(
                            expression => RakuAST::ApplyInfix.new(:left(Var_R),:infix(Assign),
                                right => RakuAST::ApplyInfix.new(:left(Var_R),:infix(Plus),:right(RakuAST::IntLiteral.new(4)))
                        )   ),
                        RakuAST::Statement::Expression.new(
                            expression => RakuAST::ApplyInfix.new(:left(Var_C),:infix(Assign),
                                right => RakuAST::ApplyInfix.new(:left(Var_C),:infix(Times),:right(RakuAST::IntLiteral.new(10000)))
                )   )   )   ),
                RakuAST::Statement::Loop::While.new(
                    condition => RakuAST::ApplyInfix.new(:left(Var_N),:infix(GreaterEqual),
                        right => RakuAST::ApplyInfix.new(:left(Var_C),:infix(Times),:right(Ten))
                    ),
                    body => block-blockoid-statement-list(
                        RakuAST::Statement::Expression.new(
                            expression => RakuAST::ApplyInfix.new(:left(Var_R),:infix(Assign),
                                right => RakuAST::ApplyInfix.new(:left(Var_R),:infix(Plus),:right(One))
                        )   ),
                        RakuAST::Statement::Expression.new(
                            expression => RakuAST::ApplyInfix.new(:left(Var_C),:infix(Assign),
                                right => RakuAST::ApplyInfix.new(:left(Var_C),:infix(Times),:right(Ten))
                )   )   )   ),
                RakuAST::Statement::Expression.new(
                    expression => RakuAST::Call::Name.new(
                        name => RakuAST::Name.from-identifier("return"),
                        args => RakuAST::ArgList.new(Var_R)
    )   )   )   )   );
}

#| Calls the magnitude sub, storing results in `$power`
sub rast-magnitude is pure {
    # my $power = magnitude($n);
    RakuAST::Statement::Expression.new(
        expression => RakuAST::VarDeclaration::Simple.new( :scope<my>, :name<$power>,
            initializer => RakuAST::Initializer::Assign.new(
                RakuAST::Call::Name.new(
                    name => RakuAST::Name.from-identifier('magnitude'),
                    args => RakuAST::ArgList.new(Var_N)
    )   )   )   )
}


