Quadruple Copies
with Raku

by Arne Sommer

Quadruple Copies with Raku

[223] Published 11. February 2023.

This is my response to The Weekly Challenge #203.

Challenge #203.1: Special Quadruplets

You are given an array of integers.

Write a script to find out the total special quadruplets for the given array.

Special Quadruplets are such that satisfies the following 2 rules.
  1. nums[a] + nums[b] + nums[c] == nums[d]
  2. a < b < c < d
Example 1:
Input: @nums = (1,2,3,6)
Output: 1

Since the only special quadruplets found is
  $nums[0] + $nums[1] + $nums[2] == $nums[3].
Example 2:
Input: @nums = (1,1,1,3,5)
Output: 4

$nums[0] + $nums[1] + $nums[2] == $nums[3]
$nums[0] + $nums[1] + $nums[3] == $nums[4]
$nums[0] + $nums[2] + $nums[3] == $nums[4]
$nums[1] + $nums[2] + $nums[3] == $nums[4]
Example 3:
Input: @nums = (3,3,6,4,5)
Output: 0
File: special-quadruplets
#! /usr/bin/env raku

unit sub MAIN (*@nums where @nums.elems >= 4              # [1]
	         && all(@nums) ~~ /^<[0..9]>*$/,          # [1a]
	       :v(:$verbose)); 

my @combinations = (^@nums.elems).combinations(4);        # [2]

say ": Combinations: { \
  @combinations.map({ "(" ~ $_.join(", ") ~ ")" }).join(", ") }"
    if $verbose;

my $specials = 0;                                         # [3]

for @combinations -> @candidate                           # [4]
{
  for @candidate.permutations -> ($a, $b, $c, $d)         # [5]
  { 
    unless $a < $b < $c < $d                              # [6]
    {
      say ": a:$a b:$b c:$c d:$d - Not OK (Rule 2)" if $verbose;
      next                                                # [6a]
    }

    unless @nums[$a] + @nums[$b] + @nums[$c] == @nums[$d]  # [7]
    {
      say ": a:$a b:$b c:$c d:$d - Not OK (Rule 1)" if $verbose;
      next;                                                # [7a]
    }

    $specials++;                                           # [8]
    say ": a:$a b:$b c:$c d:$d - OK (Rule 1+2)" if $verbose;
  }
}

say $specials;                                             # [9]

[1] We must have at least 4 elements in the array, and I have chosen to restrict them to non-negative integers [1a].

[2] The «a», «b», «c» and «d» values are indices. We use combinations(4) to get all possible combinations of 4 of those. (All the indices are given by ^@nums.elems). The result is in effect a Set, so the order is irrelevant (e.g. we get the indices (0,1,2,3) for the first example, and only that).

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

[3] The number of Special Quadruplets; none initially.

[4] Iterate over the combinations (from [2]).

[5] Then we use permutations to shuffle the values. (E.g. a shorter (1,2,3) candidate would give the (1,2,3), (1,3,2), (2,1,3), (2,3,1), (3,1,2), (3,2,1) permutations)

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

[6] This is the second rule check. Note the chained comparisons. Skip the candidate if it is not met [6a].

[7] This is the first rule check. Skip the candidate if it is not met [7a].

[8] Both rules are met. Increase the count (from [3]).

[9] Print the count.

Running it:

$ ./special-quadruplets 1 2 3 6
1

$ ./special-quadruplets 1 1 1 3 5
4

$ ./special-quadruplets 2 3 6 4 5
0

Looking good.

With verbose mode, and note the first line showing the combinations:

$ ./special-quadruplets -v 1 2 3 6
: Combinations: (0, 1, 2, 3)
: a:0 b:1 c:2 d:3 - OK (Rule 1+2)
: a:0 b:1 c:3 d:2 - Not OK (Rule 2)
: a:0 b:2 c:1 d:3 - Not OK (Rule 2)
: a:0 b:2 c:3 d:1 - Not OK (Rule 2)
: a:0 b:3 c:1 d:2 - Not OK (Rule 2)
: a:0 b:3 c:2 d:1 - Not OK (Rule 2)
: a:1 b:0 c:2 d:3 - Not OK (Rule 2)
: a:1 b:0 c:3 d:2 - Not OK (Rule 2)
: a:1 b:2 c:0 d:3 - Not OK (Rule 2)
: a:1 b:2 c:3 d:0 - Not OK (Rule 2)
: a:1 b:3 c:0 d:2 - Not OK (Rule 2)
: a:1 b:3 c:2 d:0 - Not OK (Rule 2)
: a:2 b:0 c:1 d:3 - Not OK (Rule 2)
: a:2 b:0 c:3 d:1 - Not OK (Rule 2)
: a:2 b:1 c:0 d:3 - Not OK (Rule 2)
: a:2 b:1 c:3 d:0 - Not OK (Rule 2)
: a:2 b:3 c:0 d:1 - Not OK (Rule 2)
: a:2 b:3 c:1 d:0 - Not OK (Rule 2)
: a:3 b:0 c:1 d:2 - Not OK (Rule 2)
: a:3 b:0 c:2 d:1 - Not OK (Rule 2)
: a:3 b:1 c:0 d:2 - Not OK (Rule 2)
: a:3 b:1 c:2 d:0 - Not OK (Rule 2)
: a:3 b:2 c:0 d:1 - Not OK (Rule 2)
: a:3 b:2 c:1 d:0 - Not OK (Rule 2)
1
$ ./special-quadruplets -v 1 1 1 3 5
: Combinations: (0, 1, 2, 3), (0, 1, 2, 4), (0, 1, 3, 4), (0, 2, 3, 4),\
  (1, 2, 3, 4)
: a:0 b:1 c:2 d:3 - OK (Rule 1+2)
: a:0 b:1 c:3 d:2 - Not OK (Rule 2)
: a:0 b:2 c:1 d:3 - Not OK (Rule 2)
: a:0 b:2 c:3 d:1 - Not OK (Rule 2)
: a:0 b:3 c:1 d:2 - Not OK (Rule 2)
: a:0 b:3 c:2 d:1 - Not OK (Rule 2)
: a:1 b:0 c:2 d:3 - Not OK (Rule 2)
: a:1 b:0 c:3 d:2 - Not OK (Rule 2)
: a:1 b:2 c:0 d:3 - Not OK (Rule 2)
: a:1 b:2 c:3 d:0 - Not OK (Rule 2)
: a:1 b:3 c:0 d:2 - Not OK (Rule 2)
: a:1 b:3 c:2 d:0 - Not OK (Rule 2)
: a:2 b:0 c:1 d:3 - Not OK (Rule 2)
: a:2 b:0 c:3 d:1 - Not OK (Rule 2)
: a:2 b:1 c:0 d:3 - Not OK (Rule 2)
: a:2 b:1 c:3 d:0 - Not OK (Rule 2)
: a:2 b:3 c:0 d:1 - Not OK (Rule 2)
: a:2 b:3 c:1 d:0 - Not OK (Rule 2)
: a:3 b:0 c:1 d:2 - Not OK (Rule 2)
: a:3 b:0 c:2 d:1 - Not OK (Rule 2)
: a:3 b:1 c:0 d:2 - Not OK (Rule 2)
: a:3 b:1 c:2 d:0 - Not OK (Rule 2)
: a:3 b:2 c:0 d:1 - Not OK (Rule 2)
: a:3 b:2 c:1 d:0 - Not OK (Rule 2)
: a:0 b:1 c:2 d:4 - Not OK (Rule 1)
: a:0 b:1 c:4 d:2 - Not OK (Rule 2)
: a:0 b:2 c:1 d:4 - Not OK (Rule 2)
: a:0 b:2 c:4 d:1 - Not OK (Rule 2)
: a:0 b:4 c:1 d:2 - Not OK (Rule 2)
: a:0 b:4 c:2 d:1 - Not OK (Rule 2)
: a:1 b:0 c:2 d:4 - Not OK (Rule 2)
: a:1 b:0 c:4 d:2 - Not OK (Rule 2)
: a:1 b:2 c:0 d:4 - Not OK (Rule 2)
: a:1 b:2 c:4 d:0 - Not OK (Rule 2)
: a:1 b:4 c:0 d:2 - Not OK (Rule 2)
: a:1 b:4 c:2 d:0 - Not OK (Rule 2)
: a:2 b:0 c:1 d:4 - Not OK (Rule 2)
: a:2 b:0 c:4 d:1 - Not OK (Rule 2)
: a:2 b:1 c:0 d:4 - Not OK (Rule 2)
: a:2 b:1 c:4 d:0 - Not OK (Rule 2)
: a:2 b:4 c:0 d:1 - Not OK (Rule 2)
: a:2 b:4 c:1 d:0 - Not OK (Rule 2)
: a:4 b:0 c:1 d:2 - Not OK (Rule 2)
: a:4 b:0 c:2 d:1 - Not OK (Rule 2)
: a:4 b:1 c:0 d:2 - Not OK (Rule 2)
: a:4 b:1 c:2 d:0 - Not OK (Rule 2)
: a:4 b:2 c:0 d:1 - Not OK (Rule 2)
: a:4 b:2 c:1 d:0 - Not OK (Rule 2)
: a:0 b:1 c:3 d:4 - OK (Rule 1+2)
: a:0 b:1 c:4 d:3 - Not OK (Rule 2)
: a:0 b:3 c:1 d:4 - Not OK (Rule 2)
: a:0 b:3 c:4 d:1 - Not OK (Rule 2)
: a:0 b:4 c:1 d:3 - Not OK (Rule 2)
: a:0 b:4 c:3 d:1 - Not OK (Rule 2)
: a:1 b:0 c:3 d:4 - Not OK (Rule 2)
: a:1 b:0 c:4 d:3 - Not OK (Rule 2)
: a:1 b:3 c:0 d:4 - Not OK (Rule 2)
: a:1 b:3 c:4 d:0 - Not OK (Rule 2)
: a:1 b:4 c:0 d:3 - Not OK (Rule 2)
: a:1 b:4 c:3 d:0 - Not OK (Rule 2)
: a:3 b:0 c:1 d:4 - Not OK (Rule 2)
: a:3 b:0 c:4 d:1 - Not OK (Rule 2)
: a:3 b:1 c:0 d:4 - Not OK (Rule 2)
: a:3 b:1 c:4 d:0 - Not OK (Rule 2)
: a:3 b:4 c:0 d:1 - Not OK (Rule 2)
: a:3 b:4 c:1 d:0 - Not OK (Rule 2)
: a:4 b:0 c:1 d:3 - Not OK (Rule 2)
: a:4 b:0 c:3 d:1 - Not OK (Rule 2)
: a:4 b:1 c:0 d:3 - Not OK (Rule 2)
: a:4 b:1 c:3 d:0 - Not OK (Rule 2)
: a:4 b:3 c:0 d:1 - Not OK (Rule 2)
: a:4 b:3 c:1 d:0 - Not OK (Rule 2)
: a:0 b:2 c:3 d:4 - OK (Rule 1+2)
: a:0 b:2 c:4 d:3 - Not OK (Rule 2)
: a:0 b:3 c:2 d:4 - Not OK (Rule 2)
: a:0 b:3 c:4 d:2 - Not OK (Rule 2)
: a:0 b:4 c:2 d:3 - Not OK (Rule 2)
: a:0 b:4 c:3 d:2 - Not OK (Rule 2)
: a:2 b:0 c:3 d:4 - Not OK (Rule 2)
: a:2 b:0 c:4 d:3 - Not OK (Rule 2)
: a:2 b:3 c:0 d:4 - Not OK (Rule 2)
: a:2 b:3 c:4 d:0 - Not OK (Rule 2)
: a:2 b:4 c:0 d:3 - Not OK (Rule 2)
: a:2 b:4 c:3 d:0 - Not OK (Rule 2)
: a:3 b:0 c:2 d:4 - Not OK (Rule 2)
: a:3 b:0 c:4 d:2 - Not OK (Rule 2)
: a:3 b:2 c:0 d:4 - Not OK (Rule 2)
: a:3 b:2 c:4 d:0 - Not OK (Rule 2)
: a:3 b:4 c:0 d:2 - Not OK (Rule 2)
: a:3 b:4 c:2 d:0 - Not OK (Rule 2)
: a:4 b:0 c:2 d:3 - Not OK (Rule 2)
: a:4 b:0 c:3 d:2 - Not OK (Rule 2)
: a:4 b:2 c:0 d:3 - Not OK (Rule 2)
: a:4 b:2 c:3 d:0 - Not OK (Rule 2)
: a:4 b:3 c:0 d:2 - Not OK (Rule 2)
: a:4 b:3 c:2 d:0 - Not OK (Rule 2)
: a:1 b:2 c:3 d:4 - OK (Rule 1+2)
: a:1 b:2 c:4 d:3 - Not OK (Rule 2)
: a:1 b:3 c:2 d:4 - Not OK (Rule 2)
: a:1 b:3 c:4 d:2 - Not OK (Rule 2)
: a:1 b:4 c:2 d:3 - Not OK (Rule 2)
: a:1 b:4 c:3 d:2 - Not OK (Rule 2)
: a:2 b:1 c:3 d:4 - Not OK (Rule 2)
: a:2 b:1 c:4 d:3 - Not OK (Rule 2)
: a:2 b:3 c:1 d:4 - Not OK (Rule 2)
: a:2 b:3 c:4 d:1 - Not OK (Rule 2)
: a:2 b:4 c:1 d:3 - Not OK (Rule 2)
: a:2 b:4 c:3 d:1 - Not OK (Rule 2)
: a:3 b:1 c:2 d:4 - Not OK (Rule 2)
: a:3 b:1 c:4 d:2 - Not OK (Rule 2)
: a:3 b:2 c:1 d:4 - Not OK (Rule 2)
: a:3 b:2 c:4 d:1 - Not OK (Rule 2)
: a:3 b:4 c:1 d:2 - Not OK (Rule 2)
: a:3 b:4 c:2 d:1 - Not OK (Rule 2)
: a:4 b:1 c:2 d:3 - Not OK (Rule 2)
: a:4 b:1 c:3 d:2 - Not OK (Rule 2)
: a:4 b:2 c:1 d:3 - Not OK (Rule 2)
: a:4 b:2 c:3 d:1 - Not OK (Rule 2)
: a:4 b:3 c:1 d:2 - Not OK (Rule 2)
: a:4 b:3 c:2 d:1 - Not OK (Rule 2)
4
$ ./special-quadruplets -v 2 3 6 4 5
: Combinations: (0, 1, 2, 3), (0, 1, 2, 4), (0, 1, 3, 4), (0, 2, 3, 4), \
  (1, 2, 3, 4)
: a:0 b:1 c:2 d:3 - Not OK (Rule 1)
: a:0 b:1 c:3 d:2 - Not OK (Rule 2)
: a:0 b:2 c:1 d:3 - Not OK (Rule 2)
: a:0 b:2 c:3 d:1 - Not OK (Rule 2)
: a:0 b:3 c:1 d:2 - Not OK (Rule 2)
: a:0 b:3 c:2 d:1 - Not OK (Rule 2)
: a:1 b:0 c:2 d:3 - Not OK (Rule 2)
: a:1 b:0 c:3 d:2 - Not OK (Rule 2)
: a:1 b:2 c:0 d:3 - Not OK (Rule 2)
: a:1 b:2 c:3 d:0 - Not OK (Rule 2)
: a:1 b:3 c:0 d:2 - Not OK (Rule 2)
: a:1 b:3 c:2 d:0 - Not OK (Rule 2)
: a:2 b:0 c:1 d:3 - Not OK (Rule 2)
: a:2 b:0 c:3 d:1 - Not OK (Rule 2)
: a:2 b:1 c:0 d:3 - Not OK (Rule 2)
: a:2 b:1 c:3 d:0 - Not OK (Rule 2)
: a:2 b:3 c:0 d:1 - Not OK (Rule 2)
: a:2 b:3 c:1 d:0 - Not OK (Rule 2)
: a:3 b:0 c:1 d:2 - Not OK (Rule 2)
: a:3 b:0 c:2 d:1 - Not OK (Rule 2)
: a:3 b:1 c:0 d:2 - Not OK (Rule 2)
: a:3 b:1 c:2 d:0 - Not OK (Rule 2)
: a:3 b:2 c:0 d:1 - Not OK (Rule 2)
: a:3 b:2 c:1 d:0 - Not OK (Rule 2)
: a:0 b:1 c:2 d:4 - Not OK (Rule 1)
: a:0 b:1 c:4 d:2 - Not OK (Rule 2)
: a:0 b:2 c:1 d:4 - Not OK (Rule 2)
: a:0 b:2 c:4 d:1 - Not OK (Rule 2)
: a:0 b:4 c:1 d:2 - Not OK (Rule 2)
: a:0 b:4 c:2 d:1 - Not OK (Rule 2)
: a:1 b:0 c:2 d:4 - Not OK (Rule 2)
: a:1 b:0 c:4 d:2 - Not OK (Rule 2)
: a:1 b:2 c:0 d:4 - Not OK (Rule 2)
: a:1 b:2 c:4 d:0 - Not OK (Rule 2)
: a:1 b:4 c:0 d:2 - Not OK (Rule 2)
: a:1 b:4 c:2 d:0 - Not OK (Rule 2)
: a:2 b:0 c:1 d:4 - Not OK (Rule 2)
: a:2 b:0 c:4 d:1 - Not OK (Rule 2)
: a:2 b:1 c:0 d:4 - Not OK (Rule 2)
: a:2 b:1 c:4 d:0 - Not OK (Rule 2)
: a:2 b:4 c:0 d:1 - Not OK (Rule 2)
: a:2 b:4 c:1 d:0 - Not OK (Rule 2)
: a:4 b:0 c:1 d:2 - Not OK (Rule 2)
: a:4 b:0 c:2 d:1 - Not OK (Rule 2)
: a:4 b:1 c:0 d:2 - Not OK (Rule 2)
: a:4 b:1 c:2 d:0 - Not OK (Rule 2)
: a:4 b:2 c:0 d:1 - Not OK (Rule 2)
: a:4 b:2 c:1 d:0 - Not OK (Rule 2)
: a:0 b:1 c:3 d:4 - Not OK (Rule 1)
: a:0 b:1 c:4 d:3 - Not OK (Rule 2)
: a:0 b:3 c:1 d:4 - Not OK (Rule 2)
: a:0 b:3 c:4 d:1 - Not OK (Rule 2)
: a:0 b:4 c:1 d:3 - Not OK (Rule 2)
: a:0 b:4 c:3 d:1 - Not OK (Rule 2)
: a:1 b:0 c:3 d:4 - Not OK (Rule 2)
: a:1 b:0 c:4 d:3 - Not OK (Rule 2)
: a:1 b:3 c:0 d:4 - Not OK (Rule 2)
: a:1 b:3 c:4 d:0 - Not OK (Rule 2)
: a:1 b:4 c:0 d:3 - Not OK (Rule 2)
: a:1 b:4 c:3 d:0 - Not OK (Rule 2)
: a:3 b:0 c:1 d:4 - Not OK (Rule 2)
: a:3 b:0 c:4 d:1 - Not OK (Rule 2)
: a:3 b:1 c:0 d:4 - Not OK (Rule 2)
: a:3 b:1 c:4 d:0 - Not OK (Rule 2)
: a:3 b:4 c:0 d:1 - Not OK (Rule 2)
: a:3 b:4 c:1 d:0 - Not OK (Rule 2)
: a:4 b:0 c:1 d:3 - Not OK (Rule 2)
: a:4 b:0 c:3 d:1 - Not OK (Rule 2)
: a:4 b:1 c:0 d:3 - Not OK (Rule 2)
: a:4 b:1 c:3 d:0 - Not OK (Rule 2)
: a:4 b:3 c:0 d:1 - Not OK (Rule 2)
: a:4 b:3 c:1 d:0 - Not OK (Rule 2)
: a:0 b:2 c:3 d:4 - Not OK (Rule 1)
: a:0 b:2 c:4 d:3 - Not OK (Rule 2)
: a:0 b:3 c:2 d:4 - Not OK (Rule 2)
: a:0 b:3 c:4 d:2 - Not OK (Rule 2)
: a:0 b:4 c:2 d:3 - Not OK (Rule 2)
: a:0 b:4 c:3 d:2 - Not OK (Rule 2)
: a:2 b:0 c:3 d:4 - Not OK (Rule 2)
: a:2 b:0 c:4 d:3 - Not OK (Rule 2)
: a:2 b:3 c:0 d:4 - Not OK (Rule 2)
: a:2 b:3 c:4 d:0 - Not OK (Rule 2)
: a:2 b:4 c:0 d:3 - Not OK (Rule 2)
: a:2 b:4 c:3 d:0 - Not OK (Rule 2)
: a:3 b:0 c:2 d:4 - Not OK (Rule 2)
: a:3 b:0 c:4 d:2 - Not OK (Rule 2)
: a:3 b:2 c:0 d:4 - Not OK (Rule 2)
: a:3 b:2 c:4 d:0 - Not OK (Rule 2)
: a:3 b:4 c:0 d:2 - Not OK (Rule 2)
: a:3 b:4 c:2 d:0 - Not OK (Rule 2)
: a:4 b:0 c:2 d:3 - Not OK (Rule 2)
: a:4 b:0 c:3 d:2 - Not OK (Rule 2)
: a:4 b:2 c:0 d:3 - Not OK (Rule 2)
: a:4 b:2 c:3 d:0 - Not OK (Rule 2)
: a:4 b:3 c:0 d:2 - Not OK (Rule 2)
: a:4 b:3 c:2 d:0 - Not OK (Rule 2)
: a:1 b:2 c:3 d:4 - Not OK (Rule 1)
: a:1 b:2 c:4 d:3 - Not OK (Rule 2)
: a:1 b:3 c:2 d:4 - Not OK (Rule 2)
: a:1 b:3 c:4 d:2 - Not OK (Rule 2)
: a:1 b:4 c:2 d:3 - Not OK (Rule 2)
: a:1 b:4 c:3 d:2 - Not OK (Rule 2)
: a:2 b:1 c:3 d:4 - Not OK (Rule 2)
: a:2 b:1 c:4 d:3 - Not OK (Rule 2)
: a:2 b:3 c:1 d:4 - Not OK (Rule 2)
: a:2 b:3 c:4 d:1 - Not OK (Rule 2)
: a:2 b:4 c:1 d:3 - Not OK (Rule 2)
: a:2 b:4 c:3 d:1 - Not OK (Rule 2)
: a:3 b:1 c:2 d:4 - Not OK (Rule 2)
: a:3 b:1 c:4 d:2 - Not OK (Rule 2)
: a:3 b:2 c:1 d:4 - Not OK (Rule 2)
: a:3 b:2 c:4 d:1 - Not OK (Rule 2)
: a:3 b:4 c:1 d:2 - Not OK (Rule 2)
: a:3 b:4 c:2 d:1 - Not OK (Rule 2)
: a:4 b:1 c:2 d:3 - Not OK (Rule 2)
: a:4 b:1 c:3 d:2 - Not OK (Rule 2)
: a:4 b:2 c:1 d:3 - Not OK (Rule 2)
: a:4 b:2 c:3 d:1 - Not OK (Rule 2)
: a:4 b:3 c:1 d:2 - Not OK (Rule 2)
: a:4 b:3 c:2 d:1 - Not OK (Rule 2)
0

Challenge #203.2: Copy Directory

You are given path to two folders, $source and $target.

Write a script that recursively copy the directory from $source to $target except any files.

Example:
Input: $source = '/a/b/c' and $target = '/x/y'

Source directory structure:

├── a
│   └── b
│       └── c
│           ├── 1
│           │   └── 1.txt
│           ├── 2
│           │   └── 2.txt
│           ├── 3
│           │   └── 3.txt
│           ├── 4
│           └── 5
│               └── 5.txt

Target directory structure:

├── x
│   └── y

Expected Result:

├── x
│   └── y
|       ├── 1
│       ├── 2
│       ├── 3
│       ├── 4
│       └── 5

The terms «directory» and «folder» are generally synonymous, but I will stick to the former.

My program messes with the notion of the current directory, so I have decided to require absolute paths on the $source and $target. The example does so, so it is probably reasonable to do so.

/a/b is not a path that I would like to see on my pc, so I have decided to prefix it (and the target /x/y) with /tmp.

Use the following shell script to set up the directories and files:

File: setup.sh
#! /usr/bin/env sh

rm -R /tmp/x
rm -R /tmp/a

mkdir /tmp/x
mkdir /tmp/x/y

mkdir /tmp/a /tmp/a/b /tmp/a/b/c
mkdir /tmp/a/b/c/1 /tmp/a/b/c/2 /tmp/a/b/c/3 /tmp/a/b/c/4 /tmp/a/b/c/5

touch /tmp/a/b/c/1/1.txt
touch /tmp/a/b/c/2/2.txt
touch /tmp/a/b/c/3/3.txt
touch /tmp/a/b/c/5/5.txt

Then the surprisingly short program:

File: copy-directory
#! /usr/bin/env raku

unit sub MAIN ($source where $source.substr(0,1) eq '/'            # [1]
                  && $source.IO.e && $source.IO.d = '/tmp/a/b/c',
               $target where $target.substr(0,1) eq '/'            # [2]
                  && $target.IO.e && $target.IO.d = '/tmp/x/y',
	       :d(:$dryrun),                                       # [3]
	       :v(:$verbose) = $dryrun);

indir($source, { do-dir(".") });                                   # [4]

sub do-dir ($dir)                                                  # [5]
{
  for dir($dir).sort -> $file                                      # [6]
  {
    next unless $file.d;                                           # [7]
    say ": mkdir $target/$file { $dryrun ?? ' [Dryrun]' !! '' }" if $verbose;
    mkdir "$target/$file" unless $dryrun && "$target/$file".IO.e;  # [8]
    do-dir($file);                                                 # [9]
  }
}

[1] The source directory (with path). The first chracter must be «/» (the substr part), the directory must exist (IO.e) and it must be a directory (IO.d). Note the default value at the end.

See docs.raku.org/routine/e and docs.raku.org/routine/d for more information about those file tests.

[2] The target directory (with path).

[3] Use the «--dryrun» (or «-d») option to omit the creation of the directories. It is only useful when combined with «--verbose» (or «-v»), so the latter is enabled by default by dryrun.

[4] This recursive call starts the show. The indir call executes the given code block (the second argument), with the current directory set to the first argument inside that block only. The resursive procedure starts off in the source directory, which we then specify as «.» in the call.

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

[5] The recursive procedure. The argument is the directory to inspect. It is specified relative to the source directory.

[6] Get the content of the directory (with dir), and iterate over them. Sorted, so that we get the same order each time.

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

[7] Skip non-directories. (Note that Raku does not give you «.» and «..», so we do not have to handle them.)

[8] Create the directory in the target location, unless it exists already.

[9] Recursively follow the directory.

Running it, with verbose mode to show what is going on:

$ ./copy-directory -v 
: mkdir /tmp/x/y/1
: mkdir /tmp/x/y/2
: mkdir /tmp/x/y/3
: mkdir /tmp/x/y/4
: mkdir /tmp/x/y/5

Looking good.

Let us check that it works recursively:

$ ./setup.sh

$ ./copy-directory -d /tmp/a /tmp/x/y
: mkdir /tmp/x/y/b  [Dryrun]
: mkdir /tmp/x/y/b/c  [Dryrun]
: mkdir /tmp/x/y/b/c/1  [Dryrun]
: mkdir /tmp/x/y/b/c/2  [Dryrun]
: mkdir /tmp/x/y/b/c/3  [Dryrun]
: mkdir /tmp/x/y/b/c/4  [Dryrun]
: mkdir /tmp/x/y/b/c/5  [Dryrun]

Looking good.

And that's it.