=begin pod :kind("Type") :subkind("role") :category("composite")

=TITLE role Baggy

=SUBTITLE Collection of distinct weighted objects

    role Baggy does QuantHash { }

A role for collections of weighted objects.  See L<Bag|/type/Bag>, L<BagHash|/type/BagHash>, and
L<Mixy|/type/Mixy>.

=head1 Methods

=head2 method new-from-pairs

Defined as:

    method new-from-pairs(Baggy: *@pairs --> Baggy:D)

Constructs a Baggy objects from a list of L«C<Pair> objects|/type/Pair»
given as positional arguments:

    say Mix.new-from-pairs: 'butter' => 0.22, 'sugar' => 0.1, 'sugar' => 0.02;
    # OUTPUT: «Mix(butter(0.22) sugar(0.12))␤»

B<Note:> be sure you aren't accidentally passing the Pairs as positional arguments;
the quotes around the keys in the above example are significant.

=head2 method grab

Defined as:

    multi method grab(Baggy:D: --> Any)
    multi method grab(Baggy:D: $count --> Seq:D)

Like L<pick|#method pick>, a C<grab> returns a random selection of elements, weighted
by the values corresponding to each key. Unlike C<pick>, it works only on mutable
structures, e.g. L<BagHash|/type/BagHash>. Use of C<grab> on an immutable structure results in an
C<X::Immutable> exception. If C<*> is passed as C<$count>, or C<$count> is greater than
or equal to the L<total|#method total> of the invocant, then C<total> elements from the
invocant are returned in a random sequence; i.e. they are returned shuffled.

Grabbing decrements the grabbed key's weight by one (deleting the key
when it reaches 0). By definition, the C<total> of the invocant also decreases by one, so the
probabilities stay consistent through subsequent C<grab> operations.

    my $cars = ('Ford' => 2, 'Rover' => 3).BagHash;
    say $cars.grab;                                   # OUTPUT: «Ford␤»
    say $cars.grab(2);                                # OUTPUT: «(Rover Rover)␤»
    say $cars.grab(*);                                # OUTPUT: «(Rover Ford)␤»

    my $breakfast = ('eggs' => 2, 'bacon' => 3).Bag;
    say $breakfast.grab;
    CATCH { default { put .^name, ': ', .Str } };
    # OUTPUT: «X::Immutable: Cannot call 'grab' on an immutable 'Bag'␤»

=head2 method grabpairs

Defined as:

    multi method grabpairs(Baggy:D: --> Any)
    multi method grabpairs(Baggy:D: $count --> Seq:D)

Returns a C<Pair> or a C<Seq> of C<Pair>s depending on the version of the method
being invoked. Each C<Pair> returned has an element of the invocant as its key and the
element's weight as its value. Unlike L<pickpairs|/routine/pickpairs>, it works only on mutable structures,
e.g. L<BagHash|/type/BagHash>. Use of C<grabpairs> on an immutable structure results in
an C<X::Immutable> exception. If C<*> is passed as C<$count>, or C<$count> is greater
than or equal to the number of L<elements|#method elems> of the invocant, then all
element/weight C<Pair>s from the invocant are returned in a random sequence.

What makes C<grabpairs> different from L<pickpairs|#method pickpairs> is that the
'grabbed' elements are in fact removed from the invocant.

    my $breakfast = (eggs => 2, bacon => 3).BagHash;
    say $breakfast.grabpairs;                         # OUTPUT: «bacon => 3␤»
    say $breakfast;                                   # OUTPUT: «BagHash.new(eggs(2))␤»
    say $breakfast.grabpairs(1);                      # OUTPUT: «(eggs => 2)␤»
    say $breakfast.grabpairs(*);                      # OUTPUT: «()␤»

    my $diet = ('eggs' => 2, 'bacon' => 3).Bag;
    say $diet.grabpairs;
    CATCH { default { put .^name, ': ', .Str } };
    # OUTPUT: «X::Immutable: Cannot call 'grabpairs' on an immutable 'Bag'␤»

=head2 method pick

Defined as:

    multi method pick(Baggy:D: --> Any)
    multi method pick(Baggy:D: $count --> Seq:D)

Like an ordinary list L<pick|/type/List#routine_pick>, but returns keys
of the invocant weighted by their values, as if the keys were replicated
the number of times indicated by the corresponding value and then list
pick used. The underlying metaphor for picking is that you're pulling
colored marbles out a bag. (For "picking with replacement" see
L<roll|#method roll> instead). If C<*> is passed as C<$count>, or C<$count> is
greater than or equal to the L<total|#method total> of the invocant, then
C<total> elements from the invocant are returned in a random sequence.

Note that each C<pick> invocation maintains its own private state and has
no effect on subsequent C<pick> invocations.

    my $breakfast = bag <eggs bacon bacon bacon>;
    say $breakfast.pick;                              # OUTPUT: «eggs␤»
    say $breakfast.pick(2);                           # OUTPUT: «(eggs bacon)␤»

    say $breakfast.total;                             # OUTPUT: «4␤»
    say $breakfast.pick(*);                           # OUTPUT: «(bacon bacon bacon eggs)␤»

=head2 method pickpairs

Defined as:

    multi method pickpairs(Baggy:D: --> Pair:D)
    multi method pickpairs(Baggy:D: $count --> Seq:D)

Returns a C<Pair> or a C<Seq> of C<Pair>s depending on the version of the method
being invoked. Each C<Pair> returned has an element of the invocant as its key and the
element's weight as its value. The elements are 'picked' without replacement. If C<*>
is passed as C<$count>, or C<$count> is greater than or equal to the number of
L<elements|#method elems> of the invocant, then all element/weight C<Pair>s from
the invocant are returned in a random sequence.

Note that each C<pickpairs> invocation maintains its own private state and has
no effect on subsequent C<pickpairs> invocations.

    my $breakfast = bag <eggs bacon bacon bacon>;
    say $breakfast.pickpairs;                         # OUTPUT: «eggs => 1␤»
    say $breakfast.pickpairs(1);                      # OUTPUT: «(bacon => 3)␤»
    say $breakfast.pickpairs(*);                      # OUTPUT: «(eggs => 1 bacon => 3)␤»

=head2 method roll

Defined as:

    multi method roll(Baggy:D: --> Any:D)
    multi method roll(Baggy:D: $count --> Seq:D)

Like an ordinary list L<roll|/type/List#routine_roll>, but returns keys of the invocant weighted
by their values, as if the keys were replicated the number of times indicated
by the corresponding value and then list roll used. The underlying
metaphor for rolling is that you're throwing C<$count> dice that are
independent of each other, which (in bag terms) is equivalent to picking
a colored marble out your bag and then putting it back, and doing this
C<$count> times. In dice terms, the number of marbles corresponds to the
number of sides, and the number of marbles of the same color corresponds
to the number of sides with the same color. (For "picking without replacement"
see L<pick|#method pick> instead).

If C<*> is passed to C<$count>, returns a lazy, infinite sequence of randomly
chosen elements from the invocant.

    my $breakfast = bag <eggs bacon bacon bacon>;
    say $breakfast.roll;                                  # OUTPUT: «bacon␤»
    say $breakfast.roll(3);                               # OUTPUT: «(bacon eggs bacon)␤»

    my $random_dishes := $breakfast.roll(*);
    say $random_dishes[^5];                               # OUTPUT: «(bacon eggs bacon bacon bacon)␤»

=head2 method pairs

Defined as:

    method pairs(Baggy:D: --> Seq:D)

Returns all elements and their respective weights as a L<Seq|/type/Seq> of C<Pair>s
where the key is the element itself and the value is the weight of that element.

    my $breakfast = bag <bacon eggs bacon>;
    my $seq = $breakfast.pairs;
    say $seq.sort;                                    # OUTPUT: «(bacon => 2 eggs => 1)␤»

=head2 method antipairs

Defined as:

    method antipairs(Baggy:D: --> Seq:D)

Returns all elements and their respective weights as a L<Seq|/type/Seq> of L<Pairs|/type/Pair>,
where the element itself is the value and the weight of that element is the key, i.e.
the opposite of method L<pairs|#method pairs>.

    my $breakfast = bag <bacon eggs bacon>;
    my $seq = $breakfast.antipairs;
    say $seq.sort;                                    # OUTPUT: «(1 => eggs 2 => bacon)␤»

=head2 method invert

Defined as:

    method invert(Baggy:D: --> Seq:D)

Returns all elements and their respective weights as a L<Seq|/type/Seq> of
L<Pairs|/type/Pair>, where the element itself is the value and the weight of
that element is the key, i.e. the opposite of method L<pairs|#method pairs>.
Except for some esoteric cases, C<invert> on a Baggy type returns the same
result as L<antipairs|#method_antipairs>.

    my $breakfast = bag <bacon eggs bacon>;
    my $seq = $breakfast.invert;
    say $seq.sort;                                    # OUTPUT: «(1 => eggs 2 => bacon)␤»

=head2 method classify-list

Defined as:

    multi method classify-list(&mapper, *@list --> Baggy:D)
    multi method classify-list(%mapper, *@list --> Baggy:D)
    multi method classify-list(@mapper, *@list --> Baggy:D)

Populates a I<mutable> L«C<Baggy>|/type/Baggy» by classifying the
possibly-empty C<@list> of values using the given C<mapper>. The C<@list>
cannot be lazy.

    say BagHash.new.classify-list: { $_ %% 2 ?? 'even' !! 'odd' }, ^10;
    # OUTPUT: BagHash(even(5) odd(5))

    my @mapper = <zero one two three four five>;
    say MixHash.new.classify-list: @mapper, 1, 2, 3, 4, 4, 6;
    # OUTPUT: MixHash((Any) two three four(2) one)

The mapper can be a L«C<Callable>|/type/Callable» that takes a single argument,
an L«C<Associative>|/type/Associative», or an L«C<Iterable>|/type/Iterable».
With L«C<Associative>|/type/Associative» and an L«C<Iterable>|/type/Iterable»
mappers, the values in the C<@list> represent the key and index of the mapper's
value respectively. A L«C<Callable>|/type/Callable» mapper will be executed
once per each item in the C<@list>, with that item as the argument and its
return value will be used as the mapper's value.

The mapper's value is used as the key of the L«C<Baggy>|/type/Baggy» that will
be incremented by C<1>. See L«C<.categorize-list>|/routine/categorize-list» if
you wish to classify an item into multiple categories at once.

B<Note:> unlike the L«C<Hash>|/type/Hash»'s
C<.classify-list>, returning an L«C<Iterable>|/type/Iterable» mapper's value
will throw, as L«C<Baggy>|/type/Baggy» types do not support nested
classification. For the same reason, L«C<Baggy>|/type/Baggy»'s C<.classify-list>
does not accept C<:&as> parameter.

=head2 method categorize-list

Defined as:

    multi method categorize-list(&mapper, *@list --> Baggy:D)
    multi method categorize-list(%mapper, *@list --> Baggy:D)
    multi method categorize-list(@mapper, *@list --> Baggy:D)

Populates a I<mutable> L«C<Baggy>|/type/Baggy» by categorizing the
possibly-empty C<@list> of values using the given C<mapper>. The C<@list>
cannot be lazy.

    say BagHash.new.categorize-list: {
        gather {
            take 'largish' if $_ > 5;
            take .is-prime ?? 'prime' !! 'non-prime';
            take $_ %% 2   ?? 'even'  !! 'odd';
        }
    }, ^10;
    # OUTPUT: BagHash(largish(4) even(5) non-prime(6) prime(4) odd(5))

    my %mapper = :sugar<sweet white>, :lemon<sour>, :cake('sweet', 'is-a-lie');
    say MixHash.new.categorize-list: %mapper, <sugar lemon cake>;
    # OUTPUT: MixHash(is-a-lie sour white sweet(2))

The mapper can be a L«C<Callable>|/type/Callable» that takes a single argument,
an L«C<Associative>|/type/Associative», or an L«C<Iterable>|/type/Iterable».
With L«C<Associative>|/type/Associative» and an L«C<Iterable>|/type/Iterable»
mappers, the values in the C<@list> represent the key and index of the mapper's
value respectively. A L«C<Callable>|/type/Callable» mapper will be executed
once per each item in the C<@list>, with that item as the argument and its
return value will be used as the mapper's value.

The mapper's value is used as a possibly-empty list of keys of the
L«C<Baggy>|/type/Baggy» that will be incremented by C<1>.

B<Note:> unlike the L«C<Hash>|/type/Hash»'s
C<.categorize-list>, returning a list of L«C<Iterables>|/type/Iterable»
as mapper's value will throw, as L«C<Baggy>|/type/Baggy» types do not support
nested categorization. For the same reason, L«C<Baggy>|/type/Baggy»'s
C<.categorize-list> does not accept C<:&as> parameter.

=head2 method keys

Defined as:

    method keys(Baggy:D: --> Seq:D)

Returns a C<Seq> of all keys in the C<Baggy> object without taking
their individual weights into account as opposed to L<kxxv|#method kxxv>.

    my $breakfast = bag <eggs spam spam spam>;
    say $breakfast.keys.sort;                        # OUTPUT: «(eggs spam)␤»

    my $n = ("a" => 5, "b" => 2).BagHash;
    say $n.keys.sort;                                # OUTPUT: «(a b)␤»

=head2 method values

Defined as:

    method values(Baggy:D: --> Seq:D)

Returns a C<Seq> of all values, i.e. weights, in the C<Baggy> object.

    my $breakfast = bag <eggs spam spam spam>;
    say $breakfast.values.sort;                      # OUTPUT: «(1 3)␤»

    my $n = ("a" => 5, "b" => 2, "a" => 1).BagHash;
    say $n.values.sort;                              # OUTPUT: «(2 6)␤»

=head2 method kv

Defined as:

    method kv(Baggy:D: --> Seq:D)

Returns a C<Seq> of keys and values interleaved.

    my $breakfast = bag <eggs spam spam spam>;
    say $breakfast.kv;                                # OUTPUT: «(spam 3 eggs 1)␤»

    my $n = ("a" => 5, "b" => 2, "a" => 1).BagHash;
    say $n.kv;                                        # OUTPUT: «(a 6 b 2)␤»

=head2 method kxxv

Defined as:

    method kxxv(Baggy:D: --> Seq:D)

Returns a C<Seq> of the keys of the invocant, with each key multiplied by its
weight. Note that C<kxxv> only works for C<Baggy> types which have integer
weights, i.e. L<Bag|/type/Bag> and L<BagHash|/type/BagHash>.

    my $breakfast = bag <spam eggs spam spam bacon>;
    say $breakfast.kxxv.sort;                         # OUTPUT: «(bacon eggs spam spam spam)␤»

    my $n = ("a" => 0, "b" => 1, "b" => 2).BagHash;
    say $n.kxxv;                                      # OUTPUT: «(b b b)␤»

=head2 method elems

Defined as:

    method elems(Baggy:D: --> Int:D)

Returns the number of elements in the C<Baggy> object without
taking the individual elements' weight into account.

    my $breakfast = bag <eggs spam spam spam>;
    say $breakfast.elems;                             # OUTPUT: «2␤»

    my $n = ("b" => 9.4, "b" => 2).MixHash;
    say $n.elems;                                     # OUTPUT: «1␤»

=head2 method total

Defined as:

    method total(Baggy:D:)

Returns the sum of weights for all elements in the C<Baggy>
object.

    my $breakfast = bag <eggs spam spam bacon>;
    say $breakfast.total;                             # OUTPUT: «4␤»

    my $n = ("a" => 5, "b" => 1, "b" => 2).BagHash;
    say $n.total;                                     # OUTPUT: «8␤»

=head2 method default

Defined as:

    method default(Baggy:D: --> 0)

Returns zero.

    my $breakfast = bag <eggs bacon>;
    say $breakfast.default;                           # OUTPUT: «0␤»

=head2 method hash

Defined as:

    method hash(Baggy:D: --> Hash:D)

Returns a L<Hash|/type/Hash> where the elements of the invocant
are the keys and their respective weights the values.

    my $breakfast = bag <eggs bacon bacon>;
    my $h = $breakfast.hash;
    say $h.^name;                    # OUTPUT: «Hash[Any,Any]␤»
    say $h;                          # OUTPUT: «{bacon => 2, eggs => 1}␤»

=head2 method Bool

Defined as:

    method Bool(Baggy:D: --> Bool:D)

Returns C<True> if the invocant contains at least one element.

    my $breakfast = ('eggs' => 1).BagHash;
    say $breakfast.Bool;                              # OUTPUT: «True␤»
                                                      # (since we have one element)
    $breakfast<eggs> = 0;                             # weight == 0 will lead to element removal
    say $breakfast.Bool;                              # OUTPUT: «False␤»

=head2 method Set

Defined as:

    method Set(--> Set:D)

Returns a L<Set|/type/Set> whose elements are the L<keys|#method keys> of the invocant.

    my $breakfast = (eggs => 2, bacon => 3).BagHash;
    say $breakfast.Set;                               # OUTPUT: «Set(bacon eggs)␤»

=head2 method SetHash

Defined as:

    method SetHash(--> SetHash:D)

Returns a L<SetHash|/type/SetHash> whose elements are the L<keys|#method keys> of the invocant.

    my $breakfast = (eggs => 2, bacon => 3).BagHash;
    my $sh = $breakfast.SetHash;
    say $sh.^name;                            # OUTPUT: «SetHash␤»
    say $sh.elems;                            # OUTPUT: «2␤»

=head2 method ACCEPTS

Defined as:

    method ACCEPTS($other --> Bool:D)

Used in smartmatching if the right-hand side is a C<Baggy>.

If the right-hand side is the type object, i.e. C<Baggy>, the method
returns C<True> if C<$other> L<does|/routine/does#class_Mu> C<Baggy>
otherwise C<False> is returned.

If the right-hand side is a C<Baggy> object, C<True> is returned only if
C<$other> has the same elements, with the same weights, as the invocant.

    my $breakfast = bag <eggs bacon>;
    say $breakfast ~~ Baggy;                            # OUTPUT: «True␤»
    say $breakfast.does(Baggy);                         # OUTPUT: «True␤»

    my $second-breakfast = (eggs => 1, bacon => 1).Mix;
    say $breakfast ~~ $second-breakfast;                # OUTPUT: «True␤»

    my $third-breakfast = (eggs => 1, bacon => 2).Bag;
    say $second-breakfast ~~ $third-breakfast;          # OUTPUT: «False␤»

=head1 See Also

L<Sets, Bags, and Mixes|/language/setbagmix>

=end pod

# vim: expandtab softtabstop=4 shiftwidth=4 ft=perl6
