# This file implements the following set operators: # (-) set difference (ASCII) # ∖ set difference proto sub infix:<(-)>(|) is pure {*} multi sub infix:<(-)>() { set() } multi sub infix:<(-)>(QuantHash:D $a) { $a } # Set/Bag/Mix multi sub infix:<(-)>(SetHash:D $a) { $a.Set } multi sub infix:<(-)>(BagHash:D $a) { $a.Bag } multi sub infix:<(-)>(MixHash:D $a) { $a.Mix } multi sub infix:<(-)>(Setty:D $a, Setty:D $b) { (my $araw := $a.RAW-HASH) && nqp::elems($araw) && (my $braw := $b.RAW-HASH) && nqp::elems($braw) ?? nqp::create($a.WHAT).SET-SELF( # both have elems Rakudo::QuantHash.SUB-SET-FROM-SET($araw, $braw) ) !! $a # no elems in a or b } multi sub infix:<(-)>(Setty:D $a, Map:D \b) { nqp::if( (my \araw := $a.RAW-HASH) && nqp::elems(araw), nqp::create($a.WHAT).SET-SELF( # elems in a nqp::if( nqp::elems(my \braw := nqp::getattr(nqp::decont(b),Map,'$!storage')), Rakudo::QuantHash.SUB-MAP-FROM-SET(araw, b), # both have elems nqp::clone(araw) # no elems in b ) ), $a # no elems in a ) } multi sub infix:<(-)>(Setty:D $a, Iterable:D \b) { nqp::if( (my $iterator := b.iterator).is-lazy, Set.fail-iterator-cannot-be-lazy('set difference'), nqp::if( (my $raw := $a.RAW-HASH) && nqp::elems($raw), nqp::create($a.WHAT).SET-SELF( # elems in b Rakudo::QuantHash.SUB-PAIRS-FROM-SET($raw, $iterator) ), $a # no elems in b ) ) } multi sub infix:<(-)>(Mixy:D $a, Mixy:D $b) { # needed as tie-breaker Rakudo::QuantHash.DIFFERENCE-MIXY-QUANTHASH($a, $b) } multi sub infix:<(-)>(Mixy:D $a, QuantHash:D $b) { Rakudo::QuantHash.DIFFERENCE-MIXY-QUANTHASH($a, $b) } multi sub infix:<(-)>(QuantHash:D $a, Mixy:D $b) { Rakudo::QuantHash.DIFFERENCE-MIXY-QUANTHASH($a.Mixy, $b) } multi sub infix:<(-)>(Mixy:D $a, Map:D \b) { Rakudo::QuantHash.DIFFERENCE-MIXY-QUANTHASH($a, b.Set) } multi sub infix:<(-)>(Mixy:D $a, Any:D \b) { # also Iterable Rakudo::QuantHash.DIFFERENCE-MIXY-QUANTHASH($a, b.Set) } multi sub infix:<(-)>(Any:D \a, Mixy:D $b) { Rakudo::QuantHash.DIFFERENCE-MIXY-QUANTHASH(a.Mix, $b) } multi sub infix:<(-)>(Baggy:D $a, Mixy:D $b) { # needed as tie-breaker Rakudo::QuantHash.DIFFERENCE-MIXY-QUANTHASH($a.Mixy, $b) } multi sub infix:<(-)>(Baggy:D $a, Baggy:D $b) { # needed as tie-breaker Rakudo::QuantHash.DIFFERENCE-BAGGY-QUANTHASH($a, $b) } multi sub infix:<(-)>(Baggy:D $a, QuantHash:D $b) { Rakudo::QuantHash.DIFFERENCE-BAGGY-QUANTHASH($a, $b) } multi sub infix:<(-)>(QuantHash:D $a, Baggy:D $b) { Rakudo::QuantHash.DIFFERENCE-BAGGY-QUANTHASH($a.Baggy, $b) } multi sub infix:<(-)>(Baggy:D $a, Map:D \b) { Rakudo::QuantHash.DIFFERENCE-BAGGY-QUANTHASH($a, b.Bag) } multi sub infix:<(-)>(Baggy:D $a, Any:D \b) { # also Iterable Rakudo::QuantHash.DIFFERENCE-BAGGY-QUANTHASH($a, b.Bag) } multi sub infix:<(-)>(Any \a, Baggy:D $b) { Rakudo::QuantHash.DIFFERENCE-BAGGY-QUANTHASH(a.Bag, $b) } multi sub infix:<(-)>(Any \a, Map:D \b) { infix:<(-)>(a.Set, b) } multi sub infix:<(-)>(Any \a, Iterable:D \b) { infix:<(-)>(a.Set, b) } multi sub infix:<(-)>(Any $, Failure:D $b) { $b.throw } multi sub infix:<(-)>(Failure:D $a, Any $) { $a.throw } multi sub infix:<(-)>(Any \a, Any \b) { infix:<(-)>(a.Set,b.Set) } multi sub infix:<(-)>(+@p) { # also Any sub subtract(Mu \elems, Mu \iter, \clone, \value --> Nil) { my $pair := nqp::ifnull( nqp::atkey(elems, nqp::iterkey_s(iter)), nqp::bindkey( elems, nqp::iterkey_s(iter), nqp::if( clone, nqp::p6bindattrinvres( nqp::clone(nqp::iterval(iter)), Pair, '$!value', 0 ), Pair.new(nqp::iterval(iter),0) ) ) ); nqp::bindattr($pair,Pair,'$!value', nqp::getattr($pair,Pair,'$!value') - value ); } nqp::if( (my $params := @p.iterator).is-lazy, Set.fail-iterator-cannot-be-lazy('set difference'), # bye bye nqp::stmts( # fixed list of things to diff (my $type := nqp::if( nqp::istype((my $p := $params.pull-one),Mixy), Mix, nqp::if(nqp::istype($p,Baggy),Bag,Set) )), (my $mutable := nqp::eqaddr($p.WHAT,MixHash) || nqp::eqaddr($p.WHAT,BagHash) || nqp::eqaddr($p.WHAT,SetHash) ), (my $elems := nqp::if( nqp::istype($p,Baggy), nqp::if( # already have a Baggy, clone (my $raw := $p.RAW-HASH), Rakudo::QuantHash.BAGGY-CLONE($raw), nqp::create(Rakudo::Internals::IterationSet) ), nqp::unless( # something else, Mix it! $p.Set.Mix.RAW-HASH, nqp::create(Rakudo::Internals::IterationSet) ) )), nqp::until( nqp::eqaddr(($p := $params.pull-one),IterationEnd), nqp::if( # not done parsing nqp::istype($p,Baggy), nqp::stmts( # Mixy/Baggy semantics apply nqp::unless( # upgrade type if needed nqp::istype($type,Mix), ($type := nqp::if(nqp::istype($p,Mixy),Mix,Bag)) ), nqp::if( ($raw := $p.RAW-HASH) && (my $iter := nqp::iterator($raw)), nqp::while( # something to process $iter, subtract( $elems, nqp::shift($iter), 1, nqp::getattr(nqp::iterval($iter),Pair,'$!value') ) ) ) ), nqp::stmts( # not a Baggy/Mixy, assume Set ($raw := nqp::if(nqp::istype($p,Setty),$p,$p.Set).RAW-HASH) && ($iter := nqp::iterator($raw)), nqp::while( # something to process $iter, subtract($elems, nqp::shift($iter), 0, 1) ) ) ) ), ($iter := nqp::iterator($elems)), # start post-processing nqp::if( nqp::istype($type,Set), nqp::while( # need to create a Set $iter, nqp::if( nqp::getattr(nqp::iterval(nqp::shift($iter)),Pair,'$!value') > 0, nqp::bindkey( $elems, nqp::iterkey_s($iter), nqp::getattr(nqp::iterval($iter),Pair,'$!key') ), nqp::deletekey($elems,nqp::iterkey_s($iter)) ) ), nqp::if( nqp::istype($type,Mix), nqp::while( # convert to Mix semantics $iter, nqp::unless( nqp::getattr(nqp::iterval(nqp::shift($iter)),Pair,'$!value'), nqp::deletekey($elems,nqp::iterkey_s($iter)) # not valid in Mix ) ), nqp::while( # convert to Bag semantics $iter, nqp::unless( nqp::getattr(nqp::iterval(nqp::shift($iter)),Pair,'$!value') >0, nqp::deletekey($elems,nqp::iterkey_s($iter)) # not valid in Bag ) ) ) ), nqp::if( # set to mutable if so started $mutable, $type := nqp::if( nqp::eqaddr($type,Mix), MixHash, nqp::if(nqp::eqaddr($type,Bag),BagHash,SetHash) ) ), nqp::create($type).SET-SELF($elems) ) ) } # U+2216 SET MINUS my constant &infix:<∖> := &infix:<(-)>; # vim: expandtab shiftwidth=4