#  Copyright (c) 1997-2018
#  Ewgenij Gawrilow, Michael Joswig (Technische Universitaet Berlin, Germany)
#  http://www.polymake.org
#
#  This program is free software; you can redistribute it and/or modify it
#  under the terms of the GNU General Public License as published by the
#  Free Software Foundation; either version 2, or (at your option) any
#  later version: http://www.gnu.org/licenses/gpl.txt.
#
#  This program is distributed in the hope that it will be useful,
#  but WITHOUT ANY WARRANTY; without even the implied warranty of
#  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
#  GNU General Public License for more details.
#-------------------------------------------------------------------------------

INCLUDE
  global_categories.rules

# @topic core/top
# You are in the interactive mode of polymake.
#
# You may type in any valid perl expression, it will be evaluated as soon as it is complete,
# that is, terminated with a semicolon.  Longer constructs (if-else-clauses, loops,
# long lists, etc.) can be continued on several lines; continuation lines are marked
# with ordinal numbers in parentheses.  Typing Control-C amidst such a block quits without
# execution.
#
# The input prompt shows the name of the currently active application.
# To change to another application, type:   application "NAME";
#
# All command line editing features of GNU Readline library are available,
# including history search, key bindings defined in ~/.inputrc, etc.
# Please refer to the documentation shipped with Readline for details.
#
# TAB completion tries to guess the context and to present suitable alternatives.
# When used in complex expressions, it can, however, make some invalid suggestions;
# please be careful.
#
# Variables can be introduced like this: 'declare $x;' or 'declare $x=1;'
# or simply via first assignment: '$x=1;'
# 'my' variables should be used with caution, as their life scope is limited
# to the current expression.  
#
# Here are some help topics for the beginners:
# help 'functions/Basic';
#    An overview of the most useful commands.
# help 'help';
#    How to navigate in the help system.
# help 'functions/Interactive';
#    Other commands available in the interactive shell mode.
# help '/';
#    Describe the current application and list all subtopics in the help tree.
# help 'citation';
#    How to cite polymake correctly.
#
# Press F1 to get context-sensitive help to the keyword near cursor.


# @topic category core/functions/Basic
# These functions are part of the core user interface.
# They are available in both scripting and interactive modes.

# @topic category core/functions/Interactive
# These functions are available in the interactive shell only.

# @topic core/functions/application
# @category Basic
#
# Called in void context sets the current application to the one given by //name//
# (loading its rules if necessary) or by an object of type ''Polymake::Core::Application''.
#
# Called in non-void context loads the application if necessary and returns the ''Application''
# object representing it, but does not make it the current application.
#
# Finally, if called without arguments, just returns the current ''Application'' object,
# which is also always available in the variable ''$application''.
# Say ''print $application->name;'' to see its name.
#
# @param String name | Application app_obj
# @example To set the graph application as current, type this:
# > application "graph";


# @topic core/functions/include
# @category Basic
# Read additional rule files, interpret them in the context of the current application.
# The rule files may reside in the application's standard subdirectory ''rules'' or in any
# of the known extensions.
# @param String rulefile : one or more filenames.


# @topic core/functions/load
# @category Basic
# Read the object data file created earlier by [[save]] and return the object.
#
# If the data file is in old plain ASCII format and does not contain a type declaration,
# the default type of the current application is assumed.
# @param String filename
# @return Object


# @topic core/functions/save (Object;$)
# @category Basic
#
# Store the given //object// in a data file.
#
# If //filename// is specified, create the file with this name (or overwrite an existing one)
# and store the object there.  The filename may have the suffix ".gz" in which case
# the contents are compressed with gzip.
#
# If //filename// is omitted, write the object back to the file it was loaded from.
# If the object is not yet associated with a file (e.g. it was created with a ''new'' constructor)
# a new file is created, with the name consisting of the object name and the application-specific suffix.
# If the object name is empty, you will be asked for it.  In non-interactive mode an artificial name
# of the form ''ObjectType_NNN'' will be generated, where ''NNN'' is an ordinal number.
#
# The save operation is only performed if the boolean attribute ''object->changed'' has the true value.
# @param Object object
# @param String filename


# @topic core/functions/load_data
# @category Basic
#
# Read the data file created earlier by [[save_data]] and return the data object stored there.
# @param String filename
# @return any data object; in list context additionally returns the optional description read from in the file.


# @topic core/functions/save_data ($$;$)
# @category Basic
#
# Create an XML data file and store the given data object there, together with an optional description.
# Only objects of complex types declared as ''property_type'' can be stored this way.
# The filename may have the suffix ".gz" in which case the contents are compressed with gzip.
# @param any data object
# @param String filename
# @param String description


# @topic core/functions/save_schema ($$)
# @category Basic
#
# Generate a Relax-NG schema describing one or more `big' or `small' object types in the form as they
# would appear when saved with commands [[save]] resp. [[save_data]].
# @param any object ... an object whose type should be described, may be specified as a type expression as well.
#        Multiple objects or type expressions may be passed as a list.
#        Mixing `big' and `small' object types is not allowed.
# @param String filename  a suffix .rng is appended automatically if no suffix is given
#
# @example [notest] create a schema for a Polytope<Rational> and save it in the file poly_rat.rng
# > save_schema(typeof Polytope<Rational>, "poly_rat");
# the same, specifying a type via a representative object
# > $c=cube(3);
# > save_schema($c, "poly_rat");
#
# @example [notest] create a schema for graphs and save it in the file graphs.rng
# > save_schema(typeof props::Graph<Undirected>, typeof props::Graph<Directed>, "graph");


# @topic core/functions/prefer
# @category Basic
#
# Make the production rules, user functions, or methods (further called //items//)
# matching the given //label_expression// be preferred over competing rules (functions, methods).
#
# The new preference list is also saved in your personal file ''~/.polymake/prefer.pl''
# It remains active in all forthcoming sessions until you revoke it with another
# //prefer// or [[reset_preference]] command.
#
# For the sake of symmetry you can call this function by its alias name //set_preference//.
#
# @param String label_expression : may have one of the following forms:
#
#   "top_label" or "top_label.sublevels"
#      selects all items whose labels start with the given path,
#      e.g. "javaview" or "cdd.convex_hull".
#
#   "*.sublevels top_label_1, top_label_2 ... "
#      selects all items with matching labels and establishes a ranking between them.
#      For example, "*.convex_hull cdd,lrs" makes rules labeled with cdd.convex_hull
#      and lrs.convex_hull preferred over all other convex hull rules,
#      with cdd rules having the topmost rank.


# @topic core/functions/prefer_now
# @category Basic
#
# The same as [[prefer]], but does not store the changes persistently.  The lifespan of the
# new preference order lasts up to the end of the current user cycle
# (that is, end of user script or interpretation of the current input line in interactive mode).
# @param String label_expression


# @topic core/functions/reset_preference
# @category Basic
#
# Reset the matching preference lists to the default state.
# If there are matching preference lists declared in the rule files, they come back into effect;
# otherwise the preferences are neutralized, i.e. all competing items (rules/methods/etc.) get equal ranks.
#
# This change is permanent, the former settings are wiped out from your preference file
# ''~/.polymake/prefer.pl''
#
# @param String label_expression : "label" or "label.sublevels" or "*.sublevels" or "*" or "all"
#
#   Two last forms match all preference lists visible from the current application.


# @topic core/functions/show_preferences
# @category Interactive
# Prints the currently active preference lists.
# Please be aware that the output only reflects the persistent preferences;
# temporary changes made by [[prefer_now]] are not displayed even if they might be in effect
# when this command is executed.


# @topic core/functions/set_custom
# @category Basic
#
# Set the custom variable, array, hash, or hash element to a new value and store it perpetually
# in your personal settings file ''~/.polymake/customize.pl''
#
# @param any assignment : following expressions are allowed:
#  ''$var=value;''
#  ''@array=(values);''
#  ''%hash=( key => value, ... );''
#  ''$hash{key}=value;''


# @topic core/functions/reset_custom
# @category Basic
#
# Reset the custom variable, array, hash, or hash element to the default value(s) as defined in the rule base
# and store it perpetually in your personal settings file ''~/.polymake/customize.pl''
# @param any Variable : following expressions are allowed:
#  ''$var;''
#  ''@array;''
#  ''%hash;''
#  ''$hash{key};''


# @topic core/functions/disable_rules ($)
# @category Basic
#
# Temporarily disable production rules matching given pattern.
# Rules are re-enabled after the complete execution of the current script
# or input expression in interactive mode.
#
# This ban applies to all production rules defined in the current application.
# If you need more fine-granular control on object type level or even for individual
# objects, please use user methods of the same name:
#  ''$obj->type->disable_rules(...)''
#  ''$obj->disable_rules(...)''
#
# @param String pattern : either a label (see [[prefer]]) or a rule header.
#        The rule header must exactly match the definition in the rulefile,
#        up to white spaces around property names.
#        If the given pattern matches headers of several rules, or the given label
#        is associated with several rules, they all will be disabled regardless
#        their precoditions, weights, or other attributes.


# @topic core/functions/help
# @category Interactive
#
# Display the help information or comments associated with a given item.
#
# If a single word without path separators ''/'' is given, it is looked for
# in the entire help hierarchy.
# If there are more than 5 matches, the corresponding help commands with full paths
# are generated and placed into the shell history buffer, so you can choose the themes
# of interest using the standard history navigation (arrow up/down keys).
#
# You may use TAB completion to navigate thru the category tree.
# Start with '/' to see the description of the current application.
# Descend via 'functions', 'objects', 'types', or 'preferences' to the topics
# of interest.  If you stop in an intermediate category level, both the comment
# specific to this category and the list of subtopics will be displayed.
# @param String item : "word" | "category/subcategory/.../item"


# @topic core/functions/apropos
# @category Interactive
#
# List all help topics matching the given pattern.
# @param String pattern (part of) an item name to be looked for, may contain regular expression elements.
# @example To get all help topics that include the word "simplicial", type this:
# > apropos("simplicial");
# | /objects/Visual::SimplicialComplexLattice
# | /objects/Visual::SimplicialComplex
# | /objects/GeometricSimplicialComplex
# | /objects/SimplicialComplex
# | /functions/Producing a new simplicial complex from others/simplicial_product
# In order to view a help entry, you can use the help() funtion.
# > help("/functions/Producing a new simplicial complex from others/simplicial_product");


# @topic core/functions/history
# @category Interactive
#
# Let you choose and edit the commands typed earlier in the interactive shell.
# The complete history list is written in a temporary file, which is opened with a text editor.
# Until you store your favorite editor in the custom variable ''$history_editor'',
# the values of environment variables ''VISUAL'' and ''EDITOR'' are consulted.


# @topic core/functions/replay_history
# @category Interactive
#
# Bring the interactive shell in the replay mode.  In this mode you can revisit, edit and execute the commands stored in the history.
# Simply press RETURN if you want to execute the command as is; alternatively you can edit it or jump over it using the
# usual history navigation keystrokes.  The replay mode is switched off when the last history line has been executed or navigated over.
# You can leave it prematurely by entering Control-C as well.
# The replay mode is visually signaled by the history line number in square brackets displayed in the shell prompt.


# @topic core/functions/load_commands
# @category Interactive
#
# Read the lines from the given file, filter out comments and empty lines, append them to the
# history list of the interactive shell, and enter the replay mode (see [[replay_history]]) starting
# at the first inserted line.
# @param String filename


# @topic core/functions/script ($@)
# @category Basic
#
# Run the polymake script, passing the arguments in ''@ARGV''.
# The scripts specified without path are first looked for in the current application's ''scripts'' subdirectory,
# then in the user's script directories (custom array ''@lookup_scripts''), and finally in the
# top-level ''scripts'' subdirectory.
# @param String filename : script code
# @param Any Arguments : arbitrary many arguments passed to the script


# @topic core/functions/found_extension ($)
# @category Interactive
#
# Found a new extension at the given //path//.  The specified directory may be absent, then it will be created;
# if it already exists, it should be empty or at least not contain any files and subdirectories with
# names having special meaning for polymake, like polymake.ext or apps.
#
# A new bundled extension can be created by entering a name of the form ''"bundled:NAME"'' instead of the //path// argument.
# This action is only allowed in developer's mode, i.e. when polymake is invoked from the git copy of the source tree.
#
# Please note that this command only creates an empty skeleton of top-level directories.  In order to be able to populate the
# extension with new rules, scripts, clients, etc. you must execute the command [[extend_application]] for every application
# you want to to extend.
#
# @param String path : top-level directory of the extension, or ''"bundled:NAME"'' for a bundled extension.


# @topic core/functions/extend_application ($;$)
# @category Interactive
#
# Create a directory tree for rules, scripts, clients, etc. in the given //extension// pertaining to the given application.
# If the extension is managed by a VCS, the added subtree will be registered for check-in.
# @param String extension : URI or top-level directory of a registered extension.
# @param String app_name : name of the application to be extended; by default, the current application is assumed.


# @topic core/functions/found_application ($$)
# @category Interactive
#
# Create a directory tree for a new application, rooted at the given extension.
#
# The new application is neither loaded at this moment (just because it's quite empty at its birth),
# nor it is inserted in the ''@start_applications'' list.
# Both must be done manually at the appropriate time.
#
# A new core application can be created by entering the string "core" instead of the //extension// argument.
# This action is only allowed in developer's mode, i.e. when polymake is invoked from the git copy of the source tree.
#
# @param String extension : URI or top-level directory of a registered extension, or special string ''"core"''.
# @param String app_name : name of the new application


# @topic core/functions/import_extension ($;@)
# @category Interactive
#
# Load the extension installed in the given directory. The //path// is also included in the custom list ''@extensions''
# for future sessions.
#
# If the extension requires some third-party libraries, which can't automatically be found by its configuration script,
# you can pass options to it as trailing arguments of this command.  Usually, the error message displayed after the failed
# attempt to configure the extension will tell you which options you should specify.
# To obtain the complete list of configuration options supported by the extension, you can pass a single option ''"--help"'',
# examine its script configure.pl, or just make use of the TAB completion when typing this command.
#
# @param String path top-level directory of the extension.
# @param List options options passed to the extension script configure.pl


# @topic core/functions/reconfigure_extension ($;@)
# @category Interactive
#
# Re-run the configuration script of the extension, passing some options to it.  The extension must already have been imported;
# if not, please use the command [[import_extension]], which supports passing configuration options too.
#
# @param String path : URI or top-level directory of the extension.
# @param List options options passed to the extension script configure.pl


# @topic core/functions/obliterate_extension ($)
# @category Interactive
# Removes all references to the specified extension from your settings, so that the //following// polymake session won't load anything from it.
# Beware that in the //current// session all the rules and functions defined in the extension remain active!
# Therefore it's strongly recommended to quit the current session right after executing this command.
# The files comprising the extension itself are not deleted, however, so that you can re-import the extension later once you need it again.
# @param String path URI or top-level directory of the extension.


# @topic core/functions/reconfigure ($)
# @category Interactive
# Repeats the auto-configuration routines stored in the given rulefile, possibly asking for program paths, options, etc.
# If the configuration succeeds, and the rulefile hasn't been configured until now, it will be loaded, as well as all other known rulefiles
# depending on it.
# @param String rulename name of the rulefile with optional prefix and suffix: "[APPNAME::]rulefile[@URI]".
#                        APPNAME is to be specified when the rulefile does not belong to the current application;
#                        URI specifies an extension the rulefile comes from.


# @topic core/functions/unconfigure ($)
# @category Interactive
# Forgets the configuration made for the given rulefile and disables it for the //following// polymake sessions.
# @param String rulename name of the rulefile with optional prefix and suffix: "[APPNAME::]rulefile[@URI]".
#                        APPNAME is to be specified when the rulefile does not belong to the current application;
#                        URI specifies an extension the rulefile comes from.


# @topic core/functions/show_unconfigured
# @category Interactive
# Prints the names of all rulefiles visible from the current application,
# which could not be loaded because of configuration failures
# (in most cases, due to some lacking external software).
# You may use the command [[reconfigure]] to load some of them if you have installed
# the required software and/or cleared out other configuration troubles.


# @topic core/functions/export_configured ($;%)
# @category Interactive
# Saves all custom variables having non-defalt values in a file.
# This includes all results of automatical or manual rule configuration.
# The obtained file can be edited manually, if needed, and used as a global configuration
# source (see also [[wiki:howto/configure|How to configure polymake for a group of users]]).
# @param String filename where to store the settings
# @option Bool merge_with_global if TRUE, the settings inherited from *your* global configuration
#                                will be included in the resulting file as well.
# @option Bool only_configured if TRUE, configuration status of unconfigured or only partially
#                              configured rulefiles is not exported
# @option String suppress regular expression matching fully qualified names of custom variables
#                         that should not be exported


# @topic core/functions/show_credits (;$)
# @category Interactive
# Prints the short descriptions of third-party software packages available from within polymake,
# including the copyright holders and project site URLs.
# Please note that here you will see only packages for which the polymake interface has been
# successfully configured; try [[show_unconfigured]] command to see what's you currently missing.
# @param Bool brief : list the product names only, no descriptive text


# @topic core/functions/show_extensions
# @category Interactive
# Prints the list of active extensions including their URIs and credit notes.

# @topic core/functions/save_history (;$)
# @category Interactive
# Saves the commands entered in the current and recent sessions in the file.
# Polymake does it automatically at the end of each interactive session; this command is provided
# for the case that you might want to store the commands in an alternative file and/or don't want to
# exit the session.
# Commands can be loaded using [[load_commands]].
# @param String filename : defaults to ~/.polymake/history


# @topic core/citation
#
# If you are using polymake, please, cite:
#
# @incollection{polymake,
#  author =       {Ewgenij Gawrilow and Michael Joswig},
#  title =        {polymake: a Framework for Analyzing Convex Polytopes},
#  pages =        {43-74},
#  editor =       {Gil Kalai and G\"unter M. Ziegler},
#  booktitle =    {Polytopes --- Combinatorics and Computation},
#  publisher =    {Birkh\"auser},
#  year =         {2000}
# }


# @topic core/objects/Core::Object
# This is the common base class of all `big' objects in polymake.
# It does not have any properties, but provides several useful methods
# for scripting and interactive use.


# @topic core/objects/Core::Object/methods/get_schedule ($@)
# Compose an optimal chain of production rules providing all requested properties.
# The returned RuleChain object can be applied to the original object as well as to any other object
# with the same initial set of properties.  If no feasible rule chain exists, `undef' is returned.
#
# To watch the rule scheduler at work, e.g. to see announcements about tried preconditions,
# you may temporarily increase the verbosity levels ''$Verbose::rules'' and ''$Verbose::scheduler''.
#
# @param String request : name of a property with optional alternatives or a property path in dotted notation.
#                         Several requests may be listed.
# @return Core::RuleChain
# @example generate an optimal rule chain for a parameterized family of polytopes:
# > @p=map { new Polytope("POINTS" => my_matrix($_) ) } 1..10;
# > $s=$p[0]->get_schedule("FACETS", "TRIANGULATION.FACETS");
# > $s->apply($_) for @p;


# @topic core/objects/Core::Object/methods/dont_save ()
# Clears the `changed' flag in the object, so that it won't be saved in the XML file it stems from.
# This method is primarily designed for unit tests, but could be also useful in interactive mode
# if you want to revert all recent changes and reload the object from the data file.


# @topic core/objects/Core::Object/methods/disable_rules ($)
# Temporarily disable production rules matching given pattern for an object.
# Rules are re-enabled after the complete execution of the current script
# or input expression in interactive mode.
#
# Works much like the user function [[disable_rules]] but only affecting
# the given object.
# @param String pattern : either a label (see [[prefer]]) or a rule header.
#        The rule header must exactly match the definition in the rulefile,
#        up to white spaces around property names.
#        If the given pattern matches headers of several rules, or the given label
#        is associated with several rules, they all will be disabled regardless
#        their precoditions, weights, or other attributes.


# @topic core/objects/Core::Object/methods/apply_rule ($)
# Executes the specified production rule.
# If the object does not possess enough initial properties to provide all required
# rule sources, or any of its preconditions are not satisfied, an exception is raised.
# @param String pattern : either a label (see [[prefer]]) or a rule header.
#        The rule header must exactly match the definition in the rulefile,
#        up to white spaces around property names.
#        If the given pattern matches headers of several rules, or the given label
#        is associated with several rules, the rule chain with smallest total weight
#        (including the rules supplying the source properties) is chosen.


# @topic core/objects/Core::Object/methods/set_as_default ()
# Makes the multiple subobject instance the default one.
# Physically this means moving it at the 0-th position in the instance list.
#
# The instance can be selected by give() or PROPERTY_NAME access method.
# @example by current position:
#  > $p->TRIANGULATION->[$i]->set_as_default;
# @example by subobject name:
#  > $p->TRIANGULATION("placing")->set_as_default;
# @example by checking for a specific property:
#  > $p->TRIANGULATION(sub { defined($_->lookup("WEIGHTS")) })->set_as_default;
# @example by analyzing all instances and picking the best one:
#  > for (@{$p->TRIANGULATION}) { assign_min($min_facets, $_->N_FACETS) and $t=$_ } $t->set_as_default;

# @topic core/objects/Core::Object/methods/set_as_default_now ()
# Temporarily make the multiple subobject instance the default one.
# The change is automatically reverted at the end of the current user cycle.
# Usage as [[set_as_default]].

# @topic core/objects/Core::Object/methods/list_names ()
# Returns the list of names of multiple subobject instances.
# This method can be applied to any instance of a multiple subobject.
# For a normal (non-multiple) subobject or a top-level object just returns its name.
# @example List all names of linear programs associated with a polytope:
# > print join(" ", $p->LP->list_names);


# @topic core/objects/Core::Object/methods/list_properties (;$)
# Returns the list of names of all properties kept in the object.
# @param Bool deep : recursively descend in all subobjects
#                    and list their properties in dotted notation.

# @topic core/objects/Core::Object/methods/properties (;maxdepth=>0)
# Returns or prints a string with all properties (names and values) and subobjects.
# @option Int maxdepth descend into subobjects up to given depth, default 0

# @topic core/objects/Core::Object/methods/remove (String+)
# Remove the property //prop// from the object.
# The property must be //mutable//, //multiple//,
# or unambiguously reconstructible from the remaining properties.
# @param String prop : property name or a path to a property in a subobject in dotted notation,
#                      Several properties may be removed at once.

# @topic core/objects/Core::Object/methods/remove (Object+)
# Remove the multiple subobject instance(s) from the object.
# @param Object subobj : multiple subobject instance.
#                        Several subobjects may be removed at once.


# @topic core/objects/Core::RuleChain
# A rule chain to compute properties of a `big' object.
# It can be obtained by calling the method [[get_schedule]].

# @topic core/objects/Core::RuleChain/methods/list
# Return the headers of rules in the chain.

# @topic core/objects/Core::RuleChain/methods/list_new_properties
# Return the names of properties computed by the rule chain.

# @topic core/objects/Core::RuleChain/methods/apply (Object)
# Apply the rule chain to an object.
# @param Object o

# @topic core/functions/exit
# @category Interactive
# use this command only if you want to restart polymake.


# @topic core/install_mongodb
# To install the perl-MongoDB module please either try via your package-manager:
#   libmongodb-perl on ubuntu
#
# Or use the below guide to install it via cpan in a custom directory:
#
# Insert the following lines into your .bashrc (or .profile or ).
# (Note that you should adapt the path in the first line to your needs.)
#
#   MYCPANSTUFF=/some/path/for/cpan/installation
#   export PERL_LOCAL_LIB_ROOT="$PERL_LOCAL_LIB_ROOT:$MYCPANSTUFF";
#   export PERL5LIB="$MYCPANSTUFF/lib/perl5:$PERL5LIB";
#   export PATH="$MYCPANSTUFF/bin:$PATH";
#
# Now start a new shell and run
#   env PERL_MB_OPT="--install_base $MYCPANSTUFF" \
#     PERL_MM_OPT="INSTALL_BASE=$MYCPANSTUFF" \
#     cpan
#
# Inside cpan (answer "yes" everywhere) and enter the following lines:
#   o conf build_dir "/tmp/cpanstuff"
#   o conf commit
#   install Params::Validate
#
# Exit and restart cpan with the above command.
#   install MongoDB
#
# (Installing Params::Validate separately first avoids some dependency problems.)




# @topic core/polymake
#       MM                            ,,
#      MM M                         `7MM                                    `7MM
#     M M  M                          MM                                      MM
#    M M    M    `7MMpdMAo.  ,pW"Wq.  MM `7M'   `MF`7MMpMMMb.pMMMb.  ,6"Yb.   MM  ,MP .gP"Ya
#   ,  M   M M     MM   `Wb 6W'   `Wb MM   VA   ,V   MM    MM    MM 8)   MM   MM ;Y  ,M'   Yb
#  MM  M  M   M    MM    M8 8M     M8 MM    VA ,V    MM    MM    MM  ,pm9MM   MM;Mm  8M""""""
# M M MMN      M   MM   ,AP YA.   ,A9 MM     VVV     MM    MM    MM 8M   MM   MM `Mb YM.    ,
#M   MM M       M  MMbmmd'   `Ybmd9'.JMML.   ,V    .JMML  JMML  JMML`Moo9^Yo.JMML. YA `Mbmmd'
#M  M M  M M  MM   MM                       ,V
# MMM M   M M    .JMML.                  OOb"
#  M     MM
#   MM MM
#    MM
#



# Local Variables:
# mode: perl
# cperl-indent-level:3
# indent-tabs-mode:nil
# End:
