# This file implements the following set operators:
#   (.)  set multiplication (ASCII)
#   ⊍    set multiplication

proto sub infix:<(.)>(|) is pure {*}
multi sub infix:<(.)>()               { bag()    }
multi sub infix:<(.)>(Setty:D $a)     { $a.Baggy }
multi sub infix:<(.)>(Baggy:D $a)     { $a       }  # also Mixy

multi sub infix:<(.)>(Setty:D $a, Setty:D $b) {
    (my $elems := $a.Bag.RAW-HASH) && nqp::elems($elems)
      ?? nqp::create($a.WHAT.Baggy).SET-SELF(
           Rakudo::QuantHash.MULTIPLY-SET-TO-BAG($elems,$b.RAW-HASH),
         )
      !! $a.Baggy
}

multi sub infix:<(.)>(Mixy:D $a, Mixy:D $b) {
    nqp::if(
      (my $elems := Rakudo::QuantHash.BAGGY-CLONE-RAW($a.RAW-HASH))
        && nqp::elems($elems),
      nqp::stmts(
        Rakudo::QuantHash.MULTIPLY-MIX-TO-MIX($elems,$b.RAW-HASH),
        nqp::create($a.WHAT).SET-SELF($elems)
      ),
      $a
    )
}

multi sub infix:<(.)>(Mixy:D  $a, Baggy:D $b) { infix:<(.)>($a, $b.Mix)  }
multi sub infix:<(.)>(Mixy:D  $a, Any     \b) { infix:<(.)>($a,  b.Mix)  }
multi sub infix:<(.)>(Setty:D $a, Mixy:D  $b) { infix:<(.)>($a.Mixy, $b) }
multi sub infix:<(.)>(Baggy:D $a, Mixy:D  $b) { infix:<(.)>($a.Mixy, $b) }
multi sub infix:<(.)>(Any     \a, Mixy:D  $b) { infix:<(.)>( a.Mix, $b)  }
multi sub infix:<(.)>(Baggy:D $a, Baggy:D $b) {
    (my $elems := Rakudo::QuantHash.BAGGY-CLONE-RAW($a.RAW-HASH))
      && nqp::elems($elems)
      ?? nqp::create($a.WHAT).SET-SELF(
           Rakudo::QuantHash.MULTIPLY-BAG-TO-BAG($elems,$b.RAW-HASH),
         )
      !! $a
}

multi sub infix:<(.)>(Any, Failure:D $b) { $b.throw }
multi sub infix:<(.)>(Failure:D $a, Any) { $a.throw }

# Note that we cannot create a Setty|Baggy:D,Any candidate because that will
# result in an ambiguous dispatch, so we need to hack a check for Setty|Baggy
# in here.
multi sub infix:<(.)>(Any \a, Any \b) {
    infix:<(.)>(
      nqp::isconcrete(a)
        ?? nqp::istype(a,Setty)
          ?? a.Baggy
          !! nqp::istype(a,Baggy) ?? a !! a.Bag
        !! a.Bag,
      b.Bag
    )
}

multi sub infix:<(.)>(+@p) {   # also Any
    my $iterator := @p.iterator;
    nqp::if(
      nqp::eqaddr((my $result := $iterator.pull-one),IterationEnd),
      bag(),          # nothing to process
      nqp::if(
        nqp::eqaddr((my $pulled := $iterator.pull-one),IterationEnd),
        $result.Bag,  # only 1 elem to process
        nqp::stmts(
          nqp::repeat_until(
            nqp::eqaddr(($pulled := $iterator.pull-one),IterationEnd),
            ($result := $result (.) $pulled)
          ),
          $result
        )
      )
    )
}

# U+228D MULTISET MULTIPLICATION
my constant &infix:<⊍> := &infix:<(.)>;

# vim: expandtab shiftwidth=4
