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

=TITLE class MixHash

=SUBTITLE Mutable collection of distinct objects with Real weights

    class MixHash does Mixy { }

A C<MixHash> is a mutable mix, meaning a collection of distinct elements in no
particular order that each have a real-number weight assigned to them. (For
I<immutable> mixes, see L<Mix|/type/Mix> instead.)

Objects/values of any type are allowed as mix elements. Within a C<MixHash>,
items that would compare positively with the L<===|/routine/===> operator are considered the
same element, with a combined weight.

=begin code
my $recipe = (butter => 0.22, sugar => 0.1,
              flour => 0.275, sugar => 0.02).MixHash;

say $recipe.elems;      # OUTPUT: «3␤»
say $recipe.keys.sort;  # OUTPUT: «butter flour sugar␤»
say $recipe.pairs.sort; # OUTPUT: «"butter" => 0.22 "flour" => 0.275 "sugar" => 0.12␤»
say $recipe.total;      # OUTPUT: «0.615␤»
=end code

C<MixHash>es can be treated as object hashes using the
L«C<{ }> postcircumfix operator|/language/operators#postcircumfix_{_}»,
or the
L«C< < > > postcircumfix operator|/language/operators#postcircumfix_<_>»
for literal string keys, which
returns the corresponding numeric weight for keys that are
elements of the mix, and C<0> for keys that aren't. It can also be used to modify
weights; Setting a weight to C<0> automatically removes that element from the
mix, and setting a weight to a non-zero number adds that element if it didn't
already exist:

=begin code
my $recipe = (butter => 0.22, sugar => 0.1,
              flour => 0.275, sugar => 0.02).MixHash;

say $recipe<butter>;     # OUTPUT: «0.22␤»
say $recipe<sugar>;      # OUTPUT: «0.12␤»
say $recipe<chocolate>;  # OUTPUT: «0␤»

$recipe<butter> = 0;
$recipe<chocolate> = 0.30;
say $recipe.pairs;       # OUTPUT: «"sugar" => 0.12 "flour" => 0.275 "chocolate" => 0.3␤»
=end code


=head1 Creating C<MixHash> objects

C<MixHash>es can be composed using C<MixHash.new>. Any positional parameters,
regardless of their type, become elements of the mix - with a weight of C<1> for
each time the parameter occurred:

    my $n = MixHash.new: "a", "a", "b" => 0, "c" => 3.14;
    say $n.keys.map(&WHAT);  # OUTPUT: «((Str) (Pair) (Pair))␤»
    say $n.pairs;            # OUTPUT: «(a => 2 (c => 3.14) => 1 (b => 0) => 1)␤»

Alternatively, the C<.MixHash> coercer (or its functional form, C<MixHash()>)
can be called on an existing object to coerce it to a C<MixHash>. Its semantics
depend on the type and contents of the object. In general it evaluates the
object in list context and creates a mix with the resulting items as elements,
although for Hash-like objects or Pair items, only the keys become elements of
the mix, and the (cumulative) values become the associated numeric weights:

    my $n = ("a", "a", "b" => 0, "c" => 3.14).MixHash;
    say $n.keys.map(&WHAT);  # OUTPUT: «((Str) (Str))␤»
    say $n.pairs;            # OUTPUT: «(a => 2 c => 3.14)␤»

Since 6.d (2019.03 and later) it is also possible to specify the type of values
you would like to allow in a C<MixHash>.  This can either be done when calling
C<.new>:

    # only allow strings
    my $n = MixHash[Str].new: <a b b c c c>;

or using the masquerading syntax:

    # only allow strings
    my %mh is MixHash[Str] = <a b b c c c>;
    say %mh<b>;  # 2
    say %mh<d>;  # 0

    # only allow whole numbers
    my %mh is MixHash[Int] = <a b b c c c>;
    # Type check failed in binding; expected Int but got Str ("a")

=head1 Operators

See L<Operators with set
semantics|/language/setbagmix#Operators_with_set_semantics> for a complete
list of "set operators" applicable to, among other types, C<MixHash>.

Examples:

=begin code
my ($a, $b) = MixHash(2 => 2, 4), MixHash(2 => 1.5, 3 => 2, 4);

say $a (<) $b;   # OUTPUT: «False␤»
say $a (<=) $b;  # OUTPUT: «False␤»
say $a (^) $b;   # OUTPUT: «MixHash(2(0.5) 3(2))␤»
say $a (+) $b;   # OUTPUT: «MixHash(2(3.5) 4(2) 3(2))␤»

# Unicode versions:
say $a ⊂ $b;  # OUTPUT: «False␤»
say $a ⊆ $b;  # OUTPUT: «False␤»
say $a ⊖ $b;  # OUTPUT: «MixHash(2(0.5) 3(2))␤»
say $a ⊎ $b;  # OUTPUT: «MixHash(2(3.5) 4(2) 3(2))␤»
=end code

=head1 Note on C<reverse> and ordering.

MixHash inherits C<reverse> from L<Any|/type/Any#routine_reverse>,
however, C<Mix>es do not have an inherent order and you should not trust
it returning a consistent output.

If you sort a MixHash, the result is a list of pairs, at which point
C<reverse> makes perfect sense:

=begin code
my $a = MixHash.new(2, 2, 18, 3, 4);
say $a;  # OUTPUT: «MixHash(18 2(2) 3 4)␤»

say $a.sort;  # OUTPUT: «(2 => 2 3 => 1 4 => 1 18 => 1)␤»
say $a.sort.reverse;  # OUTPUT: «(18 => 1 4 => 1 3 => 1 2 => 2)␤»
=end code

=head1 Methods

=head2 method Bag

Defined as:

    method Bag (--> Bag:D)

Coerces the C<MixHash> to a L«C<Bag>|/type/Bag». The weights are converted
to L«C<Int>|/type/Int»,
which means the number of keys in the resulting C<Bag> can be fewer than in the
original C<MixHash>, if any of the weights are negative or truncate to zero.

=head2 method BagHash

Defined as:

    method BagHash (--> BagHash:D)

Coerces the C<MixHash> to a L«C<BagHash>|/type/BagHash». The weights are converted
to L«C<Int>|/type/Int»,
which means the number of keys in the resulting C<BagHash> can be fewer than in the
original C<MixHash>, if any of the weights are negative or truncate to zero.

=head1 See Also

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

=end pod

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