### ----------------------------------------------------
### -- Arithmetic::PaperAndPencil
### -- Licenses: Artistic-2.0
### -- Authors: Jean Forget
### -- File: lib/Arithmetic/PaperAndPencil.rakumod
### ----------------------------------------------------
### -- Chunk 4 of 5
($radix), :value($dividend.value.substr(0, $col-r)));
    while $col-r ≤ $len1 {
      my Arithmetic::PaperAndPencil::Number $part-dvd1 = $part-dvd.carry($delta);  # single-digit dividend or 2-digit dividend to compute the quotient first candidate
      my Arithmetic::PaperAndPencil::Number $theo-quo  = $part-dvd1 ☈÷ $part-dvr1; # theoretical quotient first candidate
      my Arithmetic::PaperAndPencil::Number $act-quo;                              # actual quotient first candidate
      my Str $label;
      if $part-dvd ☈< $divisor {
        $theo-quo = $zero;
        $act-quo  = $zero;
      }
      elsif $type eq 'cheating' {
        $act-quo .= new(radix => $radix, value => %mult-cache.keys.grep(-> $x { %mult-cache{$x} ☈≤ $part-dvd }).max);
        $label    = 'DIV03';
      }
      elsif $theo-quo.chars == 2 {
        $act-quo  = max-unit($radix);
        $label    = 'DIV02';
      }
      else {
        $act-quo  = $theo-quo;
      }
      my Bool $too-much = True; # we must loop with the next lower candidate
      if $theo-quo.value eq '0' {
        $action .= new(level => $basic-level + 5, label => 'DIV01'
                     , val1  => $part-dvd.value , r1l => $lin-d, r1c => $c-dd + $col-r   , r1val => $part-dvd.value
                     , val2  => $divisor .value , r2l => $l-dr , r2c => $c-dr + $len2 - 1, r2val => $divisor.value
                     , val3  => '0'             , w1l => $l-qu , w1c => $c-qu            , w1val => '0');
        self.action.push($action);
        $too-much = False; # no need to loop on candidate values, no need to execute the mult-and-sub routine
        $rem = $part-dvd.value;
      }
      elsif $theo-quo.value eq $act-quo.value {
        $action .= new(level => $basic-level + 5, label => 'DIV01'
                     , val1  => $part-dvd1.value, r1l => $lin-d, r1c => $c-dd - $len1 + $col-r - $delta, r1val => $part-dvd1.value
                     , val2  => $part-dvr1.value, r2l => $l-dr , r2c => $c-dr - $len2 + 1      - $delta, r2val => $part-dvr1.value
                     , val3  => $theo-quo .value, w1l => $l-qu , w1c => $c-qu                          , w1val => $act-quo.value);
        self.action.push($action);
      }
      else {
        $action .= new(level => $basic-level + 6, label => 'DIV01'
                     , val1  => $part-dvd1.value, r1l => $lin-d, r1c => $c-dd - $len1 + $col-r - $delta, r1val => $part-dvd1.value
                     , val2  => $part-dvr1.value, r2l => $l-dr , r2c => $c-dr - $len2 + 1              , r2val => $part-dvr1.value
                     , val3  => $theo-quo.value);
        self.action.push($action);
        $action .= new(level => $basic-level + 5, label => $label, val1 => $act-quo.value, w1l => $l-qu, w1c => $c-qu, w1val => $act-quo.value);
        self.action.push($action);
      }
      my Int $l-re;
      while $too-much {
        if $mult-and-sub eq 'separate' {
          $l-re = $lin-d + 2;
        }
        else {
          $l-re = $lin-d + 1;
        }
        if $bot < $l-re {
          $bot = $l-re;
          $action .= new(level => $basic-level + 5, label => 'DRA01', w1l => $l-dd, w1c => $c-dr - $len2
                                                                    , w2l => $bot , w2c => $c-dr - $len2);
          self.action.push($action);
        }
        ($too-much, $rem) = self!mult-and-sub(l-dd => $lin-d    , c-dd => $c-dd - $len1 + $col-r, dividend     => $part-dvd
                                            , l-dr => $l-dr     , c-dr => $c-dr                 , divisor      => $divisor
                                            , l-qu => $l-qu     , c-qu => $c-qu                 , quotient     => $act-quo
                                            , l-re => $l-re     , c-re => $c-dd - $len1 + $col-r, basic-level  => $basic-level
                                            , l-pr => $lin-d + 1, c-pr => $c-dd - $len1 + $col-r, mult-and-sub => $mult-and-sub);
        if $too-much {
          self.action[* - 1].level = $basic-level + 4;
          $act-quo ☈-= $one;
          my Int $erased-column;
          if $stand-alone {
            $erased-column = $c-dd - $len1;
          }
          else {
            $erased-column = $c-dd - $len1 + 1;
          }
          $action .= new(level => $basic-level + 5, label => 'ERA01', w1l => $lin-d + 1, w1c => $c-dd, w2l => $lin-d + 1, w2c => $erased-column);
          self.action.push($action);
          $action .= new(level => $basic-level + 4, label => 'DIV02', val1 => $act-quo.value, w1l => $l-qu, w1c => $c-qu, w1val => $act-quo.value);
          self.action.push($action);
        }
      }

      $quotient ~= $act-quo.value;
      if $act-quo.value ne '0' {
        $lin-d = $l-re;
      }
      self.action[* - 1].level = $basic-level + 3;
      if $col-r < $len1 {
        $action .= new(level => $basic-level + 5, label => 'DRA01', w1l => $l-dr, w1c => $c-dr - $len2
                                                   , w2l => $bot , w2c => $c-dr - $len2);
        self.action.push($action);
        my Str $new-digit = $dividend.value.substr($col-r, 1);
        $action .= new(level => $basic-level + 3   , label => 'DIV04'   , val1  => $new-digit
                     , r1l => $l-dd , r1c => $c-dd - $len1 + $col-r  + 1, r1val => $new-digit
                     , w1l => $lin-d, w1c => $c-dd - $len1 + $col-r  + 1, w1val => $new-digit);
        self.action.push($action);
        $part-dvd .= new(radix => $radix, value => $rem ~ $new-digit);
      }
      $col-r++;
      $c-qu++;
    }
    self.action[* - 1].level = $basic-level;
  }
  if $type eq 'prepared' {
    my Arithmetic::PaperAndPencil::Number $part-div .= new(:radix($radix), :value($dividend.value.substr(0, $col-r)));
    my Int $n = 0;
    my Int $lin-d = $l-dd;
    while $col-r ≤ $len1 {
      my Str $part-quo = %mult-cache.keys.grep(-> $x { %mult-cache{$x} ☈≤ $part-div }).max;
      $action .= new(level => $basic-level + 5, label => 'DIV01'
                   , val1 => $part-div.value, r1l => $lin-d, r1c => $col-r       , r1val => $part-div.value
                   , val2 => $divisor.value , r2l => $l-dr , r2c => $c-dr        , r2val => $divisor.value
                   , val3 => $part-quo      , w1l => $l-qu , w1c => $c-qu + $n   , w1val => $part-quo);
      self.action.push($action);
      $quotient ~= $part-quo;
      if $part-quo eq '0' {
        $rem = $part-div.value;
      }
      else {
        $action .= new(level => $basic-level + 5, label => 'WRI05', val1 => %mult-cache{$part-quo}.value);
        self.action.push($action);
        $bot += 2;
        $action .= new(level => $basic-level + 8, label => 'WRI00'
                     , w1l => $lin-d + 1, w1c => $c-dd - $len1 + $col-r, w1val => %mult-cache{$part-quo}.value);
        self.action.push($action);
        $rem = self!embedded-sub(basic-level => $basic-level + 3
                     , l-hi => $lin-d    , c-hi => $c-dd - $len1 + $col-r, high => $part-div
                     , l-lo => $lin-d + 1, c-lo => $c-dd - $len1 + $col-r, low  => %mult-cache{$part-quo}
                     , l-re => $lin-d + 2, c-re => $c-dd - $len1 + $col-r);
        self.action[* - 1].level = $basic-level + 3;
        $lin-d += 2;
      }

      if $col-r < $len1 {
        $action .= new(level => $basic-level + 5, label => 'DRA01', w1l => $l-dd, w1c => $c-dd
                                                                  , w2l => $bot , w2c => $c-dd);
        self.action.push($action);
        my Str $new-digit = $dividend.value.substr($col-r, 1);
        $action .= new(level => $basic-level + 3, label => 'DIV04'      , val1  => $new-digit
                     , r1l => $l-dd , r1c => $c-dd - $len1 + $col-r  + 1, r1val => $new-digit
                     , w1l => $lin-d, w1c => $c-dd - $len1 + $col-r  + 1, w1val => $new-digit);
        self.action.push($action);
        $part-div .= new(radix => $radix, value => $rem ~ $new-digit);
      }
      ++$col-r;
      ++$n;
    }
  }
  return ( Arithmetic::PaperAndPencil::Number.new(:radix($radix), :value($quotient))
         , Arithmetic::PaperAndPencil::Number.new(:radix($radix), :value($rem)));
}

method !preparation(Arithmetic::PaperAndPencil::Number :$factor, Str :$limit, :%cache, :$basic-level = 0) {
  my Arithmetic::PaperAndPencil::Action $action;
  my Arithmetic::PaperAndPencil::Number $one .= new(:radix($factor.radix), :value<1>);
  my Int $radix = $factor.radix;
  my Int $col   = $factor.chars + 3;

  # cache first entry
  %cache<1> = $factor;
  $action .= new(level => $basic-level + 3, label => 'WRI00'
               , w1l => 0, w1c => 0   , w1val => '1'
               , w2l => 0, w2c => $col, w2val => $factor.value);
  self.action.push($action);

  my @digits; # storing the numbers' digits
  my @total;  # storing the total's digit positions
  for $factor.value.flip.comb.kv -> $i, $ch {
    @digits[$i][0] = %( lin => 0, col => $col - $i, val => $ch);
    @total[ $i]    = %( lin => 1, col => $col - $i);
  }
  # in case the last partial products are longer than the factor
  @total[$factor.chars] = %( lin => 1, col => $col - $factor.chars);

  my Str $result = $factor.value;
  my Int $lin    = 1;
  my Arithmetic::PaperAndPencil::Number $mul = $one ☈+ $one; # starting from 2; yet stopping immediately with a 2-digit $mul if $radix == 2
  while $mul.value le $limit && $mul.chars == 1 {
    # displaying the line number
    $action .= new(level => $basic-level + 9, label => 'WRI00', w1l => $lin, w1c => 0, w1val => $mul.value);
    self.action.push($action);

    # computation
    for $result.flip.comb.kv -> $i, $ch {
      @digits[$i][1] = %( lin => $lin - 1, col => $col - $i, val => $ch);
      @total[$i]<lin> = $lin;
    }
    $result = self!adding(@digits, @total, $basic-level + 1, $radix);
    self.action[* - 1].level = $basic-level + 3;

    # storing into cache
    %cache{$mul.value} = Arithmetic::PaperAndPencil::Number.new(:radix($radix), :value($result));

    # loop iteration
    $lin++;
    $mul ☈+= $one;
  }

  $action .= new(:level(1), :label<NXP01>);
  self.action.push($action);
}

method !prep-conv(Int $old-radix, Int $new-radix, %cache, :$basic-level = 0) {
  my Arithmetic::PaperAndPencil::Action $action;
  my Arithmetic::PaperAndPencil::Number $old-number .= new(value => '0', radix => $old-radix);
  my Arithmetic::PaperAndPencil::Number $new-number .= new(value => '0', radix => $new-radix);
  my Arithmetic::PaperAndPencil::Number $old-one    .= new(value => '1', radix => $old-radix);
  my Arithmetic::PaperAndPencil::Number $new-one    .= new(value => '1', radix => $new-radix);
  my Int $line = 1;
  while $old-number.value ne '11' {
    %cache{$old-number.value} = $new-number;
    if $new-number.chars > 1 {
      $action .= new(level => $basic-level + 6, label => 'WRI00'
                   , w1l => $line, w1c =>  2, w1val => $old-number.value
                   , w2l => $line, w2c => 10, w2val => $new-number.value);
      self.action.push($action);
      $line++;
    }
    $old-number ☈+= $old-one;
    $new-number ☈+= $new-one;
  }
  if $line != 1 {
    self.action[* - 1].level = 1;
    $action .= new(:level($basic-level + 9), :label<NXP01>);
    self.action.push($action);
  }
}

method !push-below($number, $col is copy, @lines-below) {
  my Arithmetic::PaperAndPencil::Action $action;
  for $number.value.flip.comb -> $digit {
    $action .= new(level => 9, label => 'WRI00', w1l => @lines-below[$col]++, w1c => $col, w1val => $digit);
    self.action.push($action);
    $col--;
  }
  self.action[* - 1].level = 4;
}

method !push-above($number, $col is copy, @lines-above, @addition, Int $bias) {
  my Arithmetic::PaperAndPencil::Action $action;
  for $number.value.flip.comb -> $digit {
    @addition[$bias - $col; - @lines-above[$col] ] = %( lin => @lines-above[$col], col => $col, val => $digit );
    $action .= new(level => 9, label => 'WRI00', w1l => @lines-above[$col]--, w1c => $col, w1val => $digit);
    self.action.push($action);
    $col--;
  }
}

method !add-above($number is copy, $col is copy, @lines-above, @result) {
  my Arithmetic::PaperAndPencil::Action $action;
  my Int $radix = $number.radix;
  while $number.value ne '0' {
    if @lines-above[$col] == -1 {
      my Str $code = 'WRI02';
      if $number.chars == 1 {
        $code = 'WRI04';
      }
      $action .= new(level => 5, label => $code                ,  val1 => $number.unit.value, val2 => $number.carry.value
                   , w1l   => @lines-above[$col]--, w1c => $col, w1val => $number.unit.value);
      self.action.push($action);
      @result[$col] = $number.unit.value;
      $number       = $number.carry;
    }
    else {
      my Arithmetic::PaperAndPencil::Number $already-there .= new(radix => $radix, value => @result[$col]);
      my Arithmetic::PaperAndPencil::Number $sum = $number ☈+ $already-there;
      $action .= new(level => 6, label => 'ADD01'  , val1  => $number.value, val2 => $already-there.value, val3 => $sum.value
                   , r2l  => @lines-above[$col] + 1, r2c   => $col        , r2val => $already-there.value, r2str => True
                );
      self.action.push($action);
      my Str $code = 'WRI02';
      if $sum.chars == 1 {
        $code = 'WRI03';
      }
      $action .= new(level => 5                , label => $code, val1 => $sum.unit.value, val2 => $sum.carry.value
                    , w1l => @lines-above[$col]--, w1c => $col, w1val => $sum.unit.value
                    );
      self.action.push($action);
      @result[$col] = $sum.unit.value;
      $number       = $sum.carry;
    }
    --$col;
  }
}

method !halving(Int :$l1, Int :$c1, Int :$l2, Int :$c2, Arithmetic::PaperAndPencil::Number :$number, Int :$basic-level = 0
                --> Arithmetic::PaperAndPencil::Number) {
  my Int $radix = $number.radix;
  my Str $res   = '';
  my Arithmetic::PaperAndPencil::Action $action;
  if $radix == 2 {
    if $number.chars > 1 {
      $res = $number.value.substr(0, $number.chars - 1);
    }
    else {
      $res = '0';
    }
    $action .= new(:level($basic-level + 4), :label<SHF01>
                 , r1l => $l1, r1c => $c1, r1val => $number.value, val1 => $number.value
                 , w1l => $l2, w1c => $c2, w1val => $res         , val2 => $res
                 );
    self.action.push($action);
  }
  else {
    my Arithmetic::PaperAndPencil::Number $one .= new(:radix($radix), :value<1>);
    my Arithmetic::PaperAndPencil::Number $two .= new(:radix($radix), :value<2>);
    my Int $len = $number.chars;
    my Int $carry = 0;
    for $number.value.comb.kv -> Int $n, Str $digit {
      my Arithmetic::PaperAndPencil::Number $dividend;
      if $carry == 0 {
        $dividend .= new(:radix($radix), :value($digit));
      }
      else {
        $dividend .= new(:radix($radix), :value('1' ~ $digit));
      }
      my Arithmetic::PaperAndPencil::Number $quotient = $dividend ☈÷ $two;
      if $dividend.is-odd {
        $carry = 1;
      }
      else {
        $carry = 0;
      }
      $action .= new(level => $basic-level + 5, :label<DIV07>, :val1($dividend.value), :val2($quotient.value), :val3($carry.Str)
                   , r1l => $l1, r1c => $c1 -$len + $n + 1, r1val => $digit
                   , w1l => $l2, w1c => $c2 -$len + $n + 1, w1val => $quotient.value
                   );
      self.action.push($action);
      $res ~= $quotient.value;
    }
  }
  return Arithmetic::PaperAndPencil::Number.new(:radix($radix), :value($res));
}

method !doubling(Int :$l1, Int :$c1, Int :$l2, Int :$c2, Arithmetic::PaperAndPencil::Number :$number, Int :$basic-level = 0
                 --> Arithmetic::PaperAndPencil::Number) {
  my Int $radix = $number.radix;
  my Str $res   = '';
  my Arithmetic::PaperAndPencil::Action $action;
  if $radix == 2 {
    $res = $number.value ~ '0';
    $action .= new(:level($basic-level + 4), :label<SHF01>
                 , r1l => $l1, r1c => $c1, r1val => $number.value, val1 => $number.value
                 , w1l => $l2, w1c => $c2, w1val => $res         , val2 => $res
                 );
    self.action.push($action);
  }
  else {
    my Arithmetic::PaperAndPencil::Number $carry .= new(:radix($radix), :value<0>);
    my Arithmetic::PaperAndPencil::Number $one   .= new(:radix($radix), :value<1>);
    my Arithmetic::PaperAndPencil::Number $two   .= new(:radix($radix), :value<2>);
    for $number.value.flip.comb.kv -> Int $n, Str $digit {
      my Arithmetic::PaperAndPencil::Number $product .= new(:radix($radix), :value($digit));
      $product ☈×= $two;
      if $carry.value eq '0' {
        $action .= new(level => $basic-level + 5, :label<MUL01>, :val1<2>, :val2($digit), :val3($product.value)
                     , r1l => $l1, r1c => $c1 - $n, r1val => $digit
                     , w1l => $l2, w1c => $c2 - $n, w1val => $product.value
                     );
        self.action.push($action);
      }
      else {
        $action .= new(level => $basic-level + 6, :label<MUL01>, :val1<2>, :val2($digit), :val3($product.value)
                     , r1l => $l1, r1c => $c1 - $n, r1val => $digit
                     );
        self.action.push($action);
        $product ☈+= $one;
        $action .= new(level => $basic-level + 5, :label<ADD02>, :val1<1>, :val2($product.value)
                     , w1l => $l2, w1c => $c2 - $n, w1val => $product.value
                     );
        self.action.push($action);
      }
      $res = $product.unit.value ~ $res;
      $carry = $product.carry;
    }
    if $carry.value eq '1' {
      $res = '1' ~ $res;
    }
  }
  return Arithmetic::PaperAndPencil::Number.new(:radix($radix), :value($res));
}

method html(Str :$lang, Bool :$silent, Int :$level, :%css = %() --> Str) {
  my Bool $talkative = not $silent; # "silent" better for API, "talkative" better for programming
  my Str  $result    = '';
  my      @sheet     = ();
  my Int  %vertical-lines;
  my Int  %cache-l2p-col;
  my Int  $c-min     = 0;
  my Int  $l-min     = 0;

  # checking the line minimum number
  sub check-l-min(Int $l) {
    if $l < $l-min {
      # inserting new empty lines before the existing ones
      for $l ..^ $l-min {
        unshift @sheet, Nil;
      }
      # updating the line minimum number
      $l-min = $l;
    }
  }
  # logical to physical line number
  sub l2p-lin(Int $logl --> Int) {
    my Int $result = $logl - $l-min;
    return $result;
  }

  # checking the column minimum number
  sub check-c-min(Int $c) {
    if $c < $c-min {
      my Int $delta-c = $c-min - $c;
      for @sheet <-> $line {
        prepend $line, space-char() xx $delta-c;
      }
      $c-min = $c;
      %cache-l2p-col  = %();
    }
  }
  # logical to physical column number
  sub l2p-col(Int $logc --> Int) {
    if %cache-l2p-col{$logc} {
      return %cache-l2p-col{$logc};
    }
    my Int $result = $logc - $c-min;
    for %vertical-lines.keys -> $col {
      if $logc > $col {
        ++$result;
      }
    }
    %cache-l2p-col{$logc} = $result;
    return $result;
  }

  sub filling-spaces(Int $l, Int $c) {
    # putting spaces into all uninitialised boxes
    for 0 .. l2p-lin($l) -> $l1 {
       @sheet[$l1; 0] //= space-char;
    }
    for 0 .. l2p-col($c) -> $c1 {
       @sheet[l2p-lin($l); $c1] //= space-char;
    }
  }

  for @.action -> $action {
    if $action.label.starts-with('TIT') or $action.label eq 'NXP01' {
      @sheet          =  ();
      %vertical-lines = %();
      %cache-l2p-col  = %();
      $c-min          = 0;
      $l-min          = 0;
    }

    # Drawing a vertical line
    if $action.label eq 'DRA01' {
      if  $action.w1c != $action.w2c {
        die "The line is not vertical, starting at column {$action.w1c} and ending at column {$action.w2c}";
      }
      # checking the line and column minimum numbers
      check-l-min($action.w1l);
      check-l-min($action.w2l);
      check-c-min($action.w1c);
      # making some clear space for the vertical line
      unless %vertical-lines{$action.w1c} {
        %vertical-lines{$action.w1c} = 1;
        # clearing the cache
        %cache-l2p-col  = %();

        # shifting characters past the new vertical line's column
        for @sheet.keys -> $l {
          for 0 .. l2p-col($action.w1c) -> $c {
             @sheet[$l; $c] //= space-char;
          }
          my $line = @sheet[$l];
          splice($line, l2p-col($action.w1c) + 1, 0, space-char);
          @sheet[$l] = $line;
        }
      }
      # making the vertical line
      for $action.w1l .. $action.w2l -> $l {
        filling-spaces($l, $action.w1c);
        @sheet[l2p-lin($l); l2p-col($action.w1c) + 1] = pipe-char;
      }
    }

    # drawing an horizontal line or drawing a hook over a dividend
    sub draw-h(Int :$at, Int :$from, Int :$to) {
      # checking the line and column minimum numbers
      check-l-min($at);
      check-c-min($from);
      check-c-min($to);
      # begin and end
      my ($c-beg, $c-end);
      if $from > $to {
        $c-beg = l2p-col($to);
        $c-end = l2p-col($from);
        filling-spaces($at, $from);
      }
      else {
        $c-beg = l2p-col($from);
        $c-end = l2p-col($to);
        filling-spaces($at, $to);
      }
      for $c-beg .. $c-end -> $i {
        @sheet[l2p-lin($at); $i].underline = True;
      }
    }

    # Drawing an horizontal line
    if $action.label eq 'DRA02' {
      if  $action.w1l != $action.w2l {
        die "The line is not horizontal, starting at line {$action.w1l} and ending at line {$action.w2l}";
      }
      draw-h(:at($action.w1l), :from($action.w1c), :to($action.w2c));
    }

    # Drawing a hook over a dividend (that is, an horizontal line above)
    if $action.label eq 'HOO01' {
      if  $action.w1l != $action.w2l {
        die "The hook is not horizontal, starting at line {$action.w1l} and ending at line {$action.w2l}";
      }
      draw-h(:at($action.w1l - 1), :from($action.w1c), :to($action.w2c));
    }

    # Drawing an oblique line
    if $action.label eq 'DRA03' {
      if $action.w2c - $action.w1c != $action.w2l - $action.w1l {
        die "The line is not oblique";
      }
      # checking the line and column minimum numbers
      check-l-min($action.w1l);
      check-l-min($action.w2l);
      check-c-min($action.w1c);
      check-c-min($action.w2c);
      # begin and end
      my ($l-beg, $c-beg);
      if $action.w2l > $action.w1l {
        # line is defined top-left to bot-right
        $l-beg = $action.w1l;
        $c-beg = $action.w1c;
      }
      else {
        # line was defined bot-right to top-left
        $l-beg = $action.w2l;
        $c-beg = $action.w2c;
      }
      # drawing the line top-left to bot-right
      for 0 .. ($action.w2l - $action.w1l).abs -> $i {
        filling-spaces($l-beg + $i, $c-beg + $i);
        my $l1 = l2p-lin($l-beg + $i);
        my $c1 = l2p-col($c-beg + $i);
        @sheet[$l1; $c1].char = backslash-char.char;
        # the line
        #   @sheet[$l1; $c1] = backslash-char();
        # would be wrong, because in some cases it would clobber the "underline" attribute of an already existing char
      }
    }
    if $action.label eq 'DRA04' {
      if $action.w2c - $action.w1c != $action.w1l - $action.w2l {
        die "The line is not oblique";
      }
      # checking the line and column minimum numbers
      check-l-min($action.w1l);
      check-l-min($action.w2l);
      check-c-min($action.w1c);
      check-c-min($action.w2c);
      # begin and end
      my ($l-beg, $c-beg);
      if $action.w2l > $action.w1l {
        # line is defined top-right to bot-left
        $l-beg = $action.w1l;
        $c-beg = $action.w1c;
      }
      else {
        # line was defined bot-left to top-right
        $l-beg = $action.w2l;
        $c-beg = $action.w2c;
      }
      # drawing the line top-right to bot-left
      for 0 .. ($action.w2l - $action.w1l).abs -> $i {
        filling-spaces($l-beg + $i, $c-beg - $i);
        my $l1 = l2p-lin($l-beg + $i);
        my $c1 = l2p-col($c-beg - $i);
        @sheet[$l1; $c1].char = slash-char.char;
        # the line
        #   @sheet[$l1; $c1] = slash-char();
        # would be wrong, because in some cases it would clobber the "underline" attribute of an already existing char
      }
    }

    # Reading some digits (or other characters) and possibly striking them
    if $action.r1val ne '' {

      # checking the line and column minimum numbers
      # (should not be necessary: if the digits are being read, they must have been previously written)
      check-l-min($action.r1l);
      check-c-min($action.r1c - $action.r1val.chars + 1);

      # putting spaces into all uninitialised boxes
      # (should not be necessary, for the same reason)
      filling-spaces($action.r1l, $action.r1c);

      # tagging each char
      for $action.r1val.comb('').kv -> $i, $str {
         with @sheet[l2p-lin($action.r1l); l2p-col($action.r1c - $action.r1val.chars + $i + 1)] {
           $_.read = True;
           if $action.r1str {
             $_.strike = True;
           }
         }
      }
    }
    if $action.r2val ne '' {

      # checking the line and column minimum numbers
      # (should not be necessary, for the same reason as r1val)
      check-l-min($action.r2l);
      check-c-min($action.r2c - $action.r2val.chars + 1);

      # putting spaces into all uninitialised boxes
      # (should not be necessary, for the same reason)
      filling-spaces($action.r2l, $action.r2c);

      # tagging each char
      for $action.r2val.comb('').kv -> $i, $str {
         with @sheet