txt: ## dist_zef-antononcube-ML-TriesWithFrequencies.md
  # Raku ML::TriesWithFrequencies
  
  [![MacOS](https://github.com/antononcube/Raku-ML-TriesWithFrequencies/actions/workflows/macos.yml/badge.svg)](https://github.com/antononcube/Raku-ML-TriesWithFrequencies/actions/workflows/macos.yml)
  [![Linux](https://github.com/antononcube/Raku-ML-TriesWithFrequencies/actions/workflows/linux.yml/badge.svg)](https://github.com/antononcube/Raku-ML-TriesWithFrequencies/actions/workflows/linux.yml)
  [![Win64](https://github.com/antononcube/Raku-ML-TriesWithFrequencies/actions/workflows/windows.yml/badge.svg)](https://github.com/antononcube/Raku-ML-TriesWithFrequencies/actions/workflows/windows.yml)
  [![](https://img.shields.io/badge/License-Artistic%202.0-0298c3.svg)](https://opensource.org/licenses/Artistic-2.0)
  [![](https://raku.land/zef:antononcube/ML::TriesWithFrequencies/badges/version)](https://raku.land/zef:antononcube/ML::TriesWithFrequencies)
  [![](https://raku.land/zef:antononcube/ML::TriesWithFrequencies/badges/downloads)](https://raku.land/zef:antononcube/ML::TriesWithFrequencies)
  
  This Raku package has functions for creation and manipulation of
  [Tries (Prefix trees)](https://en.wikipedia.org/wiki/Trie)
  with frequencies.
  
  The package provides Machine Learning (ML) functionalities,
  not "just" a Trie data structure.
  
  This Raku implementation closely follows the Java implementation [AAp3].
  
  The subset of functions with the prefix "trie-" follows the one used in the Mathematica package [AAp2].
  That is the "top-level" sub-system of function names; the sub-system is follows the typical Object-Oriented Programming (OOP)
  Raku style.
  
  **Remark:** Below Mathematica and Wolfram Language (WL) are used as synonyms.
  
  **Remark:** There is a Raku package with an alternative implementation, [AAp6],
  made mostly for comparison studies. (See the implementation notes below.)
  The package in this repository, `ML::TriesWithFrequencies`, is my *primary*
  Tries-with-frequencies package.
  
  ---
  
  ## Installation
  
  Via zef-ecosystem:
  
  ```
  zef install ML::TriesWithFrequencies
  
  ```
  
  From GitHub:
  
  ```
  zef install https://github.com/antononcube/Raku-ML-TriesWithFrequencies
  
  ```
  
  ---
  
  ## Usage
  
  Consider a trie (prefix tree) created over a list of words:
  
  ```
  use ML::TriesWithFrequencies;
  my $tr = trie-create-by-split( <bar bark bars balm cert cell> );
  trie-say($tr);
  
  ```
  
  ```
  # TRIEROOT => 6
  # ├─b => 4
  # │ └─a => 4
  # │   ├─l => 1
  # │   │ └─m => 1
  # │   └─r => 3
  # │     ├─k => 1
  # │     └─s => 1
  # └─c => 2
  #   └─e => 2
  #     ├─l => 1
  #     │ └─l => 1
  #     └─r => 1
  #       └─t => 1
  
  ```
  
  Here we convert the trie with frequencies above into a trie with probabilities:
  
  ```
  my $ptr = trie-node-probabilities( $tr );
  trie-say($ptr);
  
  ```
  
  ```
  # TRIEROOT => 1
  # ├─b => 0.6666666666666666
  # │ └─a => 1
  # │   ├─l => 0.25
  # │   │ └─m => 1
  # │   └─r => 0.75
  # │     ├─k => 0.3333333333333333
  # │     └─s => 0.3333333333333333
  # └─c => 0.3333333333333333
  #   └─e => 1
  #     ├─l => 0.5
  #     │ └─l => 1
  #     └─r => 0.5
  #       └─t => 1
  
  ```
  
  Here we shrink the trie with probabilities above:
  
  ```
  trie-say(trie-shrink($ptr));
  
  ```
  
  ```
  # TRIEROOT => 1
  # ├─ba => 0.6666666666666666
  # │ ├─lm => 0.25
  # │ └─r => 0.75
  # │   ├─k => 0.3333333333333333
  # │   └─s => 0.3333333333333333
  # └─ce => 0.3333333333333333
  #   ├─ll => 0.5
  #   └─rt => 0.5
  
  ```
  
  Here we retrieve a sub-trie with a key:
  
  ```
  trie-say(trie-retrieve($ptr, 'bar'.comb))
  
  ```
  
  ```
  # r => 0.75
  # ├─k => 0.3333333333333333
  # └─s => 0.3333333333333333
  
  ```
  
  Here is a "dot-pipeline" that combines the steps above:
  
  ```
  <bar bark bars balm cert cell>.&trie-create-by-split
  .node-probabilities
  .shrink
  .retrieve(<ba r>)        
  .form
  
  ```
  
  ```
  # r => 0.75
  # ├─k => 0.3333333333333333
  # └─s => 0.3333333333333333
  
  ```
  
  **Remark:** In the pipeline above we retrieve with `<ba r>`, not with `<b a r>`,
  because the trie is already shrunk.
  
  The package provides a fair amount of functions in order to facilitate ML applications.
  In support of that statement, here are the methods of `ML::TriesWithFrequencies::Trie`:
  
  ```
  ML::TriesWithFrequencies::Trie.^method_names
  
  ```
  
  ```
  # (clone make merge insert create create-by-split node-probabilities leaf-probabilities leafQ position retrieve has-complete-match contains is-key shrink node-counts remove-by-threshold remove-by-pareto-fraction remove-by-regex select-by-threshold select-by-pareto-fraction select-by-regex root-to-leaf-paths words words-with-probabilities classify echo echo-function form trieRootLabel trieValueLabel getKey getValue getChildren setKey setValue setChildren to-map-format hash WL XML JSON Str random-choice from-map-format from-json-map-format new gist key value children BUILDALL)
  
  ```
  
  Generate random words using trie, make a new trie, and visualize it:
  
  ```
  my @randomWords = $ptr.random-choice(200):drop-root;
  my $ptrRandom = trie-create(@randomWords).node-probabilities;
  $ptrRandom.form;
  
  ```
  
  ```
  # TRIEROOT => 1
  # ├─b => 0.675
  # │ └─a => 1
  # │   ├─l => 0.2
  # │   │ └─m => 1
  # │   └─r => 0.8
  # │     ├─k => 0.4351851851851852
  # │     └─s => 0.46296296296296297
  # └─c => 0.325
  #   └─e => 1
  #     ├─l => 0.5384615384615384
  #     │ └─l => 1
  #     └─r => 0.46153846153846156
  #       └─t => 1
  
  ```
  
  Compare with the original one:
  
  ```
  $ptr.form
  
  ```
  
  ```
  # TRIEROOT => 1
  # ├─b => 0.6666666666666666
  # │ └─a => 1
  # │   ├─l => 0.25
  # │   │ └─m => 1
  # │   └─r => 0.75
  # │     ├─k => 0.3333333333333333
  # │     └─s => 0.3333333333333333
  # └─c => 0.3333333333333333
  #   └─e => 1
  #     ├─l => 0.5
  #     │ └─l => 1
  #     └─r => 0.5
  #       └─t => 1
  
  ```
  
  **Remark:** It is expected with large numbers of generated words to get frequencies
  very close to those of the original trie.
  
  ---
  
  ## Representation
  
  Each trie is a tree of objects of the class `ML::TriesWithFrequencies::Trie`.
  Such trees can be nicely represented as hash-maps. For example:
  
  ```
  my $tr = trie-shrink(trie-create-by-split(<core cort>));
  say $tr.gist;
  
  ```
  
  ```
  # {TRIEROOT => {TRIEVALUE => 2, cor => {TRIEVALUE => 2, e => {TRIEVALUE => 1}, t => {TRIEVALUE => 1}}}}
  
  ```
  
  The function `trie-say` uses that Hash-representation:
  
  ```
  trie-say($tr)
  
  ```
  
  ```
  # TRIEROOT => 2
  # └─cor => 2
  #   ├─e => 1
  #   └─t => 1
  
  ```
  
  ### JSON
  
  The JSON-representation follows the inherent object-tree
  representation with `ML::TriesWithFrequencies::Trie`:
  
  ```
  say $tr.JSON;
  
  ```
  
  ```
  # {"key":"TRIEROOT", "value":2, "children":[{"key":"cor", "value":2, "children":[{"key":"e", "value":1, "children":[]}, {"key":"t", "value":1, "children":[]}]}]}
  
  ```
  
  ### XML
  
  The XML-representation follows (resembles) the Hash-representation
  (and output from `trie-say`):
  
  ```
  say $tr.XML;
  
  ```
  
  ```
  # <TRIEROOT>
  #  <TRIEVALUE>2</TRIEVALUE>
  #  <cor>
  #   <TRIEVALUE>2</TRIEVALUE>
  #   <e>
  #    <TRIEVALUE>1</TRIEVALUE>
  #   </e>
  #   <t>
  #    <TRIEVALUE>1</TRIEVALUE>
  #   </t>
  #  </cor>
  # </TRIEROOT>
  
  ```
  
  Using the XML representation allows for
  [XPath](https://www.w3schools.com/xml/xml_xpath.asp)
  searches, say, using the package
  [`XML::XPath`](https://github.com/ufobat/p6-XML-XPath).
  Here is an example:
  
  ```
  use XML::XPath;
  my $tr0 = trie-create-by-split(<bell best>);
  trie-say($tr0);
  
  ```
  
  ```
  # TRIEROOT => 2
  # └─b => 2
  #   └─e => 2
  #     ├─l => 1
  #     │ └─l => 1
  #     └─s => 1
  #       └─t => 1
  
  ```
  
  Convert to XML:
  
  ```
  say $tr0.XML;
  
  ```
  
  ```
  # <TRIEROOT>
  #  <TRIEVALUE>2</TRIEVALUE>
  #  <b>
  #   <TRIEVALUE>2</TRIEVALUE>
  #   <e>
  #    <TRIEVALUE>2</TRIEVALUE>
  #    <l>
  #     <TRIEVALUE>1</TRIEVALUE>
  #     <l>
  #      <TRIEVALUE>1</TRIEVALUE>
  #     </l>
  #    </l>
  #    <s>
  #     <TRIEVALUE>1</TRIEVALUE>
  #     <t>
  #      <TRIEVALUE>1</TRIEVALUE>
  #     </t>
  #    </s>
  #   </e>
  #  </b>
  # </TRIEROOT>
  
  ```
  
  Search for `<b e l>`:
  
  ```
  say XML::XPath.new(xml=>$tr0.XML).find('//b/e/l');
  
  ```
  
  ```
  # <l>
  #     <TRIEVALUE>1</TRIEVALUE> 
  #     <l>
  #      <TRIEVALUE>1</TRIEVALUE> 
  #     </l> 
  #    </l>
  
  ```
  
  ### WL
  
  The Hash-representation is used in the Mathematica package [AAp2].
  Hence, such WL format is provided by the Raku package:
  
  ```
  say $tr.WL;
  
  ```
  
  ```
  # <|$TrieRoot -> <|$TrieValue -> 2, "cor" -> <|$TrieValue -> 2, "e" -> <|$TrieValue -> 1|>, "t" -> <|$TrieValue -> 1|>|>|>|>
  
  ```
  
  ---
  
  ## Cloning
  
  All `trie-*` functions and `ML::TriesWithFrequencies::Trie` methods that manipulate tries produce trie clones.
  
  For performance reasons I considered having in-place trie manipulations, but that, of course, confuses reasoning
  in development, testing, and usage. Hence, ubiquitous cloning.
  
  ---
  
  ## Two stiles of pipelining
  
  As it was mentioned above the package was initially developed to have the functional programming design
  of the Mathematica package [AAp2]. With that design and using the
  [feed operator `==>`](https://docs.raku.org/language/operators#infix_==%3E)
  we can construct pipelines like this one:
  
  ```
  my @words2 = <bar barman bask bell belly>;
  my @words3 = <call car cast>;
  
  trie-create-by-split(@words2)==>
  trie-merge(trie-create-by-split(@words3))==>
  trie-node-probabilities==>
  trie-shrink==>
  trie-say
  
  ```
  
  ```
  # TRIEROOT => 1
  # ├─b => 0.625
  # │ ├─a => 0.6
  # │ │ ├─r => 0.6666666666666666
  # │ │ │ └─man => 0.5
  # │ │ └─sk => 0.3333333333333333
  # │ └─ell => 0.4
  # │   └─y => 0.5
  # └─ca => 0.375
  #   ├─ll => 0.3333333333333333
  #   ├─r => 0.3333333333333333
  #   └─st => 0.3333333333333333
  
  ```
  
  The package also supports "dot pipelining" through chaining of methods:
  
  ```
  @words2.&trie-create-by-split
          .merge(@words3.&trie-create-by-split)
          .node-probabilities
          .shrink
          .form
  
  ```
  
  ```
  # TRIEROOT => 1
  # ├─b => 0.625
  # │ ├─a => 0.6
  # │ │ ├─r => 0.6666666666666666
  # │ │ │ └─man => 0.5
  # │ │ └─sk => 0.3333333333333333
  # │ └─ell => 0.4
  # │   └─y => 0.5
  # └─ca => 0.375
  #   ├─ll => 0.3333333333333333
  #   ├─r => 0.3333333333333333
  #   └─st => 0.3333333333333333
  
  ```
  
  **Remark:** The `trie-*` functions are implemented through the methods of `ML::TriesWithFrequencies::Trie`.
  Given the method the corresponding function is derived by adding the prefix `trie-`.
  (For example, `$tr.shrink` vs `trie-shrink($tr)`.)
  
  Here is the previous pipeline re-written to use only methods of `ML::TriesWithFrequencies::Trie`:
  
  ```
  ML::TriesWithFrequencies::Trie.create-by-split(@words2)
          .merge(ML::TriesWithFrequencies::Trie.create-by-split(@words3))
          .node-probabilities
          .shrink
          .form
  
  ```
  
  ---
  
  ## Implementation notes
  
  ### UML diagram
  
  Here is a UML diagram that shows package's structure:
  
  ```
  use UML::Translators;
  to-uml-spec('ML::TriesWithFrequencies', format => 'mermaid')
  
  ```
  
  ```
  classDiagram
  class ML_TriesWithFrequencies_PathsGatherer {
    +$!ulp
    +@!tracedPaths
    +BUILDALL()
    +new()
    +trace()
    +tracedPaths()
    +trie-trace()
    +ulp()
  }
  
  
  class TRIEROOT {
    <<constant>>
  }
  TRIEROOT --|> Stringy
  
  
  class TRIEVALUE {
    <<constant>>
  }
  TRIEVALUE --|> Stringy
  
  
  class ML_TriesWithFrequencies_ChildRandomChooser {
    +$!ulp
    +$!weighted
    +@!tracedPaths
    +BUILDALL()
    +new()
    +trace()
    +tracedPaths()
    +trie-trace()
    +ulp()
    +weighted()
  }
  
  
  class ML_TriesWithFrequencies_Trieish {
    <<role>>
    +$!key
    +$!value
    +%!children
    +JSON()
    +Str()
    +WL()
    +XML()
    +clone()
    +getChildren()
    +getKey()
    +getValue()
    +hash()
    +setChildren()
    +setKey()
    +setValue()
    +to-map-format()
    +trieRootLabel()
    +trieValueLabel()
  }
  
  
  class ML_TriesWithFrequencies_ParetoBasedRemover {
    +$!pareto-fraction
    +$!postfix
    +$!remove-bottom
    +BUILDALL()
    +new()
    +pareto-fraction()
    +postfix()
    +remove()
    +remove-bottom()
    +trie-map()
    +trie-pareto-remove()
  }
  ML_TriesWithFrequencies_ParetoBasedRemover --|> ML_TriesWithFrequencies_TrieTraverse
  
  
  class ML_TriesWithFrequencies_ThresholdBasedRemover {
    +$!below-threshold
    +$!postfix
    +$!threshold
    +BUILDALL()
    +below-threshold()
    +new()
    +postfix()
    +remove()
    +threshold()
    +trie-map()
    +trie-threshold-remove()
  }
  ML_TriesWithFrequencies_ThresholdBasedRemover --|> ML_TriesWithFrequencies_TrieTraverse
  
  
  class ML_TriesWithFrequencies_RegexBasedRemover {
    +$!invert
    +$!key-pattern
    +$!postfix
    +BUILDALL()
    +invert()
    +key-pattern()
    +new()
    +postfix()
    +remove()
    +trie-map()
    +trie-regex-remove()
  }
  ML_TriesWithFrequencies_RegexBasedRemover --|> ML_TriesWithFrequencies_TrieTraverse
  
  
  class ML_TriesWithFrequencies_TrieTraverse {
    <<role>>
  }
  
  
  class ML_TriesWithFrequencies_Trie {
    +$!key
    +$!value
    +%!children
    +BUILDALL()
    +JSON()
    +Str()
    +WL()
    +XML()
    +children()
    +classify()
    +clone()
    +contains()
    +create()
    +create-by-split()
    +echo()
    +echo-function()
    +form()
    +from-json-map-format()
    +from-map-format()
    +getChildren()
    +getKey()
    +getValue()
    +gist()
    +has-complete-match()
    +hash()
    +insert()
    +is-key()
    +key()
    +leaf-probabilities()
    +leafQ()
    +make()
    +merge()
    +new()
    +node-counts()
    +node-probabilities()
    +position()
    +random-choice()
    +remove-by-pareto-fraction()
    +remove-by-regex()
    +remove-by-threshold()
    +retrieve()
    +root-to-leaf-paths()
    +select-by-pareto-fraction()
    +select-by-regex()
    +select-by-threshold()
    +setChildren()
    +setKey()
    +setValue()
    +shrink()
    +to-map-format()
    +trieRootLabel()
    +trieValueLabel()
    +value()
    +words()
    +words-with-probabilities()
  }
  ML_TriesWithFrequencies_Trie --|> ML_TriesWithFrequencies_Trieish
  
  
  class ML_TriesWithFrequencies_LeafProbabilitiesGatherer {
    +$!counts-trie
    +$!ulp
    +BUILDALL()
    +counts-trie()
    +new()
    +trace()
    +trie-trace()
    +ulp()
  }
  
  ```
  
  **Remark:** The function `to-uml-spec` is provided by the package ["UML::Translators"](https://raku.land/zef:antononcube/UML::Translators), [AAp7].
  
  ### Performance
  
  This package is a Raku re-implementation of the Java Trie package [AAp3].
  
  The initial implementation was:
  
  * ≈ 5-6 times slower than the Mathematica implementation [AAp2]
  * ≈ 100 times slower than the Java implementation [AAp3]
  
  The initial implementation used:
  
  * General types for Trie nodes, i.e. `Str` for the key and `Numeric` for the value
  * Argument type verification with `where` statements in the signatures of the `trie-*` functions
  
  After reading [RAC1] I refactored the code to use native types (`num`, `str`)
  and moved the `where` verifications inside the functions.
  
  I also refactored the function `trie-merge` to use less copying of data and
  to take into account which of the two tries has smaller number of children.
  
  After those changes the current Raku implementation is:
  
  * ≈ 2.5 times slower than the Mathematica implementation [AAp2]
  * ≈ 40 times slower than the Java implementation [AAp3]
  
  After the (monumental) work on
  [the new MoarVM dispatch mechanism](https://6guts.wordpress.com/2021/09/29/the-new-moarvm-dispatch-mechanism-is-here/),
  [JW1], was incorporated in standard Rakudo releases (September/October 2021)
  additional 20% speed-up was obtained. Currently this package is:
  
  * ≈ 2.0 times slower than the Mathematica implementation [AAp2]
  * ≈ 30 times slower than the Java implementation [AAp3]
  
  These speed improvements are definitely not satisfactory. I strongly consider:
  
  1. Re-implementing in Raku the Mathematica package [AAp2], i.e. to move into Tries that are hashes.
  
     * (It turned out option 1 does not produce better results; see [AAp6].)
  2. Re-implementing in C or C++ the Java package [AAp3] and hooking it up to Raku.
  
  ### Moving from FP design and OOP design
  
  The initial versions of the package -- up to version 0.5.0 -- had exported functions only
  in the namespace `ML::TriesWithFrequencies` with the prefix `trie-`.
  Those functions came from a purely Functional Programming (FP) design.
  
  In order to get chains of OOP methods application that
  are typical in Raku programming the package versions after version 0.6.0 and later have trie
  manipulation transformation methods in the class `ML::TriesWithFrequencies::Trie`.
  
  In order to get trie-class methods a fairly fundamental code refactoring was required.
  Here are the steps:
  
  1. The old class `ML::TriesWithFrequencies::Trie` was made into the role
     `ML::TriesWithFrequencies::Trieish`.
  2. The traversal and remover classes were made to use `ML::TriesWithFrequencies::Trieish` type
     instead of `ML::TriesWithFrequencies::Trie`.
  3. The trie functions implementations -- with the prefix "trie-" --
     of `ML::TriesWithFrequencies` were moved as methods implementations in `ML::TriesWithFrequencies::Trie`.
  4. The trie functions in `ML::TriesWithFrequencies` were reimplemented using the methods
     of `ML::TriesWithFrequencies::Trie`.
  
  **Remark:** See the section "Two stiles of pipelining" above for illustrations of the two approaches.
  
  ---
  
  ## TODO
  
  In the following list the most important items are placed first.
  
  * DONE Implement "get words" and "get root-to-leaf paths" functions.
  
    * See `trie-words` and `trie-root-to-leaf-paths`.
  * DONE Convert most of the WL unit tests in [AAp5] into Raku tests.
  * DONE Implement Trie traversal functions.
  
    * The general `trie-map` function is in a separate role.
    * A concrete traversal functionality is a class that does the role
      and provides additional context.
  * DONE Implement (sub-)trie removal functions.
  
    * DONE By threshold (below and above)
    * DONE By Pareto principle adherence (top and bottom)
    * DONE By regex over the keys
  * TODO Implement optional ULP spec argument for relevant functions:
  
    * DONE `trie-root-to-leaf-paths`
    * DONE `trie-words`
    * TODO Membership test functions?
  * DONE Design and code refactoring so trie objects to have OOP interface.
  
    * Instead of just having `trie-words($tr, <c>)` we should be also able to say `$tr.trie-words(<c>)`.
  * TODO Implement `trie-prune` function.
  * DONE Implement Trie-based classification.
  * DONE Create trie from hash representation.
  * TODO Investigate faster implementations.
  
    * DONE Re-implement the Trie functionalities using hash representation (instead of a tree of Trie-node objects.)
  
      * See [AAp6].
    * TODO Make a C or C++ implementation and hook it up to Raku.
  * DONE Program a trie-form visualization that is "wide", i.e. places the children nodes horizontally.
  
    * Using "Pretty::Table".
    * Using the function `to-pretty-table` of "Data::Reshapers". (Also based on "Pretty::Table".)
  * TODO Document examples of doing Trie-based text mining or data-mining.
  
  ---
  
  ## References
  
  ### Articles
  
  [AA1] Anton Antonov,
  ["Tries with frequencies for data mining"](https://mathematicaforprediction.wordpress.com/2013/12/06/tries-with-frequencies-for-data-mining/),
  (2013),
  [MathematicaForPrediction at WordPress](https://mathematicaforprediction.wordpress.com).
  
  [AA2] Anton Antonov,
  ["Removal of sub-trees in tries"](https://mathematicaforprediction.wordpress.com/2014/10/12/removal-of-sub-trees-in-tries/),
  (2013),
  [MathematicaForPrediction at WordPress](https://mathematicaforprediction.wordpress.com).
  
  [AA3] Anton Antonov,
  ["Tries with frequencies in Java"](https://mathematicaforprediction.wordpress.com/2017/01/31/tries-with-frequencies-in-java/),
  (2017),
  [MathematicaForPrediction at WordPress](https://mathematicaforprediction.wordpress.com).
  [GitHub Markdown](https://github.com/antononcube/MathematicaForPrediction).
  
  [JW1] Jonathan Worthington,
  ["The new MoarVM dispatch mechanism is here!"](https://6guts.wordpress.com/2021/09/29/the-new-moarvm-dispatch-mechanism-is-here/),
  (2021),
  [6guts at WordPress](https://6guts.wordpress.com).
  
  [RAC1] Tib,
  ["Day 10: My 10 commandments for Raku performances"](https://raku-advent.blog/2020/12/10/day-10-my-10-commandments-for-raku-performances/),
  (2020),
  [Raku Advent Calendar](https://raku-advent.blog).
  
  [WK1] Wikipedia entry, [Trie](https://en.wikipedia.org/wiki/Trie).
  
  ### Packages
  
  [AAp1] Anton Antonov,
  [Tries with frequencies Mathematica Version 9.0 package](https://github.com/antononcube/MathematicaForPrediction/blob/master/TriesWithFrequenciesV9.m),
  (2013),
  [MathematicaForPrediction at GitHub](https://github.com/antononcube/MathematicaForPrediction).
  
  [AAp2] Anton Antonov,
  [Tries with frequencies Mathematica package](https://github.com/antononcube/MathematicaForPrediction/blob/master/TriesWithFrequencies.m),
  (2013-2018),
  [MathematicaForPrediction at GitHub](https://github.com/antononcube/MathematicaForPrediction).
  
  [AAp3] Anton Antonov,
  [Tries with frequencies in Java](https://github.com/antononcube/MathematicaForPrediction/tree/master/Java/TriesWithFrequencies),
  (2017),
  [MathematicaForPrediction at GitHub](https://github.com/antononcube/MathematicaForPrediction).
  
  [AAp4] Anton Antonov,
  [Java tries with frequencies Mathematica package](https://github.com/antononcube/MathematicaForPrediction/blob/master/JavaTriesWithFrequencies.m),
  (2017),
  [MathematicaForPrediction at GitHub](https://github.com/antononcube/MathematicaForPrediction).
  
  [AAp5] Anton Antonov,
  [Java tries with frequencies Mathematica unit tests](https://github.com/antononcube/MathematicaForPrediction/blob/master/UnitTests/JavaTriesWithFrequencies-Unit-Tests.wlt),
  (2017),
  [MathematicaForPrediction at GitHub](https://github.com/antononcube/MathematicaForPrediction).
  
  [AAp6] Anton Antonov,
  [ML::HashTriesWithFrequencies Raku package](https://github.com/antononcube/Raku-ML-HashTriesWithFrequencies),
  (2021),
  [GitHub/antononcube](https://github.com/antononcube).
  
  [AAp7] Anton Antonov,
  [UML::Translators Raku package](https://raku.land/zef:antononcube/UML::Translators),
  (2022),
  [GitHub/antononcube](https://github.com/antononcube).
  
  ### Videos
  
  [AAv1] Anton Antonov,
  ["Prefix Trees with Frequencies for Data Analysis and Machine Learning"](https://www.youtube.com/watch?v=MdVp7t8xQbQ),
  (2017),
  Wolfram Technology Conference 2017,
  [Wolfram channel at YouTube](https://www.youtube.com/channel/UCJekgf6k62CQHdENWf2NgAQ).
  