Increment Tax
with Raku

by Arne Sommer

Increment Tax with Raku

[345] Published 1. June 2025.

This is my response to The Weekly Challenge #323.

Challenge #323.1: Increment Decrement

You are given a list of operations.

Write a script to return the final value after performing the given operations in order. The initial value is always 0.

Possible Operations:
  • ++x or x++: increment by 1
  • --x or x--: decrement by 1
Example 1:
Input: @operations = ("--x", "x++", "x++")
Output: 1

Operation "--x" =>  0 - 1 => -1
Operation "x++" => -1 + 1 =>  0
Operation "x++" =>  0 + 1 =>  1
Example 2:
Input: @operations = ("x++", "++x", "x++")
Output: 3
Example 3:
Input: @operations = ("x++", "++x", "--x", "x--")
Output: 0

Operation "x++" => 0 + 1 => 1
Operation "++x" => 1 + 1 => 2
Operation "--x" => 2 - 1 => 1
Operation "x--" => 1 - 1 => 0
File: increment-decrement
#! /usr/bin/env raku

unit sub MAIN (*@operations where @operations.elems > 0                 # [1]
                && all(@operations) eq any('++x', 'x++', '--x', 'x--'), # [1a]
               :v(:$verbose));

my %val = ( '++x' => 1, 'x++' => 1, '--x' => -1, 'x--' => -1 );         # [2]

my @val = @operations.map({ %val{$_} });                                # [3]

say ": Values: { @val.join(", ") }" if $verbose;

say @val.sum;                                                           # [4]

[1] A slurpy array, with at least one element. The values must be one of the four legal operations.

[2] A hash mapping the operations to the values they represent.

[3] Use map to convert each operation to the value.

See docs.raku.org/routine/map for more information about map.

[4] Add the values together with sum, and print the result.

See docs.raku.org/routine/sum for more information about sum.

Running it:

$ ./increment-decrement -- "--x" "x++" "x++"
1

$ ./increment-decrement "x++" "++x" "x++"
3

$ ./increment-decrement "x++" "++x" "--x" "x--"
0

Looking good.

With verbose mode:

$ ./increment-decrement -v -- "--x" "x++" "x++"
: Values: -1, 1, 1
1

$ ./increment-decrement -v "x++" "++x" "x++"
: Values: 1, 1, 1
3

$ ./increment-decrement -v "x++" "++x" "--x" "x--"
: Values: 1, 1, -1, -1
0

Challenge #323.2: Tax Amount

You are given an income amount and tax brackets.

Write a script to calculate the total tax amount.

Example 1:
Input: $income = 10, @tax = ([3, 50], [7, 10], [12,25])
Output: 1.65

1st tax bracket upto  3, tax is 50%.
2nd tax bracket upto  7, tax is 10%.
3rd tax bracket upto 12, tax is 25%.

Total Tax => (3 * 50/100) + (4 * 10/100) + (3 * 25/100)
          => 1.50 + 0.40 + 0.75
          => 2.65
Example 2:
Input: $income = 2, @tax = ([1, 0], [4, 25], [5,50])
Output: 0.25

Total Tax => (1 * 0/100) + (1 * 25/100)
          => 0 + 0.25
          => 0.25
Example 3:
Input: $income = 0, @tax = ([2, 50])
Output: 0

The program iterates over the brackets. It calculates the income and tax for each one, adding the bracket tax to the total tax, and subtracting the bracket income from the total income. It stops when it has run out of income to tax, even if we have additional brackets.

File: tax-amount
unit sub MAIN (UInt $income is copy,                          # [1]
               *@tax-brackets where @tax-brackets.elems > 0,  # [2]
               :v(:$verbose));

my @tax = @tax-brackets>>.words;                              # [3]

my @limits   = @tax[*;0];                                     # [4]
my @percents = @tax[*;1];                                     # [5]

die "Overlapping brackets" unless [<] @limits;                # [6]

die "Wrong percentage values (0..100 only)"
  unless all(@percents) ~~ Numeric                            # [7]
       && all(@percents) >= 0                                 # [7a]
       && all(@percents) <= 100;                              # [7b]

my $i   = 0;                                                  # [8]
my $tax = 0;                                                  # [9]

for @tax -> ($at, $pct)                                       # [10]
{
  last if $income <= 0;                                       # [11]

  my $bracket = $at - $i;                                     # [12]
  my $add = min($bracket, $income) * $pct / 100;              # [13]

  say ": Add [{ $i +1 } .. $at]: $bracket @ $pct% = $add" if $verbose;

  $i = $at;                                                   # [14]

  $tax += $add;                                               # [15]
  $income -= $bracket;                                        # [16]
}

say $tax;                                                     # [17]

[1] The income as an unsigned integer, i.e. zero and above. Note the is copy, so that we can change it (in [16]).

See docs.raku.org/routine/UInt for more information about UInt.

See docs.raku.org/type/Parameter#method_copy for more information about is copy.

[2] A slurpy array of tax brackets, with at least one element. Each value is the limit and percentage, separated by a space.

[3] Unwrap the tax brackets. This gives a two dimentional array, as in the examples.

[4] Get the limits, the first element in the subarrays.

[5] Get the percentages, the second element in the subarrays.

[6] Ensure that the limits come in an orderly fashion, using the Reduction Metaoperator [] in combination with <.

See docs.raku.org/language/operators#Reduction_metaoperators for more information about the Reduction Metaoperator [].

[7] Ensure that the percentage values are between 0 and 100, both included.

[8] The current starting point, in monetary units.

[9] The total tax to pay will end up here.

[10] Iterate over the brackets, assigning the two subarray parts to individial variables.

[11] Exit the loop if we have no more income to tax.

[12] The taxable income in this bracket.

[13] The amount of tax to add, by applying the percentage. Note that we have to tax the amount only, so stop if we run out of taxable income.

[14] The start of the next bracket, ready for the next iteration.

[15] Add the tax for this bracket to the total.

[16] Subract the income taxed in this bracet, ready for the next iteration.

[17] Print the result.

Running it:

$ ./tax-amount 10 "3 50" "7 10" "12 25"
2.65

$ ./tax-amount 2 "1 0" "4 25" "5 50"
0.25

$ ./tax-amount 0 "2 50"
0

Looking good.

With verbose mode:

$ ./tax-amount -v 10 "3 50" "7 10" "12 25"
: Add [1 .. 3]: 3 @ 50% = 1.5
: Add [4 .. 7]: 4 @ 10% = 0.4
: Add [8 .. 12]: 5 @ 25% = 0.75
2.65

$ ./tax-amount -v 2 "1 0" "4 25" "5 50"
: Add [1 .. 1]: 1 @ 0% = 0
: Add [2 .. 4]: 3 @ 25% = 0.25
0.25

$ ./tax-amount -v 0 "2 50"
0

And that's it.