Full Code of MarkBaggett/freq for AI

master e13f41f43e5f cached
9 files
66.3 KB
25.7k tokens
28 symbols
1 requests
Download .txt
Repository: MarkBaggett/freq
Branch: master
Commit: e13f41f43e5f
Files: 9
Total size: 66.3 KB

Directory structure:
gitextract_2pt2i8lf/

├── CONTRIBUTE.md
├── LICENSE
├── README.md
├── freq.py
├── freq_server.py
├── freq_sort.py
├── freqtable2018.freq
├── systemd/
│   └── freq.service
└── upstart/
    └── sample_upstart.config

================================================
FILE CONTENTS
================================================

================================================
FILE: CONTRIBUTE.md
================================================
# Contribute.md

## Team members

* [Mark Baggett](https://github.com/MarkBaggett)

## Adding new features

If you're interested in adding new features or fixing issues to the `freq` project, the overall process is:

1. Select an issue to work on and assign the issue to yourself.  If not already created, first create the issue.
2. Fork the project.
3. Make the changes, including any additional test cases that should be added.
4. Create a Pull Request against the `master` branch of this repo.
5. After a review of the PR, make any changes as requested by the reviewer.
6. The PR will then be merged into the repo.

Don’t get discouraged! We estimate that the response time from the maintainers is around a few days.

## Bug Triage

We welcome the reporting of issues and do so by using the [integrated Github issues](https://github.com/MarkBaggett/freq/issues).  When creating a new issue, you can help us diagnose and fix existing bugs by asking and providing answers for the following:

* Is the bug reproducible as explained?
* Is it reproducible in other environments (for instance, different versions of python, or different operating systems)?
* Are the steps to reproduce the bug clear? If not, can you describe how you might reproduce it?
* What tags should the bug have?

## Documentation

If you're interested in working on the documentation for the project, feel free to do so as well.  The same process for adding new features goes for adding documentation.


================================================
FILE: LICENSE
================================================
MIT License

Copyright (c) 2018 Mark Baggett

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.


================================================
FILE: README.md
================================================
# freq

This is a repository for `freq.py` and `freq_server.py`.

## Background:

While sitting in SANS SEC511 I listened to [`@sethmisenar`](https://twitter.com/sethmisenar) laement the difficulty in using existing tools to detect DGA (Domain Generation Algorithm) hostnames often used by malware.  There are lots of AI based tools out there that do this but some are rather complex. I thought I could quickly write a tool that would work.  In about 30 minutes I threw together some old code I had lying around from a SQL Injection tool I worked on and I had a working proof of concept.  `freq.py` was born and it worked pretty well.  A year later [`@securitymapper`](https://twitter.com/securitymapper) had me wrap it in a web interface so he could query it from a SIEM and then the tool took off.  It turns out to be a pretty effective technique and gained some popularity and wide use!   This is a rewrite of the tool that incorporates some lessons learned and performance enhancements.

## Recent Improvements:

- Only one table is required for case sensitve or insensitive lookups. The tables are all case sensitive.  You can turn off and on case sensitivity and the .probability lookups will do what is needed to make them case insensitive.
- Ignored characters with `--exclude` option.  Like `--ignore_case`, the characters are only ignored in the calculations of the probability. They are not ignored in the building of the table.
- Speed.  Like I said.  It was a proof of concept and never really built with any performance in mind.  This fixes that.
- Accuracy.  Some errors in calulations were identified by Pepe Berta (thanks!).  This fixes those and several others.  If you find others let me know.
- Two calculations - I've added a second frequency score that I've calculated differently.  It will requires some testing to see if it is more useful than the previous number in detecting random hosts.
- Multiple freq tables can be passed and loaded on the CLI then processed in the url.  For example: `python3 freq_server.py 9000 files.freq dns.freq`  Then query the url with the table name instead of measure like this `http://127.0.0.1:9000/dns.freq/hostname` or `http://127.0.0.1:9000/dns.freq1/hostname` for just measurement 1.

## Version Compatibility:

Both `freq.py` and `freq_server.py` will work in either Python2 or Python3, but ship as a python3 script.

## System-level Service Startup:

A systemd startup file is provided, although you will likely need to adjust paths to the script and `freqtable2018.freq` file. The provided sample assumes you've cloned this repository to /usr/local/share/freq/. Enable with something like the following, again substituting the appropriate paths:

```console
sudo systemctl enable /usr/local/share/freq/systemd/freq.service
sudo systemctl start freq.service
```

## Conntributing

If you're interested in contributing to the project, feel free to read through our [Contributing](CONTRIBUTE.md) document.

## To Do:


================================================
FILE: freq.py
================================================
#!/usr/bin/env python3
from __future__ import division

import sys
import string
import re
import weakref
import json

from collections import Counter, defaultdict
from pprint import pprint

class node():
    """
    A class to represent a node.

    Note: Assigning a weight actually adds that value.  It doesn't set
    it to that value. For example node['c'] = 5    increases the value
    in node by 5

    Attributes
    ----------

    parent : node
        the parent to this node
    count : int
        the current count for the node
"""

    def __init__(self,parent):
        self.parent = parent
        self._pairs = Counter()
        self._cachecount = 0
        self._dirtycount = False

    def __getitem__(self,key):
        if self.parent.ignore_case and (key.islower() or key.isupper()):
            return  self._pairs[key.lower()] + self._pairs[key.upper()]
        else:
            return self._pairs[key]

    def __setitem__(self,key,value):

        # TODO: should consider using the __addr__ magic method instead
        # since the described behavior is this instead
        self._dirtycount = True
        self._pairs[key] += value

    @property
    def count(self):
        if self._dirtycount:
           self._cachecount = sum(self._pairs.values())
           self._dirtycount = False
        return self._cachecount

class FreqCounter(dict):
    """
    A class used for frequency counting.

    Attributes
    ----------

    ignore_case : bool
        TODO: Update
    ignorechars : str
        TODO: Update
    verbose : bool
        When enabled, print verbose messages to the console.
    count : int
        The number of entries in the table

    
    Methods
    -------

    toJSON()
        Returns a JSON representation of the Table as a string
    fromJSON(jsondata)
        Imports the JSON representation into the Table
    tally_str(line, weight=1)
        TODO: Update
    probability(line)
        Returns the probability of a given letter combination
    save(filename)
        Writes the table to a frequency file
    load(filename)
        Loads the table from a given frequency file
    printtable()
        Prints the JSON table to the console
    """
    def __init__(self, *args,**kwargs):
        self._table = defaultdict(lambda :node(self))
        self.ignore_case = False

        # TODO: consider refactoring to ignore_chars for consistency
        self.ignorechars = ""
        self.verbose = "verbose" in kwargs

    def __getitem__(self,key):
        return self._table[key]

    def __iter__(self):
        return iter(self._table)

    def __len__(self):
        return len(self._table)

    def toJSON(self):
        """
        Returns a string JSON reperesentation of the table.
        """
        serial = []
        for key,val in self._table.items():
            serial.append( (key, list(val._pairs.items())) )
        return json.dumps((self.ignore_case, self.ignorechars, serial))

    def fromJSON(self,jsondata):
        """
        Imports the table from a string JSON representation of the table

        Parameters
        ----------
        jsondata : str
            A string that represents the JSON Data

        Returns
        -------
        str
            the JSON representation of the table
        """

        # TODO: Raise an error if we get something that we didn't 
        # expect.
        args = json.loads(jsondata)
        if args:
            self.ignore_case = args[0]
            self.ignorechars = args[1]
            for outerkey,val in args[2]:
                self._table[outerkey] = node(self)
                for letter,count in val:
                   self._table[outerkey][letter] = count

    def tally_str(self,line,weight=1):
        """
        TODO: Update
        
        Parameters
        ----------
        line : string
            TODO: Update
        weight : int, optional
            the weight to be assigned to the pair (default = 1)
        """
        allpairs = re.findall(r"..", line)
        allpairs.extend(re.findall(r"..",line[1:]))
        for eachpair in allpairs:
            self[eachpair[0]][eachpair[1]] = weight

    def probability(self,line):
        """
        Calculates the probability of the word pair

        Parameters
        ----------
        line : str
            TODO: Update

        Returns
        -------
        float
            TODO: verify; the probability of the given word pair
        """
        allpairs = re.findall(r"..", line)
        allpairs.extend(re.findall(r"..",line[1:]))
        if self.verbose: 
            print("All pairs: {0}".format(allpairs))
        probs = []
        for eachpair in allpairs:
            pair = [eachpair[0], eachpair[1]]

            # check if any part of the pair should be ignored and alert
            # the user this was skipped
            if not all(x in self.ignorechars for x in pair):
                probs.append(self._probability(eachpair))
                if self.verbose: 
                    print ("Probability of {0}: {1}".format(eachpair,probs))
            elif self.verbose:
                print("Pair '{}' was ignored",format(self.ignorechars))
        if probs:
            average_probability = sum(probs) / len(probs) * 100
        else:
            average_probability = 0.0
        if self.verbose:
            print("Average Probability: {0}% \n\n".format(average_probability))
        
        totl1 = 0
        totl2 = 0
        for eachpair in allpairs:
            l1 = l2 = 0
            pair = [eachpair[0], eachpair[1]]

            if not all(x in self.ignorechars for x in pair):
                l1 += self[eachpair[0]].count
                if self.ignore_case and (eachpair[0].islower() or eachpair[0].isupper()):
                    l1 += self[eachpair[0].swapcase()].count
                l2 += self[eachpair[0]][eachpair[1]]
                if self.ignore_case and (eachpair[0].islower() or eachpair[0].isupper()):
                    l2 += self[eachpair[0].swapcase()][eachpair[1]]
                totl1 += l1
                totl2 += l2
                if self.verbose: 
                    print("Letter1:{0} Letter2:{1}  - This pair {2}:{3} {4}:{5}".format(
                        totl1,
                        totl2, 
                        eachpair[0],
                        l1,
                        eachpair[1],
                        l2
                    ))
        if (totl1 == 0) or (totl2 == 0):
            total_word_probability = 0.0
        else:
            total_word_probability = totl2/totl1 * 100
        if self.verbose: print("Total Word Probability: {0}/{1} = {2}".format(totl2, totl1, total_word_probability))
        return round(average_probability,4),round(total_word_probability,4)

    def _probability(self,twoletters):
        if self.ignore_case and (self[twoletters[0]].count == 0 and self[twoletters[0].swapcase()].count == 0):
            return 0.0
        if not self.ignore_case and self[twoletters[0]].count == 0:
            return 0.0
        if self.ignore_case and (twoletters[0].islower() or twoletters[0].isupper()):
            ignored_tot = sum([self[twoletters[0].lower()][eachlet] for eachlet in self.ignorechars]) + sum([self[twoletters[0].upper()][eachlet] for eachlet in self.ignorechars])
            let2 = self[twoletters[0].lower()][twoletters[1]] + self[twoletters[0].upper()][twoletters[1]]
            let1 = self[twoletters[0].lower()].count + self[twoletters[0].upper()].count
            if let1 - ignored_tot == 0:
                return 0.0
            return let2/(let1-ignored_tot)
        else:
            ignored_tot = sum([self[twoletters[0]][eachlet] for eachlet in self.ignorechars])
            if self[twoletters[0]].count - ignored_tot == 0:
                return 0.0
            return self[twoletters[0]][twoletters[1]] / (self[twoletters[0]].count - ignored_tot)

    def save(self,filename):
        try:
            file_handle =  open(filename, 'wb')
            file_handle.write(self.toJSON().encode("latin1"))
            file_handle.flush()
            file_handle.close()
        except Exception as e:
            print("Unable to write freq file :" + str(e))
            raise(e)

    def load(self,filename):
        try:
            file_handle =  open(filename,"rb")
            self.fromJSON(file_handle.read().decode("latin1"))
            file_handle.close()
        except Exception as e:
            print("Unable to load freq file :",str(e))
            raise(e)

    @property
    def count(self):
        return sum(map(lambda y:y.count, x._table.values()))

    def printtable(self):
        pprint(self.toJSON())



if __name__ == "__main__":
    import argparse
    import os
    parser=argparse.ArgumentParser()
    parser.add_argument(
        '-m',
        '--measure',
        required=False,
        help='Measure likelihood of a given string',
        dest='measure'
    )
    parser.add_argument(
        '-n',
        '--normal',
        required=False,
        help='Update the table based on the following normal string',
        dest='normal'
    )
    parser.add_argument(
        '-f',
        '--normalfile',
        required=False,
        help='Update the table based on the contents of the normal file',
        dest='normalfile'
    )
    parser.add_argument(
        '-p',
        '--print',
        action='store_true',
        required=False,
        help='Print a table of the most likely letters in order',
        dest='printtable'
    )
    parser.add_argument(
        '-c',
        '--create',
        action='store_true',
        required=False,
        help='Create a new empty frequency table',dest='create'
    )
    parser.add_argument(
        '-v',
        '--verbose',
        action='store_true',
        required=False,
        help='show calculation process',
        dest='verbose'
    )

    parser.add_argument(
        '-b',
        '--bulk',
        default=None,
        required=False,
        help='Measure every line in a file.',
        dest='bulk'
    )

    # TODO: break these up as well
    parser.add_argument('-t','--toggle_case_sensitivity',action='store_true',required=False,help='Ignore case in all future frequecy tabulations',dest='toggle_case')
    parser.add_argument('-s','--case_sensitive',action='store_true',required=False,help='Consider case in calculations. Default ignores case.',dest='case_sensitive')
    parser.add_argument('-w','--weight',type=int,default = 1, required=False,help='Affects weight of promote, update and update file (default is 1)',dest='weight')
    parser.add_argument('-e','--exclude',default = "\n\t~`!@#$%^&*()_+-", required=False,help='Provide a list of characters to ignore from the tabulations.',dest='exclude')
    parser.add_argument('freqtable',help='File storing character frequencies.')

    args=parser.parse_args()

    if args.verbose:
        fc = FreqCounter(verbose=True)
    else:
        fc = FreqCounter()
    if args.create and os.path.exists(args.freqtable):
        print("Frequency table already exists. " + args.freqtable)
        sys.exit(1)

    if not args.create:
        if not os.path.exists(args.freqtable):
           print("Frequency Character file not found. - %s " % (args.freqtable))
           raise(Exception("File not found."))
        fc.load(args.freqtable)

    if args.printtable: 
        fc.printtable()
    if args.normal: 
        fc.tally_str(args.normal, args.weight)
    if args.toggle_case:
        print("This feature has been depricated. By default all calculations ignore case.  Use -s to consider case.")
    fc.ignore_case = not args.case_sensitive
    fc.ignorechars = args.exclude
    if args.verbose: 
        print("Ignoring Case: {0}".format(fc.ignore_case))
    if args.verbose: 
        print("Ignoring Characters: {0}".format(fc.ignorechars))
    if args.normalfile:
        with open(args.normalfile,"rb") as filehandle:
            for eachline in filehandle:
                fc.tally_str(eachline.decode("latin1"))
    if args.measure: 
        print(fc.probability(args.measure))
    if args.bulk:
        with open(args.bulk,"rt") as filehandle:
            for eachline in filehandle:
                print(fc.probability(eachline),"-",eachline)
                
    fc.save(args.freqtable)

================================================
FILE: freq_server.py
================================================
#!/usr/bin/env python3
#freq_server.py by Mark Baggett
#Twitter @MarkBaggett
#github http://github.com/MarkBaggett/
#This scripts runs a web server to provide a callable API to use frequency tables.
#
#Start the server passing it a port and a frequecy table.  For example:
#python freq_server.py 8080 english_lowercase.freq
#
#Now you can query the API to measure the character frequency of its characters.  
#wget http://127.0.0.1:8080/?cmd=measure\&tgt=measurethisstring
#
#You can also mark a string as normal.  NOTE: There is a performance impact to updating via the API.  Use CLI freq.py to update tables instead.
#wget http://127.0.0.1:8080/?cmd=normal\&tgt=UpdateFreqWithTheseChars&weight=10
#
#Thanks to @securitymapper for Testing & suggestions

from __future__ import print_function
from freq import *
import six
if six.PY2:
    import BaseHTTPServer
    import SocketServer
    import urlparse
else:
    import http.server as BaseHTTPServer
    import socketserver as SocketServer
    import urllib.parse as urlparse
import threading
import re
import argparse
import os
#import resource


class freqapi(BaseHTTPServer.BaseHTTPRequestHandler):
    def do_GET(self):
        if self.server.verbose: self.server.safe_print("Currently %s threads are active." % (threading.activeCount()))
        #if self.server.verbose: self.server.safe_print("Memory usage: %s (kb)" % resource.getrusage(resource.RUSAGE_SELF).ru_maxrss)
        self.send_response(200)
        self.send_header('Content-type','text/plain')
        self.end_headers()
        (ignore, ignore, urlpath, urlparams, ignore) = urlparse.urlsplit(self.path)
        cmdstr = tgtstr = None
        if self.server.verbose: self.server.safe_print(urlparams)
        if re.search(legit_urls, urlpath):
            if self.server.verbose: self.server.safe_print("REST API CALL", urlpath)
            cmdstr = re.search(cmd_regex, urlpath)
            tgtstr = re.search(tgtstr_regex, urlpath)
            if not cmdstr or not tgtstr:
                help_str = 'API Documentation \nhttp://%s:%s/measure/<string> \nhttp://%s:%s/normal/<string> \n' % (self.server.server_address[0], self.server.server_address[1],self.server.server_address[0], self.server.server_address[1],self.server.server_address[0], self.server.server_address[1])
                self.wfile.write(help_str.encode("Latin-1"))
                return
            params = {}
            params["cmd"] = cmdstr.group(1)
            params["tgt"] = tgtstr.group(2)
        else:
            if self.server.verbose: self.server.safe_print("STANDARD API CALL", urlpath)        
            cmdstr=re.search(r"cmd=",urlparams)
            tgtstr =  re.search(r"tgt=",urlparams)
            if not cmdstr or not tgtstr:
                help_str = 'API Documentation\nhttp://%s:%s/?cmd=measure&tgt=<string> \nhttp://%s:%s/measure/<string> \nhttp://%s:%s/?cmd=normal&tgt=<string>&weight=<weight> \n' % (self.server.server_address[0], self.server.server_address[1],self.server.server_address[0], self.server.server_address[1],self.server.server_address[0], self.server.server_address[1])
                self.wfile.write(help_str.encode("LATIN-1"))
                return
            params={}
            try:
                for prm in urlparams.split("&"):
                    key,value = prm.split("=")
                    params[key]=value
            except:
                self.wfile.write('<html><body>Unable to parse the url. </body></html>'.encode("LATIN-1"))
                return
        if params["cmd"] == "normal":
            self.server.safe_print("cache cleared")
            try:
                self.server.cache_lock.acquire()
                self.server.cache ={}
            finally:
                self.server.cache_lock.release()
            try:
                self.server.fc_lock.acquire()
                weight = int(params.get("weight","1"))
                self.server.fc.tally_str(params["tgt"], weight=weight)
                self.server.dirty_fc = True
            finally:
                self.server.fc_lock.release()
            self.wfile.write('<html><body>Frequency Table updated</body></html>'.encode("LATIN-1")) 
        elif params["cmd"][:7] == "measure":
            cache_key = "{0}".format(params["tgt"])
            if cache_key in self.server.cache:
                if self.server.verbose: self.server.safe_print ("Query from cache:", params["tgt"])
                measure =  self.server.cache.get(cache_key)
            else:
                if self.server.verbose: self.server.safe_print ( "Added to cache:", params["tgt"])
                measure = self.server.fc.probability(params["tgt"])
                try:
                    self.server.cache_lock.acquire()
                    self.server.cache[cache_key]=measure
                finally:
                    self.server.cache_lock.release()
                if self.server.verbose>=2: self.server.safe_print ( "Server cache: ", str(self.server.cache))
            if params["cmd"].endswith("1"):
                measure = measure[0]
            elif params["cmd"].endswith("2"):
                measure = measure[1]
            self.wfile.write(str(measure).encode("LATIN-1"))
        elif any([params["cmd"].startswith(x) for x in freqtables]):
            table = params['cmd']
            if table.endswith("1") or table.endswith("2"):
                table = table[:-1]
            cache_key = "{0}:{1}".format(table, params["tgt"])
            if self.server.verbose: self.server.safe_print("Call to table ", params['cmd'])  
            if cache_key in self.server.cache:
                if self.server.verbose: self.server.safe_print ("Query from cache:",cache_key, params["tgt"])
                measure =  self.server.cache.get(cache_key)
            else:
                if self.server.verbose: self.server.safe_print ( "Added to cache:",cache_key, params["tgt"])
                measure = self.server.fcs[table].probability(params["tgt"])
                try:
                    self.server.cache_lock.acquire()
                    self.server.cache[cache_key]=measure
                finally:
                    self.server.cache_lock.release()
                if self.server.verbose>=2: self.server.safe_print ( "Server cache: ", str(self.server.cache))
            if params["cmd"].endswith("1"):
                measure = measure[0]
            elif params["cmd"].endswith("2"):
                measure = measure[1]
            self.wfile.write(str(measure).encode("LATIN-1"))
            return
        else:
            print("Invalid command {} or target {}".format(params["cmd"], params["tgt"]))
          
    def log_message(self, format, *args):
        return

class ThreadedFreqServer(SocketServer.ThreadingMixIn, BaseHTTPServer.HTTPServer):
    def __init__(self, *args,**kwargs):
        self.fcs = {}
        self.fc = FreqCounter()
        self.cache = {}
        self.cache_lock = threading.Lock()
        self.screen_lock = threading.Lock()
        self.verbose = False
        self.fc_lock = threading.Lock()
        self.dirty_fc = False
        self.exitthread = threading.Event()
        self.exitthread.clear()
        BaseHTTPServer.HTTPServer.__init__(self, *args, **kwargs)

    def safe_print(self,*args,**kwargs):
        try:
            self.screen_lock.acquire()
            print(*args,**kwargs)
        finally:
            self.screen_lock.release()

    def save_freqtable(self,save_path,save_interval):
        if self.verbose: self.safe_print ( "Save interval reached.")
        if self.dirty_fc:
            if self.verbose: self.safe_print ("Frequency counter changed.  Saving to disk.",save_path)
            try:
                self.fc_lock.acquire()
                self.fc.save(save_path)
                self.dirty_fc = False
            finally:
                self.fc_lock.release()
        else:
            if self.verbose: self.safe_print ("Frequency counter not changed.  Not Saving to disk.")
        #Reschedule yourself
        if not self.exitthread.isSet():
            self.timer = threading.Timer(60*save_interval, self.save_freqtable, args = (save_path,save_interval))
            self.timer.start()

if __name__=="__main__":
    parser=argparse.ArgumentParser()
    parser.add_argument('-ip','--address',required=False,help='IP Address for the server to listen on.  Default is 127.0.0.1',default='127.0.0.1')
    parser.add_argument('-s','--save_interval',type=float,required=False,help='Number of minutes to wait before trying to save frequency table updates. Default is 10 minutes.  Set to 0 to never save.',default=10)
    parser.add_argument('port',type=int,help='You must provide a TCP Port to bind to')
    parser.add_argument('freq_table',nargs="+", help='You must provide the frequency table name (optionally including the path)')
    parser.add_argument('-v','--verbose',action='count',default=0,required=False,help='Print verbose output to the server screen.  -vv is more verbose.')

    #args = parser.parse_args("-s 1 -vv 8081 english_lowercase.freq".split())
    args = parser.parse_args()
    
    #split paths and filenames on frequency tables
    freqtables = list(map(lambda x:x[1], map(os.path.split, args.freq_table)))


    #Setup the server.
    server = ThreadedFreqServer((args.address, args.port), freqapi)

    #Load Each of the Frequency Table
    for eachtable in args.freq_table:
        path,tablename = os.path.split(eachtable)
        server.fcs[tablename] = FreqCounter()
        try:
            server.fcs[tablename].load(eachtable)
        except:
            err = "********** Unable to load Frequency table {0}. ************".format(eachtable)
            raise(Exception(err))

    #add psuedo table names for the two measurements
    tables = list(freqtables)
    tables += [x+"1" for x in freqtables]
    tables += [x+"2" for x in freqtables]

    #build regex for parsing urls based on table names
    legit_urls = r"[\/](measure|measure1|measure2|normal|{0})[\/].*?".format( "|".join(tables))
    cmd_regex = r"[\/](measure|measure1|measure2|normal|{0})[\/].*$".format( "|".join(tables))
    tgtstr_regex = r"[\/](measure|measure1|measure2|normal|{0})[\/](.*)$".format( "|".join(tables))

    #setup default freq_table
    server.fc = server.fcs[freqtables[0]]
    server.verbose = args.verbose

    #Schedule the first save interval unless save_interval was set to 0.
    if args.save_interval:
        server.timer = threading.Timer(60 *args.save_interval, server.save_freqtable, args = (args.freq_table[0], args.save_interval))
        server.timer.start()
 
    #start the server
    print('Server is Ready. http://%s:%s/<command>/<string>' % (args.address, args.port))
    print("Commands include {}".format(cmd_regex))
    print('[?] - Remember: If you are going to call the api with wget, curl or something else from the bash prompt you need to escape the & with \& \n\n')
    while True:
        try:
            server.handle_request()
        except KeyboardInterrupt:
            break
        
    server.safe_print("Control-C hit: Exiting server...")
    server.safe_print("Web API Disabled...")
    if args.save_interval and server.dirty_fc:
        server.safe_print("The Frequency counter has changed since the last save interval. Saving final update.")
        server.exitthread.set()
        server.timer.cancel()
        try:
            server.fc_lock.acquire()
            server.fc.save(args.freq_table[0])
            server.fc_lock.release()
        except:
            server.safe_print("[!] An error occured during the final save.")
    elif args.save_interval:
        server.safe_print( "No Changes made since last file save.  Canceling scheduled save...")
        server.timer.cancel()
    server.safe_print("Server has stopped.")
    



================================================
FILE: freq_sort.py
================================================
#!/usr/bin/env python3
from freq import *
import argparse
import signal
import sys

parser = argparse.ArgumentParser()
parser.add_argument('infile', nargs='?', type=argparse.FileType('r'), default=sys.stdin)
parser.add_argument('-f','--freq_table',default = "freqtable2018.freq", required=False,help='Specify the frequency table to use. Default is freqtable2018.freq',dest='freqtable')

parser.add_argument("-v", "--verbose", action="count", help = "Show details of how sort order is determined.")
parser.add_argument('-s','--case_sensitive',action='store_true',required=False,help='Consider case in calculations. Default ignores case.',dest='case_sensitive')
parser.add_argument('-e','--exclude',default = "\n\t~`!@#$%^&*()_+-;", required=False,help='Specify a full list of characters to ignore from the tabulations.',dest='exclude')
parser.add_argument('-a','--add_exclude',default = "", required=False,help='Provide a list of characters to add to default ignored characters.',dest='additional')
parser.add_argument('-n','--numeric',action='store_true',required=False,help='Convert input to number for sorting.',dest='numeric')
parser.add_argument('-c','--cut',nargs=2,help='Specify a delimer and field value to cut a substring for the target of your search. Example -c "/" 3 will use the value between the 3rd and 4th "/" for sorting.',dest='cut')
parser.add_argument('-S','--substring',nargs=2,help='Slice out a substring given the starting and stoping position. If cut is used this is performed after cut. Example -S 10,13 would only use the string comprised of characters 10,11 and 12 to determine the sort order.',dest='slice')
parser.add_argument('-r','--reverse',action='store_true',required=False,help='Reverses the sort order with highest frequency values last',dest='reverse')
parser.add_argument('-N','--no_length',action='store_true',required=False,help='Do not consider length of string when determining sort order',dest='length')
parser.add_argument('-l','--line',action='store_true',required=False,help='Include the line number where the string was located in the output.',dest='line')

args = parser.parse_args()

def score(line):
    if args.cut:
        try:
            line = line.split(args.cut[0])[int(args.cut[1])]
        except IndexError:
            line = ""
        except:
            raise(Exception("Unable to process -c cut options."))
        if args.verbose: print("Now sorting on {}".format(line))
    if args.slice:
        try:
            line = line[int(args.slice[0]):int(args.slice[1])]
        except:
            raise(Exception("Unable to process -S substring options."))
        if args.verbose: print("Now sorting on {}".format(line))
    if args.numeric:
        try:
            return float(line)
        except:
            raise(Exception("The value {} can not be treated as numeric. Try without -n.".format(line)))
    line_len = len(line) if not args.length else 1
    return line_len * sum(freq.probability(line))/2

def signal_handler(sig, frame):
    sys.exit(0)

signal.signal(signal.SIGINT, signal_handler)
signal.signal(signal.SIGPIPE,signal.SIG_DFL) 

freq = FreqCounter()

freq.load(args.freqtable)
if args.additional:
    args.exclude += args.additional
freq.ignorechars = args.exclude
freq.ignore_case = not args.case_sensitive
if args.verbose and args.verbose > 1:
   freq.verbose = True
filecontent = args.infile.readlines()
for eachline in sorted(filecontent, key = score, reverse=not args.reverse):
    if args.verbose:
       print(score(eachline), freq.probability(eachline), end=" ")
    if args.line:
       print("Found on line {} :".format(filecontent.index(eachline)), end=" ")
    print(eachline,end="")


================================================
FILE: freqtable2018.freq
================================================
[true, "\n\t~`!@#$%^&*()_+-", [["\f", [["f", 2]]], [" ", [[" ", 312527], ["$", 12], ["(", 1520], [",", 6], ["0", 2], ["4", 210], ["8", 75], ["<", 58], ["D", 5449], ["H", 14898], ["L", 6849], ["P", 10276], ["T", 23773], ["X", 290], ["`", 1958], ["d", 74474], ["h", 195782], ["l", 64742], ["p", 65902], ["t", 408490], ["x", 22], ["|", 38], ["#", 6], ["'", 3062], ["+", 2], ["/", 12], ["3", 300], ["7", 134], [";", 8], ["?", 8], ["C", 9334], ["G", 5688], ["K", 2484], ["O", 4266], ["S", 13139], ["W", 9355], ["[", 408], ["_", 220], ["c", 90632], ["g", 44086], ["k", 13940], ["o", 161371], ["s", 182472], ["w", 187994], ["{", 8], ["\"", 22346], ["&", 42], ["*", 112], [".", 2358], ["2", 691], ["6", 180], [":", 14], [">", 2], ["B", 12213], ["F", 8428], ["J", 5957], ["N", 7370], ["R", 5046], ["V", 3389], ["Z", 250], ["b", 109654], ["f", 95818], ["j", 6186], ["n", 56010], ["r", 54486], ["v", 15242], ["z", 238], ["~", 2], ["%", 2], [")", 2], ["-", 550], ["1", 1613], ["5", 132], ["9", 74], ["A", 16635], ["E", 4590], ["I", 45393], ["M", 17353], ["Q", 356], ["U", 753], ["Y", 2574], ["a", 293192], ["e", 47200], ["i", 125201], ["m", 99016], ["q", 5914], ["u", 27850], ["y", 29288]]], ["$", [[" ", 2], ["3", 2], ["2", 6], ["4", 10]]], ["(", [[" ", 2], ["\"", 34], ["$", 12], ["'", 24], ["*", 8], ["1", 28], ["3", 24], ["2", 30], ["5", 2], ["A", 54], ["C", 12], ["B", 32], ["E", 12], ["D", 16], ["G", 6], ["F", 40], ["I", 120], ["H", 48], ["K", 10], ["J", 2], ["M", 48], ["L", 14], ["O", 20], ["N", 26], ["P", 26], ["S", 46], ["U", 8], ["T", 124], ["W", 38], ["V", 2], ["Y", 4], ["_", 14], ["a", 306], ["`", 2], ["c", 22], ["b", 50], ["e", 24], ["d", 18], ["g", 10], ["f", 80], ["i", 122], ["h", 102], ["k", 2], ["j", 2], ["m", 20], ["l", 20], ["o", 86], ["n", 38], ["p", 16], ["s", 106], ["r", 6], ["u", 6], ["t", 240], ["w", 212], ["v", 2], ["y", 6], ["~", 8]]], [",", [["!", 2], [" ", 200706], ["\"", 10148], ["'", 1656], [")", 4], ["*", 18], ["-", 780], [",", 10], ["1", 40], ["0", 263], ["3", 6], ["2", 42], ["5", 44], ["4", 28], ["7", 4], ["6", 20], ["9", 12], ["8", 18], [":", 4], ["A", 4], ["I", 42], ["J", 2], ["M", 2], ["T", 2], ["[", 2], ["a", 328], ["c", 8], ["b", 76], ["e", 6], ["d", 4], ["g", 10], ["f", 60], ["i", 36], ["h", 36], ["k", 6], ["m", 10], ["l", 6], ["o", 22], ["n", 8], ["q", 2], ["p", 2], ["s", 64], ["r", 8], ["t", 84], ["w", 70]]], ["0", [[" ", 512], ["%", 16], ["'", 14], [")", 20], ["-", 20], [",", 155], [".", 70], ["1", 38], ["0", 714], ["3", 17], ["2", 32], ["5", 34], ["4", 13], ["7", 30], ["6", 21], ["9", 34], ["8", 20], [";", 8], [":", 22], ["@", 20], ["I", 6], ["]", 46], ["m", 2], ["s", 6], ["t", 74], ["x", 10], ["}", 18]]], ["4", [[" ", 70], ["'", 4], [")", 2], ["-", 12], [",", 70], [".", 46], ["1", 24], ["0", 82], ["3", 24], ["2", 24], ["5", 34], ["4", 16], ["7", 28], ["6", 16], ["9", 24], ["8", 34], [";", 6], [":", 44], ["@", 8], ["T", 2], ["]", 60], ["t", 64], ["}", 18], ["|", 32]]], ["8", [[" ", 64], ["'", 6], ["-", 12], [",", 68], [".", 28], ["1", 192], ["0", 155], ["3", 132], ["2", 89], ["5", 26], ["4", 56], ["7", 26], ["6", 74], ["9", 37], ["8", 12], [";", 12], [":", 14], ["?", 2], ["@", 10], ["]", 54], ["m", 2], ["t", 56], ["}", 18], ["|", 44]]], ["<", [["A", 64], ["C", 132], ["B", 10], ["E", 18], ["D", 14], ["G", 4], ["F", 20], ["I", 14], ["H", 94], ["K", 2], ["M", 14], ["L", 8], ["O", 14], ["N", 2], ["P", 14], ["S", 14], ["R", 10], ["T", 224], ["W", 24], ["Y", 2], ["m", 2], ["s", 2]]], ["@", [[" ", 102], ["c", 8], ["e", 14], [",", 8], [".", 8], ["u", 6], ["v", 16]]], ["D", [["!", 4], [" ", 358], ["'", 72], ["*", 16], ["-", 64], [",", 14], [".", 66], [";", 2], ["?", 2], ["A", 93], ["C", 2], ["E", 240], ["D", 2], ["G", 10], ["F", 8], ["I", 220], ["M", 2], ["L", 2], ["O", 89], ["N", 14], ["P", 2], ["S", 28], ["R", 70], ["U", 24], ["V", 2], ["Y", 12], ["a", 1027], ["e", 2124], ["i", 779], ["j", 6], ["m", 148], ["o", 2366], ["n", 8], ["r", 642], ["u", 914], ["w", 4], ["y", 20]]], ["H", [[" ", 112], ["'", 14], [",", 8], [".", 210], ["1", 126], ["3", 42], ["2", 54], ["5", 12], ["4", 12], ["7", 12], ["6", 12], ["9", 12], ["8", 12], ["?", 2], ["A", 410], ["E", 722], ["F", 4], ["I", 298], ["M", 4], ["O", 260], ["N", 2], ["Q", 4], ["S", 6], ["R", 20], ["U", 26], ["T", 60], ["Y", 20], ["a", 2890], ["e", 16114], ["i", 2886], ["h", 2], ["m", 2], ["o", 2880], ["s", 4], ["u", 596], ["v", 2], ["y", 24]]], ["L", [["!", 2], [" ", 102], ["'", 40], [")", 2], ["-", 4], [",", 10], [".", 40], ["1", 6], ["2", 8], ["5", 2], ["4", 2], ["6", 2], ["8", 2], [":", 8], ["A", 120], ["C", 14], ["E", 261], ["D", 38], ["G", 14], ["F", 26], ["I", 210], ["H", 4], ["K", 46], ["J", 2], ["M", 6], ["L", 134], ["O", 98], ["N", 2], ["P", 4], ["S", 36], ["R", 6], ["U", 84], ["T", 12], ["W", 2], ["Y", 32], ["a", 2534], ["e", 1957], ["i", 1482], ["h", 70], ["l", 2], ["o", 2216], ["u", 614], ["w", 2], ["y", 30]]], ["P", [["!", 2], [" ", 30], ["-", 4], [".", 72], ["A", 198], ["E", 228], ["G", 8], ["I", 66], ["H", 24], ["K", 2], ["M", 8], ["L", 90], ["O", 110], ["P", 26], ["S", 18], ["R", 198], ["U", 54], ["T", 202], ["Y", 10], ["a", 2387], ["e", 1866], ["f", 50], ["i", 2690], ["h", 472], ["l", 486], ["o", 1094], ["s", 84], ["r", 4898], ["u", 314], ["t", 12], ["w", 2], ["y", 38]]], ["T", [["!", 20], [" ", 496], ["'", 18], ["*", 34], ["-", 20], [",", 66], [".", 132], [":", 6], ["A", 150], ["C", 14], ["B", 2], ["E", 2568], ["F", 2], ["I", 236], ["H", 1216], ["M", 20], ["L", 24], ["O", 330], ["N", 14], ["P", 14], ["S", 62], ["R", 71], ["U", 70], ["T", 106], ["W", 110], ["Y", 144], ["Z", 2], ["a", 643], ["e", 802], ["i", 1134], ["h", 41758], ["o", 3328], ["s", 100], ["r", 532], ["u", 500], ["w", 517], ["v", 8], ["y", 54], ["z", 4]]], ["X", [["A", 2], [" ", 22], ["C", 6], ["E", 2], ["'", 2], ["I", 444], ["-", 2], [",", 4], [".", 84], ["1", 2], ["P", 6], ["2", 2], ["u", 30], ["T", 78], ["V", 302], ["X", 266], [":", 6], ["e", 4]]], ["`", [[" ", 4], ["\"", 2], ["'", 2], ["2", 2], ["A", 122], ["C", 26], ["B", 74], ["E", 14], ["D", 38], ["G", 30], ["F", 28], ["I", 270], ["H", 66], ["K", 4], ["J", 30], ["M", 66], ["L", 44], ["O", 26], ["N", 52], ["P", 34], ["S", 80], ["R", 14], ["U", 6], ["T", 166], ["W", 92], ["V", 4], ["Y", 66], ["_", 2], ["a", 62], ["c", 32], ["b", 40], ["e", 40], ["d", 24], ["g", 28], ["f", 38], ["i", 24], ["h", 26], ["k", 6], ["j", 2], ["m", 40], ["l", 28], ["o", 24], ["n", 14], ["q", 2], ["p", 40], ["s", 34], ["r", 18], ["u", 10], ["t", 98], ["w", 22], ["v", 2], ["y", 8]]], ["d", [["!", 1346], [" ", 316392], ["\"", 78], ["'", 892], [")", 158], ["-", 2110], [",", 27454], [".", 15448], ["1", 6], [";", 2238], [":", 1318], ["?", 904], [">", 32], ["]", 6], ["_", 10], ["a", 15612], ["`", 12], ["c", 64], ["b", 98], ["e", 66856], ["d", 5952], ["g", 2672], ["f", 682], ["i", 36856], ["h", 178], ["k", 152], ["j", 316], ["m", 1162], ["l", 4894], ["o", 27386], ["n", 2680], ["q", 32], ["p", 44], ["s", 12352], ["r", 13004], ["u", 5376], ["t", 176], ["w", 382], ["v", 1300], ["y", 5438], ["z", 4], ["}", 6]]], ["h", [["!", 1480], [" ", 66490], ["\"", 16], ["'", 580], [")", 46], ["-", 674], [",", 7634], [".", 3060], [";", 500], [":", 124], ["?", 354], [">", 8], ["_", 8], ["a", 130321], ["`", 2], ["c", 78], ["b", 454], ["e", 366316], ["d", 180], ["g", 2], ["f", 464], ["i", 118000], ["h", 16], ["k", 98], ["m", 1012], ["l", 810], ["o", 56794], ["n", 878], ["q", 34], ["p", 20], ["s", 1392], ["r", 8693], ["u", 9628], ["t", 23686], ["w", 410], ["v", 4], ["y", 4342], ["z", 2]]], ["l", [["!", 688], [" ", 55493], ["\"", 28], ["'", 776], [")", 58], ["*", 8], ["-", 1168], [",", 9058], ["/", 2], [".", 4175], ["1", 2], ["2", 4], [";", 588], [":", 138], ["?", 512], [">", 18], ["@", 2], ["]", 2], ["_", 14], ["a", 40424], ["c", 880], ["b", 428], ["e", 90647], ["d", 35897], ["g", 498], ["f", 11866], ["i", 53960], ["h", 30], ["k", 3952], ["j", 880], ["m", 2350], ["l", 72010], ["o", 43088], ["n", 488], ["q", 6], ["p", 1936], ["s", 9240], ["r", 1482], ["u", 8878], ["t", 7890], ["w", 1710], ["v", 3288], ["y", 43772], ["x", 2], ["z", 52]]], ["p", [["!", 222], [" ", 12684], ["\"", 12], ["'", 274], [")", 8], ["*", 16], ["-", 458], [",", 2678], [".", 1598], [";", 206], [":", 32], ["?", 102], [">", 2], ["_", 2], ["a", 24384], ["c", 110], ["b", 74], ["e", 40018], ["d", 12], ["g", 26], ["f", 100], ["i", 12578], ["h", 3890], ["k", 248], ["m", 220], ["l", 19938], ["o", 24960], ["n", 82], ["p", 12676], ["s", 4980], ["r", 27372], ["u", 7468], ["t", 8624], ["w", 150], ["y", 1538], ["z", 4]]], ["t", [["!", 1698], [" ", 244288], ["\"", 74], ["'", 4144], [")", 206], ["*", 10], ["-", 2634], [",", 24442], ["/", 22], [".", 15012], ["9", 30], [";", 1952], [":", 386], ["?", 2014], [">", 34], ["@", 16], ["I", 4], ["N", 2], ["]", 6], ["_", 20], ["a", 36864], ["c", 4804], ["b", 164], ["e", 93708], ["d", 32], ["g", 50], ["f", 1186], ["i", 67393], ["h", 380618], ["k", 30], ["j", 2], ["m", 1109], ["l", 15192], ["o", 120320], ["n", 980], ["p", 168], ["s", 20376], ["r", 31046], ["u", 18070], ["t", 25046], ["w", 8348], ["v", 6], ["y", 14950], ["x", 22], ["z", 596]]], ["x", [["!", 16], [" ", 1654], ["'", 108], [")", 6], ["-", 174], [",", 480], ["/", 2], [".", 262], ["1", 10], [";", 40], [":", 6], ["?", 20], ["_", 4], ["a", 1314], ["c", 2462], ["b", 4], ["e", 1456], ["g", 2], ["f", 26], ["i", 1676], ["h", 354], ["l", 20], ["o", 82], ["q", 56], ["p", 3828], ["s", 6], ["u", 144], ["t", 3514], ["w", 4], ["y", 88], ["x", 30]]], ["|", [[" ", 30], ["C", 294]]], ["#", [["1", 6], [" ", 2]]], ["'", [["!", 22], [" ", 5218], ["\"", 130], ["'", 52], [")", 8], ["-", 136], [",", 274], [".", 194], ["9", 24], ["8", 10], [";", 12], [":", 4], ["?", 40], ["A", 506], ["C", 94], ["B", 292], ["E", 88], ["D", 124], ["G", 154], ["F", 90], ["I", 826], ["H", 360], ["K", 12], ["J", 52], ["M", 174], ["L", 102], ["O", 202], ["N", 222], ["Q", 12], ["P", 86], ["S", 328], ["R", 24], ["U", 38], ["T", 922], ["W", 482], ["V", 22], ["Y", 356], ["a", 288], ["c", 614], ["b", 42], ["e", 614], ["d", 2832], ["g", 48], ["f", 28], ["i", 98], ["h", 36], ["k", 6], ["m", 1148], ["l", 1792], ["o", 90], ["n", 44], ["q", 2], ["p", 40], ["s", 16835], ["r", 660], ["u", 62], ["t", 7172], ["w", 60], ["v", 880], ["y", 66], ["}", 2]]], ["+", [[";", 2], ["B", 2], ["-", 4]]], ["/", [[" ", 26], ["\"", 2], ["e", 20], ["I", 14], ["h", 6], ["1", 2], ["s", 2], ["2", 4], ["5", 4], ["4", 2], ["6", 2]]], ["3", [["!", 2], [" ", 76], [")", 18], ["*", 8], ["-", 6], [",", 98], ["/", 2], [".", 66], ["1", 54], ["0", 158], ["3", 44], ["2", 60], ["5", 42], ["4", 18], ["7", 38], ["6", 24], ["9", 26], ["8", 20], [";", 10], [":", 48], ["?", 2], ["@", 4], ["]", 60], ["d", 6], ["i", 4], ["h", 2], ["r", 50], ["t", 26], ["v", 2], ["}", 18], ["|", 38]]], ["7", [["!", 2], [" ", 64], ["'", 10], ["-", 8], [",", 68], ["/", 4], [".", 46], ["1", 21], ["0", 36], ["3", 12], ["2", 35], ["5", 16], ["4", 14], ["7", 32], ["6", 26], ["9", 40], ["8", 34], [";", 10], [":", 28], ["@", 18], ["]", 48], ["h", 2], ["m", 4], ["t", 34], ["}", 18], ["|", 26]]], [";", [[" ", 14368], ["\"", 42], ["'", 54], ["h", 2], ["*", 2], ["-", 146], [",", 2], ["[", 4]]], ["?", [[" ", 5000], ["\"", 7386], ["'", 680], [")", 14], ["-", 90], [",", 2], [".", 118], ["[", 2], ["?", 2], [">", 2]]], ["C", [[" ", 50], ["\"", 14], ["'", 12], ["*", 2], ["-", 4], [",", 6], ["/", 2], [".", 24], ["A", 126], ["C", 22], ["E", 112], ["D", 20], ["I", 76], ["H", 3260], ["K", 60], ["L", 26], ["O", 164], ["P", 6], ["S", 8], ["R", 38], ["U", 26], ["T", 117], ["Y", 16], ["a", 2763], ["e", 294], ["i", 277], ["h", 2428], ["l", 446], ["o", 4926], ["s", 4], ["r", 556], ["u", 212], ["y", 42], ["z", 24]]], ["G", [["!", 2], [" ", 132], ["\"", 8], ["'", 8], ["-", 56], [",", 22], [".", 10], [";", 2], [":", 2], ["?", 4], ["A", 72], ["E", 134], ["G", 10], ["F", 2], ["I", 34], ["H", 70], ["L", 16], ["O", 60], ["N", 14], ["S", 10], ["R", 64], ["U", 92], ["T", 2], ["Y", 2], ["Z", 2], ["a", 744], ["e", 958], ["d", 2], ["i", 460], ["h", 266], ["l", 212], ["o", 2628], ["n", 6], ["r", 1160], ["u", 880], ["w", 2], ["y", 2]]], ["K", [[" ", 28], ["'", 2], [",", 6], [".", 8], ["?", 2], ["A", 4], ["E", 64], ["F", 2], ["I", 44], ["H", 2], ["K", 4], ["L", 12], ["O", 2], ["N", 4], ["S", 12], ["R", 6], ["U", 2], ["W", 4], ["Y", 2], ["a", 442], ["e", 504], ["i", 860], ["h", 122], ["l", 40], ["o", 290], ["n", 96], ["r", 96], ["u", 782], ["y", 10]]], ["O", [["!", 6], [" ", 466], ["\"", 2], ["'", 106], ["-", 4], [",", 12], [".", 38], [":", 2], ["?", 2], ["A", 6], ["C", 47], ["B", 32], ["E", 13], ["D", 52], ["G", 52], ["F", 288], ["I", 18], ["H", 28], ["K", 222], ["J", 50], ["M", 160], ["L", 98], ["O", 78], ["N", 489], ["P", 30], ["S", 64], ["R", 346], ["U", 338], ["T", 104], ["W", 64], ["V", 39], ["Y", 8], ["Z", 2], ["a", 2], ["c", 234], ["b", 128], ["e", 2], ["d", 26], ["g", 14], ["f", 1458], ["i", 6], ["h", 1000], ["k", 2], ["m", 60], ["l", 240], ["o", 44], ["n", 3744], ["p", 86], ["s", 22], ["r", 798], ["u", 554], ["t", 162], ["w", 32], ["v", 74], ["y", 2], ["x", 6], ["z", 68]]], ["S", [["!", 8], [" ", 616], ["'", 12], ["*", 6], ["-", 20], [",", 40], [".", 122], [";", 2], [":", 12], ["?", 2], ["A", 56], ["C", 58], ["E", 296], ["D", 2], ["G", 4], ["F", 4], ["I", 91], ["H", 76], ["K", 8], ["M", 14], ["L", 10], ["O", 98], ["N", 2], ["Q", 4], ["P", 30], ["S", 104], ["R", 2], ["U", 42], ["T", 322], ["W", 4], ["Y", 2], ["a", 2150], ["c", 1208], ["e", 1405], ["i", 968], ["h", 5152], ["k", 34], ["m", 374], ["l", 166], ["o", 4132], ["n", 120], ["q", 44], ["p", 776], ["s", 2], ["u", 1246], ["t", 1564], ["w", 144], ["v", 8], ["y", 126], ["z", 18]]], ["W", [["!", 2], [" ", 58], [")", 2], [",", 10], [".", 28], ["A", 136], ["E", 96], ["D", 4], ["G", 2], ["I", 90], ["H", 128], ["L", 4], ["O", 76], ["N", 12], ["S", 2], ["R", 8], ["Y", 4], ["a", 1096], ["e", 4362], ["i", 2096], ["h", 10098], ["o", 1611], ["r", 58], ["u", 18]]], ["[", [["*", 2], ["1", 112], ["3", 56], ["2", 112], ["5", 30], ["4", 40], ["7", 4], ["6", 34], ["9", 4], ["8", 2], ["A", 12], ["C", 2], ["B", 6], ["E", 24], ["D", 4], ["G", 14], ["F", 2], ["I", 22], ["H", 30], ["J", 34], ["M", 28], ["L", 16], ["N", 4], ["P", 42], ["S", 6], ["R", 36], ["T", 18], ["W", 4], ["a", 2], ["b", 4], ["d", 2], ["g", 2], ["f", 14], ["m", 2], ["l", 4], ["o", 4], ["p", 6], ["s", 4], ["t", 50]]], ["_", [[" ", 100], ["'", 2], ["-", 12], [",", 38], [".", 28], [";", 4], ["A", 14], ["D", 2], ["I", 30], ["H", 4], ["M", 4], ["L", 2], ["O", 2], ["N", 2], ["T", 12], ["_", 736], ["^", 6], ["a", 14], ["c", 12], ["b", 4], ["e", 6], ["d", 6], ["f", 14], ["i", 4], ["h", 2], ["m", 8], ["l", 6], ["o", 2], ["n", 14], ["p", 4], ["s", 10], ["r", 2], ["u", 4], ["t", 10], ["w", 8], ["v", 4], ["y", 4], ["x", 4]]], ["c", [["!", 38], [" ", 2940], ["\"", 6], ["'", 62], ["-", 122], [",", 498], [".", 480], [";", 56], [":", 18], ["?", 22], ["C", 8], ["G", 8], ["F", 2], ["L", 230], ["Q", 2], ["P", 2], ["S", 2], ["a", 38996], ["c", 5012], ["e", 54872], ["d", 38], ["i", 13186], ["h", 55038], ["k", 20111], ["m", 4], ["l", 12408], ["o", 57624], ["n", 26], ["q", 496], ["p", 66], ["s", 944], ["r", 13732], ["u", 9748], ["t", 19868], ["w", 8], ["v", 6], ["y", 1692], ["z", 12]]], ["g", [["!", 572], [" ", 77496], ["\"", 32], ["'", 490], [")", 44], ["-", 1388], [",", 8820], [".", 5284], [";", 654], [":", 276], ["?", 530], [">", 4], ["a", 16556], ["c", 8], ["b", 10], ["e", 31120], ["d", 134], ["g", 3662], ["f", 14], ["i", 11514], ["h", 36708], ["m", 368], ["l", 8714], ["o", 16464], ["n", 4300], ["p", 22], ["s", 6714], ["r", 16774], ["u", 6849], ["t", 1104], ["w", 42], ["y", 516], ["z", 14], ["}", 8]]], ["k", [["!", 306], [" ", 20132], ["\"", 14], ["'", 392], [")", 32], ["-", 554], [",", 4148], [".", 2414], ["1", 2], [";", 294], [":", 60], ["?", 214], [">", 10], ["@", 10], ["a", 870], ["c", 66], ["b", 38], ["e", 34087], ["d", 24], ["g", 58], ["f", 344], ["i", 13220], ["h", 948], ["k", 244], ["j", 22], ["m", 100], ["l", 2880], ["o", 816], ["n", 11134], ["q", 2], ["p", 12], ["s", 4228], ["r", 68], ["u", 68], ["t", 20], ["w", 314], ["v", 10], ["y", 768], ["z", 2]]], ["o", [["!", 648], [" ", 114177], ["\"", 18], ["'", 1162], [")", 38], ["-", 958], [",", 5210], [".", 2232], [";", 354], [":", 58], ["?", 516], ["K", 2], ["J", 2], ["]", 4], ["_", 6], ["a", 7834], ["`", 2], ["c", 9710], ["b", 5854], ["e", 2996], ["d", 16602], ["g", 5294], ["f", 99287], ["i", 10524], ["h", 872], ["k", 14852], ["j", 986], ["m", 51326], ["l", 30027], ["o", 36626], ["n", 131194], ["q", 172], ["p", 16136], ["s", 26802], ["r", 99219], ["u", 128095], ["t", 47116], ["w", 48246], ["v", 20522], ["y", 3366], ["x", 840], ["z", 456], ["}", 2]]], ["s", [["!", 2144], [" ", 245069], ["\"", 154], ["'", 1554], [")", 266], ["*", 14], ["-", 1948], [",", 37726], [".", 19990], ["1", 4], [";", 3000], [":", 928], ["=", 2], ["?", 1424], [">", 50], ["[", 6], ["]", 38], ["_", 24], ["a", 37342], ["c", 11410], ["b", 1092], ["e", 89922], ["d", 372], ["g", 288], ["f", 1206], ["i", 41018], ["h", 48184], ["k", 7002], ["j", 20], ["m", 5638], ["l", 7892], ["o", 41490], ["n", 2836], ["q", 1070], ["p", 17022], ["s", 38918], ["r", 118], ["u", 22362], ["t", 98283], ["w", 5082], ["v", 104], ["y", 2112], ["z", 8]]], ["w", [["!", 302], [" ", 25552], ["\"", 32], ["'", 332], [")", 38], ["-", 630], [",", 4620], ["/", 2], [".", 2130], [";", 296], [":", 56], ["?", 312], [">", 6], ["_", 4], ["a", 69912], ["c", 62], ["b", 88], ["e", 43746], ["d", 980], ["g", 230], ["f", 300], ["i", 50742], ["h", 55558], ["k", 232], ["j", 2], ["m", 12], ["l", 1768], ["o", 28157], ["n", 11280], ["p", 14], ["s", 3452], ["r", 3078], ["u", 116], ["t", 106], ["w", 8], ["y", 240], ["z", 2]]], ["{", [["`", 2], ["c", 2], ["E", 2], ["G", 2], ["s", 6], ["o", 4], ["1", 224], ["3", 226], ["2", 230], ["5", 24], ["4", 34], ["7", 22], ["6", 22], ["9", 22], ["8", 24], ["t", 8]]], ["\"", [[" ", 19496], ["\"", 12], ["'", 120], [")", 34], ["*", 70], ["-", 198], [",", 42], [".", 98], ["1", 4], ["3", 2], ["2", 4], ["5", 4], ["4", 4], ["6", 2], ["8", 6], [";", 74], [":", 2], ["?", 4], ["A", 4542], ["C", 1250], ["B", 2388], ["E", 472], ["D", 1474], ["G", 1096], ["F", 924], ["I", 8984], ["H", 2746], ["K", 138], ["J", 270], ["M", 1850], ["L", 912], ["O", 1848], ["N", 2630], ["Q", 110], ["P", 880], ["S", 1906], ["R", 350], ["U", 236], ["T", 5524], ["W", 6722], ["V", 340], ["Y", 4228], ["X", 4], ["[", 14], ["Z", 6], ["]", 8], ["_", 20], ["a", 732], ["`", 158], ["c", 118], ["b", 452], ["e", 46], ["d", 138], ["g", 54], ["f", 182], ["i", 408], ["h", 256], ["k", 10], ["j", 14], ["m", 148], ["l", 118], ["o", 112], ["n", 94], ["q", 4], ["p", 130], ["s", 230], ["r", 36], ["u", 32], ["t", 1014], ["w", 398], ["v", 22], ["y", 272]]], ["&", [["h", 2], ["c", 8], [" ", 26]]], ["*", [[" ", 206], ["\"", 146], [")", 6], ["*", 636], [",", 12], [".", 4], [":", 8], [">", 2], ["A", 16], ["C", 4], ["B", 16], ["E", 20], ["D", 8], ["G", 2], ["F", 12], ["I", 6], ["H", 4], ["K", 4], ["L", 4], ["O", 6], ["N", 2], ["P", 2], ["S", 16], ["T", 92], ["W", 18], ["V", 14], ["Y", 2], ["[", 36], ["]", 54], ["n", 6]]], [".", [["!", 36], [" ", 88376], ["\"", 12990], ["'", 1624], [")", 132], ["(", 2], ["*", 27], ["-", 436], [",", 236], [".", 4176], ["0", 16], ["2", 8], ["4", 6], ["7", 2], ["6", 2], ["9", 14], [";", 20], [":", 166], ["?", 38], ["A", 46], ["C", 12], ["B", 12], ["E", 20], ["D", 4], ["G", 22], ["F", 12], ["I", 138], ["H", 38], ["K", 2], ["J", 2], ["M", 34], ["L", 10], ["O", 10], ["N", 20], ["Q", 2], ["P", 8], ["S", 38], ["R", 2], ["U", 4], ["T", 108], ["W", 42], ["V", 6], ["Y", 20], ["[", 26], ["]", 10], ["_", 8], ["a", 4], ["`", 6], ["c", 32], ["b", 2], ["e", 36], ["i", 16], ["m", 26], ["o", 2], ["s", 4], ["u", 24], ["t", 40], ["x", 10], ["z", 2]]], ["2", [[" ", 128], ["\"", 2], ["'", 6], [")", 12], ["*", 6], ["-", 20], [",", 132], ["/", 2], [".", 82], ["1", 127], ["0", 229], ["3", 90], ["2", 98], ["5", 112], ["4", 96], ["7", 78], ["6", 88], ["9", 51], ["8", 80], [";", 4], [":", 56], ["@", 14], ["]", 70], ["d", 6], ["n", 28], ["t", 20], ["}", 20], ["|", 66]]], ["6", [[" ", 60], ["-", 12], [",", 70], ["/", 2], [".", 30], ["1", 24], ["0", 76], ["3", 14], ["2", 34], ["5", 26], ["4", 19], ["7", 26], ["6", 32], ["9", 21], ["8", 22], [";", 10], [":", 32], ["?", 2], ["@", 10], ["]", 44], ["m", 4], ["t", 68], ["}", 18], ["|", 52]]], [":", [[" ", 3056], ["\"", 2], ["'", 20], [")", 2], ["(", 8], ["*", 2], ["-", 1142], [".", 260], ["1", 118], ["I", 4], ["3", 44], ["2", 90], ["5", 14], ["4", 30], ["7", 14], ["6", 16], ["9", 12], ["8", 12], ["R", 4], ["r", 4]]], [">", [[" ", 88], ["#", 2], ["\"", 4], ["$", 2], ["-", 2], [",", 2], [":", 2], ["<", 24], ["A", 2], ["@", 2], ["C", 2], ["F", 8], ["I", 10], ["M", 2], ["L", 2], ["T", 14], ["W", 4], ["_", 2], ["^", 2], ["a", 6], ["c", 8], ["f", 10], ["i", 12], ["h", 6], ["m", 8], ["o", 8], ["p", 2], ["s", 8], ["t", 8], ["w", 6], ["v", 2], ["{", 2]]], ["B", [[" ", 16], ["'", 2], [",", 4], [".", 12], ["A", 44], ["C", 38], ["B", 6], ["E", 200], ["I", 62], ["K", 702], ["M", 6], ["L", 69], ["O", 247], ["S", 8], ["R", 26], ["U", 52], ["Y", 62], ["a", 2602], ["e", 3594], ["i", 1068], ["h", 100], ["j", 2], ["l", 520], ["o", 2392], ["r", 974], ["u", 8016], ["w", 2], ["y", 828]]], ["F", [["A", 194], [" ", 300], ["E", 36], ["F", 18], ["I", 98], ["j", 2], ["l", 372], ["O", 166], [",", 2], [">", 2], ["i", 958], ["r", 5046], ["U", 14], ["o", 3334], ["a", 2482], ["e", 430], ["R", 78], [".", 10], ["u", 184], ["L", 20], ["T", 30]]], ["J", [["A", 18], ["a", 1388], ["E", 82], ["d", 2], ["'", 2], ["I", 2], ["-", 2], ["o", 2830], [".", 132], ["i", 314], ["s", 2], ["U", 30], ["O", 54], ["e", 1904], ["u", 1679]]], ["N", [["!", 6], [" ", 451], ["\"", 8], ["'", 28], ["-", 4], [",", 48], [".", 96], [";", 2], [":", 8], ["?", 2], ["A", 108], ["C", 124], ["B", 48], ["E", 247], ["D", 328], ["G", 216], ["F", 2], ["I", 92], ["H", 2], ["K", 22], ["J", 2], ["L", 4], ["O", 186], ["N", 40], ["S", 124], ["R", 13], ["U", 20], ["T", 298], ["V", 13], ["Y", 14], ["a", 3392], ["e", 2202], ["i", 1384], ["o", 4850], ["u", 72]]], ["R", [["!", 2], [" ", 2352], ["\"", 4], ["'", 20], ["*", 6], ["-", 4], [",", 78], [".", 718], [":", 2], ["?", 2], ["A", 178], ["C", 20], ["B", 12], ["E", 320], ["D", 100], ["G", 68], ["F", 4], ["I", 219], ["K", 70], ["M", 30], ["L", 42], ["O", 189], ["N", 84], ["Q", 2], ["P", 18], ["S", 90], ["R", 70], ["U", 34], ["T", 272], ["W", 16], ["V", 14], ["Y", 93], ["a", 374], ["e", 1237], ["i", 323], ["h", 52], ["o", 2304], ["u", 2014], ["t", 2], ["y", 20]]], ["V", [["A", 47], ["a", 1798], ["B", 2], ["E", 276], ["'", 2], [" ", 18], ["I", 587], ["-", 2], [",", 12], ["O", 26], ["l", 30], ["i", 510], ["r", 4], ["U", 2], ["o", 228], ["y", 26], ["e", 465], ["R", 22], [".", 162], ["u", 4], ["Y", 6]]], ["Z", [["a", 32], ["\"", 2], ["E", 16], ["d", 4], ["I", 2], ["h", 68], [",", 4], ["o", 22], ["n", 30], ["i", 26], ["u", 10], ["O", 4], ["e", 124]]], ["b", [["!", 40], [" ", 1058], ["'", 68], [")", 4], ["*", 4], ["-", 98], [",", 414], [".", 244], [";", 24], [":", 6], ["?", 36], [">", 4], ["a", 13924], ["c", 22], ["b", 1618], ["e", 60104], ["d", 78], ["g", 4], ["f", 16], ["i", 6998], ["h", 46], ["j", 990], ["m", 324], ["l", 22082], ["o", 19980], ["n", 44], ["s", 2992], ["r", 13256], ["u", 20366], ["t", 1570], ["w", 30], ["v", 88], ["y", 13906]]], ["f", [["!", 178], [" ", 90391], ["\"", 8], ["'", 72], [")", 16], ["*", 2], ["-", 876], [",", 2770], [".", 1846], [";", 240], [":", 102], ["?", 144], ["G", 2], ["I", 2], ["a", 20108], ["c", 14], ["b", 38], ["e", 23990], ["d", 2], ["g", 10], ["f", 10886], ["i", 22788], ["h", 6], ["k", 12], ["j", 16], ["m", 12], ["l", 7808], ["o", 46468], ["n", 16], ["p", 2], ["s", 464], ["r", 21052], ["u", 10540], ["t", 10202], ["w", 62], ["v", 2], ["y", 396], ["x", 2]]], ["j", [["a", 724], ["!", 2], ["e", 4002], ["'", 2], ["i", 118], ["o", 3558], [".", 4], ["u", 4654]]], ["n", [["!", 1140], [" ", 164227], ["\"", 84], ["'", 9982], [")", 108], ["*", 6], ["-", 2184], [",", 19804], [".", 11592], [";", 1670], [":", 478], ["?", 1178], [">", 50], ["J", 4], ["]", 18], ["_", 4], ["a", 17316], ["c", 30762], ["b", 372], ["e", 74881], ["d", 155134], ["g", 116276], ["f", 3720], ["i", 23814], ["h", 996], ["k", 7828], ["j", 896], ["m", 518], ["l", 7014], ["o", 57750], ["n", 7832], ["q", 950], ["p", 298], ["s", 29725], ["r", 506], ["u", 4864], ["t", 72732], ["w", 558], ["v", 3580], ["y", 9090], ["x", 498], ["z", 154], ["}", 4]]], ["r", [["!", 1102], [" ", 128583], ["\"", 78], ["'", 2438], [")", 108], ["*", 14], ["-", 2050], [",", 18518], [".", 12898], [";", 1346], [":", 332], ["?", 1112], [">", 28], ["A", 2], ["@", 14], ["_", 6], ["a", 45838], ["c", 7992], ["b", 2520], ["e", 175663], ["d", 22490], ["g", 6450], ["f", 3064], ["i", 57977], ["h", 1474], ["k", 6764], ["j", 14], ["m", 12284], ["l", 8396], ["o", 61720], ["n", 15999], ["q", 220], ["p", 3358], ["s", 36440], ["r", 17042], ["u", 12282], ["t", 29678], ["w", 1522], ["v", 4906], ["y", 24200], ["x", 6], ["z", 128]]], ["v", [["!", 34], [" ", 1478], ["'", 360], [")", 2], ["-", 58], [",", 566], [".", 316], [";", 12], [":", 4], ["?", 30], ["_", 2], ["a", 8210], ["e", 85189], ["g", 2], ["i", 17242], ["k", 2], ["m", 16], ["l", 218], ["o", 6350], ["n", 508], ["s", 216], ["r", 658], ["u", 248], ["t", 4], ["v", 22], ["y", 640]]], ["z", [["!", 8], [" ", 344], ["\"", 2], ["'", 14], [")", 4], ["-", 40], [",", 172], [".", 178], [";", 8], [":", 2], ["?", 22], ["a", 592], ["b", 6], ["e", 3788], ["d", 18], ["g", 8], ["i", 920], ["h", 122], ["k", 6], ["m", 104], ["l", 356], ["o", 1122], ["n", 2], ["s", 6], ["r", 2], ["u", 156], ["v", 14], ["y", 202], ["z", 622]]], ["~", [[")", 6]]], ["\t", [["\t", 174], [" ", 136], ["D", 2]]], ["!", [["!", 12], [" ", 7580], ["\"", 6860], ["'", 556], [")", 42], ["*", 12], ["-", 108], [",", 4], [".", 170], ["I", 2], ["v", 6], ["[", 2], ["_", 6]]], ["%", [[" ", 8]]], [")", [[" ", 830], ["-", 56], [",", 506], [".", 102], ["5", 2], ["[", 2], [":", 16], [";", 58], ["?", 2]]], ["-", [["!", 8], [" ", 5090], ["\"", 550], ["'", 68], ["(", 2], ["+", 4], ["-", 6008], [",", 22], [".", 2], ["1", 12], ["2", 16], ["5", 24], ["4", 2], ["7", 4], ["6", 6], ["8", 4], ["?", 22], ["A", 170], ["C", 118], ["B", 200], ["E", 88], ["D", 116], ["G", 118], ["F", 62], ["I", 194], ["H", 172], ["K", 16], ["J", 90], ["M", 196], ["L", 74], ["O", 38], ["N", 44], ["Q", 6], ["P", 134], ["S", 172], ["R", 44], ["T", 160], ["W", 74], ["V", 28], ["Y", 22], ["Z", 4], ["a", 1098], ["`", 16], ["c", 1092], ["b", 1366], ["e", 476], ["d", 942], ["g", 452], ["f", 1014], ["i", 408], ["h", 966], ["k", 224], ["j", 64], ["m", 808], ["l", 922], ["o", 468], ["n", 446], ["q", 40], ["p", 884], ["s", 1836], ["r", 562], ["u", 122], ["t", 1410], ["w", 812], ["v", 66], ["y", 140], ["z", 12]]], ["1", [[" ", 112], ["'", 4], [")", 16], ["*", 10], ["-", 14], [",", 112], ["/", 2], [".", 68], ["1", 246], ["0", 318], ["3", 164], ["2", 280], ["5", 224], ["4", 214], ["7", 188], ["6", 204], ["9", 126], ["8", 572], [";", 4], [":", 40], ["@", 4], ["O", 2], ["]", 48], ["s", 52], ["t", 22], ["}", 18], ["|", 88]]], ["5", [[" ", 98], ["\"", 2], ["'", 2], ["-", 4], [",", 88], [".", 46], ["1", 22], ["0", 140], ["3", 28], ["2", 32], ["5", 32], ["4", 22], ["7", 32], ["6", 16], ["9", 14], ["8", 18], [";", 10], [":", 48], ["?", 2], ["@", 14], ["]", 64], ["t", 82], ["}", 18], ["|", 44]]], ["9", [[" ", 74], ["'", 2], [")", 4], ["-", 6], [",", 40], [".", 22], ["1", 26], ["0", 43], ["3", 48], ["2", 14], ["5", 18], ["4", 15], ["7", 34], ["6", 20], ["9", 18], ["8", 10], [";", 12], [":", 38], ["@", 6], ["]", 44], ["t", 32], ["}", 18], ["|", 46]]], ["=", [["E", 2], ["=", 8], ["T", 14], [" ", 12]]], ["A", [[" ", 4464], ["\"", 2], ["'", 12], ["-", 18], [",", 4], [".", 24], ["A", 6], ["C", 108], ["B", 58], ["E", 22], ["D", 77], ["G", 50], ["F", 18], ["I", 98], ["H", 10], ["K", 18], ["M", 89], ["L", 220], ["N", 497], ["Q", 2], ["P", 2116], ["S", 220], ["R", 481], ["U", 44], ["T", 292], ["W", 18], ["V", 78], ["Y", 66], ["X", 6], ["Z", 2], ["a", 2], ["c", 208], ["b", 496], ["e", 2], ["d", 372], ["g", 352], ["f", 1144], ["i", 66], ["h", 554], ["k", 64], ["j", 4], ["m", 1358], ["l", 3042], ["o", 140], ["n", 12454], ["q", 4], ["p", 230], ["s", 2898], ["r", 1643], ["u", 754], ["t", 3060], ["w", 48], ["v", 60], ["y", 30], ["x", 2], ["z", 30]]], ["E", [["!", 20], [" ", 1253], ["\"", 2], ["'", 18], ["*", 2], ["-", 14], [",", 30], [".", 100], [":", 8], ["?", 4], ["A", 176], ["C", 152], ["B", 30], ["E", 92], ["D", 166], ["G", 32], ["F", 34], ["I", 60], ["H", 6], ["K", 8], ["M", 80], ["L", 142], ["O", 14], ["N", 490], ["Q", 6], ["P", 124], ["S", 360], ["R", 730], ["U", 18], ["T", 184], ["W", 90], ["V", 102], ["Y", 34], ["X", 64], ["a", 450], ["c", 46], ["b", 14], ["d", 90], ["g", 62], ["f", 20], ["i", 64], ["h", 58], ["k", 6], ["m", 1500], ["l", 226], ["o", 4], ["n", 1398], ["q", 20], ["p", 138], ["s", 148], ["r", 118], ["u", 358], ["t", 98], ["v", 1326], ["y", 70], ["x", 298], ["z", 2], ["}", 2]]], ["I", [["!", 34], [" ", 40514], ["\"", 4], ["'", 2748], ["-", 52], [",", 526], [".", 576], [";", 42], [":", 6], ["?", 88], ["A", 76], ["C", 182], ["B", 47], ["E", 82], ["D", 78], ["G", 140], ["F", 78], ["I", 1054], ["K", 12], ["M", 76], ["L", 150], ["O", 134], ["N", 704], ["Q", 4], ["P", 18], ["S", 277], ["R", 122], ["U", 2], ["T", 378], ["V", 298], ["X", 156], ["Z", 8], ["_", 46], ["a", 2], ["c", 146], ["b", 10], ["d", 16], ["g", 48], ["f", 1944], ["m", 196], ["l", 216], ["o", 30], ["n", 5298], ["p", 28], ["s", 930], ["r", 122], ["t", 8656], ["v", 100], ["x", 2], ["z", 2]]], ["M", [["!", 4], [" ", 68], ["\"", 2], ["'", 12], ["-", 4], [",", 12], ["/", 8], [".", 1208], [":", 2], ["A", 346], ["C", 28], ["B", 34], ["E", 243], ["D", 6], ["G", 12], ["I", 130], ["M", 24], ["L", 2], ["O", 117], ["N", 16], ["P", 50], ["S", 26], ["R", 8], ["U", 28], ["W", 6], ["Y", 24], ["a", 8688], ["c", 378], ["e", 1576], ["f", 2], ["i", 2158], ["o", 4266], ["s", 2], ["r", 2578], ["u", 518], ["y", 1492]]], ["Q", [["U", 48], ["C", 2], ["u", 494], [".", 4]]], ["U", [["!", 2], [" ", 106], ["'", 6], [",", 2], [".", 10], ["A", 16], ["C", 48], ["B", 24], ["E", 54], ["D", 44], ["G", 12], ["F", 6], ["I", 12], ["K", 2], ["M", 60], ["L", 68], ["N", 116], ["P", 26], ["S", 130], ["R", 182], ["U", 2], ["T", 156], ["V", 2], ["Z", 6], ["c", 2], ["g", 12], ["h", 34], ["k", 4], ["m", 16], ["l", 30], ["n", 859], ["p", 360], ["s", 32], ["r", 68], ["t", 50], ["v", 16]]], ["Y", [["!", 4], [" ", 260], ["\"", 2], ["'", 4], ["-", 68], [",", 10], [".", 22], [";", 2], ["A", 2], ["B", 2], ["E", 26], ["L", 4], ["O", 112], ["S", 26], ["R", 4], ["T", 2], ["a", 122], ["c", 2], ["e", 1328], ["i", 10], ["o", 3878], ["s", 10], ["u", 26], ["v", 8]]], ["]", [["!", 2], [" ", 322], ["J", 2], ["-", 2], [",", 44], [".", 14], [";", 24], [">", 2]]], ["a", [["!", 310], [" ", 65518], ["\"", 24], ["'", 716], [")", 20], ["*", 1], ["-", 724], [",", 2766], [".", 1558], ["1", 2], [";", 152], [":", 30], ["?", 144], [">", 2], ["S", 2], ["_", 6], ["a", 122], ["`", 6], ["c", 35608], ["b", 19042], ["e", 752], ["d", 56288], ["g", 18377], ["f", 7666], ["i", 47228], ["h", 1140], ["k", 13604], ["j", 480], ["m", 24754], ["l", 68865], ["o", 314], ["n", 216874], ["q", 102], ["p", 18548], ["s", 105951], ["r", 97182], ["u", 12725], ["t", 135418], ["w", 10880], ["v", 25744], ["y", 29029], ["x", 666], ["z", 1906], ["}", 2]]], ["e", [["!", 3010], [" ", 487805], ["\"", 166], ["'", 4249], [")", 336], ["*", 10], ["-", 4298], [",", 40540], [".", 24278], [";", 3704], [":", 890], ["?", 2866], [">", 78], ["B", 2], ["I", 2], ["S", 2], ["[", 6], ["]", 18], ["_", 42], ["a", 79086], ["`", 2], ["c", 27918], ["b", 1598], ["e", 45884], ["d", 142368], ["g", 8394], ["f", 13916], ["i", 18692], ["h", 2842], ["k", 1590], ["j", 380], ["m", 31126], ["l", 54118], ["o", 4898], ["n", 129345], ["q", 1706], ["p", 17078], ["s", 100714], ["r", 205409], ["u", 2658], ["t", 41944], ["w", 11422], ["v", 24561], ["y", 23800], ["x", 14052], ["z", 560], ["}", 4]]], ["i", [["!", 50], [" ", 1222], ["\"", 2], ["'", 184], [")", 4], ["*", 2], ["-", 384], [",", 450], [".", 254], [";", 14], [":", 6], ["?", 14], ["@", 2], ["G", 2], ["Y", 2], ["]", 2], ["_", 8], ["a", 10716], ["c", 45183], ["b", 6986], ["e", 33445], ["d", 40015], ["g", 26388], ["f", 17002], ["i", 24], ["h", 72], ["k", 7006], ["j", 18], ["m", 40198], ["l", 43870], ["o", 33038], ["n", 232657], ["q", 394], ["p", 5884], ["s", 107323], ["r", 32008], ["u", 1844], ["t", 102683], ["w", 38], ["v", 16166], ["y", 2], ["x", 2074], ["z", 2892]]], ["m", [["!", 462], [" ", 35852], ["\"", 24], ["'", 294], [")", 58], ["-", 610], [",", 7564], [".", 6290], ["1", 2], [";", 758], [":", 298], ["?", 470], [">", 22], ["]", 2], ["a", 45673], ["c", 96], ["b", 7420], ["e", 83120], ["d", 36], ["g", 4], ["f", 768], ["i", 24836], ["h", 10], ["k", 22], ["m", 5770], ["l", 522], ["o", 32940], ["n", 1218], ["p", 15046], ["s", 8640], ["r", 202], ["u", 10278], ["t", 200], ["w", 24], ["y", 17480]]], ["q", [["a", 2], [" ", 2], ["u", 12073], [",", 2], ["'", 2]]], ["u", [["!", 428], [" ", 17752], ["\"", 16], ["'", 1182], [")", 6], ["-", 186], [",", 1924], [".", 1140], [";", 124], [":", 18], ["?", 428], ["S", 12], ["T", 2], ["_", 2], ["a", 6632], ["c", 12934], ["b", 5730], ["e", 10797], ["d", 6988], ["g", 18286], ["f", 2056], ["i", 9148], ["h", 30], ["k", 496], ["j", 84], ["m", 8440], ["l", 37068], ["o", 708], ["n", 43221], ["q", 64], ["p", 17232], ["s", 44836], ["r", 48568], ["u", 18], ["t", 49162], ["w", 10], ["v", 428], ["y", 210], ["x", 498], ["z", 722]]], ["y", [["!", 1032], [" ", 118640], ["\"", 62], ["'", 1440], [")", 154], ["-", 2010], [",", 18388], [".", 10860], [";", 1366], [":", 466], ["?", 946], [">", 26], ["]", 2], ["_", 4], ["a", 2624], ["`", 2], ["c", 242], ["b", 640], ["e", 11272], ["d", 206], ["g", 138], ["f", 198], ["i", 4422], ["h", 84], ["k", 76], ["m", 714], ["l", 818], ["o", 28106], ["n", 212], ["p", 410], ["s", 8723], ["r", 738], ["u", 58], ["t", 3028], ["w", 464], ["v", 110], ["x", 14], ["z", 40]]], ["}", [[" ", 6], [",", 2]]]]]

================================================
FILE: systemd/freq.service
================================================
[Unit]
Description=Freq service
After=network.target

[Service]
WorkingDirectory=/usr/bin
ExecStart=/usr/local/share/freq/freq_server.py -s 0 10004 /usr/local/share/freq/freqtable2018.freq
PIDFile=/var/run/freq.pid
Restart=always

[Install]
WantedBy=multi-user.target


================================================
FILE: upstart/sample_upstart.config
================================================
description     "Freq Server"
start on filesystem or runlevel [2345]
stop on runlevel [!2345]

respawn

script
      export HOME="/usr/bin"
      echo $$ > /var/run/freqserver.pid
      exec python3 /opt/freqserver/freq_server.py -ip <IP> <port> /opt/freqserver/freqtable2018.freq
end script

pre-start script
      echo "[`date`] Freq Server starting" >> /var/log/messages
end script

pre-stop script
      echo "[`date`] Freq Server stopping" >> /var/log/messages
end script
Download .txt
gitextract_2pt2i8lf/

├── CONTRIBUTE.md
├── LICENSE
├── README.md
├── freq.py
├── freq_server.py
├── freq_sort.py
├── freqtable2018.freq
├── systemd/
│   └── freq.service
└── upstart/
    └── sample_upstart.config
Download .txt
SYMBOL INDEX (28 symbols across 3 files)

FILE: freq.py
  class node (line 13) | class node():
    method __init__ (line 30) | def __init__(self,parent):
    method __getitem__ (line 36) | def __getitem__(self,key):
    method __setitem__ (line 42) | def __setitem__(self,key,value):
    method count (line 50) | def count(self):
  class FreqCounter (line 56) | class FreqCounter(dict):
    method __init__ (line 91) | def __init__(self, *args,**kwargs):
    method __getitem__ (line 99) | def __getitem__(self,key):
    method __iter__ (line 102) | def __iter__(self):
    method __len__ (line 105) | def __len__(self):
    method toJSON (line 108) | def toJSON(self):
    method fromJSON (line 117) | def fromJSON(self,jsondata):
    method tally_str (line 143) | def tally_str(self,line,weight=1):
    method probability (line 159) | def probability(self,line):
    method _probability (line 227) | def _probability(self,twoletters):
    method save (line 245) | def save(self,filename):
    method load (line 255) | def load(self,filename):
    method count (line 265) | def count(self):
    method printtable (line 268) | def printtable(self):

FILE: freq_server.py
  class freqapi (line 36) | class freqapi(BaseHTTPServer.BaseHTTPRequestHandler):
    method do_GET (line 37) | def do_GET(self):
    method log_message (line 134) | def log_message(self, format, *args):
  class ThreadedFreqServer (line 137) | class ThreadedFreqServer(SocketServer.ThreadingMixIn, BaseHTTPServer.HTT...
    method __init__ (line 138) | def __init__(self, *args,**kwargs):
    method safe_print (line 151) | def safe_print(self,*args,**kwargs):
    method save_freqtable (line 158) | def save_freqtable(self,save_path,save_interval):

FILE: freq_sort.py
  function score (line 24) | def score(line):
  function signal_handler (line 47) | def signal_handler(sig, frame):
Condensed preview — 9 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (75K chars).
[
  {
    "path": "CONTRIBUTE.md",
    "chars": 1474,
    "preview": "# Contribute.md\n\n## Team members\n\n* [Mark Baggett](https://github.com/MarkBaggett)\n\n## Adding new features\n\nIf you're in"
  },
  {
    "path": "LICENSE",
    "chars": 1069,
    "preview": "MIT License\n\nCopyright (c) 2018 Mark Baggett\n\nPermission is hereby granted, free of charge, to any person obtaining a co"
  },
  {
    "path": "README.md",
    "chars": 2979,
    "preview": "# freq\n\nThis is a repository for `freq.py` and `freq_server.py`.\n\n## Background:\n\nWhile sitting in SANS SEC511 I listene"
  },
  {
    "path": "freq.py",
    "chars": 12323,
    "preview": "#!/usr/bin/env python3\nfrom __future__ import division\n\nimport sys\nimport string\nimport re\nimport weakref\nimport json\n\nf"
  },
  {
    "path": "freq_server.py",
    "chars": 11820,
    "preview": "#!/usr/bin/env python3\n#freq_server.py by Mark Baggett\n#Twitter @MarkBaggett\n#github http://github.com/MarkBaggett/\n#Thi"
  },
  {
    "path": "freq_sort.py",
    "chars": 3682,
    "preview": "#!/usr/bin/env python3\nfrom freq import *\nimport argparse\nimport signal\nimport sys\n\nparser = argparse.ArgumentParser()\np"
  },
  {
    "path": "freqtable2018.freq",
    "chars": 33839,
    "preview": "[true, \"\\n\\t~`!@#$%^&*()_+-\", [[\"\\f\", [[\"f\", 2]]], [\" \", [[\" \", 312527], [\"$\", 12], [\"(\", 1520], [\",\", 6], [\"0\", 2], [\"4"
  },
  {
    "path": "systemd/freq.service",
    "chars": 268,
    "preview": "[Unit]\nDescription=Freq service\nAfter=network.target\n\n[Service]\nWorkingDirectory=/usr/bin\nExecStart=/usr/local/share/fre"
  },
  {
    "path": "upstart/sample_upstart.config",
    "chars": 477,
    "preview": "description     \"Freq Server\"\nstart on filesystem or runlevel [2345]\nstop on runlevel [!2345]\n\nrespawn\n\nscript\n      exp"
  }
]

About this extraction

This page contains the full source code of the MarkBaggett/freq GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 9 files (66.3 KB), approximately 25.7k tokens, and a symbol index with 28 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.

Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.

Copied to clipboard!